Page MenuHomePhabricator

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/CMake/FindEigen.cmake b/CMake/FindEigen.cmake
deleted file mode 100644
index 2ec430484c..0000000000
--- a/CMake/FindEigen.cmake
+++ /dev/null
@@ -1,16 +0,0 @@
-find_path(Eigen_INCLUDE_DIR
- NAMES Eigen/Eigen
- PATHS ${Eigen_DIR} ${MITK_EXTERNAL_PROJECT_PREFIX} ${CMAKE_PREFIX_PATH}
- PATH_SUFFIXES include include/eigen3
-)
-
-if (NOT TARGET Eigen)
- add_library(Eigen INTERFACE IMPORTED GLOBAL)
- set_property(TARGET Eigen APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Eigen_INCLUDE_DIR})
-endif()
-
-find_package_handle_standard_args(Eigen
- FOUND_VAR Eigen_FOUND
- REQUIRED_VARS Eigen_INCLUDE_DIR
-)
-
diff --git a/CMake/GetPrerequisites.cmake b/CMake/GetPrerequisites.cmake
index be5e8548cb..da4e1cad29 100644
--- a/CMake/GetPrerequisites.cmake
+++ b/CMake/GetPrerequisites.cmake
@@ -1,1095 +1,1095 @@
# Special version of GetPrerequisites that implements a caching mechanism.
# Thank you to Daniel Maleike for the contribution
message("Using MITK version of GetPrerequisites.cmake")
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#.rst:
# GetPrerequisites
# ----------------
#
# Functions to analyze and list executable file prerequisites.
#
# This module provides functions to list the .dll, .dylib or .so files
# that an executable or shared library file depends on. (Its
# prerequisites.)
#
# It uses various tools to obtain the list of required shared library
# files:
#
# ::
#
# dumpbin (Windows)
# ldd (Linux/Unix)
# otool (macOS)
#
# The following functions are provided by this module:
#
# ::
#
# get_prerequisites
# list_prerequisites
# list_prerequisites_by_glob
# gp_append_unique
# is_file_executable
# gp_item_default_embedded_path
# (projects can override with gp_item_default_embedded_path_override)
# gp_resolve_item
# (projects can override with gp_resolve_item_override)
# gp_resolved_file_type
# (projects can override with gp_resolved_file_type_override)
# gp_file_type
#
# Requires CMake 2.6 or greater because it uses function, break, return
# and PARENT_SCOPE.
#
# ::
#
# GET_PREREQUISITES(<target> <prerequisites_var> <exclude_system> <recurse>
# <exepath> <dirs> [<rpaths>])
#
# Get the list of shared library files required by <target>. The list
# in the variable named <prerequisites_var> should be empty on first
# entry to this function. On exit, <prerequisites_var> will contain the
# list of required shared library files.
#
# <target> is the full path to an executable file. <prerequisites_var>
# is the name of a CMake variable to contain the results.
# <exclude_system> must be 0 or 1 indicating whether to include or
# exclude "system" prerequisites. If <recurse> is set to 1 all
# prerequisites will be found recursively, if set to 0 only direct
# prerequisites are listed. <exepath> is the path to the top level
-# executable used for @executable_path replacment on the Mac. <dirs> is
+# executable used for @executable_path replacement on the Mac. <dirs> is
# a list of paths where libraries might be found: these paths are
# searched first when a target without any path info is given. Then
# standard system locations are also searched: PATH, Framework
# locations, /usr/lib...
#
# ::
#
# LIST_PREREQUISITES(<target> [<recurse> [<exclude_system> [<verbose>]]])
#
# Print a message listing the prerequisites of <target>.
#
# <target> is the name of a shared library or executable target or the
# full path to a shared library or executable file. If <recurse> is set
# to 1 all prerequisites will be found recursively, if set to 0 only
# direct prerequisites are listed. <exclude_system> must be 0 or 1
# indicating whether to include or exclude "system" prerequisites. With
# <verbose> set to 0 only the full path names of the prerequisites are
-# printed, set to 1 extra informatin will be displayed.
+# printed, set to 1 extra information will be displayed.
#
# ::
#
# LIST_PREREQUISITES_BY_GLOB(<glob_arg> <glob_exp>)
#
# Print the prerequisites of shared library and executable files
# matching a globbing pattern. <glob_arg> is GLOB or GLOB_RECURSE and
# <glob_exp> is a globbing expression used with "file(GLOB" or
# "file(GLOB_RECURSE" to retrieve a list of matching files. If a
# matching file is executable, its prerequisites are listed.
#
# Any additional (optional) arguments provided are passed along as the
# optional arguments to the list_prerequisites calls.
#
# ::
#
# GP_APPEND_UNIQUE(<list_var> <value>)
#
# Append <value> to the list variable <list_var> only if the value is
# not already in the list.
#
# ::
#
# IS_FILE_EXECUTABLE(<file> <result_var>)
#
# Return 1 in <result_var> if <file> is a binary executable, 0
# otherwise.
#
# ::
#
# GP_ITEM_DEFAULT_EMBEDDED_PATH(<item> <default_embedded_path_var>)
#
# Return the path that others should refer to the item by when the item
# is embedded inside a bundle.
#
# Override on a per-project basis by providing a project-specific
# gp_item_default_embedded_path_override function.
#
# ::
#
# GP_RESOLVE_ITEM(<context> <item> <exepath> <dirs> <resolved_item_var>
# [<rpaths>])
#
# Resolve an item into an existing full path file.
#
# Override on a per-project basis by providing a project-specific
# gp_resolve_item_override function.
#
# ::
#
# GP_RESOLVED_FILE_TYPE(<original_file> <file> <exepath> <dirs> <type_var>
# [<rpaths>])
#
# Return the type of <file> with respect to <original_file>. String
# describing type of prerequisite is returned in variable named
# <type_var>.
#
# Use <exepath> and <dirs> if necessary to resolve non-absolute <file>
# values -- but only for non-embedded items.
#
# Possible types are:
#
# ::
#
# system
# local
# embedded
# other
#
# Override on a per-project basis by providing a project-specific
# gp_resolved_file_type_override function.
#
# ::
#
# GP_FILE_TYPE(<original_file> <file> <type_var>)
#
# Return the type of <file> with respect to <original_file>. String
# describing type of prerequisite is returned in variable named
# <type_var>.
#
# Possible types are:
#
# ::
#
# system
# local
# embedded
# other
function(gp_append_unique list_var value)
set(contains 0)
foreach(item ${${list_var}})
if(item STREQUAL "${value}")
set(contains 1)
break()
endif()
endforeach()
if(NOT contains)
set(${list_var} ${${list_var}} "${value}" PARENT_SCOPE)
endif()
endfunction()
function(is_file_executable file result_var)
#
# A file is not executable until proven otherwise:
#
set(${result_var} 0 PARENT_SCOPE)
get_filename_component(file_full "${file}" ABSOLUTE)
string(TOLOWER "${file_full}" file_full_lower)
# If file name ends in .exe on Windows, *assume* executable:
#
if(WIN32 AND NOT UNIX)
if("${file_full_lower}" MATCHES "\\.exe$")
set(${result_var} 1 PARENT_SCOPE)
return()
endif()
# A clause could be added here that uses output or return value of dumpbin
# to determine ${result_var}. In 99%+? practical cases, the exe name
# match will be sufficient...
#
endif()
# Use the information returned from the Unix shell command "file" to
# determine if ${file_full} should be considered an executable file...
#
# If the file command's output contains "executable" and does *not* contain
# "text" then it is likely an executable suitable for prerequisite analysis
# via the get_prerequisites macro.
#
if(UNIX)
if(NOT file_cmd)
find_program(file_cmd "file")
mark_as_advanced(file_cmd)
endif()
if(file_cmd)
execute_process(COMMAND "${file_cmd}" "${file_full}"
RESULT_VARIABLE file_rv
OUTPUT_VARIABLE file_ov
ERROR_VARIABLE file_ev
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT file_rv STREQUAL "0")
message(FATAL_ERROR "${file_cmd} failed: ${file_rv}\n${file_ev}")
endif()
# Replace the name of the file in the output with a placeholder token
# (the string " _file_full_ ") so that just in case the path name of
# the file contains the word "text" or "executable" we are not fooled
# into thinking "the wrong thing" because the file name matches the
# other 'file' command output we are looking for...
#
string(REPLACE "${file_full}" " _file_full_ " file_ov "${file_ov}")
string(TOLOWER "${file_ov}" file_ov)
#message(STATUS "file_ov='${file_ov}'")
if("${file_ov}" MATCHES "executable")
#message(STATUS "executable!")
if("${file_ov}" MATCHES "text")
#message(STATUS "but text, so *not* a binary executable!")
else()
set(${result_var} 1 PARENT_SCOPE)
return()
endif()
endif()
# Also detect position independent executables on Linux,
# where "file" gives "shared object ... (uses shared libraries)"
if("${file_ov}" MATCHES "shared object.*\(uses shared libs\)")
set(${result_var} 1 PARENT_SCOPE)
return()
endif()
# "file" version 5.22 does not print "(used shared libraries)"
# but uses "interpreter"
if("${file_ov}" MATCHES "shared object.*interpreter")
set(${result_var} 1 PARENT_SCOPE)
return()
endif()
else()
message(STATUS "warning: No 'file' command, skipping execute_process...")
endif()
endif()
endfunction()
function(gp_item_default_embedded_path item default_embedded_path_var)
# On Windows and Linux, "embed" prerequisites in the same directory
# as the executable by default:
#
set(path "@executable_path")
set(overridden 0)
# On the Mac, relative to the executable depending on the type
# of the thing we are embedding:
#
if(APPLE)
#
# The assumption here is that all executables in the bundle will be
# in same-level-directories inside the bundle. The parent directory
# of an executable inside the bundle should be MacOS or a sibling of
# MacOS and all embedded paths returned from here will begin with
# "@executable_path/../" and will work from all executables in all
# such same-level-directories inside the bundle.
#
# By default, embed things right next to the main bundle executable:
#
set(path "@executable_path/../../Contents/MacOS")
# Embed .dylibs right next to the main bundle executable:
#
if(item MATCHES "\\.dylib$")
set(path "@executable_path/../MacOS")
set(overridden 1)
endif()
# Embed frameworks in the embedded "Frameworks" directory (sibling of MacOS):
#
if(NOT overridden)
if(item MATCHES "[^/]+\\.framework/")
set(path "@executable_path/../Frameworks")
set(overridden 1)
endif()
endif()
endif()
# Provide a hook so that projects can override the default embedded location
# of any given library by whatever logic they choose:
#
if(COMMAND gp_item_default_embedded_path_override)
gp_item_default_embedded_path_override("${item}" path)
endif()
set(${default_embedded_path_var} "${path}" PARENT_SCOPE)
endfunction()
function(gp_resolve_item context item exepath dirs resolved_item_var)
set(resolved 0)
set(resolved_item "${item}")
if(ARGC GREATER 5)
set(rpaths "${ARGV5}")
else()
set(rpaths "")
endif()
# Is it already resolved?
#
if(IS_ABSOLUTE "${resolved_item}" AND EXISTS "${resolved_item}")
set(resolved 1)
endif()
if(NOT resolved)
if(item MATCHES "^@executable_path")
#
# @executable_path references are assumed relative to exepath
#
string(REPLACE "@executable_path" "${exepath}" ri "${item}")
get_filename_component(ri "${ri}" ABSOLUTE)
if(EXISTS "${ri}")
#message(STATUS "info: embedded item exists (${ri})")
set(resolved 1)
set(resolved_item "${ri}")
else()
message(STATUS "warning: embedded item does not exist '${ri}'")
endif()
endif()
endif()
if(NOT resolved)
if(item MATCHES "^@loader_path")
#
# @loader_path references are assumed relative to the
# PATH of the given "context" (presumably another library)
#
get_filename_component(contextpath "${context}" PATH)
string(REPLACE "@loader_path" "${contextpath}" ri "${item}")
get_filename_component(ri "${ri}" ABSOLUTE)
if(EXISTS "${ri}")
#message(STATUS "info: embedded item exists (${ri})")
set(resolved 1)
set(resolved_item "${ri}")
else()
message(STATUS "warning: embedded item does not exist '${ri}'")
endif()
endif()
endif()
if(NOT resolved)
if(item MATCHES "^@rpath")
#
# @rpath references are relative to the paths built into the binaries with -rpath
# We handle this case like we do for other Unixes
#
string(REPLACE "@rpath/" "" norpath_item "${item}")
set(ri "ri-NOTFOUND")
find_file(ri "${norpath_item}" ${exepath} ${dirs} ${rpaths} NO_DEFAULT_PATH)
if(ri)
#message(STATUS "info: 'find_file' in exepath/dirs/rpaths (${ri})")
set(resolved 1)
set(resolved_item "${ri}")
set(ri "ri-NOTFOUND")
endif()
endif()
endif()
if(NOT resolved)
set(ri "ri-NOTFOUND")
find_file(ri "${item}" ${exepath} ${dirs} NO_DEFAULT_PATH)
find_file(ri "${item}" ${exepath} ${dirs} /usr/lib)
get_filename_component(basename_item "${item}" NAME)
find_file(ri "${basename_item}" PATHS ${exepath} ${dirs} NO_DEFAULT_PATH)
find_file(ri "${basename_item}" PATHS /usr/lib)
if(ri)
#message(STATUS "info: 'find_file' in exepath/dirs (${ri})")
set(resolved 1)
set(resolved_item "${ri}")
set(ri "ri-NOTFOUND")
endif()
endif()
if(NOT resolved)
if(item MATCHES "[^/]+\\.framework/")
set(fw "fw-NOTFOUND")
find_file(fw "${item}"
"~/Library/Frameworks"
"/Library/Frameworks"
"/System/Library/Frameworks"
)
if(fw)
#message(STATUS "info: 'find_file' found framework (${fw})")
set(resolved 1)
set(resolved_item "${fw}")
set(fw "fw-NOTFOUND")
endif()
endif()
endif()
# Using find_program on Windows will find dll files that are in the PATH.
# (Converting simple file names into full path names if found.)
#
if(WIN32 AND NOT UNIX)
if(NOT resolved)
set(ri "ri-NOTFOUND")
find_program(ri "${item}" PATHS ${exepath} ${dirs} NO_DEFAULT_PATH)
find_program(ri "${item}" PATHS ${exepath} ${dirs})
if(ri)
#message(STATUS "info: 'find_program' in exepath/dirs (${ri})")
set(resolved 1)
set(resolved_item "${ri}")
set(ri "ri-NOTFOUND")
endif()
endif()
endif()
# Provide a hook so that projects can override item resolution
# by whatever logic they choose:
#
if(COMMAND gp_resolve_item_override)
gp_resolve_item_override("${context}" "${item}" "${exepath}" "${dirs}" resolved_item resolved)
endif()
if(NOT resolved)
message(STATUS "
warning: cannot resolve item '${item}'
possible problems:
need more directories?
need to use InstallRequiredSystemLibraries?
run in install tree instead of build tree?
")
# message(STATUS "
#******************************************************************************
#warning: cannot resolve item '${item}'
#
# possible problems:
# need more directories?
# need to use InstallRequiredSystemLibraries?
# run in install tree instead of build tree?
#
# context='${context}'
# item='${item}'
# exepath='${exepath}'
# dirs='${dirs}'
# resolved_item_var='${resolved_item_var}'
#******************************************************************************
#")
endif()
set(${resolved_item_var} "${resolved_item}" PARENT_SCOPE)
endfunction()
function(gp_resolved_file_type original_file file exepath dirs type_var)
if(ARGC GREATER 5)
set(rpaths "${ARGV5}")
else()
set(rpaths "")
endif()
#message(STATUS "**")
if(NOT IS_ABSOLUTE "${original_file}")
message(STATUS "warning: gp_resolved_file_type expects absolute full path for first arg original_file")
endif()
if(IS_ABSOLUTE "${original_file}")
get_filename_component(original_file "${original_file}" ABSOLUTE) # canonicalize path
endif()
set(is_embedded 0)
set(is_local 0)
set(is_system 0)
set(resolved_file "${file}")
if("${file}" MATCHES "^@(executable|loader)_path")
set(is_embedded 1)
endif()
if(NOT is_embedded)
if(NOT IS_ABSOLUTE "${file}")
gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file "${rpaths}")
endif()
if(IS_ABSOLUTE "${resolved_file}")
get_filename_component(resolved_file "${resolved_file}" ABSOLUTE) # canonicalize path
endif()
string(TOLOWER "${original_file}" original_lower)
string(TOLOWER "${resolved_file}" lower)
if(UNIX)
if(resolved_file MATCHES "^(/lib/|/lib32/|/libx32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/libx32/|/usr/lib64/|/usr/X11R6/|/usr/bin/)")
set(is_system 1)
endif()
endif()
if(APPLE)
if(resolved_file MATCHES "^(/System/Library/|/usr/lib/)")
set(is_system 1)
endif()
endif()
if(WIN32)
string(TOLOWER "$ENV{SystemRoot}" sysroot)
file(TO_CMAKE_PATH "${sysroot}" sysroot)
string(TOLOWER "$ENV{windir}" windir)
file(TO_CMAKE_PATH "${windir}" windir)
if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*(msvc|api-ms-win-)[^/]+dll)")
set(is_system 1)
endif()
if(UNIX)
# if cygwin, we can get the properly formed windows paths from cygpath
find_program(CYGPATH_EXECUTABLE cygpath)
if(CYGPATH_EXECUTABLE)
execute_process(COMMAND ${CYGPATH_EXECUTABLE} -W
RESULT_VARIABLE env_rv
OUTPUT_VARIABLE env_windir
ERROR_VARIABLE env_ev
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT env_rv STREQUAL "0")
message(FATAL_ERROR "${CYGPATH_EXECUTABLE} -W failed: ${env_rv}\n${env_ev}")
endif()
execute_process(COMMAND ${CYGPATH_EXECUTABLE} -S
RESULT_VARIABLE env_rv
OUTPUT_VARIABLE env_sysdir
ERROR_VARIABLE env_ev
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT env_rv STREQUAL "0")
message(FATAL_ERROR "${CYGPATH_EXECUTABLE} -S failed: ${env_rv}\n${env_ev}")
endif()
string(TOLOWER "${env_windir}" windir)
string(TOLOWER "${env_sysdir}" sysroot)
if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*(msvc|api-ms-win-)[^/]+dll)")
set(is_system 1)
endif()
endif()
endif()
endif()
if(NOT is_system)
get_filename_component(original_path "${original_lower}" PATH)
get_filename_component(path "${lower}" PATH)
if(original_path STREQUAL path)
set(is_local 1)
else()
string(LENGTH "${original_path}/" original_length)
string(LENGTH "${lower}" path_length)
if(${path_length} GREATER ${original_length})
string(SUBSTRING "${lower}" 0 ${original_length} path)
if("${original_path}/" STREQUAL path)
set(is_embedded 1)
endif()
endif()
endif()
endif()
endif()
# Return type string based on computed booleans:
#
set(type "other")
if(is_system)
set(type "system")
elseif(is_embedded)
set(type "embedded")
elseif(is_local)
set(type "local")
endif()
#message(STATUS "gp_resolved_file_type: '${file}' '${resolved_file}'")
#message(STATUS " type: '${type}'")
if(NOT is_embedded)
if(NOT IS_ABSOLUTE "${resolved_file}")
if(lower MATCHES "^(msvc|api-ms-win-)[^/]+dll" AND is_system)
message(STATUS "info: non-absolute msvc file '${file}' returning type '${type}'")
else()
message(STATUS "warning: gp_resolved_file_type non-absolute file '${file}' returning type '${type}' -- possibly incorrect")
endif()
endif()
endif()
# Provide a hook so that projects can override the decision on whether a
# library belongs to the system or not by whatever logic they choose:
#
if(COMMAND gp_resolved_file_type_override)
gp_resolved_file_type_override("${resolved_file}" type)
endif()
set(${type_var} "${type}" PARENT_SCOPE)
#message(STATUS "**")
endfunction()
function(gp_file_type original_file file type_var)
if(NOT IS_ABSOLUTE "${original_file}")
message(STATUS "warning: gp_file_type expects absolute full path for first arg original_file")
endif()
get_filename_component(exepath "${original_file}" PATH)
set(type "")
gp_resolved_file_type("${original_file}" "${file}" "${exepath}" "" type)
set(${type_var} "${type}" PARENT_SCOPE)
endfunction()
function(get_prerequisites_clear_cache)
set_property(GLOBAL PROPERTY prerequisites_cachevariables "")
endfunction(get_prerequisites_clear_cache)
function(get_prerequisites target prerequisites_var exclude_system recurse exepath dirs)
# See if we know the answer from our cache. If so, we are done early
# incorporate all parameters into cache key
string(SHA1 param_hash "${exclude_system}_${recurse}_${exepath}_${dirs}")
set(prerequisites_cache_var_name "prerequisites_cache_${target}_${param_hash}")
string(TOLOWER "${prerequisites_cache_var_name}" prerequisites_cache_var_name)
string(REGEX REPLACE "[:-\\/.]" "_" prerequisites_cache_var_name "${prerequisites_cache_var_name}")
get_property(cache_value GLOBAL PROPERTY ${prerequisites_cache_var_name} SET)
if (cache_value)
get_property(cache_value GLOBAL PROPERTY ${prerequisites_cache_var_name}) # if defined, then get value
if (cache_value) # already something non-empty -> append
set(${prerequisites_var} "${${prerequisites_var}};${cache_value}" PARENT_SCOPE)
endif()
return()
endif()
set(verbose 0)
set(eol_char "E")
if(ARGC GREATER 6)
set(rpaths "${ARGV6}")
else()
set(rpaths "")
endif()
if(NOT IS_ABSOLUTE "${target}")
message("warning: target '${target}' is not absolute...")
endif()
if(NOT EXISTS "${target}")
message("warning: target '${target}' does not exist...")
set(${prerequisites_var} "" PARENT_SCOPE)
return()
endif()
set(gp_cmd_paths ${gp_cmd_paths}
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/../../VC/bin"
"$ENV{VS140COMNTOOLS}/../../VC/bin"
"C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/../../VC/bin"
"$ENV{VS120COMNTOOLS}/../../VC/bin"
"C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/../../VC/bin"
"$ENV{VS110COMNTOOLS}/../../VC/bin"
"C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/../../VC/bin"
"$ENV{VS100COMNTOOLS}/../../VC/bin"
"C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/../../VC/bin"
"$ENV{VS90COMNTOOLS}/../../VC/bin"
"C:/Program Files/Microsoft Visual Studio 9.0/VC/bin"
"C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/../../VC/bin"
"$ENV{VS80COMNTOOLS}/../../VC/bin"
"C:/Program Files/Microsoft Visual Studio 8/VC/BIN"
"C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/../../VC7/bin"
"$ENV{VS71COMNTOOLS}/../../VC7/bin"
"C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN"
"C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN"
"/usr/local/bin"
"/usr/bin"
)
# <setup-gp_tool-vars>
#
# Try to choose the right tool by default. Caller can set gp_tool prior to
# calling this function to force using a different tool.
#
if(NOT gp_tool)
set(gp_tool "ldd")
if(APPLE)
set(gp_tool "otool")
endif()
if(WIN32 AND NOT UNIX) # This is how to check for cygwin, har!
find_program(gp_dumpbin "dumpbin" PATHS ${gp_cmd_paths})
if(gp_dumpbin)
set(gp_tool "dumpbin")
else() # Try harder.
set(gp_tool "objdump")
endif()
endif()
endif()
find_program(gp_cmd ${gp_tool} PATHS ${gp_cmd_paths})
if(NOT gp_cmd)
message(STATUS "warning: could not find '${gp_tool}' - cannot analyze prerequisites...")
return()
endif()
set(gp_cmd_maybe_filter) # optional command to pre-filter gp_tool results
if(gp_tool STREQUAL "ldd")
set(gp_cmd_args "")
set(gp_regex "^[\t ]*[^\t ]+ => ([^\t\(]+) .*${eol_char}$")
set(gp_regex_error "not found${eol_char}$")
set(gp_regex_fallback "^[\t ]*([^\t ]+) => ([^\t ]+).*${eol_char}$")
set(gp_regex_cmp_count 1)
elseif(gp_tool STREQUAL "otool")
set(gp_cmd_args "-L")
set(gp_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$")
set(gp_regex_error "")
set(gp_regex_fallback "")
set(gp_regex_cmp_count 3)
elseif(gp_tool STREQUAL "dumpbin")
set(gp_cmd_args "/dependents")
set(gp_regex "^ ([^ ].*[Dd][Ll][Ll])${eol_char}$")
set(gp_regex_error "")
set(gp_regex_fallback "")
set(gp_regex_cmp_count 1)
elseif(gp_tool STREQUAL "objdump")
set(gp_cmd_args "-p")
set(gp_regex "^\t*DLL Name: (.*\\.[Dd][Ll][Ll])${eol_char}$")
set(gp_regex_error "")
set(gp_regex_fallback "")
set(gp_regex_cmp_count 1)
# objdump generates copious output so we create a grep filter to pre-filter results
if(WIN32)
find_program(gp_grep_cmd findstr)
else()
find_program(gp_grep_cmd grep)
endif()
if(gp_grep_cmd)
set(gp_cmd_maybe_filter COMMAND ${gp_grep_cmd} "-a" "^[[:blank:]]*DLL Name: ")
endif()
else()
message(STATUS "warning: gp_tool='${gp_tool}' is an unknown tool...")
message(STATUS "CMake function get_prerequisites needs more code to handle '${gp_tool}'")
message(STATUS "Valid gp_tool values are dumpbin, ldd, objdump and otool.")
return()
endif()
if(gp_tool STREQUAL "dumpbin")
# When running dumpbin, it also needs the "Common7/IDE" directory in the
# PATH. It will already be in the PATH if being run from a Visual Studio
# command prompt. Add it to the PATH here in case we are running from a
# different command prompt.
#
get_filename_component(gp_cmd_dir "${gp_cmd}" PATH)
get_filename_component(gp_cmd_dlls_dir "${gp_cmd_dir}/../../Common7/IDE" ABSOLUTE)
# Use cmake paths as a user may have a PATH element ending with a backslash.
# This will escape the list delimiter and create havoc!
if(EXISTS "${gp_cmd_dlls_dir}")
# only add to the path if it is not already in the path
set(gp_found_cmd_dlls_dir 0)
file(TO_CMAKE_PATH "$ENV{PATH}" env_path)
foreach(gp_env_path_element ${env_path})
if(gp_env_path_element STREQUAL gp_cmd_dlls_dir)
set(gp_found_cmd_dlls_dir 1)
endif()
endforeach()
if(NOT gp_found_cmd_dlls_dir)
file(TO_NATIVE_PATH "${gp_cmd_dlls_dir}" gp_cmd_dlls_dir)
set(ENV{PATH} "$ENV{PATH};${gp_cmd_dlls_dir}")
endif()
endif()
endif()
#
# </setup-gp_tool-vars>
if(gp_tool STREQUAL "ldd")
set(old_ld_env "$ENV{LD_LIBRARY_PATH}")
set(new_ld_env "${exepath}")
foreach(dir ${dirs})
string(APPEND new_ld_env ":${dir}")
endforeach()
set(ENV{LD_LIBRARY_PATH} "${new_ld_env}:$ENV{LD_LIBRARY_PATH}")
endif()
# Track new prerequisites at each new level of recursion. Start with an
# empty list at each level:
#
set(unseen_prereqs)
# Run gp_cmd on the target:
#
execute_process(
COMMAND ${gp_cmd} ${gp_cmd_args} ${target}
${gp_cmd_maybe_filter}
RESULT_VARIABLE gp_rv
OUTPUT_VARIABLE gp_cmd_ov
ERROR_VARIABLE gp_ev
)
if(gp_tool STREQUAL "dumpbin")
# Exclude delay load dependencies under windows (they are listed in dumpbin output after the message below)
string(FIND "${gp_cmd_ov}" "Image has the following delay load dependencies" gp_delayload_pos)
if (${gp_delayload_pos} GREATER -1)
string(SUBSTRING "${gp_cmd_ov}" 0 ${gp_delayload_pos} gp_cmd_ov_no_delayload_deps)
string(SUBSTRING "${gp_cmd_ov}" ${gp_delayload_pos} -1 gp_cmd_ov_delayload_deps)
if (verbose)
message(STATUS "GetPrequisites(${target}) : ignoring the following delay load dependencies :\n ${gp_cmd_ov_delayload_deps}")
endif()
set(gp_cmd_ov ${gp_cmd_ov_no_delayload_deps})
endif()
endif()
if(NOT gp_rv STREQUAL "0")
if(gp_tool STREQUAL "dumpbin")
# dumpbin error messages seem to go to stdout
message(FATAL_ERROR "${gp_cmd} failed: ${gp_rv}\n${gp_ev}\n${gp_cmd_ov}")
else()
message(FATAL_ERROR "${gp_cmd} failed: ${gp_rv}\n${gp_ev}")
endif()
endif()
if(gp_tool STREQUAL "ldd")
set(ENV{LD_LIBRARY_PATH} "${old_ld_env}")
endif()
if(verbose)
message(STATUS "<RawOutput cmd='${gp_cmd} ${gp_cmd_args} ${target}'>")
message(STATUS "gp_cmd_ov='${gp_cmd_ov}'")
message(STATUS "</RawOutput>")
endif()
get_filename_component(target_dir "${target}" PATH)
# Convert to a list of lines:
#
string(REPLACE ";" "\\;" candidates "${gp_cmd_ov}")
string(REPLACE "\n" "${eol_char};" candidates "${candidates}")
# check for install id and remove it from list, since otool -L can include a
# reference to itself
set(gp_install_id)
if(gp_tool STREQUAL "otool")
execute_process(
COMMAND otool -D ${target}
RESULT_VARIABLE otool_rv
OUTPUT_VARIABLE gp_install_id_ov
ERROR_VARIABLE otool_ev
)
if(NOT otool_rv STREQUAL "0")
message(FATAL_ERROR "otool -D failed: ${otool_rv}\n${otool_ev}")
endif()
# second line is install name
string(REGEX REPLACE ".*:\n" "" gp_install_id "${gp_install_id_ov}")
if(gp_install_id)
# trim
string(REGEX MATCH "[^\n ].*[^\n ]" gp_install_id "${gp_install_id}")
#message("INSTALL ID is \"${gp_install_id}\"")
endif()
endif()
# Analyze each line for file names that match the regular expression:
#
list(LENGTH ${prerequisites_var} list_length_before_candidates)
set(targets_added "")
foreach(candidate ${candidates})
if("${candidate}" MATCHES "${gp_regex}")
# Extract information from each candidate:
if(gp_regex_error AND "${candidate}" MATCHES "${gp_regex_error}")
string(REGEX REPLACE "${gp_regex_fallback}" "\\1" raw_item "${candidate}")
else()
string(REGEX REPLACE "${gp_regex}" "\\1" raw_item "${candidate}")
endif()
if(gp_regex_cmp_count GREATER 1)
string(REGEX REPLACE "${gp_regex}" "\\2" raw_compat_version "${candidate}")
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" compat_major_version "${raw_compat_version}")
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" compat_minor_version "${raw_compat_version}")
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" compat_patch_version "${raw_compat_version}")
endif()
if(gp_regex_cmp_count GREATER 2)
string(REGEX REPLACE "${gp_regex}" "\\3" raw_current_version "${candidate}")
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" current_major_version "${raw_current_version}")
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" current_minor_version "${raw_current_version}")
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" current_patch_version "${raw_current_version}")
endif()
# Use the raw_item as the list entries returned by this function. Use the
# gp_resolve_item function to resolve it to an actual full path file if
# necessary.
#
set(item "${raw_item}")
# Add each item unless it is excluded:
#
set(add_item 1)
if(item STREQUAL gp_install_id)
set(add_item 0)
endif()
if(add_item AND ${exclude_system})
set(type "")
gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type "${rpaths}")
if(type STREQUAL "system")
set(add_item 0)
endif()
endif()
if(add_item)
list(LENGTH ${prerequisites_var} list_length_before_append)
gp_append_unique(${prerequisites_var} "${item}")
list(LENGTH ${prerequisites_var} list_length_after_append)
if(${recurse})
# If item was really added, this is the first time we have seen it.
# Add it to unseen_prereqs so that we can recursively add *its*
# prerequisites...
#
# But first: resolve its name to an absolute full path name such
# that the analysis tools can simply accept it as input.
#
if(NOT list_length_before_append EQUAL list_length_after_append)
gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item "${rpaths}")
if(EXISTS "${resolved_item}")
# Recurse only if we could resolve the item.
# Otherwise the prerequisites_var list will be cleared
set(unseen_prereqs ${unseen_prereqs} "${resolved_item}")
endif()
endif()
endif()
endif()
else()
if(verbose)
message(STATUS "ignoring non-matching line: '${candidate}'")
endif()
endif()
endforeach()
list(LENGTH ${prerequisites_var} prerequisites_var_length)
if(prerequisites_var_length GREATER 0)
list(SORT ${prerequisites_var})
endif()
if(${recurse})
set(more_inputs ${unseen_prereqs})
foreach(input ${more_inputs})
get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}" "${rpaths}")
endforeach()
endif()
# Make result visible to caller
set(${prerequisites_var} ${${prerequisites_var}} PARENT_SCOPE)
# See if we added anything
list(LENGTH ${prerequisites_var} list_length_after_candidates)
if(list_length_after_candidates GREATER ${list_length_before_candidates})
# Something has been added to prerequisites. Note this in cache
set(targets_added "${${prerequisites_var}}")
if (list_length_before_candidates GREATER 0)
foreach(i RANGE 1 ${list_length_before_candidates}) # from 1 to old list length, remove first item, i.e. remove all pre-existing items
list(REMOVE_AT targets_added 0) # not the most elegant way of cutting the list start. Simplifications welcome
endforeach()
endif()
endif()
# Update our cache
set_property(GLOBAL PROPERTY ${prerequisites_cache_var_name} "${targets_added}")
get_property(cache_variables GLOBAL PROPERTY prerequisites_cachevariables)
if (cache_variables)
list(APPEND cache_variables ${prerequisites_cache_var_name})
list(LENGTH cache_variables len)
else()
set(cache_variables ${prerequisites_cache_var_name})
endif()
set_property(GLOBAL PROPERTY prerequisites_cachevariables ${cache_variables})
message("Analyzed prerequisites of ${target}")
endfunction()
function(list_prerequisites target)
if(ARGC GREATER 1 AND NOT "${ARGV1}" STREQUAL "")
set(all "${ARGV1}")
else()
set(all 1)
endif()
if(ARGC GREATER 2 AND NOT "${ARGV2}" STREQUAL "")
set(exclude_system "${ARGV2}")
else()
set(exclude_system 0)
endif()
if(ARGC GREATER 3 AND NOT "${ARGV3}" STREQUAL "")
set(verbose "${ARGV3}")
else()
set(verbose 0)
endif()
set(count 0)
set(count_str "")
set(print_count "${verbose}")
set(print_prerequisite_type "${verbose}")
set(print_target "${verbose}")
set(type_str "")
get_filename_component(exepath "${target}" PATH)
set(prereqs "")
get_prerequisites("${target}" prereqs ${exclude_system} ${all} "${exepath}" "")
if(print_target)
message(STATUS "File '${target}' depends on:")
endif()
foreach(d ${prereqs})
math(EXPR count "${count} + 1")
if(print_count)
set(count_str "${count}. ")
endif()
if(print_prerequisite_type)
gp_file_type("${target}" "${d}" type)
set(type_str " (${type})")
endif()
message(STATUS "${count_str}${d}${type_str}")
endforeach()
endfunction()
function(list_prerequisites_by_glob glob_arg glob_exp)
message(STATUS "=============================================================================")
message(STATUS "List prerequisites of executables matching ${glob_arg} '${glob_exp}'")
message(STATUS "")
file(${glob_arg} file_list ${glob_exp})
foreach(f ${file_list})
is_file_executable("${f}" is_f_executable)
if(is_f_executable)
message(STATUS "=============================================================================")
list_prerequisites("${f}" ${ARGN})
message(STATUS "")
endif()
endforeach()
endfunction()
diff --git a/CMake/NSIS.template.in b/CMake/NSIS.template.in
index 619bfcd3f8..887a86485c 100644
--- a/CMake/NSIS.template.in
+++ b/CMake/NSIS.template.in
@@ -1,1014 +1,1014 @@
; CPack install script designed for a nmake build
;--------------------------------
; You must define these values
!define VERSION "@CPACK_PACKAGE_VERSION@"
!define PATCH "@CPACK_PACKAGE_VERSION_PATCH@"
!define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@"
;--------------------------------
;Variables
Var MUI_TEMP
Var STARTMENU_FOLDER
Var SV_ALLUSERS
Var START_MENU
Var DO_NOT_ADD_TO_PATH
Var ADD_TO_PATH_ALL_USERS
Var ADD_TO_PATH_CURRENT_USER
Var INSTALL_DESKTOP
Var IS_DEFAULT_INSTALLDIR
;--------------------------------
;Include Modern UI
!include "MUI.nsh"
;Default installation folder
InstallDir "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
;Allow for very long title on the welcome page
!define MUI_WELCOMEPAGE_TITLE_3LINES
;--------------------------------
;General
;Name and file
Name "@CPACK_NSIS_PACKAGE_NAME@"
OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@"
;Set compression
SetCompressor @CPACK_NSIS_COMPRESSOR@
RequestExecutionLevel user
@CPACK_NSIS_DEFINES@
!include Sections.nsh
;--- Component support macros: ---
; The code for the add/remove functionality is from:
; https://nsis.sourceforge.io/Add/Remove_Functionality
; It has been modified slightly and extended to provide
; inter-component dependencies.
Var AR_SecFlags
Var AR_RegFlags
@CPACK_NSIS_SECTION_SELECTED_VARS@
; Loads the "selected" flag for the section named SecName into the
; variable VarName.
!macro LoadSectionSelectedIntoVar SecName VarName
SectionGetFlags ${${SecName}} $${VarName}
IntOp $${VarName} $${VarName} & ${SF_SELECTED} ;Turn off all other bits
!macroend
; Loads the value of a variable... can we get around this?
!macro LoadVar VarName
IntOp $R0 0 + $${VarName}
!macroend
; Sets the value of a variable
!macro StoreVar VarName IntValue
IntOp $${VarName} 0 + ${IntValue}
!macroend
!macro InitSection SecName
; This macro reads component installed flag from the registry and
;changes checked state of the section on the components page.
;Input: section index constant name specified in Section command.
ClearErrors
;Reading component status from registry
ReadRegDWORD $AR_RegFlags HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@\Components\${SecName}" "Installed"
IfErrors "default_${SecName}"
;Status will stay default if registry value not found
;(component was never installed)
IntOp $AR_RegFlags $AR_RegFlags & ${SF_SELECTED} ;Turn off all other bits
SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading default section flags
IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off
IntOp $AR_SecFlags $AR_RegFlags | $AR_SecFlags ;Change lowest bit
; Note whether this component was installed before
!insertmacro StoreVar ${SecName}_was_installed $AR_RegFlags
IntOp $R0 $AR_RegFlags & $AR_RegFlags
;Writing modified flags
SectionSetFlags ${${SecName}} $AR_SecFlags
"default_${SecName}:"
!insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected
!macroend
!macro FinishSection SecName
; This macro reads section flag set by user and removes the section
;if it is not selected.
;Then it writes component installed flag to registry
;Input: section index constant name specified in Section command.
SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags
;Checking lowest bit:
IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED}
IntCmp $AR_SecFlags 1 "leave_${SecName}"
;Section is not selected:
;Calling Section uninstall macro and writing zero installed flag
!insertmacro "Remove_${${SecName}}"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@\Components\${SecName}" \
"Installed" 0
Goto "exit_${SecName}"
"leave_${SecName}:"
;Section is selected:
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@\Components\${SecName}" \
"Installed" 1
"exit_${SecName}:"
!macroend
; NSIS example code to only include some code if a file exists at compile time (whereas IfFileExists works at runtime)
; See https://nsis.sourceforge.io/Check_if_a_file_exists_at_compile_time for documentation
!macro !defineifexist _VAR_NAME _FILE_NAME
!tempfile _TEMPFILE
!system 'if exist "${_FILE_NAME}" echo !define ${_VAR_NAME} > "${_TEMPFILE}"'
!include '${_TEMPFILE}'
!delfile '${_TEMPFILE}'
!undef _TEMPFILE
!macroend
!define !defineifexist "!insertmacro !defineifexist"
; Determine whether the selection of SecName changed
!macro MaybeSelectionChanged SecName
!insertmacro LoadVar ${SecName}_selected
SectionGetFlags ${${SecName}} $R1
IntOp $R1 $R1 & ${SF_SELECTED} ;Turn off all other bits
; See if the status has changed:
IntCmp $R0 $R1 "${SecName}_unchanged"
!insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected
IntCmp $R1 ${SF_SELECTED} "${SecName}_was_selected"
!insertmacro "Deselect_required_by_${SecName}"
goto "${SecName}_unchanged"
"${SecName}_was_selected:"
!insertmacro "Select_${SecName}_depends"
"${SecName}_unchanged:"
!macroend
;--- End of Add/Remove macros ---
;--------------------------------
;Interface Settings
!define MUI_HEADERIMAGE
!define MUI_ABORTWARNING
;--------------------------------
; path functions
!verbose 3
!include "WinMessages.NSH"
!verbose 4
;----------------------------------------
; based upon a script of "Written by KiCHiK 2003-01-18 05:57:02"
;----------------------------------------
!verbose 3
!include "WinMessages.NSH"
!verbose 4
;====================================================
; get_NT_environment
; Returns: the selected environment
; Output : head of the stack
;====================================================
!macro select_NT_profile UN
Function ${UN}select_NT_profile
StrCmp $ADD_TO_PATH_ALL_USERS "1" 0 environment_single
DetailPrint "Selected environment for all users"
Push "all"
Return
environment_single:
DetailPrint "Selected environment for current user only."
Push "current"
Return
FunctionEnd
!macroend
!insertmacro select_NT_profile ""
!insertmacro select_NT_profile "un."
;----------------------------------------------------
!define NT_current_env 'HKCU "Environment"'
!define NT_all_env 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
!ifndef WriteEnvStr_RegKey
!ifdef ALL_USERS
!define WriteEnvStr_RegKey \
'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
!else
!define WriteEnvStr_RegKey 'HKCU "Environment"'
!endif
!endif
; AddToPath - Adds the given dir to the search path.
; Input - head of the stack
; Note - Win9x systems requires reboot
Function AddToPath
Exch $0
Push $1
Push $2
Push $3
# don't add if the path doesn't exist
IfFileExists "$0\*.*" "" AddToPath_done
ReadEnvStr $1 PATH
; if the path is too long for a NSIS variable NSIS will return a 0
; length string. If we find that, then warn and skip any path
; modification as it will trash the existing path.
StrLen $2 $1
IntCmp $2 0 CheckPathLength_ShowPathWarning CheckPathLength_Done CheckPathLength_Done
CheckPathLength_ShowPathWarning:
DetailPrint "Warning: Could not modify PATH variable - current PATH is too long.\n\
This does not impact the functionality of MITK itself, but you will not be able to start\
it from anywhere via command line."
; The message box is probably too much of an escalation, most users won't care or notice
;Messagebox MB_OK|MB_ICONEXCLAMATION "Warning! PATH too long installer unable to modify PATH!"
Goto AddToPath_done
CheckPathLength_Done:
Push "$1;"
Push "$0;"
Call StrStr
Pop $2
StrCmp $2 "" "" AddToPath_done
Push "$1;"
Push "$0\;"
Call StrStr
Pop $2
StrCmp $2 "" "" AddToPath_done
GetFullPathName /SHORT $3 $0
Push "$1;"
Push "$3;"
Call StrStr
Pop $2
StrCmp $2 "" "" AddToPath_done
Push "$1;"
Push "$3\;"
Call StrStr
Pop $2
StrCmp $2 "" "" AddToPath_done
Call IsNT
Pop $1
StrCmp $1 1 AddToPath_NT
; Not on NT
StrCpy $1 $WINDIR 2
FileOpen $1 "$1\autoexec.bat" a
FileSeek $1 -1 END
FileReadByte $1 $2
IntCmp $2 26 0 +2 +2 # DOS EOF
FileSeek $1 -1 END # write over EOF
FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n"
FileClose $1
SetRebootFlag true
Goto AddToPath_done
AddToPath_NT:
StrCmp $ADD_TO_PATH_ALL_USERS "1" ReadAllKey
ReadRegStr $1 ${NT_current_env} "PATH"
Goto DoTrim
ReadAllKey:
ReadRegStr $1 ${NT_all_env} "PATH"
DoTrim:
StrCmp $1 "" AddToPath_NTdoIt
Push $1
Call Trim
Pop $1
StrCpy $0 "$1;$0"
AddToPath_NTdoIt:
StrCmp $ADD_TO_PATH_ALL_USERS "1" WriteAllKey
WriteRegExpandStr ${NT_current_env} "PATH" $0
Goto DoSend
WriteAllKey:
WriteRegExpandStr ${NT_all_env} "PATH" $0
DoSend:
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
AddToPath_done:
Pop $3
Pop $2
Pop $1
Pop $0
FunctionEnd
; RemoveFromPath - Remove a given dir from the path
; Input: head of the stack
Function un.RemoveFromPath
Exch $0
Push $1
Push $2
Push $3
Push $4
Push $5
Push $6
IntFmt $6 "%c" 26 # DOS EOF
Call un.IsNT
Pop $1
StrCmp $1 1 unRemoveFromPath_NT
; Not on NT
StrCpy $1 $WINDIR 2
FileOpen $1 "$1\autoexec.bat" r
GetTempFileName $4
FileOpen $2 $4 w
GetFullPathName /SHORT $0 $0
StrCpy $0 "SET PATH=%PATH%;$0"
Goto unRemoveFromPath_dosLoop
unRemoveFromPath_dosLoop:
FileRead $1 $3
StrCpy $5 $3 1 -1 # read last char
StrCmp $5 $6 0 +2 # if DOS EOF
StrCpy $3 $3 -1 # remove DOS EOF so we can compare
StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine
StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine
StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine
StrCmp $3 "" unRemoveFromPath_dosLoopEnd
FileWrite $2 $3
Goto unRemoveFromPath_dosLoop
unRemoveFromPath_dosLoopRemoveLine:
SetRebootFlag true
Goto unRemoveFromPath_dosLoop
unRemoveFromPath_dosLoopEnd:
FileClose $2
FileClose $1
StrCpy $1 $WINDIR 2
Delete "$1\autoexec.bat"
CopyFiles /SILENT $4 "$1\autoexec.bat"
Delete $4
Goto unRemoveFromPath_done
unRemoveFromPath_NT:
StrCmp $ADD_TO_PATH_ALL_USERS "1" unReadAllKey
ReadRegStr $1 ${NT_current_env} "PATH"
Goto unDoTrim
unReadAllKey:
ReadRegStr $1 ${NT_all_env} "PATH"
unDoTrim:
StrCpy $5 $1 1 -1 # copy last char
StrCmp $5 ";" +2 # if last char != ;
StrCpy $1 "$1;" # append ;
Push $1
Push "$0;"
Call un.StrStr ; Find `$0;` in $1
Pop $2 ; pos of our dir
StrCmp $2 "" unRemoveFromPath_done
; else, it is in path
# $0 - path to add
# $1 - path var
StrLen $3 "$0;"
StrLen $4 $2
StrCpy $5 $1 -$4 # $5 is now the part before the path to remove
StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove
StrCpy $3 $5$6
StrCpy $5 $3 1 -1 # copy last char
StrCmp $5 ";" 0 +2 # if last char == ;
StrCpy $3 $3 -1 # remove last char
StrCmp $ADD_TO_PATH_ALL_USERS "1" unWriteAllKey
WriteRegExpandStr ${NT_current_env} "PATH" $3
Goto unDoSend
unWriteAllKey:
WriteRegExpandStr ${NT_all_env} "PATH" $3
unDoSend:
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
unRemoveFromPath_done:
Pop $6
Pop $5
Pop $4
Pop $3
Pop $2
Pop $1
Pop $0
FunctionEnd
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Uninstall stuff
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
###########################################
# Utility Functions #
###########################################
;====================================================
; IsNT - Returns 1 if the current system is NT, 0
; otherwise.
; Output: head of the stack
;====================================================
; IsNT
; no input
; output, top of the stack = 1 if NT or 0 if not
;
; Usage:
; Call IsNT
; Pop $R0
; ($R0 at this point is 1 or 0)
!macro IsNT un
Function ${un}IsNT
Push $0
ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
StrCmp $0 "" 0 IsNT_yes
; we are not NT.
Pop $0
Push 0
Return
IsNT_yes:
; NT!!!
Pop $0
Push 1
FunctionEnd
!macroend
!insertmacro IsNT ""
!insertmacro IsNT "un."
; StrStr
; input, top of stack = string to search for
; top of stack-1 = string to search in
; output, top of stack (replaces with the portion of the string remaining)
; modifies no other variables.
;
; Usage:
; Push "this is a long ass string"
; Push "ass"
; Call StrStr
; Pop $R0
; ($R0 at this point is "ass string")
!macro StrStr un
Function ${un}StrStr
Exch $R1 ; st=haystack,old$R1, $R1=needle
Exch ; st=old$R1,haystack
Exch $R2 ; st=old$R1,old$R2, $R2=haystack
Push $R3
Push $R4
Push $R5
StrLen $R3 $R1
StrCpy $R4 0
; $R1=needle
; $R2=haystack
; $R3=len(needle)
; $R4=cnt
; $R5=tmp
loop:
StrCpy $R5 $R2 $R3 $R4
StrCmp $R5 $R1 done
StrCmp $R5 "" done
IntOp $R4 $R4 + 1
Goto loop
done:
StrCpy $R1 $R2 "" $R4
Pop $R5
Pop $R4
Pop $R3
Pop $R2
Exch $R1
FunctionEnd
!macroend
!insertmacro StrStr ""
!insertmacro StrStr "un."
Function Trim ; Added by Pelaca
Exch $R1
Push $R2
Loop:
StrCpy $R2 "$R1" 1 -1
StrCmp "$R2" " " RTrim
StrCmp "$R2" "$\n" RTrim
StrCmp "$R2" "$\r" RTrim
StrCmp "$R2" ";" RTrim
GoTo Done
RTrim:
StrCpy $R1 "$R1" -1
Goto Loop
Done:
Pop $R2
Exch $R1
FunctionEnd
Function ConditionalAddToRegisty
Pop $0
Pop $1
StrCmp "$0" "" ConditionalAddToRegisty_EmptyString
WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" \
"$1" "$0"
;MessageBox MB_OK "Set Registry: '$1' to '$0'"
DetailPrint "Set install registry entry: '$1' to '$0'"
ConditionalAddToRegisty_EmptyString:
FunctionEnd
;--------------------------------
!ifdef CPACK_USES_DOWNLOAD
Function DownloadFile
IfFileExists $INSTDIR\* +2
CreateDirectory $INSTDIR
Pop $0
; Skip if already downloaded
IfFileExists $INSTDIR\$0 0 +2
Return
StrCpy $1 "@CPACK_DOWNLOAD_SITE@"
try_again:
NSISdl::download "$1/$0" "$INSTDIR\$0"
Pop $1
StrCmp $1 "success" success
StrCmp $1 "Cancelled" cancel
MessageBox MB_OK "Download failed: $1"
cancel:
Return
success:
FunctionEnd
!endif
;--------------------------------
; Installation types
@CPACK_NSIS_INSTALLATION_TYPES@
;--------------------------------
; Component sections
@CPACK_NSIS_COMPONENT_SECTIONS@
;--------------------------------
; Define some macro setting for the gui
@CPACK_NSIS_INSTALLER_MUI_ICON_CODE@
@CPACK_NSIS_INSTALLER_ICON_CODE@
@CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC@
@CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE@
;--------------------------------
;Pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@"
Page custom InstallOptionsPage
!insertmacro MUI_PAGE_DIRECTORY
;Start Menu Folder Page Configuration
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
@CPACK_NSIS_PAGE_COMPONENTS@
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
;--------------------------------
;Languages
!insertmacro MUI_LANGUAGE "English" ;first language is the default language
!insertmacro MUI_LANGUAGE "Albanian"
!insertmacro MUI_LANGUAGE "Arabic"
!insertmacro MUI_LANGUAGE "Basque"
!insertmacro MUI_LANGUAGE "Belarusian"
!insertmacro MUI_LANGUAGE "Bosnian"
!insertmacro MUI_LANGUAGE "Breton"
!insertmacro MUI_LANGUAGE "Bulgarian"
!insertmacro MUI_LANGUAGE "Croatian"
!insertmacro MUI_LANGUAGE "Czech"
!insertmacro MUI_LANGUAGE "Danish"
!insertmacro MUI_LANGUAGE "Dutch"
!insertmacro MUI_LANGUAGE "Estonian"
!insertmacro MUI_LANGUAGE "Farsi"
!insertmacro MUI_LANGUAGE "Finnish"
!insertmacro MUI_LANGUAGE "French"
!insertmacro MUI_LANGUAGE "German"
!insertmacro MUI_LANGUAGE "Greek"
!insertmacro MUI_LANGUAGE "Hebrew"
!insertmacro MUI_LANGUAGE "Hungarian"
!insertmacro MUI_LANGUAGE "Icelandic"
!insertmacro MUI_LANGUAGE "Indonesian"
!insertmacro MUI_LANGUAGE "Irish"
!insertmacro MUI_LANGUAGE "Italian"
!insertmacro MUI_LANGUAGE "Japanese"
!insertmacro MUI_LANGUAGE "Korean"
!insertmacro MUI_LANGUAGE "Kurdish"
!insertmacro MUI_LANGUAGE "Latvian"
!insertmacro MUI_LANGUAGE "Lithuanian"
!insertmacro MUI_LANGUAGE "Luxembourgish"
!insertmacro MUI_LANGUAGE "Macedonian"
!insertmacro MUI_LANGUAGE "Malay"
!insertmacro MUI_LANGUAGE "Mongolian"
!insertmacro MUI_LANGUAGE "Norwegian"
!insertmacro MUI_LANGUAGE "Polish"
!insertmacro MUI_LANGUAGE "Portuguese"
!insertmacro MUI_LANGUAGE "PortugueseBR"
!insertmacro MUI_LANGUAGE "Romanian"
!insertmacro MUI_LANGUAGE "Russian"
!insertmacro MUI_LANGUAGE "Serbian"
!insertmacro MUI_LANGUAGE "SerbianLatin"
!insertmacro MUI_LANGUAGE "SimpChinese"
!insertmacro MUI_LANGUAGE "Slovak"
!insertmacro MUI_LANGUAGE "Slovenian"
!insertmacro MUI_LANGUAGE "Spanish"
!insertmacro MUI_LANGUAGE "Swedish"
!insertmacro MUI_LANGUAGE "Thai"
!insertmacro MUI_LANGUAGE "TradChinese"
!insertmacro MUI_LANGUAGE "Turkish"
!insertmacro MUI_LANGUAGE "Ukrainian"
!insertmacro MUI_LANGUAGE "Welsh"
;--------------------------------
;Reserve Files
;These files should be inserted before other files in the data block
;Keep these lines before any File command
;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA)
ReserveFile "NSIS.InstallOptions.ini"
!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
;--------------------------------
;Installer Sections
Section "-Core installation"
;Use the entire tree produced by the INSTALL target. Keep the
;list of directories here in sync with the RMDir commands below.
SetOutPath "$INSTDIR"
@CPACK_NSIS_FULL_INSTALL@
;Store installation folder
WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR
;Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"
Push "DisplayName"
Push "@CPACK_NSIS_DISPLAY_NAME@"
Call ConditionalAddToRegisty
Push "DisplayVersion"
Push "@CPACK_PACKAGE_VERSION@"
Call ConditionalAddToRegisty
Push "Publisher"
Push "@CPACK_PACKAGE_VENDOR@"
Call ConditionalAddToRegisty
Push "UninstallString"
Push "$INSTDIR\Uninstall.exe"
Call ConditionalAddToRegisty
Push "NoRepair"
Push "1"
Call ConditionalAddToRegisty
!ifdef CPACK_NSIS_ADD_REMOVE
;Create add/remove functionality
Push "ModifyPath"
Push "$INSTDIR\AddRemove.exe"
Call ConditionalAddToRegisty
!else
Push "NoModify"
Push "1"
Call ConditionalAddToRegisty
!endif
; Optional registration
Push "DisplayIcon"
Push "$INSTDIR\@CPACK_NSIS_INSTALLED_ICON_NAME@"
Call ConditionalAddToRegisty
Push "HelpLink"
Push "@CPACK_NSIS_HELP_LINK@"
Call ConditionalAddToRegisty
Push "URLInfoAbout"
Push "@CPACK_NSIS_URL_INFO_ABOUT@"
Call ConditionalAddToRegisty
Push "Contact"
Push "@CPACK_NSIS_CONTACT@"
Call ConditionalAddToRegisty
!insertmacro MUI_INSTALLOPTIONS_READ $INSTALL_DESKTOP "NSIS.InstallOptions.ini" "Field 5" "State"
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
;Create shortcuts
CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
@CPACK_NSIS_CREATE_ICONS@
@CPACK_NSIS_CREATE_ICONS_EXTRA@
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
;Read a value from an InstallOptions INI file
!insertmacro MUI_INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State"
!insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_ALL_USERS "NSIS.InstallOptions.ini" "Field 3" "State"
!insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_CURRENT_USER "NSIS.InstallOptions.ini" "Field 4" "State"
; Write special uninstall registry entries
Push "StartMenu"
Push "$STARTMENU_FOLDER"
Call ConditionalAddToRegisty
Push "DoNotAddToPath"
Push "$DO_NOT_ADD_TO_PATH"
Call ConditionalAddToRegisty
Push "AddToPathAllUsers"
Push "$ADD_TO_PATH_ALL_USERS"
Call ConditionalAddToRegisty
Push "AddToPathCurrentUser"
Push "$ADD_TO_PATH_CURRENT_USER"
Call ConditionalAddToRegisty
Push "InstallToDesktop"
Push "$INSTALL_DESKTOP"
Call ConditionalAddToRegisty
!insertmacro MUI_STARTMENU_WRITE_END
@CPACK_NSIS_EXTRA_INSTALL_COMMANDS@
SectionEnd
Section "-Add to path"
Push $INSTDIR\bin
StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 doNotAddToPath
StrCmp $DO_NOT_ADD_TO_PATH "1" doNotAddToPath 0
Call AddToPath
doNotAddToPath:
SectionEnd
;--------------------------------
; Create custom pages
Function InstallOptionsPage
!insertmacro MUI_HEADER_TEXT "Install Options" "Choose options for installing @CPACK_NSIS_PACKAGE_NAME@"
!insertmacro MUI_INSTALLOPTIONS_DISPLAY "NSIS.InstallOptions.ini"
FunctionEnd
; Author: Lilla (lilla@earthlink.net) 2003-06-13
; function IsUserAdmin uses plugin \NSIS\PlusgIns\UserInfo.dll
; This function is based upon code in \NSIS\Contrib\UserInfo\UserInfo.nsi
; This function was tested under NSIS 2 beta 4 (latest CVS as of this writing).
;
; Usage:
; Call IsUserAdmin
; Pop $R0 ; at this point $R0 is "true" or "false"
;
Function IsUserAdmin
Push $R0
Push $R1
Push $R2
ClearErrors
UserInfo::GetName
IfErrors Win9x
Pop $R1
UserInfo::GetAccountType
Pop $R2
StrCmp $R2 "Admin" 0 Continue
; Observation: I get here when running Win98SE. (Lilla)
; The functions UserInfo.dll looks for are there on Win98 too,
; but just don't work. So UserInfo.dll, knowing that admin isn't required
; on Win98, returns admin anyway. (per kichik)
; MessageBox MB_OK 'User "$R1" is in the Administrators group'
StrCpy $R0 "true"
Goto Done
Continue:
; You should still check for an empty string because the functions
; UserInfo.dll looks for may not be present on Windows 95. (per kichik)
StrCmp $R2 "" Win9x
StrCpy $R0 "false"
;MessageBox MB_OK 'User "$R1" is in the "$R2" group'
Goto Done
Win9x:
; comment/message below is by UserInfo.nsi author:
; This one means you don't need to care about admin or
; not admin because Windows 9x doesn't either
;MessageBox MB_OK "Error! This DLL can't run under Windows 9x!"
StrCpy $R0 "true"
Done:
;MessageBox MB_OK 'User= "$R1" AccountType= "$R2" IsUserAdmin= "$R0"'
Pop $R2
Pop $R1
Exch $R0
FunctionEnd
;--------------------------------
; determine admin versus local install
Function un.onInit
ClearErrors
UserInfo::GetName
IfErrors noLM
Pop $0
UserInfo::GetAccountType
Pop $1
StrCmp $1 "Admin" 0 +3
SetShellVarContext all
;MessageBox MB_OK 'User "$0" is in the Admin group'
Goto done
StrCmp $1 "Power" 0 +3
SetShellVarContext all
;MessageBox MB_OK 'User "$0" is in the Power Users group'
Goto done
noLM:
;Get installation folder from registry if available
done:
FunctionEnd
;--- Add/Remove callback functions: ---
!macro SectionList MacroName
;This macro used to perform operation on multiple sections.
;List all of your components in following manner here.
@CPACK_NSIS_COMPONENT_SECTION_LIST@
!macroend
Section -FinishComponents
;Removes unselected components and writes component status to registry
!insertmacro SectionList "FinishSection"
!ifdef CPACK_NSIS_ADD_REMOVE
; Get the name of the installer executable
System::Call 'kernel32::GetModuleFileNameA(i 0, t .R0, i 1024) i r1'
StrCpy $R3 $R0
; Strip off the last 13 characters, to see if we have AddRemove.exe
StrLen $R1 $R0
IntOp $R1 $R0 - 13
StrCpy $R2 $R0 13 $R1
StrCmp $R2 "AddRemove.exe" addremove_installed
; We're not running AddRemove.exe, so install it
CopyFiles $R3 $INSTDIR\AddRemove.exe
addremove_installed:
!endif
SectionEnd
;--- End of Add/Remove callback functions ---
;--------------------------------
; Component dependencies
Function .onSelChange
!insertmacro SectionList MaybeSelectionChanged
FunctionEnd
;--------------------------------
;Uninstaller Section
Section "Uninstall"
ReadRegStr $START_MENU SHCTX \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "StartMenu"
;MessageBox MB_OK "Start menu is in: $START_MENU"
ReadRegStr $DO_NOT_ADD_TO_PATH SHCTX \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "DoNotAddToPath"
ReadRegStr $ADD_TO_PATH_ALL_USERS SHCTX \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "AddToPathAllUsers"
ReadRegStr $ADD_TO_PATH_CURRENT_USER SHCTX \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "AddToPathCurrentUser"
;MessageBox MB_OK "Add to path: $DO_NOT_ADD_TO_PATH all users: $ADD_TO_PATH_ALL_USERS"
ReadRegStr $INSTALL_DESKTOP SHCTX \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "InstallToDesktop"
;MessageBox MB_OK "Install to desktop: $INSTALL_DESKTOP "
@CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@
;Remove files we installed.
;Keep the list of directories here in sync with the File commands above.
@CPACK_NSIS_DELETE_FILES@
@CPACK_NSIS_DELETE_DIRECTORIES@
!ifdef CPACK_NSIS_ADD_REMOVE
;Remove the add/remove program
Delete "$INSTDIR\AddRemove.exe"
!endif
;Remove the uninstaller itself.
Delete "$INSTDIR\Uninstall.exe"
DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@"
;Remove the installation directory if it is empty.
RMDir "$INSTDIR"
; Remove the registry entries.
DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
; Removes all optional components
!insertmacro SectionList "RemoveSection"
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
@CPACK_NSIS_DELETE_ICONS@
@CPACK_NSIS_DELETE_ICONS_EXTRA@
- ;Delete empty start menu parent diretories
+ ;Delete empty start menu parent directories
StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP"
startMenuDeleteLoop:
ClearErrors
RMDir $MUI_TEMP
GetFullPathName $MUI_TEMP "$MUI_TEMP\.."
IfErrors startMenuDeleteLoopDone
StrCmp "$MUI_TEMP" "$SMPROGRAMS" startMenuDeleteLoopDone startMenuDeleteLoop
startMenuDeleteLoopDone:
; If the user changed the shortcut, then untinstall may not work. This should
; try to fix it.
StrCpy $MUI_TEMP "$START_MENU"
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
@CPACK_NSIS_DELETE_ICONS_EXTRA@
- ;Delete empty start menu parent diretories
+ ;Delete empty start menu parent directories
StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP"
secondStartMenuDeleteLoop:
ClearErrors
RMDir $MUI_TEMP
GetFullPathName $MUI_TEMP "$MUI_TEMP\.."
IfErrors secondStartMenuDeleteLoopDone
StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop
secondStartMenuDeleteLoopDone:
DeleteRegKey /ifempty SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
Push $INSTDIR\bin
StrCmp $DO_NOT_ADD_TO_PATH_ "1" doNotRemoveFromPath 0
Call un.RemoveFromPath
doNotRemoveFromPath:
SectionEnd
;--------------------------------
; determine admin versus local install
; Is install for "AllUsers" or "JustMe"?
; Default to "JustMe" - set to "AllUsers" if admin or on Win9x
; This function is used for the very first "custom page" of the installer.
; This custom page does not show up visibly, but it executes prior to the
; first visible page and sets up $INSTDIR properly...
; Choose different default installation folder based on SV_ALLUSERS...
; "Program Files" for AllUsers, "My Documents" for JustMe...
Function .onInit
; Reads components status for registry
!insertmacro SectionList "InitSection"
; check to see if /D has been used to change
; the install directory by comparing it to the
; install directory that is expected to be the
; default
StrCpy $IS_DEFAULT_INSTALLDIR 0
StrCmp "$INSTDIR" "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" 0 +2
StrCpy $IS_DEFAULT_INSTALLDIR 1
StrCpy $SV_ALLUSERS "JustMe"
; if default install dir then change the default
; if it is installed for JustMe
StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2
StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
ClearErrors
UserInfo::GetName
IfErrors noLM
Pop $0
UserInfo::GetAccountType
Pop $1
StrCmp $1 "Admin" 0 +3
SetShellVarContext all
;MessageBox MB_OK 'User "$0" is in the Admin group'
StrCpy $SV_ALLUSERS "AllUsers"
Goto done
StrCmp $1 "Power" 0 +3
SetShellVarContext all
;MessageBox MB_OK 'User "$0" is in the Power Users group'
StrCpy $SV_ALLUSERS "AllUsers"
Goto done
noLM:
StrCpy $SV_ALLUSERS "AllUsers"
;Get installation folder from registry if available
done:
StrCmp $SV_ALLUSERS "AllUsers" 0 +3
StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2
StrCpy $INSTDIR "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage
!insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini"
noOptionsPage:
FunctionEnd
diff --git a/CMake/PackageDepends/MITK_Boost_Config.cmake b/CMake/PackageDepends/MITK_Boost_Config.cmake
index 1413f203ea..5b55afc0bd 100644
--- a/CMake/PackageDepends/MITK_Boost_Config.cmake
+++ b/CMake/PackageDepends/MITK_Boost_Config.cmake
@@ -1,15 +1,15 @@
-set(Boost_ADDITIONAL_VERSIONS "1.82.0" "1.82")
+set(Boost_ADDITIONAL_VERSIONS "1.85.0" "1.85")
find_package(Boost REQUIRED COMPONENTS ${Boost_REQUIRED_COMPONENTS_BY_MODULE})
if(Boost_REQUIRED_COMPONENTS_BY_MODULE)
foreach(boost_component ${Boost_REQUIRED_COMPONENTS_BY_MODULE})
list(APPEND ALL_LIBRARIES "Boost::${boost_component}")
endforeach()
endif()
list(APPEND ALL_LIBRARIES "Boost::boost")
if(MSVC)
list(APPEND ALL_LIBRARIES "Boost::dynamic_linking" "bcrypt")
endif()
diff --git a/CMake/PackageDepends/MITK_Eigen_Config.cmake b/CMake/PackageDepends/MITK_Eigen_Config.cmake
deleted file mode 100644
index c4a7aba4ab..0000000000
--- a/CMake/PackageDepends/MITK_Eigen_Config.cmake
+++ /dev/null
@@ -1,2 +0,0 @@
-list(APPEND ALL_LIBRARIES Eigen)
-
diff --git a/CMake/PackageDepends/MITK_httplib_Config.cmake b/CMake/PackageDepends/MITK_httplib_Config.cmake
new file mode 100644
index 0000000000..a20aa2233b
--- /dev/null
+++ b/CMake/PackageDepends/MITK_httplib_Config.cmake
@@ -0,0 +1 @@
+set(ALL_LIBRARIES httplib::httplib)
diff --git a/CMake/Whitelists/CoreCmdApps.cmake b/CMake/Whitelists/CoreCmdApps.cmake
index f74c63a9c6..f76813ad56 100644
--- a/CMake/Whitelists/CoreCmdApps.cmake
+++ b/CMake/Whitelists/CoreCmdApps.cmake
@@ -1,25 +1,20 @@
include(${CMAKE_CURRENT_LIST_DIR}/Minimal.cmake)
list(APPEND enabled_modules
AlgorithmsExt
Annotation
CommandLine
ContourModel
CoreCmdApps
DataTypesExt
DICOMPM
- DICOMPMIO
DICOMQI
DICOM
- DICOMImageIO
RT
- DICOMRTIO
- DICOMSegIO
ModelFit
Multilabel
- MultilabelIO
LegacyGL
SceneSerialization
SceneSerializationBase
+ ROI
)
-
diff --git a/CMake/mitkFunctionCMakeDoxygenFilterCompile.cmake b/CMake/mitkFunctionCMakeDoxygenFilterCompile.cmake
deleted file mode 100644
index fc8e691b55..0000000000
--- a/CMake/mitkFunctionCMakeDoxygenFilterCompile.cmake
+++ /dev/null
@@ -1,87 +0,0 @@
-#!
-#! \brief Download and compile a CMake doxygen input filter
-#!
-#! \param OUT <out-file> (optional) Supply an absolute filename for
-#! the generated executable.
-#! \param NAMESPACE <namespace> (optional) Supply a C++ namespace in
-#! which the generated function declrarations
-#! should be wrapped.
-#!
-#! \return This function sets the <code>CMakeDoxygenFilter_EXECUTABLE</code>
-#! variable to the absolute path of the generated input filter executable
-#! in the parent scope. If <out-file> is specified, they will be the same.
-#!
-#! This CMake function compiles the https://github.com/saschazelzer/CMakeDoxygenFilter
-#! project into a doxygen input filter executable. See
-#! https://github.com/saschazelzer/CMakeDoxygenFilter/blob/master/README for more details.
-#!
-function(mitkFunctionCMakeDoxygenFilterCompile)
-
- #-------------------- parse function arguments -------------------
-
- set(DEFAULT_ARGS)
- set(prefix "FILTER")
- set(arg_names "OUT;NAMESPACE")
- set(option_names "")
-
- foreach(arg_name ${arg_names})
- set(${prefix}_${arg_name})
- endforeach(arg_name)
-
- foreach(option ${option_names})
- set(${prefix}_${option} FALSE)
- endforeach(option)
-
- set(current_arg_name DEFAULT_ARGS)
- set(current_arg_list)
-
- foreach(arg ${ARGN})
- set(larg_names ${arg_names})
- list(FIND larg_names "${arg}" is_arg_name)
- if(is_arg_name GREATER -1)
- set(${prefix}_${current_arg_name} ${current_arg_list})
- set(current_arg_name "${arg}")
- set(current_arg_list)
- else(is_arg_name GREATER -1)
- set(loption_names ${option_names})
- list(FIND loption_names "${arg}" is_option)
- if(is_option GREATER -1)
- set(${prefix}_${arg} TRUE)
- else(is_option GREATER -1)
- set(current_arg_list ${current_arg_list} "${arg}")
- endif(is_option GREATER -1)
- endif(is_arg_name GREATER -1)
- endforeach(arg ${ARGN})
-
- set(${prefix}_${current_arg_name} ${current_arg_list})
-
- #------------------- finished parsing arguments ----------------------
-
- if(FILTER_OUT)
- set(copy_file "${FILTER_OUT}")
- else()
- set(copy_file "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CMakeDoxygenFilter${CMAKE_EXECUTABLE_SUFFIX}")
- endif()
-
- set(compile_defs "")
- if(FILTER_NAMESPACE)
- set(compile_defs "${compile_defs} -DUSE_NAMESPACE=${FILTER_NAMESPACE}")
- endif()
-
- set(cmake_doxygen_filter_src "${CMAKE_CURRENT_SOURCE_DIR}/CMakeDoxygenFilter.cpp")
-
- try_compile(result_var
- "${CMAKE_CURRENT_BINARY_DIR}"
- "${cmake_doxygen_filter_src}"
- COMPILE_DEFINITIONS ${compile_defs}
- OUTPUT_VARIABLE compile_output
- COPY_FILE ${copy_file}
- )
-
- if(NOT result_var)
- message(FATAL_ERROR "error: Faild to compile ${cmake_doxygen_filter_src} (result: ${result_var})\n${compile_output}")
- endif()
-
- set(CMakeDoxygenFilter_EXECUTABLE "${copy_file}" PARENT_SCOPE)
-
-endfunction()
diff --git a/CMake/mitkFunctionCheckCompilerFlags.cmake b/CMake/mitkFunctionCheckCompilerFlags.cmake
index e1cade7ec0..65b616845b 100644
--- a/CMake/mitkFunctionCheckCompilerFlags.cmake
+++ b/CMake/mitkFunctionCheckCompilerFlags.cmake
@@ -1,113 +1,113 @@
#
# Helper macro allowing to check if the given flags are supported
# by the underlying build tool
#
# If the flag(s) is/are supported, they will be appended to the string identified by RESULT_VAR
#
# Usage:
# mitkFunctionCheckCompilerFlags(FLAGS_TO_CHECK VALID_FLAGS_VAR)
#
# The above example uses the C++ compiler to check the flags. To individually check with
# the C and C++ compiler, use:
#
# mitkFunctionCheckCompilerFlags2(FLAGS_TO_CHECK VALID_C_FLAGS_VAR VALID_CXX_FLAGS_VAR)
#
# Example:
#
# set(myflags)
# mitkFunctionCheckCompilerFlags("-fprofile-arcs" myflags)
# message(1-myflags:${myflags})
# mitkFunctionCheckCompilerFlags("-fauto-bugfix" myflags)
# message(2-myflags:${myflags})
# mitkFunctionCheckCompilerFlags("-Wall" myflags)
# message(1-myflags:${myflags})
#
# The output will be:
# 1-myflags: -fprofile-arcs
# 2-myflags: -fprofile-arcs
# 3-myflags: -fprofile-arcs -Wall
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
function(mitkFunctionCheckCompilerFlags CXX_FLAG_TO_TEST RESULT_VAR)
if(CXX_FLAG_TO_TEST STREQUAL "")
message(FATAL_ERROR "CXX_FLAG_TO_TEST shouldn't be empty")
endif()
# Internally, the macro CMAKE_CXX_ACCEPTS_FLAG calls TRY_COMPILE. To avoid
# the cost of compiling the test each time the project is configured, the variable set by
# the macro is added to the cache so that following invocation of the macro with
# the same variable name skip the compilation step.
# For that same reason, the mitkFunctionCheckCompilerFlags function appends a unique suffix to
# the HAS_FLAG variable. This suffix is created using a 'clean version' of the flag to test.
string(REGEX REPLACE "[, \\$\\+\\*\\{\\}\\(\\)\\#]" "" suffix ${CXX_FLAG_TO_TEST})
CHECK_CXX_COMPILER_FLAG(${CXX_FLAG_TO_TEST} HAS_FLAG_${suffix})
if(HAS_FLAG_${suffix})
set(${RESULT_VAR} "${${RESULT_VAR}} ${CXX_FLAG_TO_TEST}")
string(STRIP ${${RESULT_VAR}} ${RESULT_VAR})
set(${RESULT_VAR} "${${RESULT_VAR}}" PARENT_SCOPE)
endif()
endfunction()
function(mitkFunctionCheckCAndCXXCompilerFlags FLAG_TO_TEST C_RESULT_VAR CXX_RESULT_VAR)
if(FLAG_TO_TEST STREQUAL "")
message(FATAL_ERROR "FLAG_TO_TEST shouldn't be empty")
endif()
# Save the contents of CXX_RESULT_VAR temporarily.
# This is needed of ${CXX_RESULT_VAR} is one of the CMAKE_<LANG>_FLAGS_* variables.
set(_saved_c_result_var ${${C_RESULT_VAR}})
set(_saved_cxx_result_var ${${CXX_RESULT_VAR}})
# Clear all flags. If not, existing flags triggering warnings might lead to
# false-negatives when checking for certain compiler flags.
set(CMAKE_C_FLAGS )
set(CMAKE_C_FLAGS_DEBUG )
set(CMAKE_C_FLAGS_MINSIZEREL )
set(CMAKE_C_FLAGS_RELEASE )
set(CMAKE_C_FLAGS_RELWITHDEBINFO )
set(CMAKE_CXX_FLAGS )
set(CMAKE_CXX_FLAGS_DEBUG )
set(CMAKE_CXX_FLAGS_MINSIZEREL )
set(CMAKE_CXX_FLAGS_RELEASE )
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO )
# Internally, the macro CMAKE_CXX_COMPILER_FLAG calls TRY_COMPILE. To avoid
# the cost of compiling the test each time the project is configured, the variable set by
# the macro is added to the cache so that following invocation of the macro with
# the same variable name skip the compilation step.
# For that same reason, the mitkFunctionCheckCompilerFlags2 function appends a unique suffix to
# the HAS_CXX_FLAG and HAS_C_FLAG variable. This suffix is created using a 'clean version' of the
- # flag to test. The value of HAS_C(XX)_FLAG_${suffix} additonally needs to be a valid
+ # flag to test. The value of HAS_C(XX)_FLAG_${suffix} additionally needs to be a valid
# pre-processor token because CHECK_CXX_COMPILER_FLAG adds it as a definition to the compiler
# arguments. An invalid token triggers compiler warnings, which in case of the "-Werror" flag
# leads to false-negative checks.
string(REGEX REPLACE "[/-]" "_" suffix ${FLAG_TO_TEST})
string(REGEX REPLACE "[, \\$\\+\\*\\{\\}\\(\\)\\#]" "" suffix ${suffix})
# workaround for gcc's strange behaviour on -Wno-... options in combination with -Werror
# we test the flag without the "no-" prefix because that is more reliable
string(REGEX REPLACE "^-Wno-" "-W" FLAG_TO_TEST_FIXED ${FLAG_TO_TEST})
CHECK_CXX_COMPILER_FLAG(${FLAG_TO_TEST_FIXED} HAS_CXX_FLAG_${suffix})
if(HAS_CXX_FLAG_${suffix})
set(${CXX_RESULT_VAR} "${_saved_cxx_result_var} ${FLAG_TO_TEST}")
string(STRIP ${${CXX_RESULT_VAR}} ${CXX_RESULT_VAR})
set(${CXX_RESULT_VAR} "${${CXX_RESULT_VAR}}" PARENT_SCOPE)
endif()
CHECK_C_COMPILER_FLAG(${FLAG_TO_TEST_FIXED} HAS_C_FLAG_${suffix})
if(HAS_C_FLAG_${suffix})
set(${C_RESULT_VAR} "${_saved_c_result_var} ${FLAG_TO_TEST}")
string(STRIP ${${C_RESULT_VAR}} ${C_RESULT_VAR})
set(${C_RESULT_VAR} "${${C_RESULT_VAR}}" PARENT_SCOPE)
endif()
endfunction()
diff --git a/CMake/mitkFunctionCleanExternalProject.cmake b/CMake/mitkFunctionCleanExternalProject.cmake
index c7d34c24d8..bea2ff615c 100644
--- a/CMake/mitkFunctionCleanExternalProject.cmake
+++ b/CMake/mitkFunctionCleanExternalProject.cmake
@@ -1,53 +1,53 @@
#[[ mitkCleanExternalProject(<ep_name>)
The MITK superbuild script stores hashes of all external project scripts
and compares them with previous hashes, if they exist. This comparison
potentially detects changes in an external project that may require
cleaning of previous build artifacts to prevent MITK from referring to
mixed versions of the same external project.
- This function tries to mimic the manual cleaning of old build articafts
+ This function tries to mimic the manual cleaning of old build artifacts
by removing the source, build, and stamp directory of the external project
as well as all files in the external project's install manifest, if found.
The motivation for this function is to improve build times in continuous
integration and to make non-nightly build clients more robust against
external project updates.
]]
function(mitkCleanExternalProject ep_name)
message(STATUS "Cleaning '${ep_name}'")
set(source_dir "${ep_prefix}/src/${ep_name}")
set(binary_dir "${source_dir}-build")
set(stamp_dir "${source_dir}-stamp")
set(install_manifest_file "${binary_dir}/install_manifest.txt")
if(EXISTS ${install_manifest_file})
message(STATUS " Validating install manifest")
string(LENGTH ${CMAKE_BINARY_DIR} cmake_binary_dir_length)
file(STRINGS ${install_manifest_file} installed_files)
foreach(f ${installed_files})
string(SUBSTRING "${f}" 0 ${cmake_binary_dir_length} f_prefix)
if(NOT f_prefix STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "'${f}' in ${install_manifest_file}' refers to a file outside of '${CMAKE_BINARY_DIR}'!")
endif()
endforeach()
message(STATUS " Uninstalling")
file(REMOVE ${installed_files})
endif()
if(source_dir)
message(STATUS " Removing source directory")
file(REMOVE_RECURSE ${source_dir})
endif()
if(binary_dir)
message(STATUS " Removing build directory")
file(REMOVE_RECURSE ${binary_dir})
endif()
if(stamp_dir)
message(STATUS " Removing stamp directory")
file(REMOVE_RECURSE ${stamp_dir})
endif()
endfunction()
diff --git a/CMake/mitkFunctionCreateModule.cmake b/CMake/mitkFunctionCreateModule.cmake
index ce0c15267b..d20c6c61cd 100644
--- a/CMake/mitkFunctionCreateModule.cmake
+++ b/CMake/mitkFunctionCreateModule.cmake
@@ -1,647 +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 Qt6|Xml+Network
#! 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(GENERATED_CPP )
set(GENERATED_MOC_CPP )
set(GENERATED_QRC_CPP )
set(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
+ # Prepend 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.
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"
#
# 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_Qt6)
if(UI_FILES)
qt_wrap_ui(GENERATED_UI_CPP ${UI_FILES})
endif()
if(MOC_H_FILES)
qt_wrap_cpp(GENERATED_MOC_CPP ${MOC_H_FILES} OPTIONS -DBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
endif()
if(QRC_FILES)
qt_add_resources(GENERATED_QRC_CPP ${QRC_FILES})
endif()
endif()
set(GENERATED_CPP ${GENERATED_CPP} ${GENERATED_UI_CPP} ${GENERATED_MOC_CPP} ${GENERATED_QRC_CPP})
mitkFunctionOrganizeSources(
SOURCE ${CPP_FILES}
HEADER ${H_FILES}
TXX ${TXX_FILES}
DOC ${DOX_FILES}
UI ${UI_FILES}
QRC ${QRC_FILES}
MOC ${GENERATED_MOC_CPP}
GEN_QRC ${GENERATED_QRC_CPP}
GEN_UI ${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} ${GENERATED_CPP}
${DOX_FILES} ${UI_FILES} ${QRC_FILES} ${WINDOWS_ICON_RESOURCE_FILE})
if(WIN32)
mitk_add_manifest(${MODULE_TARGET})
endif()
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} ${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()
# 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/CMake/mitkFunctionGetMSVCVersion.cmake b/CMake/mitkFunctionGetMSVCVersion.cmake
index 440d316b7d..f85fd623c1 100644
--- a/CMake/mitkFunctionGetMSVCVersion.cmake
+++ b/CMake/mitkFunctionGetMSVCVersion.cmake
@@ -1,30 +1,30 @@
function(mitkFunctionGetMSVCVersion )
if(MSVC)
if(MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920)
set(VISUAL_STUDIO_PRODUCT_NAME "Visual Studio 2017" PARENT_SCOPE)
set(VISUAL_STUDIO_VERSION_MAJOR 14 PARENT_SCOPE)
string(SUBSTRING ${MSVC_VERSION} 2 -1 version_minor)
set(VISUAL_STUDIO_VERSION_MINOR ${version_minor} PARENT_SCOPE)
elseif(MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1930)
set(VISUAL_STUDIO_PRODUCT_NAME "Visual Studio 2019" PARENT_SCOPE)
set(VISUAL_STUDIO_VERSION_MAJOR 14 PARENT_SCOPE)
string(SUBSTRING ${MSVC_VERSION} 2 -1 version_minor)
set(VISUAL_STUDIO_VERSION_MINOR ${version_minor} PARENT_SCOPE)
- elseif(MSVC_VERSION GREATER_EQUAL 1930 AND MSVC_VERSION LESS 1940)
+ elseif(MSVC_VERSION GREATER_EQUAL 1930 AND MSVC_VERSION LESS 1950)
set(VISUAL_STUDIO_PRODUCT_NAME "Visual Studio 2022" PARENT_SCOPE)
set(VISUAL_STUDIO_VERSION_MAJOR 14 PARENT_SCOPE)
string(SUBSTRING ${MSVC_VERSION} 2 -1 version_minor)
set(VISUAL_STUDIO_VERSION_MINOR ${version_minor} PARENT_SCOPE)
else()
message(WARNING "Unknown Visual Studio version ${MSVC_VERSION} (CMake/mitkFunctionGetMSVCVersion.cmake)")
endif()
if(CMAKE_VS_PLATFORM_NAME STREQUAL x64)
set(CMAKE_LIBRARY_ARCHITECTURE x64 PARENT_SCOPE)
else()
set(CMAKE_LIBRARY_ARCHITECTURE x86 PARENT_SCOPE)
endif()
endif()
endfunction()
diff --git a/CMake/mitkFunctionOrganizeSources.cmake b/CMake/mitkFunctionOrganizeSources.cmake
index 5a76c8d841..e3f7029358 100644
--- a/CMake/mitkFunctionOrganizeSources.cmake
+++ b/CMake/mitkFunctionOrganizeSources.cmake
@@ -1,85 +1,85 @@
function(mitkFunctionOrganizeSources)
# this functions gets a filelist as input and looks
# for corresponding h-files to add them to the project.
# additionally files are grouped in source-groups.
# No parameters explicitly declared here, because
# we want to allow for variable argument lists, which
# are later accessed by the keyword foreach(MYFILE ${ARGV})
# output: after calling the macro, files that were found
- # correspondigly to the given files are stored in the
+ # correspondingly to the given files are stored in the
# variable:
# ${CORRESPONDING_H_FILES}
# ${CORRESPONDING_TXX_FILES}
# ${CORRESPONDING_UI_H_FILES}
# Globbed can be found in the variables
# ${GLOBBED_TXX_FILES} (CURRENTLY COMMENTED OUT)
# ${GLOBBED_DOX_FILES}
cmake_parse_arguments(_ORG "" "" "HEADER;SOURCE;TXX;DOC;MOC;GEN_QRC;GEN_UI;META;UI;QRC" ${ARGN})
set(CORRESPONDING__H_FILES "")
set(GLOBBED__H_FILES "")
if(_ORG_SOURCE)
foreach(_file ${_ORG_SOURCE})
string(REGEX REPLACE "(.*)\\.(txx|cpp|c|cxx)$" "\\1.h" H_FILE ${_file})
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${H_FILE}")
list(APPEND CORRESPONDING__H_FILES "${H_FILE}")
endif()
endforeach()
else()
file(GLOB_RECURSE GLOBBED__H_FILES *.h)
endif()
set(CORRESPONDING__H_FILES ${CORRESPONDING__H_FILES} PARENT_SCOPE)
set(GLOBBED__H_FILES ${GLOBBED__H_FILES} PARENT_SCOPE)
if(_ORG_GEN_QRC OR _ORG_GEN_UI OR _ORG_MOC)
source_group("Generated\\Qt QRC Source Files" FILES ${_ORG_GEN_QRC})
source_group("Generated\\Qt UI Header Files" FILES ${_ORG_GEN_UI})
source_group("Generated\\Qt MOC Source Files" FILES ${_ORG_MOC})
endif()
#_MACRO_APPEND_TO_LIST(_ORG_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/")
source_group("== Source Files ==" FILES ${_ORG_SOURCE})
#_MACRO_APPEND_TO_LIST(_ORG_TXX "${CMAKE_CURRENT_SOURCE_DIR}/")
source_group("== Template Files ==" FILES ${_ORG_TXX})
#_MACRO_APPEND_TO_LIST(_ORG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/")
source_group("== Header Files ==" FILES ${_ORG_HEADER} ${CORRESPONDING__H_FILES} ${GLOBBED__H_FILES})
if(_ORG_UI)
#_MACRO_APPEND_TO_LIST(_ORG_UI "${CMAKE_CURRENT_SOURCE_DIR}/")
source_group("QT UI Files" FILES ${_ORG_UI})
endif()
if(_ORG_DOC)
#_MACRO_APPEND_TO_LIST(_ORG_DOC "${CMAKE_CURRENT_SOURCE_DIR}/")
source_group("Doxygen Files" FILES ${_ORG_DOC})
endif()
if(_ORG_QRC)
#_MACRO_APPEND_TO_LIST(_ORG_QRC "${CMAKE_CURRENT_SOURCE_DIR}/")
source_group("Qt Resource Files" FILES ${_ORG_QRC})
endif()
if(_ORG_META)
source_group("Plugin META Files" FILES ${_ORG_META})
endif()
endfunction()
macro(_MACRO_APPEND_TO_LIST _list _value)
set(_origlist ${${_list}})
set(${_list} )
foreach(_element ${_origlist})
list(APPEND ${_list} "${_value}${_element}")
endforeach()
endmacro()
diff --git a/CMake/mitkFunctionWhitelists.cmake b/CMake/mitkFunctionWhitelists.cmake
index d956918f09..8e102ec54e 100644
--- a/CMake/mitkFunctionWhitelists.cmake
+++ b/CMake/mitkFunctionWhitelists.cmake
@@ -1,244 +1,244 @@
###############################################################################
#
# mitkFunctionCreateWhitelistPaths
#
#! Creates advanced cache variables for setting the internal and external
#! whitelist directories.
#!
#! USAGE:
#!
#! \code
#! mitkFunctionCreateWhitelistPaths(<prefix>)
#! \endcode
#!
#! The <prefix> parameter specifies the prefix used for the created variables
#! <prefix>_WHITELISTS_INTERNAL_PATH and <prefix>_WHITELISTS_EXTERNAL_PATH.
#!
#! Default values:
#! <prefix>_WHITELISTS_INTERNAL_PATH = <prefix>_SOURCE_DIR/CMake/Whitelists
#! <prefix>_WHITELISTS_EXTERNAL_PATH = %HOME%/.mitk/Whitelists
#!
#! List of variables available after the function is called:
#! - <prefix>_WHITELISTS_INTERNAL_PATH
#! - <prefix>_WHITELISTS_EXTERNAL_PATH
#!
#! Parameters:
#! \param <prefix> The prefix of the created cache variables.
#
###############################################################################
function(mitkFunctionCreateWhitelistPaths)
set(${ARGV0}_WHITELISTS_INTERNAL_PATH "${${ARGV0}_SOURCE_DIR}/CMake/Whitelists" CACHE PATH "")
set(${ARGV0}_WHITELISTS_EXTERNAL_PATH ".mitk/Whitelists")
if(WIN32)
set(${ARGV0}_WHITELISTS_EXTERNAL_PATH "$ENV{HOMEDRIVE}$ENV{HOMEPATH}/${${ARGV0}_WHITELISTS_EXTERNAL_PATH}")
else()
set(${ARGV0}_WHITELISTS_EXTERNAL_PATH "$ENV{HOME}/${${ARGV0}_WHITELISTS_EXTERNAL_PATH}")
endif()
FILE(TO_CMAKE_PATH "${${ARGV0}_WHITELISTS_EXTERNAL_PATH}" ${ARGV0}_WHITELISTS_EXTERNAL_PATH)
set(${ARGV0}_WHITELISTS_EXTERNAL_PATH "${${ARGV0}_WHITELISTS_EXTERNAL_PATH}" CACHE PATH "")
mark_as_advanced(
${ARGV0}_WHITELISTS_INTERNAL_PATH
${ARGV0}_WHITELISTS_EXTERNAL_PATH
)
endfunction()
###############################################################################
#
# mitkFunctionFindWhitelists
#
-#! Adds all whitelists found in specfied whitelist paths to the advanced cache
+#! Adds all whitelists found in specified whitelist paths to the advanced cache
#! variable <prefix>_WHITELIST as enumeration entries.
#!
#! USAGE:
#!
#! \code
#! mitkFunctionFindWhitelists(<prefix>)
#! \endcode
#!
#! The <prefix> parameter specifies the prefix used for the created
#! cache variable <prefix>_WHITELIST. Its default value is "None".
#! The function mitkFunctionCreateWhitelistPaths must be called
#! with the same <prefix> prior to this function.
#!
#! Whitelists are *.cmake files which set the two list variables
#! enabled_modules and enabled_plugins.
#!
#! List of variables available after the function is called:
#! - <prefix>_WHITELIST
#!
#! \sa mitkFunctionCreateWhitelistPaths
#!
#! Parameters:
#! \param <prefix> The prefix of the created cache variable.
#
###############################################################################
function(mitkFunctionFindWhitelists)
set(whitelists "None")
file(GLOB internalWhitelistFiles "${${ARGV0}_WHITELISTS_INTERNAL_PATH}/*.cmake")
foreach(whitelistFile ${internalWhitelistFiles})
get_filename_component(whitelistFile "${whitelistFile}" NAME_WE)
list(APPEND whitelists "${whitelistFile}")
endforeach()
file(GLOB externalWhitelistFiles "${${ARGV0}_WHITELISTS_EXTERNAL_PATH}/*.cmake")
foreach(whitelistFile ${externalWhitelistFiles})
get_filename_component(whitelistFile "${whitelistFile}" NAME_WE)
list(APPEND whitelists "${whitelistFile} (external)")
endforeach()
set(${ARGV0}_WHITELIST "None" CACHE STRING "")
set_property(CACHE ${ARGV0}_WHITELIST PROPERTY STRINGS ${whitelists})
mark_as_advanced(${ARGV0}_WHITELIST)
endfunction()
###############################################################################
#
# mitkFunctionWhitelistModules
#
#! Only enables modules which are present in the currently set whitelist or
#! all modules if no whitelist is specified at all.
#!
#! USAGE:
#!
#! \code
#! set(<modules>
#! ModuleDir
#! AnotherModuleDir
#! ...
#! )
#! mitkFunctionWhitelistModules(<prefix> <modules>)
#! \endcode
#!
#! The <prefix> parameter specifies the prefix used to get the
#! currently set whitelist from <prefix>_WHITELIST. Both functions
#! mitkFunctionCreateWhitelistPaths and mitkFunctionFindWhitelists
#! must be called with the same <prefix> prior to this function.
#! The <modules> list must contain the module directory names instead
#! of the module names itself, as the entries are used in
#! add_directory calls.
#!
#! \sa mitkFunctionCreateWhitelistPaths
#! \sa mitkFunctionFindWhitelists
#!
#! Parameters:
#! \param <prefix> The prefix of the white list cache variable.
#! \param <modules> The module directory list variable.
#
###############################################################################
function(mitkFunctionWhitelistModules)
if(${ARGV0}_WHITELIST STREQUAL "None")
foreach(module ${${ARGV1}})
add_subdirectory(Modules/${module})
endforeach()
else()
string(FIND "${${ARGV0}_WHITELIST}" " (external)" index REVERSE)
if(${index} EQUAL -1)
set(whitelistFile "${${ARGV0}_WHITELISTS_INTERNAL_PATH}/${${ARGV0}_WHITELIST}.cmake")
else()
string(SUBSTRING "${${ARGV0}_WHITELIST}" 0 ${index} whitelistFile)
set(whitelistFile "${${ARGV0}_WHITELISTS_EXTERNAL_PATH}/${whitelistFile}.cmake")
endif()
include(${whitelistFile})
if(NOT DEFINED enabled_modules)
message(FATAL_ERROR "Variable 'enabled_modules' not set in whitelist file '${whitelistFile}'!")
endif()
foreach(module ${${ARGV1}})
list(FIND enabled_modules ${module} index)
if(NOT index EQUAL -1)
add_subdirectory(Modules/${module})
endif()
endforeach()
endif()
endfunction()
###############################################################################
#
# mitkFunctionWhitelistPlugins
#
#! Only enables plugins which are present in the currently set whitelist or
#! all plugins if no whitelist is specified at all.
#!
#! USAGE:
#!
#! \code
#! set(<plugins>
#! org.example.plugin:OFF
#! org.example.another.plugin:ON
#! ...
#! )
#! mitkFunctionWhitelistPlugins(<prefix> <plugins>)
#! \endcode
#!
#! The <prefix> parameter specifies the prefix used to get the
#! currently set whitelist from <prefix>_WHITELIST. Both functions
#! mitkFunctionCreateWhitelistPaths and mitkFunctionFindWhitelists
#! must be called with the same <prefix> prior to this function.
#! The <plugins> list must contain the plugin names. This function
#! removes plugins not found in the currently set whitelist from
#! the <plugins> variable. Note that plugins which are OFF by
#! default are not switched on.
#!
#! \sa mitkFunctionCreateWhitelistPaths
#! \sa mitkFunctionFindWhitelists
#!
#! Parameters:
#! \param <prefix> The prefix of the white list cache variable.
#! \param <plugins> The plugin list variable to be modified.
#
###############################################################################
function(mitkFunctionWhitelistPlugins)
if(${ARGV0}_WHITELIST STREQUAL "None")
return()
endif()
string(FIND "${${ARGV0}_WHITELIST}" " (external)" index REVERSE)
if(${index} EQUAL -1)
set(whitelistFile "${${ARGV0}_WHITELISTS_INTERNAL_PATH}/${${ARGV0}_WHITELIST}.cmake")
else()
string(SUBSTRING "${${ARGV0}_WHITELIST}" 0 ${index} whitelistFile)
set(whitelistFile "${${ARGV0}_WHITELISTS_EXTERNAL_PATH}/${whitelistFile}.cmake")
endif()
include(${whitelistFile})
if(NOT DEFINED enabled_plugins)
message(FATAL_ERROR "Variable 'enabled_plugins' not set in whitelist file '${whitelistFile}'!")
endif()
set(plugins "")
foreach(plugin ${${ARGV1}})
string(FIND ${plugin} ":" index REVERSE)
if (NOT index EQUAL -1)
string(SUBSTRING ${plugin} 0 ${index} _plugin)
else()
set(_plugin ${plugin})
endif()
list(FIND enabled_plugins ${_plugin} index)
if(NOT index EQUAL -1)
list(APPEND plugins ${plugin})
endif()
endforeach()
set(${ARGV1} ${plugins} PARENT_SCOPE)
endfunction()
diff --git a/CMake/mitkMacroInstallHelperApp.cmake b/CMake/mitkMacroInstallHelperApp.cmake
index e27c3db629..e10d07b09e 100644
--- a/CMake/mitkMacroInstallHelperApp.cmake
+++ b/CMake/mitkMacroInstallHelperApp.cmake
@@ -1,74 +1,74 @@
-#! MITK specific cross plattform install macro
+#! MITK specific cross platform install macro
#!
#! Usage: MITK_INSTALL_HELPER_APP(target1 [target2] ....)
#!
macro(MITK_INSTALL_HELPER_APP)
cmake_parse_arguments(_install "GLOB_PLUGINS" "" "TARGETS;EXECUTABLES;PLUGINS;LIBRARY_DIRS" ${ARGN})
list(APPEND _install_TARGETS ${_install_DEFAULT_ARGS})
# TODO: how to supply to correct intermediate directory??
# CMAKE_CFG_INTDIR is not expanded to actual values inside the install(CODE "...") macro ...
set(intermediate_dir .)
if(WIN32)
set(intermediate_dir Release)
endif()
mitkFunctionGetLibrarySearchPaths(DIRS ${intermediate_dir})
if(APPLE)
list(APPEND DIRS "/usr/lib")
endif(APPLE)
foreach(_target ${_install_EXECUTABLES})
set(_qt_conf_install_dirs "")
set(_target_locations "")
get_filename_component(_target_name ${_target} NAME)
if(APPLE)
if(NOT MACOSX_BUNDLE_NAMES)
set(_qt_conf_install_dirs bin)
set(_target_locations bin/${_target_name})
install(PROGRAMS ${_target} 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/${_target_name})
list(APPEND _target_locations ${_current_target_location})
install(PROGRAMS ${_target} DESTINATION ${bundle_name}.app/Contents/MacOS/)
endforeach()
endif(NOT MACOSX_BUNDLE_NAMES)
else()
set(_target_location bin/${_target_name})
set(_qt_conf_install_dirs bin)
install(PROGRAMS ${_target} DESTINATION bin)
if(UNIX AND NOT WIN32)
# Remove the rpath from helper applications. We assume that all dependencies
# are installed into the same location as the helper application.
install(CODE "file(RPATH_REMOVE
FILE \"\${CMAKE_INSTALL_PREFIX}/${_target_location}\")")
endif()
endif()
foreach(_target_location ${_target_locations})
_fixup_target()
endforeach(_target_location)
#--------------------------------------------------------------------------------
# install a qt.conf file
# this inserts some cmake code into the install script to write the file
if(MITK_USE_Qt6)
set(_qt_conf_plugin_install_prefix .)
if(APPLE)
set(_qt_conf_plugin_install_prefix ./MacOS)
endif()
foreach(_qt_conf_install_dir ${_qt_conf_install_dirs})
install(CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${_qt_conf_install_dir}/qt.conf\" \"
[Paths]
Prefix=${_qt_conf_plugin_install_prefix}
\")")
endforeach()
endif()
endforeach()
endmacro(MITK_INSTALL_HELPER_APP)
diff --git a/CMake/mitkMacroInstallTargets.cmake b/CMake/mitkMacroInstallTargets.cmake
index c20837aae8..371c0218ef 100644
--- a/CMake/mitkMacroInstallTargets.cmake
+++ b/CMake/mitkMacroInstallTargets.cmake
@@ -1,71 +1,71 @@
#
-# MITK specific cross plattform install macro
+# MITK specific cross platform install macro
#
# Usage: MITK_INSTALL_TARGETS(target1 [target2] ....)
#
macro(MITK_INSTALL_TARGETS)
cmake_parse_arguments(_install "GLOB_PLUGINS" "" "TARGETS;EXECUTABLES;PLUGINS;LIBRARY_DIRS" ${ARGN})
list(APPEND _install_TARGETS ${_install_DEFAULT_ARGS})
# TODO: how to supply the correct intermediate directory??
# CMAKE_CFG_INTDIR is not expanded to actual values inside the install(CODE "...") macro ...
set(intermediate_dir .)
if(WIN32)
set(intermediate_dir Release)
endif()
foreach(_target ${_install_EXECUTABLES})
get_target_property(_is_bundle ${_target} MACOSX_BUNDLE)
set(_qt_conf_install_dirs "")
set(_target_locations "")
if(APPLE)
if(_is_bundle)
set(_target_locations ${_target}.app)
set(${_target_locations}_qt_plugins_install_dir ${_target}.app/Contents/MacOS)
set(_bundle_dest_dir ${_target}.app/Contents/MacOS)
set(_qt_conf_install_dirs ${_target}.app/Contents/Resources)
install(TARGETS ${_target} BUNDLE DESTINATION . )
else()
if(NOT MACOSX_BUNDLE_NAMES)
set(_qt_conf_install_dirs bin)
set(_target_locations bin/${_target})
install(TARGETS ${_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/${_target})
list(APPEND _target_locations ${_current_target_location})
install(TARGETS ${_target} RUNTIME DESTINATION ${bundle_name}.app/Contents/MacOS/)
endforeach()
endif()
endif()
else()
set(_target_locations bin/${_target}${CMAKE_EXECUTABLE_SUFFIX})
set(_qt_conf_install_dirs bin)
install(TARGETS ${_target} RUNTIME DESTINATION bin)
endif()
foreach(_target_location ${_target_locations})
_fixup_target()
endforeach()
#--------------------------------------------------------------------------------
# install a qt.conf file
# this inserts some cmake code into the install script to write the file
if(MITK_USE_Qt6)
set(_qt_conf_plugin_install_prefix .)
if(APPLE)
set(_qt_conf_plugin_install_prefix ./MacOS)
endif()
foreach(_qt_conf_install_dir ${_qt_conf_install_dirs})
install(CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${_qt_conf_install_dir}/qt.conf\" \"
[Paths]
Prefix=${_qt_conf_plugin_install_prefix}
\")")
endforeach()
endif()
endforeach()
endmacro()
diff --git a/CMake/mitkMacroMultiplexPicType.cmake b/CMake/mitkMacroMultiplexPicType.cmake
index d95bb50501..584bc8c8d1 100644
--- a/CMake/mitkMacroMultiplexPicType.cmake
+++ b/CMake/mitkMacroMultiplexPicType.cmake
@@ -1,20 +1,20 @@
#
# MITK_MULTIPLEX_PICTYPE: generate separated source files for different
# data types to reduce memory consumption of compiler during template
# instantiation
#
# Param "file" should be named like mitkMyAlgo-TYPE.cpp
-# in the file, every occurence of @TYPE@ is replaced by the
+# in the file, every occurrence of @TYPE@ is replaced by the
# datatype. For each datatype, a new file mitkMyAlgo-datatype.cpp
# is generated and added to CPP_FILES_GENERATED.
#
macro(MITK_MULTIPLEX_PICTYPE file)
string(REPLACE "," ";" TYPES "${MITK_ACCESSBYITK_PIXEL_TYPES}")
foreach(TYPE ${TYPES})
# create filename for destination
string(REPLACE " " "_" quoted_type "${TYPE}")
string(REPLACE TYPE ${quoted_type} quoted_file ${file})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${file} ${CMAKE_CURRENT_BINARY_DIR}/${quoted_file} @ONLY)
set(CPP_FILES_GENERATED ${CPP_FILES_GENERATED} ${CMAKE_CURRENT_BINARY_DIR}/${quoted_file})
endforeach(TYPE)
endmacro(MITK_MULTIPLEX_PICTYPE)
diff --git a/CMake/mitkSwigAddLibraryDependencies.cmake b/CMake/mitkSwigAddLibraryDependencies.cmake
index 5b46f0fc8f..58d381cf35 100644
--- a/CMake/mitkSwigAddLibraryDependencies.cmake
+++ b/CMake/mitkSwigAddLibraryDependencies.cmake
@@ -1,30 +1,30 @@
#! This CMake macro adds the necessary library and incllude
#! directories to a swig-project.
#!
#! params:
#! swig_module : Name of the SWIG module, for example pyMITK
#! library_names : Semicolon separated list of the libraries that are included, for example "MitkCore;MitkLog"
#!
# function inspired by
# https://stackoverflow.com/questions/37205274/swig-and-cmake-make-use-of-information-provided-by-target-include-directories
# This function tells cmake which additional dependencies are existing
# especially with respect to the linker dependencies.
function(mitkSwigAddLibraryDependencies swig_module library_names)
foreach(library_name ${library_names})
# Adding each library as a linker dependency:
swig_link_libraries(${swig_module} ${library_name})
# Extracting all include directories from each given project and
# then including these directories to the newly created swig project.
get_property(LIBRARY_INCLUDES
TARGET ${library_name}
PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
- # Checking each given librarie to include all includes from this library.
+ # Checking each given library to include all includes from this library.
endforeach()
# In addition include python dependencies:
include_directories( ${PYTHON_INCLUDE_DIR})
endfunction()
diff --git a/CMake/mitkTargetLinkLibrariesWithDynamicLookup.cmake b/CMake/mitkTargetLinkLibrariesWithDynamicLookup.cmake
index 52eea078a2..d4424b699e 100644
--- a/CMake/mitkTargetLinkLibrariesWithDynamicLookup.cmake
+++ b/CMake/mitkTargetLinkLibrariesWithDynamicLookup.cmake
@@ -1,581 +1,581 @@
#.rst:
#
# Public Functions
# ^^^^^^^^^^^^^^^^
#
# The following functions are defined:
#
# .. cmake:command:: mitk_target_link_libraries_with_dynamic_lookup
#
# ::
#
# mitk_target_link_libraries_with_dynamic_lookup(<Target> [<Libraries>])
#
#
# Useful to "weakly" link a loadable module. For example, it should be used
# when compiling a loadable module when the symbols should be resolve from
# the run-time environment where the module is loaded, and not a specific
# system library.
#
# Like proper linking, except that the given ``<Libraries>`` are not necessarily
# linked. Instead, the ``<Target>`` is produced in a manner that allows for
# symbols unresolved within it to be resolved at runtime, presumably by the
# given ``<Libraries>``. If such a target can be produced, the provided
# ``<Libraries>`` are not actually linked.
#
# It links a library to a target such that the symbols are resolved at
# run-time not link-time.
#
# The linker is checked to see if it supports undefined
# symbols when linking a shared library. If it does then the library
# is not linked when specified with this function.
#
# On platforms that do not support weak-linking, this function works just
# like ``mitk_target_link_libraries``.
#
# .. note::
#
# For OSX it uses ``undefined dynamic_lookup``. This is similar to using
# ``-shared`` on Linux where undefined symbols are ignored.
#
# For more details, see `blog <https://blog.tim-smith.us/2015/09/python-extension-modules-os-x/>`_
# from Tim D. Smith.
#
#
# .. cmake:command:: mitk_check_dynamic_lookup
#
# Check if the linker requires a command line flag to allow leaving symbols
# unresolved when producing a target of type ``<TargetType>`` that is
# weakly-linked against a dependency of type ``<LibType>``.
#
# ``<TargetType>``
# can be one of "STATIC", "SHARED", "MODULE", or "EXE".
#
# ``<LibType>``
# can be one of "STATIC", "SHARED", or "MODULE".
#
# Long signature:
#
# ::
#
# mitk_check_dynamic_lookup(<TargetType>
# <LibType>
# <ResultVar>
# [<LinkFlagsVar>])
#
#
# Short signature:
#
# ::
#
# mitk_check_dynamic_lookup(<ResultVar>) # <TargetType> set to "MODULE"
# # <LibType> set to "SHARED"
#
#
# The result is cached between invocations and recomputed only when the value
# of CMake's linker flag list changes; ``CMAKE_STATIC_LINKER_FLAGS`` if
# ``<TargetType>`` is "STATIC", and ``CMAKE_SHARED_LINKER_FLAGS`` otherwise.
#
#
# Defined variables:
#
# ``<ResultVar>``
# Whether the current C toolchain supports weak-linking for target binaries of
# type ``<TargetType>`` that are weakly-linked against a dependency target of
# type ``<LibType>``.
#
# ``<LinkFlagsVar>``
# List of flags to add to the linker command to produce a working target
# binary of type ``<TargetType>`` that is weakly-linked against a dependency
# target of type ``<LibType>``.
#
# ``HAS_DYNAMIC_LOOKUP_<TargetType>_<LibType>``
# Cached, global alias for ``<ResultVar>``
#
# ``DYNAMIC_LOOKUP_FLAGS_<TargetType>_<LibType>``
# Cached, global alias for ``<LinkFlagsVar>``
#
#
# Private Functions
# ^^^^^^^^^^^^^^^^^
#
# The following private functions are defined:
#
# .. warning:: These functions are not part of the scikit-build API. They
# exist purely as an implementation detail and may change from version
# to version without notice, or even be removed.
#
# We mean it.
#
#
# .. cmake:command:: _get_target_type
#
# ::
#
# _get_target_type(<ResultVar> <Target>)
#
#
# Shorthand for querying an abbreviated version of the target type
# of the given ``<Target>``.
#
# ``<ResultVar>`` is set to:
#
# - "STATIC" for a STATIC_LIBRARY,
# - "SHARED" for a SHARED_LIBRARY,
# - "MODULE" for a MODULE_LIBRARY,
# - and "EXE" for an EXECUTABLE.
#
# Defined variables:
#
# ``<ResultVar>``
# The abbreviated version of the ``<Target>``'s type.
#
#
# .. cmake:command:: _test_weak_link_project
#
# ::
#
# _test_weak_link_project(<TargetType>
# <LibType>
# <ResultVar>
# <LinkFlagsVar>)
#
#
# Attempt to compile and run a test project where a target of type
# ``<TargetType>`` is weakly-linked against a dependency of type ``<LibType>``:
#
# - ``<TargetType>`` can be one of "STATIC", "SHARED", "MODULE", or "EXE".
# - ``<LibType>`` can be one of "STATIC", "SHARED", or "MODULE".
#
# Defined variables:
#
# ``<ResultVar>``
# Whether the current C toolchain can produce a working target binary of type
# ``<TargetType>`` that is weakly-linked against a dependency target of type
# ``<LibType>``.
#
# ``<LinkFlagsVar>``
# List of flags to add to the linker command to produce a working target
# binary of type ``<TargetType>`` that is weakly-linked against a dependency
# target of type ``<LibType>``.
#
function(_get_target_type result_var target)
set(target_type "SHARED_LIBRARY")
if(TARGET ${target})
get_property(target_type TARGET ${target} PROPERTY TYPE)
endif()
set(result "STATIC")
if(target_type STREQUAL "STATIC_LIBRARY")
set(result "STATIC")
endif()
if(target_type STREQUAL "SHARED_LIBRARY")
set(result "SHARED")
endif()
if(target_type STREQUAL "MODULE_LIBRARY")
set(result "MODULE")
endif()
if(target_type STREQUAL "EXECUTABLE")
set(result "EXE")
endif()
set(${result_var} ${result} PARENT_SCOPE)
endfunction()
function(_test_weak_link_project
target_type
lib_type
can_weak_link_var
project_name)
set(gnu_ld_ignore "-Wl,--unresolved-symbols=ignore-all")
set(osx_dynamic_lookup "-undefined dynamic_lookup")
set(no_flag "")
foreach(link_flag_spec gnu_ld_ignore osx_dynamic_lookup no_flag)
set(link_flag "${${link_flag_spec}}")
set(test_project_dir "${PROJECT_BINARY_DIR}/CMakeTmp")
set(test_project_dir "${test_project_dir}/${project_name}")
set(test_project_dir "${test_project_dir}/${link_flag_spec}")
set(test_project_dir "${test_project_dir}/${target_type}")
set(test_project_dir "${test_project_dir}/${lib_type}")
set(test_project_src_dir "${test_project_dir}/src")
set(test_project_bin_dir "${test_project_dir}/build")
file(MAKE_DIRECTORY ${test_project_src_dir})
file(MAKE_DIRECTORY ${test_project_bin_dir})
set(mod_type "STATIC")
set(link_mod_lib TRUE)
set(link_exe_lib TRUE)
set(link_exe_mod FALSE)
if("${target_type}" STREQUAL "EXE")
set(link_exe_lib FALSE)
set(link_exe_mod TRUE)
else()
set(mod_type "${target_type}")
endif()
if("${mod_type}" STREQUAL "MODULE")
set(link_mod_lib FALSE)
endif()
file(WRITE "${test_project_src_dir}/CMakeLists.txt" "
cmake_minimum_required(VERSION ${CMAKE_VERSION})
project(${project_name} C)
include_directories(${test_project_src_dir})
add_library(number ${lib_type} number.c)
add_library(counter ${mod_type} counter.c)
")
if("${mod_type}" STREQUAL "MODULE")
file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
set_target_properties(counter PROPERTIES PREFIX \"\")
")
endif()
if(link_mod_lib)
file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
target_link_libraries(counter number)
")
elseif(NOT link_flag STREQUAL "")
file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
set_target_properties(counter PROPERTIES LINK_FLAGS \"${link_flag}\")
")
endif()
file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
add_executable(main main.c)
")
if(link_exe_lib)
file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
target_link_libraries(main number)
")
elseif(NOT link_flag STREQUAL "")
file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
target_link_libraries(main \"${link_flag}\")
")
endif()
if(link_exe_mod)
file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
target_link_libraries(main counter)
")
else()
file(APPEND "${test_project_src_dir}/CMakeLists.txt" "
target_link_libraries(main \"${CMAKE_DL_LIBS}\")
")
endif()
file(WRITE "${test_project_src_dir}/number.c" "
#include <number.h>
static int _number;
void set_number(int number) { _number = number; }
int get_number() { return _number; }
")
file(WRITE "${test_project_src_dir}/number.h" "
#ifndef _NUMBER_H
#define _NUMBER_H
extern void set_number(int);
extern int get_number(void);
#endif
")
file(WRITE "${test_project_src_dir}/counter.c" "
#include <number.h>
int count() {
int result = get_number();
set_number(result + 1);
return result;
}
")
file(WRITE "${test_project_src_dir}/counter.h" "
#ifndef _COUNTER_H
#define _COUNTER_H
extern int count(void);
#endif
")
file(WRITE "${test_project_src_dir}/main.c" "
#include <stdlib.h>
#include <stdio.h>
#include <number.h>
")
if(NOT link_exe_mod)
file(APPEND "${test_project_src_dir}/main.c" "
#include <dlfcn.h>
")
endif()
file(APPEND "${test_project_src_dir}/main.c" "
int my_count() {
int result = get_number();
set_number(result + 1);
return result;
}
int main(int argc, char **argv) {
int result;
")
if(NOT link_exe_mod)
file(APPEND "${test_project_src_dir}/main.c" "
void *counter_module;
int (*count)(void);
counter_module = dlopen(\"./counter.so\", RTLD_LAZY | RTLD_GLOBAL);
if(!counter_module) goto error;
count = dlsym(counter_module, \"count\");
if(!count) goto error;
")
endif()
file(APPEND "${test_project_src_dir}/main.c" "
result = count() != 0 ? EXIT_FAILURE :
my_count() != 1 ? EXIT_FAILURE :
my_count() != 2 ? EXIT_FAILURE :
count() != 3 ? EXIT_FAILURE :
count() != 4 ? EXIT_FAILURE :
count() != 5 ? EXIT_FAILURE :
my_count() != 6 ? EXIT_FAILURE : EXIT_SUCCESS;
")
if(NOT link_exe_mod)
file(APPEND "${test_project_src_dir}/main.c" "
goto done;
error:
- fprintf(stderr, \"Error occured:\\n %s\\n\", dlerror());
+ fprintf(stderr, \"Error occurred:\\n %s\\n\", dlerror());
result = 1;
done:
if(counter_module) dlclose(counter_module);
")
endif()
file(APPEND "${test_project_src_dir}/main.c" "
return result;
}
")
set(_rpath_arg)
if(APPLE AND ${CMAKE_VERSION} VERSION_GREATER 2.8.11)
set(_rpath_arg "-DCMAKE_MACOSX_RPATH='${CMAKE_MACOSX_RPATH}'")
endif()
try_compile(project_compiles
"${test_project_bin_dir}"
"${test_project_src_dir}"
"${project_name}"
CMAKE_FLAGS
"-DCMAKE_SHARED_LINKER_FLAGS='${CMAKE_SHARED_LINKER_FLAGS}'"
"-DCMAKE_ENABLE_EXPORTS=ON"
${_rpath_arg}
OUTPUT_VARIABLE compile_output)
set(project_works 1)
set(run_output)
if(project_compiles)
execute_process(COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR}
"${test_project_bin_dir}/main"
WORKING_DIRECTORY "${test_project_bin_dir}"
RESULT_VARIABLE project_works
OUTPUT_VARIABLE run_output
ERROR_VARIABLE run_output)
endif()
set(test_description
"Weak Link ${target_type} -> ${lib_type} (${link_flag_spec})")
if(project_works EQUAL 0)
set(project_works TRUE)
message(STATUS "Performing Test ${test_description} - Success")
else()
set(project_works FALSE)
message(STATUS "Performing Test ${test_description} - Failed")
file(APPEND ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CMakeError.log
"Performing Test ${test_description} failed with the "
"following output:\n"
"BUILD\n-----\n${compile_output}\nRUN\n---\n${run_output}\n")
endif()
set(${can_weak_link_var} ${project_works} PARENT_SCOPE)
if(project_works)
set(${project_name} ${link_flag} PARENT_SCOPE)
break()
endif()
endforeach()
endfunction()
function(mitk_check_dynamic_lookup)
# Two signatures are supported:
if(ARGC EQUAL "1")
#
# mitk_check_dynamic_lookup(<ResultVar>)
#
set(target_type "MODULE")
set(lib_type "SHARED")
set(has_dynamic_lookup_var "${ARGV0}")
set(link_flags_var "unused")
elseif(ARGC GREATER "2")
#
# mitk_check_dynamic_lookup(<TargetType>
# <LibType>
# <ResultVar>
# [<LinkFlagsVar>])
#
set(target_type "${ARGV0}")
set(lib_type "${ARGV1}")
set(has_dynamic_lookup_var "${ARGV2}")
if(ARGC EQUAL "3")
set(link_flags_var "unused")
else()
set(link_flags_var "${ARGV3}")
endif()
else()
message(FATAL_ERROR "missing arguments")
endif()
_check_dynamic_lookup(
${target_type}
${lib_type}
${has_dynamic_lookup_var}
${link_flags_var}
)
set(${has_dynamic_lookup_var} ${${has_dynamic_lookup_var}} PARENT_SCOPE)
if(NOT "x${link_flags_var}x" MATCHES "^xunusedx$")
set(${link_flags_var} ${${link_flags_var}} PARENT_SCOPE)
endif()
endfunction()
function(_check_dynamic_lookup
target_type
lib_type
has_dynamic_lookup_var
link_flags_var
)
# hash the CMAKE_FLAGS passed and check cache to know if we need to rerun
if("${target_type}" STREQUAL "STATIC")
string(MD5 cmake_flags_hash "${CMAKE_STATIC_LINKER_FLAGS}")
else()
string(MD5 cmake_flags_hash "${CMAKE_SHARED_LINKER_FLAGS}")
endif()
set(cache_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}")
set(cache_hash_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}_hash")
set(result_var "DYNAMIC_LOOKUP_FLAGS_${target_type}_${lib_type}")
if( NOT DEFINED ${cache_hash_var}
OR NOT "${${cache_hash_var}}" STREQUAL "${cmake_flags_hash}")
unset(${cache_var} CACHE)
endif()
if(NOT DEFINED ${cache_var})
set(skip_test FALSE)
if(CMAKE_CROSSCOMPILING AND NOT CMAKE_CROSSCOMPILING_EMULATOR)
set(skip_test TRUE)
endif()
if(skip_test)
set(has_dynamic_lookup FALSE)
set(link_flags)
else()
_test_weak_link_project(${target_type}
${lib_type}
has_dynamic_lookup
link_flags)
endif()
set(caveat " (when linking ${target_type} against ${lib_type})")
set(${cache_var} "${has_dynamic_lookup}"
CACHE BOOL
"linker supports dynamic lookup for undefined symbols${caveat}")
mark_as_advanced(${cache_var})
set(${result_var} "${link_flags}"
CACHE STRING
"linker flags for dynamic lookup${caveat}")
mark_as_advanced(${result_var})
set(${cache_hash_var} "${cmake_flags_hash}"
CACHE INTERNAL "hashed flags for ${cache_var} check")
endif()
set(${has_dynamic_lookup_var} "${${cache_var}}" PARENT_SCOPE)
set(${link_flags_var} "${${result_var}}" PARENT_SCOPE)
endfunction()
function(mitk_target_link_libraries_with_dynamic_lookup target)
_get_target_type(target_type ${target})
set(link_props)
set(link_items)
set(link_libs)
foreach(lib ${ARGN})
_get_target_type(lib_type ${lib})
mitk_check_dynamic_lookup(${target_type}
${lib_type}
has_dynamic_lookup
dynamic_lookup_flags)
if(has_dynamic_lookup)
if(dynamic_lookup_flags)
if("${target_type}" STREQUAL "EXE")
list(APPEND link_items "${dynamic_lookup_flags}")
else()
list(APPEND link_props "${dynamic_lookup_flags}")
endif()
endif()
else()
list(APPEND link_libs "${lib}")
endif()
endforeach()
if(link_props)
list(REMOVE_DUPLICATES link_props)
endif()
if(link_items)
list(REMOVE_DUPLICATES link_items)
endif()
if(link_libs)
list(REMOVE_DUPLICATES link_libs)
endif()
if(link_props)
set_target_properties(${target}
PROPERTIES LINK_FLAGS "${link_props}")
endif()
set(links "${link_items}" "${link_libs}")
if(links)
target_link_libraries(${target} "${links}")
endif()
endfunction()
diff --git a/CMake/mitkTestDriverFiles.cmake.in b/CMake/mitkTestDriverFiles.cmake.in
index c7538a7495..94174a6124 100644
--- a/CMake/mitkTestDriverFiles.cmake.in
+++ b/CMake/mitkTestDriverFiles.cmake.in
@@ -1,47 +1,47 @@
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/files.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/files.cmake)
endif()
# Write a header file containing include directives and custom code
# for the test driver.
set(TESTDRIVER_EXTRA_INCLUDES )
list(APPEND MODULE_TEST_EXTRA_DRIVER_INCLUDE "mitkLogBackend.h")
list(REMOVE_DUPLICATES MODULE_TEST_EXTRA_DRIVER_INCLUDE)
foreach(_include ${MODULE_TEST_EXTRA_DRIVER_INCLUDE})
set(TESTDRIVER_EXTRA_INCLUDES "${TESTDRIVER_EXTRA_INCLUDES}
#include <${_include}>")
endforeach()
set(TESTDRIVER_EXTRA_INCLUDES "${TESTDRIVER_EXTRA_INCLUDES}
#include <vector>
std::vector<std::string> globalCmdLineArgs;")
set(_extra_include_file ${CMAKE_CURRENT_BINARY_DIR}/${TESTDRIVER}_extras.h)
configure_file(${MITK_CMAKE_DIR}/mitkTestDriverExtraIncludes.h.in ${_extra_include_file})
set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN "
for (int avIndex = 1; avIndex < ac; ++avIndex) globalCmdLineArgs.push_back(av[avIndex]);
mitk::LogBackend::Register();
${MODULE_TEST_EXTRA_DRIVER_INIT};"
)
set(CMAKE_TESTDRIVER_AFTER_TESTMAIN "mitk::LogBackend::Unregister();")
create_test_sourcelist(_test_cpp_files ${MODULE_NAME}_main.cpp
${MODULE_TESTS} ${MODULE_RENDERING_TESTS} ${MODULE_IMAGE_TESTS} ${MODULE_SURFACE_TESTS} ${MODULE_CUSTOM_TESTS}
EXTRA_INCLUDE ${_extra_include_file}
)
list(APPEND CPP_FILES ${_test_cpp_files})
# Some old CMake scripts use TEST_CPP_FILES in their files.cmake
-# file of the test driver to add source fiels to the executable
+# file of the test driver to add source files to the executable
# (they should just use CPP_FILES like in any other files.cmake
# file instead).
if(TEST_CPP_FILES)
list(APPEND CPP_FILES ${TEST_CPP_FILES})
endif()
if(MODULE_RESOURCE_FILES)
list(APPEND RESOURCE_FILES ${MODULE_RESOURCE_FILES})
endif()
diff --git a/CMakeExternals/Boost.cmake b/CMakeExternals/Boost.cmake
index 323448f549..61bdf80809 100644
--- a/CMakeExternals/Boost.cmake
+++ b/CMakeExternals/Boost.cmake
@@ -1,343 +1,343 @@
#-----------------------------------------------------------------------------
# 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})
- set(Boost_DIR "${Boost_ROOT}/lib/cmake/Boost-1.82.0")
+ set(Boost_DIR "${Boost_ROOT}/lib/cmake/Boost-1.85.0")
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_82_0.tar.gz")
- set(md5 f7050f554a65f6a42ece221eaeec1660)
+ set(url "${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/boost_1_85_0_patched.tar.gz")
+ set(md5 8f7c3012a5b64d9b0b07b20cde12d989)
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)
+ elseif(VISUAL_STUDIO_VERSION_MAJOR EQUAL 14 AND VISUAL_STUDIO_VERSION_MINOR LESS 50)
#[[ 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_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 d9a4c606fa..1ca85b73e8 100644
--- a/CMakeExternals/DCMQI.cmake
+++ b/CMakeExternals/DCMQI.cmake
@@ -1,65 +1,65 @@
#-----------------------------------------------------------------------------
# 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)
mitk_query_custom_ep_vars()
set(proj_DEPENDENCIES DCMTK ITK)
set(DCMQI_DEPENDS ${proj} ${${proj}_CUSTOM_DEPENDENCIES})
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()
ExternalProject_Add(${proj}
LIST_SEPARATOR ${sep}
GIT_REPOSITORY https://github.com/QIICR/dcmqi.git
- GIT_TAG v1.2.5
+ GIT_TAG v1.3.2
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=${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/Eigen.cmake b/CMakeExternals/Eigen.cmake
deleted file mode 100644
index eb734a01b6..0000000000
--- a/CMakeExternals/Eigen.cmake
+++ /dev/null
@@ -1,45 +0,0 @@
-#-----------------------------------------------------------------------------
-# 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 Boost)
- set(Eigen_DEPENDS ${proj})
-
- if(NOT DEFINED Eigen_DIR)
-
- ExternalProject_Add(${proj}
- LIST_SEPARATOR ${sep}
- GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
- GIT_TAG 3.4.0
- CMAKE_GENERATOR ${gen}
- CMAKE_GENERATOR_PLATFORM ${gen_platform}
- CMAKE_ARGS
- ${ep_common_args}
- "-DBoost_DIR:PATH=${Boost_DIR}"
- -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}
- DEPENDS ${proj_DEPENDENCIES}
- )
-
- set(Eigen_DIR ${ep_prefix})
- mitkFunctionInstallExternalCMakeProject(${proj})
-
- else()
-
- mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
-
- endif()
-
-endif()
diff --git a/CMakeExternals/ExternalProjectList.cmake b/CMakeExternals/ExternalProjectList.cmake
index cdccff2a8d..dfd6ab5826 100644
--- a/CMakeExternals/ExternalProjectList.cmake
+++ b/CMakeExternals/ExternalProjectList.cmake
@@ -1,30 +1,30 @@
mitkFunctionAddExternalProject(NAME Poco ON COMPONENTS Foundation Net Util XML Zip)
mitkFunctionAddExternalProject(NAME DCMTK ON DOC "EXPERIMENTAL, superbuild only: Use DCMTK in MITK")
mitkFunctionAddExternalProject(NAME tinyxml2 ON ADVANCED)
mitkFunctionAddExternalProject(NAME GDCM ON ADVANCED)
mitkFunctionAddExternalProject(NAME Boost ON NO_CACHE)
-mitkFunctionAddExternalProject(NAME Eigen ON DEPENDS Boost ADVANCED DOC "Use the Eigen library")
mitkFunctionAddExternalProject(NAME ANN ON ADVANCED DOC "Use Approximate Nearest Neighbor Library")
mitkFunctionAddExternalProject(NAME CppUnit ON ADVANCED DOC "Use CppUnit for unit tests")
mitkFunctionAddExternalProject(NAME HDF5 ON ADVANCED)
mitkFunctionAddExternalProject(NAME ITK ON NO_CACHE DEPENDS HDF5)
mitkFunctionAddExternalProject(NAME VTK ON NO_CACHE)
mitkFunctionAddExternalProject(NAME ZLIB OFF ADVANCED)
mitkFunctionAddExternalProject(NAME lz4 ON ADVANCED)
mitkFunctionAddExternalProject(NAME cpprestsdk OFF DEPENDS Boost ZLIB ADVANCED)
mitkFunctionAddExternalProject(NAME ACVD OFF DOC "Use Approximated Centroidal Voronoi Diagrams")
mitkFunctionAddExternalProject(NAME CTK ON DEPENDS Qt6 DCMTK DOC "Use CTK in MITK")
mitkFunctionAddExternalProject(NAME DCMQI ON DEPENDS DCMTK ITK DOC "Use dcmqi in MITK")
mitkFunctionAddExternalProject(NAME MatchPoint OFF ADVANCED DEPENDS Boost ITK DOC "Use the MatchPoint translation image registration library")
mitkFunctionAddExternalProject(NAME nlohmann_json ON ADVANCED)
+mitkFunctionAddExternalProject(NAME httplib ON DEPENDS ZLIB)
if(MITK_USE_Qt6)
mitkFunctionAddExternalProject(NAME Qt6Qwt6 ON ADVANCED DEPENDS Qt6)
endif()
if(UNIX AND NOT APPLE)
mitkFunctionAddExternalProject(NAME PCRE OFF ADVANCED NO_PACKAGE)
mitkFunctionAddExternalProject(NAME SWIG OFF ADVANCED NO_PACKAGE DEPENDS PCRE)
elseif(WIN32)
mitkFunctionAddExternalProject(NAME SWIG OFF ADVANCED NO_PACKAGE)
endif()
diff --git a/CMakeExternals/ITK.cmake b/CMakeExternals/ITK.cmake
index c7445485ba..e2a4d2889b 100644
--- a/CMakeExternals/ITK.cmake
+++ b/CMakeExternals/ITK.cmake
@@ -1,78 +1,78 @@
#-----------------------------------------------------------------------------
# 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)
mitk_query_custom_ep_vars()
set(proj_DEPENDENCIES GDCM ${${proj}_CUSTOM_DEPENDENCIES})
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
)
# 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()
ExternalProject_Add(${proj}
LIST_SEPARATOR ${sep}
UPDATE_COMMAND ""
GIT_REPOSITORY https://github.com/InsightSoftwareConsortium/ITK.git
- GIT_TAG 80446b1c18fde0aaca3eecab6e1e7a30d015ac2c # tag: v5.4rc02
+ GIT_TAG 311b7060ef39e371f3cd209ec135284ff5fde735 # tag: v5.4.0
CMAKE_GENERATOR ${gen}
CMAKE_GENERATOR_PLATFORM ${gen_platform}
CMAKE_ARGS
${ep_common_args}
${additional_cmake_args}
-DITK_SKIP_PATH_LENGTH_CHECKS:BOOL=ON
-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}
-DModule_GrowCut:BOOL=ON
${${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 c0f57adf08..8a3310684c 100644
--- a/CMakeExternals/MatchPoint.cmake
+++ b/CMakeExternals/MatchPoint.cmake
@@ -1,72 +1,72 @@
#-----------------------------------------------------------------------------
# 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 Boost ITK)
set(MatchPoint_DEPENDS ${proj})
if(NOT MatchPoint_DIR)
set(additional_cmake_args)
if(MatchPoint_SOURCE_DIR)
set(download_step SOURCE_DIR ${MatchPoint_SOURCE_DIR})
else()
set(download_step
GIT_REPOSITORY https://github.com/MIC-DKFZ/MatchPoint.git
- GIT_TAG a45efdf9
+ GIT_TAG 56957430a6a1665707bcbcec36ca9dc0248e7fc7 # 04/06/2024
)
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
"-DBoost_DIR:PATH=${Boost_DIR}"
-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/httplib.cmake b/CMakeExternals/httplib.cmake
new file mode 100644
index 0000000000..0537909e10
--- /dev/null
+++ b/CMakeExternals/httplib.cmake
@@ -0,0 +1,44 @@
+set(proj httplib)
+set(proj_DEPENDENCIES ZLIB)
+
+if(MITK_USE_${proj})
+ set(${proj}_DEPENDS ${proj})
+
+ if(DEFINED ${proj}_DIR AND NOT EXISTS ${${proj}_DIR})
+ message(FATAL_ERROR "${proj}_DIR variable is defined but corresponds to non-existing directory!")
+ endif()
+
+ if(NOT DEFINED ${proj}_DIR)
+ set(cmake_cache_args
+ ${ep_common_cache_args}
+ -DHTTPLIB_REQUIRE_OPENSSL:BOOL=ON
+ -DHTTPLIB_REQUIRE_ZLIB:BOOL=ON
+ -DHTTPLIB_USE_BROTLI_IF_AVAILABLE:BOOL=OFF
+ )
+
+ if(OPENSSL_ROOT_DIR)
+ list(APPEND cmake_cache_args
+ -DOPENSSL_ROOT_DIR:PATH=${OPENSSL_ROOT_DIR}
+ )
+ endif()
+
+ if(OPENSSL_VERSION VERSION_GREATER_EQUAL 3)
+ set(GIT_TAG 5c00bbf36ba8ff47b4fb97712fc38cb2884e5b98) # v0.15.3 cpp-httplib
+ else()
+ set(GIT_TAG cbca63f091ef1147ff57e90eb1ee5e558aa05d2c) # v0.14.3 cpp-httplib fallback version with OPENSSLv1 support
+ endif()
+
+ ExternalProject_Add(${proj}
+ GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
+ GIT_TAG ${GIT_TAG}
+ CMAKE_ARGS ${ep_common_args}
+ CMAKE_CACHE_ARGS ${cmake_cache_args}
+ CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args}
+ DEPENDS ${proj_DEPENDENCIES}
+ )
+
+ set(${proj}_DIR ${ep_prefix})
+ else()
+ mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
+ endif()
+endif()
diff --git a/CMakeLists.txt b/CMakeLists.txt
index adb7e97452..82673124b8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,1413 +1,1415 @@
#[[ 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)
if(POLICY CMP0135)
cmake_policy(SET CMP0135 NEW) # https://cmake.org/cmake/help/v3.24/policy/CMP0135.html
endif()
#-----------------------------------------------------------------------------
# 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 2023.12.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()
if(CMAKE_COMPILER_IS_GNUCXX)
mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION)
else()
set(GCC_VERSION 0)
endif()
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++<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++${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_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(MITK_BUILD_CONFIGURATION "WorkbenchRelease" 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_Qt6 "Use Qt 6 library" ON)
if(MITK_USE_Qt6)
set(MITK_QT6_MINIMUM_VERSION 6.6)
set(MITK_QT6_COMPONENTS
Concurrent
Core
Core5Compat
CoreTools
Designer
DesignerComponentsPrivate
Gui
Help
LinguistTools
Network
OpenGL
OpenGLWidgets
Qml
Sql
StateMachine
Svg
ToolsTools
UiTools
WebEngineCore
WebEngineWidgets
Widgets
Xml
)
if(APPLE)
list(APPEND MITK_QT6_COMPONENTS DBus)
endif()
# Hint at default install locations of Qt
if(NOT Qt6_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") # 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()
if(APPLE)
list(APPEND _compilers macos)
endif()
foreach(_dir_candidate ${_dir_candidates})
get_filename_component(_dir_candidate ${_dir_candidate} REALPATH)
foreach(_compiler ${_compilers})
set(_glob_expression "${_dir_candidate}/6.*/${_compiler}")
file(GLOB _hints ${_glob_expression})
list(SORT _hints)
list(APPEND MITK_QT6_HINTS ${_hints})
endforeach()
endforeach()
endif()
find_package(Qt6 ${MITK_QT6_MINIMUM_VERSION} COMPONENTS ${MITK_QT6_COMPONENTS} REQUIRED HINTS ${MITK_QT6_HINTS})
get_target_property(QT_QMAKE_EXECUTABLE Qt6::qmake LOCATION)
get_target_property(QT_HELPGENERATOR_EXECUTABLE Qt6::qhelpgenerator LOCATION)
endif()
if(Qt6_DIR)
list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}/../../..")
list(REMOVE_DUPLICATES CMAKE_PREFIX_PATH)
endif()
# -----------------------------------------
# Custom dependency logic
if(WIN32 AND Qt6_DIR)
set(_dir_candidate "${Qt6_DIR}/../../../../../Tools/OpenSSLv3/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 3)
if(NOT OpenSSL_FOUND)
find_package(OpenSSL 1.1.1)
endif()
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()
+if((MITK_USE_cpprestsdk OR MITK_USE_httplib) AND NOT OpenSSL_FOUND)
+ set(openssl_message "Could not find OpenSSL (dependency of C++ REST SDK and cpp-httplib).\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 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")
+ set(openssl_message "${openssl_message}Please install the dev package of OpenSSL (i.e. libssl-dev).\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})
+ 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 (recommended):\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()
+if(MITK_USE_cpprestsdk)
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)
set(python3_mininum_version 3.11)
else()
set(python3_mininum_version 3.8)
endif()
find_package(Python3 ${python3_mininum_version} REQUIRED COMPONENTS Interpreter Development NumPy)
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(mitkFunctionAddManifest)
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)
#-----------------------------------------------------------------------------
# Global CMake variables
#-----------------------------------------------------------------------------
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)
#[[ On Windows, CMake is able to locate the link libraries for OpenSSL but it
does not look for the corresponding DLLs that we need to copy to our
binary directories and include in packaging.
Setting these paths manually is cumbersome so we try to use a simple
heuristic to automatically set them:
- Based on the link libraries (usually located in a lib folder),
try to find the "../bin" binary directory.
- Use the base file names of the link libraries to find corresponding
DLLs like "<base name>*.dll", that usually are named like
"<base name>-1_1-x64.dll" or similar.
]]
set(openssl_ssl_dll "")
set(openssl_crypto_dll "")
if(OPENSSL_SSL_LIBRARY AND EXISTS "${OPENSSL_SSL_LIBRARY}")
get_filename_component(openssl_bin_dir "${OPENSSL_SSL_LIBRARY}" DIRECTORY)
get_filename_component(openssl_bin_dir "${openssl_bin_dir}" DIRECTORY)
set(openssl_bin_dir "${openssl_bin_dir}/bin")
if(EXISTS "${openssl_bin_dir}")
get_filename_component(openssl_ssl_basename "${OPENSSL_SSL_LIBRARY}" NAME_WE)
file(GLOB openssl_ssl_dll "${openssl_bin_dir}/${openssl_ssl_basename}*.dll")
list(LENGTH openssl_ssl_dll num_findings)
if(num_findings GREATER 1)
set(openssl_ssl_dll "")
endif()
get_filename_component(openssl_crypto_basename "${OPENSSL_CRYPTO_LIBRARY}" NAME_WE)
file(GLOB openssl_crypto_dll "${openssl_bin_dir}/${openssl_crypto_basename}*.dll")
list(LENGTH openssl_crypto_dll num_findings)
if(num_findings GREATER 1)
set(openssl_crypto_dll "")
endif()
endif()
endif()
set(MITK_OPENSSL_SSL_DLL "${openssl_ssl_dll}" CACHE FILEPATH "")
if(DEFINED CACHE{MITK_OPENSSL_SSL_DLL} AND NOT MITK_OPENSSL_SSL_DLL AND openssl_ssl_dll)
set(MITK_OPENSSL_SSL_DLL "${openssl_ssl_dll}" CACHE FILEPATH "" FORCE)
endif()
set(MITK_OPENSSL_CRYPTO_DLL "${openssl_crypto_dll}" CACHE FILEPATH "")
if(DEFINED CACHE{MITK_OPENSSL_CRYPTO_DLL} AND NOT MITK_OPENSSL_CRYPTO_DLL AND openssl_crypto_dll)
set(MITK_OPENSSL_CRYPTO_DLL "${openssl_crypto_dll}" CACHE FILEPATH "" FORCE)
endif()
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_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} -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-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)
+ set(${_package}_DIR ${_temp_EP_${_package}_dir} CACHE PATH "externally 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.
+ # package information. We explicitly 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_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_Qt6)
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_Qt6)
message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because MITK_USE_Qt6 is OFF.")
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)
# 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
#-----------------------------------------------------------------------------
set(MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS)
set(MITK_DOXYGEN_ADDITIONAL_IMAGE_PATHS)
set(MITK_DOXYGEN_ADDITIONAL_EXAMPLE_PATHS)
foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS})
set(MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS "${MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS} \"${MITK_EXTENSION_DIR}\"")
set(MITK_DOXYGEN_ADDITIONAL_IMAGE_PATHS "${MITK_DOXYGEN_ADDITIONAL_IMAGE_PATHS} \"${MITK_EXTENSION_DIR}\"")
# MITK_DOXYGEN_ADDITIONAL_EXAMPLE_PATHS should be modified by MITK extensions as needed
endforeach()
if(DOXYGEN_FOUND)
foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS})
set(MITK_DOCUMENTATION_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Documentation")
file(GLOB MITK_DOCUMENTATION_EXTENSION_FILES CONFIGURE_DEPENDS "${MITK_DOCUMENTATION_EXTENSION_DIR}/*.cmake")
foreach(doc_file ${MITK_DOCUMENTATION_EXTENSION_FILES})
include("${doc_file}")
endforeach()
endforeach()
# Transform list of example paths into space-separated string of quoted paths
unset(example_paths)
foreach(example_path ${MITK_DOXYGEN_ADDITIONAL_EXAMPLE_PATHS})
set(example_paths "${example_paths} \"${example_path}\"")
endforeach()
set(MITK_DOXYGEN_ADDITIONAL_EXAMPLE_PATHS "${example_paths}")
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/CMakeDoxygenFilter.cpp b/Documentation/CMakeDoxygenFilter.cpp
deleted file mode 100644
index a6c54a2dfa..0000000000
--- a/Documentation/CMakeDoxygenFilter.cpp
+++ /dev/null
@@ -1,475 +0,0 @@
-/*============================================================================
-
- 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
-
- https://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 <cstdlib>
-#include <fstream>
-#include <iostream>
-#include <string>
-
-#include <assert.h>
-
-//--------------------------------------
-// Utilitiy classes and functions
-//--------------------------------------
-
-struct ci_char_traits : public std::char_traits<char>
-// just inherit all the other functions
-// that we don't need to override
-{
- static bool eq(char c1, char c2) { return toupper(c1) == toupper(c2); }
- static bool ne(char c1, char c2) { return toupper(c1) != toupper(c2); }
- static bool lt(char c1, char c2) { return toupper(c1) < toupper(c2); }
- static bool gt(char c1, char c2) { return toupper(c1) > toupper(c2); }
- static int compare(const char *s1, const char *s2, std::size_t n)
- {
- while (n-- > 0)
- {
- if (lt(*s1, *s2))
- return -1;
- if (gt(*s1, *s2))
- return 1;
- ++s1;
- ++s2;
- }
- return 0;
- }
-
- static const char *find(const char *s, int n, char a)
- {
- while (n-- > 0 && toupper(*s) != toupper(a))
- {
- ++s;
- }
- return s;
- }
-};
-
-typedef std::basic_string<char, ci_char_traits> ci_string;
-
-//--------------------------------------
-// Lexer
-//--------------------------------------
-
-class CMakeLexer
-{
-public:
- enum Token
- {
- TOK_EOF = -1,
- TOK_EOL = -2,
-
- // commands
- TOK_MACRO = -3,
- TOK_ENDMACRO = -4,
- TOK_FUNCTION = -5,
- TOK_ENDFUNCTION = -6,
- TOK_DOXYGEN_COMMENT = -7,
- TOK_SET = -8,
- TOK_STRING_LITERAL = -100,
- TOK_NUMBER_LITERAL = -102,
-
- // primary
- TOK_IDENTIFIER = -200
- };
-
- CMakeLexer(std::istream &is) : _lastChar(' '), _is(is), _line(1), _col(1) {}
- int getToken()
- {
- // skip whitespace
- while (isspace(_lastChar) && _lastChar != '\r' && _lastChar != '\n')
- {
- _lastChar = getChar();
- }
-
- if (isalpha(_lastChar) || _lastChar == '_')
- {
- _identifier = _lastChar;
- while (isalnum(_lastChar = getChar()) || _lastChar == '-' || _lastChar == '_')
- {
- _identifier += _lastChar;
- }
-
- if (_identifier == "set")
- return TOK_SET;
- if (_identifier == "function")
- return TOK_FUNCTION;
- if (_identifier == "macro")
- return TOK_MACRO;
- if (_identifier == "endfunction")
- return TOK_ENDFUNCTION;
- if (_identifier == "endmacro")
- return TOK_ENDMACRO;
- return TOK_IDENTIFIER;
- }
-
- if (isdigit(_lastChar))
- {
- // very lax!! number detection
- _identifier = _lastChar;
- while (isalnum(_lastChar = getChar()) || _lastChar == '.' || _lastChar == ',')
- {
- _identifier += _lastChar;
- }
- return TOK_NUMBER_LITERAL;
- }
-
- if (_lastChar == '#')
- {
- _lastChar = getChar();
- if (_lastChar == '!')
- {
- // found a doxygen comment marker
- _identifier.clear();
-
- _lastChar = getChar();
- while (_lastChar != EOF && _lastChar != '\n' && _lastChar != '\r')
- {
- _identifier += _lastChar;
- _lastChar = getChar();
- }
- return TOK_DOXYGEN_COMMENT;
- }
-
- // skip the comment
- while (_lastChar != EOF && _lastChar != '\n' && _lastChar != '\r')
- {
- _lastChar = getChar();
- }
- }
-
- if (_lastChar == '"')
- {
- _lastChar = getChar();
- _identifier.clear();
- while (_lastChar != EOF && _lastChar != '"')
- {
- _identifier += _lastChar;
- _lastChar = getChar();
- }
-
- // eat the closing "
- _lastChar = getChar();
- return TOK_STRING_LITERAL;
- }
-
- // don't eat the EOF
- if (_lastChar == EOF)
- return TOK_EOF;
-
- // don't eat the EOL
- if (_lastChar == '\r' || _lastChar == '\n')
- {
- if (_lastChar == '\r')
- _lastChar = getChar();
- if (_lastChar == '\n')
- _lastChar = getChar();
- return TOK_EOL;
- }
-
- // return the character as its ascii value
- int thisChar = _lastChar;
- _lastChar = getChar();
- return thisChar;
- }
-
- std::string getIdentifier() const { return std::string(_identifier.c_str()); }
- int curLine() const { return _line; }
- int curCol() const { return _col; }
- int getChar()
- {
- int c = _is.get();
- updateLoc(c);
- return c;
- }
-
-private:
- void updateLoc(int c)
- {
- if (c == '\n' || c == '\r')
- {
- ++_line;
- _col = 1;
- }
- else
- {
- ++_col;
- }
- }
-
- ci_string _identifier;
- int _lastChar;
- std::istream &_is;
-
- int _line;
- int _col;
-};
-
-//--------------------------------------
-// Parser
-//--------------------------------------
-
-class CMakeParser
-{
-public:
- CMakeParser(std::istream &is, std::ostream &os)
- : _is(is), _os(os), _lexer(is), _curToken(CMakeLexer::TOK_EOF), _lastToken(CMakeLexer::TOK_EOF)
- {
- }
-
- int curToken() { return _curToken; }
- int nextToken()
- {
- _lastToken = _curToken;
- _curToken = _lexer.getToken();
- while (_curToken == CMakeLexer::TOK_EOL)
- {
- // Try to preserve lines in output to allow correct line number referencing by doxygen.
- _os << std::endl;
- _curToken = _lexer.getToken();
- }
- return _curToken;
- }
-
- void handleMacro()
- {
- if (!parseMacro())
- {
- // skip token for error recovery
- nextToken();
- }
- }
-
- void handleFunction()
- {
- if (!parseFunction())
- {
- // skip token for error recovery
- nextToken();
- }
- }
-
- void handleSet()
- {
- // SET(var ...) following a documentation block is assumed to be a variable declaration.
- if (_lastToken != CMakeLexer::TOK_DOXYGEN_COMMENT)
- {
- // No comment block before
- nextToken();
- }
- else if (!parseSet())
- {
- // skip token for error recovery
- nextToken();
- }
- }
-
- void handleDoxygenComment()
- {
- _os << "///" << _lexer.getIdentifier();
- nextToken();
- }
-
- void handleTopLevelExpression()
- {
- // skip token
- nextToken();
- }
-
-private:
- void printError(const char *str)
- {
- std::cerr << "Error: " << str << " (at line " << _lexer.curLine() << ", col " << _lexer.curCol() << ")";
- }
-
- bool parseMacro()
- {
- if (nextToken() != '(')
- {
- printError("Expected '(' after MACRO");
- return false;
- }
-
- nextToken();
- std::string macroName = _lexer.getIdentifier();
- if (curToken() != CMakeLexer::TOK_IDENTIFIER || macroName.empty())
- {
- printError("Expected macro name");
- return false;
- }
-
- _os << macroName << '(';
- if (nextToken() == CMakeLexer::TOK_IDENTIFIER)
- {
- _os << _lexer.getIdentifier();
- while (nextToken() == CMakeLexer::TOK_IDENTIFIER)
- {
- _os << ", " << _lexer.getIdentifier();
- }
- }
-
- if (curToken() != ')')
- {
- printError("Missing expected ')'");
- }
- else
- {
- _os << ");";
- }
-
- // eat the ')'
- nextToken();
- return true;
- }
-
- bool parseSet()
- {
- if (nextToken() != '(')
- {
- printError("Expected '(' after SET");
- return false;
- }
-
- nextToken();
- std::string variableName = _lexer.getIdentifier();
- if (curToken() != CMakeLexer::TOK_IDENTIFIER || variableName.empty())
- {
- printError("Expected variable name");
- return false;
- }
-
- _os << "CMAKE_VARIABLE " << variableName;
-
- nextToken();
- while ((curToken() == CMakeLexer::TOK_IDENTIFIER) || (curToken() == CMakeLexer::TOK_STRING_LITERAL) ||
- (curToken() == CMakeLexer::TOK_NUMBER_LITERAL))
- {
- nextToken();
- }
-
- if (curToken() != ')')
- {
- printError("Missing expected ')'");
- }
- else
- {
- _os << ";";
- }
-
- // eat the ')'
- nextToken();
- return true;
- }
-
- bool parseFunction()
- {
- if (nextToken() != '(')
- {
- printError("Expected '(' after FUNCTION");
- return false;
- }
-
- nextToken();
- std::string funcName = _lexer.getIdentifier();
- if (curToken() != CMakeLexer::TOK_IDENTIFIER || funcName.empty())
- {
- printError("Expected function name");
- return false;
- }
-
- _os << funcName << '(';
- if (nextToken() == CMakeLexer::TOK_IDENTIFIER)
- {
- _os << _lexer.getIdentifier();
- while (nextToken() == CMakeLexer::TOK_IDENTIFIER)
- {
- _os << ", " << _lexer.getIdentifier();
- }
- }
-
- if (curToken() != ')')
- {
- printError("Missing expected ')'");
- }
- else
- {
- _os << ");";
- }
-
- // eat the ')'
- nextToken();
-
- return true;
- }
-
- std::istream &_is;
- std::ostream &_os;
- CMakeLexer _lexer;
- int _curToken;
- int _lastToken;
-};
-
-#define STRINGIFY(a) #a
-#define DOUBLESTRINGIFY(a) STRINGIFY(a)
-
-int main(int argc, char **argv)
-{
- assert(argc > 1);
-
- for (int i = 1; i < argc; ++i)
- {
- std::ifstream ifs(argv[i]);
- std::ostream &os = std::cout;
-
-#ifdef USE_NAMESPACE
- os << "namespace " << DOUBLESTRINGIFY(USE_NAMESPACE) << " {\n";
-#endif
-
- CMakeParser parser(ifs, os);
- parser.nextToken();
- while (ifs.good())
- {
- switch (parser.curToken())
- {
- case CMakeLexer::TOK_EOF:
- return ifs.get(); // eat EOF
- case CMakeLexer::TOK_MACRO:
- parser.handleMacro();
- break;
- case CMakeLexer::TOK_FUNCTION:
- parser.handleFunction();
- break;
- case CMakeLexer::TOK_SET:
- parser.handleSet();
- break;
- case CMakeLexer::TOK_DOXYGEN_COMMENT:
- parser.handleDoxygenComment();
- break;
- default:
- parser.handleTopLevelExpression();
- break;
- }
- }
-
-#ifdef USE_NAMESPACE
- os << "}\n";
-#endif
- }
-
- return EXIT_SUCCESS;
-}
diff --git a/Documentation/CMakeLists.txt b/Documentation/CMakeLists.txt
index c7583eaad5..cccd13ea8e 100644
--- a/Documentation/CMakeLists.txt
+++ b/Documentation/CMakeLists.txt
@@ -1,113 +1,109 @@
# Different doxygen versions produce significantly different behaviour in the MITK documentation
# especially in regards to the MITK Qt assistant help files and markdown files.
# The HTML documentation is supposed to be build with Doxygen 1.8.7 or newer, the
# Qt assistant QCH files are supposed to be generated with Doxygen 1.8.7 or newer.
# So we check for 1.8.7 here and QCH generation support is checked in
# BlueBerry/CMakeLists.txt
set(supported_doxygen_version "1.8.7")
if(DOXYGEN_VERSION VERSION_LESS ${supported_doxygen_version})
MESSAGE(WARNING "Unsupported doxygen version ${DOXYGEN_VERSION}. The MITK HTML documentation has been tested to work with doxygen ${supported_doxygen_version} or newer.")
endif()
option(USE_DOT "Use dot program for generating graphical class diagrams with doxygen, if available" ON)
option(MITK_DOXYGEN_BUILD_ALWAYS "Always build the MITK documentation when building the default target" OFF)
option(MITK_DOXYGEN_GENERATE_QCH_FILES "Use doxygen to generate Qt compressed help files for MITK docs" OFF)
mark_as_advanced(USE_DOT MITK_DOXYGEN_BUILD_ALWAYS MITK_DOXYGEN_GENERATE_QCH_FILES)
if (MITK_DOXYGEN_GENERATE_QCH_FILES AND DOXYGEN_VERSION VERSION_LESS "1.8.7")
message(WARNING "> Forcing MITK_DOXYGEN_GENERATE_QCH_FILES to OFF because Doxygen version 1.8.7 or newer not found.")
set(MITK_DOXYGEN_GENERATE_QCH_FILES OFF CACHE BOOL "Use doxygen to generate Qt compressed help files for MITK docs" FORCE)
endif()
set(HAVE_DOT "NO")
if(DOXYGEN_DOT_EXECUTABLE AND USE_DOT)
set(HAVE_DOT "YES")
endif()
set(MITK_DOXYGEN_TAGFILE_NAME ${MITK_DOXYGEN_OUTPUT_DIR}/MITK.tag CACHE INTERNAL "MITK Doxygen tag file")
# This is relative to the working directory of the doxygen command
set(MITK_DOXYGEN_STYLESHEET mitk_doxygen_extra.css)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${MITK_DOXYGEN_STYLESHEET}
${CMAKE_CURRENT_BINARY_DIR}/${MITK_DOXYGEN_STYLESHEET} COPYONLY)
# Create QCH files for MITK and external projects
set(MITK_DOXYGEN_GENERATE_QHP "NO")
if(MITK_DOXYGEN_GENERATE_QCH_FILES)
if(NOT QT_HELPGENERATOR_EXECUTABLE)
message(SEND_ERROR "The Qt help generator could not be found. Disabling qch generation")
else()
set(MITK_DOXYGEN_GENERATE_QHP "YES")
endif()
endif()
# The name of the generated MITK qch file, relative to the
# Doxygen HTML output folder
set(MITK_DOXYGEN_QCH_FILE "${MITK_BINARY_DIR}/MITK-${MITK_REVISION_ID}.qch")
-# Compile a doxygen input filter for processing CMake scripts
-include(mitkFunctionCMakeDoxygenFilterCompile)
-mitkFunctionCMakeDoxygenFilterCompile(NAMESPACE "CMake")
-
# Configure some doxygen options
if(NOT MITK_DOXYGEN_INTERNAL_DOCS)
set(MITK_DOXYGEN_INTERNAL_DOCS "NO")
set(MITK_DOXYGEN_HIDE_FRIEND_COMPOUNDS "YES")
set(MITK_DOXYGEN_EXCLUDE_PATTERNS "*_p.* *Private.h */internal/*")
else()
set(MITK_DOXYGEN_HIDE_FRIEND_COMPOUNDS "NO")
set(MITK_DOXYGEN_EXCLUDE_PATTERNS "")
endif()
if(NOT MITK_DOXYGEN_GENERATE_TODOLIST)
set(MITK_DOXYGEN_GENERATE_TODOLIST "NO")
endif()
if(NOT MITK_DOXYGEN_GENERATE_BUGLIST)
set(MITK_DOXYGEN_GENERATE_BUGLIST "NO")
endif()
if(NOT MITK_DOXYGEN_HTML_DYNAMIC_SECTIONS)
set(MITK_DOXYGEN_HTML_DYNAMIC_SECTIONS "NO")
endif()
if(NOT MITK_DOXYGEN_UML_LOOK)
set(MITK_DOXYGEN_UML_LOOK "NO")
endif()
if(NOT MITK_DOXYGEN_GENERATE_DEPRECATEDLIST)
set(MITK_DOXYGEN_GENERATE_DEPRECATEDLIST "YES")
endif()
if(NOT DEFINED MITK_DOXYGEN_DOT_NUM_THREADS)
set(MITK_DOXYGEN_DOT_NUM_THREADS 0)
endif()
if(NOT DEFINED US_PLATFORM)
if(UNIX)
if(APPLE)
set(US_PLATFORM "US_PLATFORM_APPLE=1")
else()
set(US_PLATFORM "US_PLATFORM_LINUX=1")
endif()
set(US_PLATFORM "${US_PLATFORM} \\\nUS_PLATFORM_POSIX=1")
else()
set(US_PLATFORM "US_PLATFORM_WINDOWS=1")
endif()
endif()
configure_file(doxygen.conf.in
${CMAKE_CURRENT_BINARY_DIR}/doxygen.conf)
if(MITK_DOXYGEN_BUILD_ALWAYS)
set(_doc_in_all "ALL")
else()
set(_doc_in_all "")
endif()
add_custom_target(doc ${_doc_in_all}
${DOXYGEN} ${CMAKE_CURRENT_BINARY_DIR}/doxygen.conf
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
set_property(TARGET doc PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Documentation")
diff --git a/Documentation/Doxygen/2-UserManual/MITKPluginManualsList.dox b/Documentation/Doxygen/2-UserManual/MITKPluginManualsList.dox
index 9a67a1ea6b..6180e08610 100644
--- a/Documentation/Doxygen/2-UserManual/MITKPluginManualsList.dox
+++ b/Documentation/Doxygen/2-UserManual/MITKPluginManualsList.dox
@@ -1,64 +1,64 @@
/**
\page PluginListPage MITK Plugin Manuals
-The plugins and bundles provide much of the extended functionality of MITK. Each encapsulates a solution to a problem and associated features. This way one can easily assemble the necessary capabilites for a workflow without adding a lot of bloat, by combining plugins as needed.
+The plugins and bundles provide much of the extended functionality of MITK. Each encapsulates a solution to a problem and associated features. This way one can easily assemble the necessary capabilities for a workflow without adding a lot of bloat, by combining plugins as needed.
<ul>
<li> \subpage org_mitk_views_basicimageprocessing </li>
<li> \subpage org_mitk_views_datamanager </li>
<li> \subpage org_mitk_editors_dicombrowser </li>
<li> \subpage org_mitk_views_dicominspector </li>
<li> \subpage org_mitk_views_imagecropper </li>
<li> \subpage org_mitk_views_imagenavigator </li>
<li> \subpage org_mitk_views_pixelvalue </li>
<li> \subpage org_blueberry_views_logview </li>
<li> \subpage org_mitk_views_matchpoint_algorithm_browser </li>
<li> \subpage org_mitk_views_matchpoint_algorithm_control </li>
<li> \subpage org_mitk_views_matchpoint_evaluator </li>
<li> \subpage org_mitk_views_matchpoint_framereg </li>
<li> \subpage org_mitk_views_matchpoint_manipulator </li>
<li> \subpage org_mitk_views_matchpoint_mapper </li>
<li> \subpage org_mitk_views_matchpoint_visualizer </li>
<li> \subpage org_mitk_gui_qt_measurementtoolbox
<ul>
<li> \subpage org_mitk_views_measurement </li>
<li> \subpage org_mitk_views_imagestatistics </li>
</ul>
</li>
<li> \subpage org_mitk_views_moviemaker </li>
<li> \subpage org_mitk_views_pointsetinteraction </li>
<li> \subpage org_mitk_views_python </li>
<li> \subpage org_mitk_views_remeshing </li>
<li> \subpage org_mitk_views_screenshotmaker </li>
<li> Segmentation
<ul>
<li> \subpage org_mitk_views_segmentation </li>
<li> \subpage org_mitk_views_segmentationutilities </li>
<li> \subpage org_mitk_views_segmentationtasklist </li>
</ul>
</li>
<li> \subpage org_mitk_editors_stdmultiwidget </li>
<li> \subpage org_mitk_editors_mxnmultiwidget </li>
<li> \subpage org_mitk_views_viewnavigator </li>
<li> \subpage org_mitk_views_volumevisualization </li>
<li> \subpage org_mitk_views_properties </li>
<li> \subpage org_mitk_gui_qt_flowapplication </li>
<li> \subpage org_mitk_gui_qt_flow_segmentation </li>
<li> \subpage org_mitk_gui_qt_aicpregistration </li>
<li> \subpage org_mitk_gui_qt_cest </li>
<li> \subpage org_mitk_views_pharmacokinetics_concentration_mri </li>
<li> \subpage org_mitk_views_pharmacokinetics_mri </li>
<li> \subpage org_mitk_views_pharmacokinetics_pet </li>
<li> \subpage org_mitk_gui_qt_examples </li>
<li> \subpage org_mitk_views_fit_demo </li>
<li> \subpage org_mitk_views_fit_genericfitting </li>
<li> \subpage org_mitk_views_fit_inspector </li>
<li> \subpage org_mitk_gui_qt_overlaymanager </li>
<li> \subpage org_mitk_gui_qt_preprocessing_resampling </li>
<li> \subpage org_mitk_views_pharmacokinetics_curvedescriptor </li>
<li> \subpage org_mitk_views_pharmacokinetics_simulation </li>
<li> \subpage org_blueberry_ui_qt_objectinspector </li>
<li> \subpage org_mitk_gui_qt_xnat </li>
</ul>
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryExtensionPointsIntro.dox b/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryExtensionPointsIntro.dox
index 23128dd14c..dfa5db4be5 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryExtensionPointsIntro.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryExtensionPointsIntro.dox
@@ -1,34 +1,34 @@
/**
\page BlueBerryExampleExtensionPoint Extension Points
-\brief A minimal applictaion that definines an extension point and collects extensions.
+\brief A minimal application that defines an extension point and collects extensions.
-# \subpage IntroductionExtensionPoints "Introduction"
-# \subpage ExtensionPointDefinition "Extension Point Definition"
-# \subpage ExtensionContribution "Extension Contribution"
\page IntroductionExtensionPoints Introduction: Extension Point/Extension Concept
The BlueBerry application framework provides the concept of extension points and extensions. The main goal is to allow the extension of functionality of a plugin (based on the contract defined by the extension point) by several other plugins. Both the extension point and the extension are defined in the according plugin.xml.
\imageMacro{ExtensionPoints.png,"Extension Point concept",5.40}
\section SimpleExample Why Extension Points?
In the following simple example we have a plugin 'a' and 'b' with two classes 'A' and 'B' in these plugins.
\imageMacro{ExtensionPointEx.png,"Simple Example",5.10}
Plugin 'a' uses the extension point mechanism and creates an extension point that can be extended by other plugins. Now if class 'A' reaches a part that can be extended it asks 'a' if another plugin is registered. If that's the case the functionality of the plugin 'b' that is defined in class 'B' is executed.
A plugin can therefore be arbitrary extended.
\section BlueBerryExampleExtensionPoint_Examples Examples
The two following example plugins describe the usage of the BlueBerry Extension Points. One example defines an extension point and the other example extends the created extension point.
\li \ref org_mitk_example_gui_extensionpointdefinition
\li \ref org_mitk_example_gui_extensionpointcontribution
[\ref BlueBerryExampleExtensionPoint] [Next: \ref ExtensionPointDefinition] [\ref BlueBerryExamples]
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryIntro.dox b/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryIntro.dox
index 5543d831ae..a63c6f6c59 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryIntro.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerry/BlueBerryIntro.dox
@@ -1,79 +1,79 @@
/**
\page BlueBerryIntro BlueBerry Application Framework
BlueBerry is an application framework used in MITK for creating modular and extensible end-user applications.
More high-level documentation can be found below:
- \subpage BlueBerryWorkbench
- \ref BlueBerryExamples
Please see the \ref BlueBerryExamples for code examples demonstrating different features of the application framework.
The BlueBerry developer reference is available here:
- \ref BlueBerryPlugins
- \subpage BlueBerryExtPointsIndex
\page BlueBerryWorkbench The Workbench: What are Views, Editors, Perspectives?
-BlueBerry makes use of the Eclipse UI guidlines which state some concepts on how to build up a GUI. The different objects of the platform UI shall be described here:
+BlueBerry makes use of the Eclipse UI guidelines which state some concepts on how to build up a GUI. The different objects of the platform UI shall be described here:
\section Workbench Workbench
\li root object of the platform UI
\li collection of \ref WorkbenchWindow "windows"
\imageMacro{workbench.jpg,"The Workbech",11.64}
\section WorkbenchWindow WorkbenchWindow
\li has one page
\imageMacro{workbench-window.jpg,"Workbench Windows",8.47}
\section WorkbenchPage Workbench Page
\li denotes to the inner part of the \ref WorkbenchWindow "window", that is: everything except the title bar
\li may have one menu bar, one toolbar, one shortcut bar, and one statusbar
\li has one or more \ref Perspective "perspectives"
\imageMacro{workbench-page.jpg,"Workbench Page",8.47}
\section Perspective Perspective
<UL>
<LI> A visual container for a set of \ref Views "views" and content \ref Editors "editors" </LI>
<LI> Shows \ref Views "views" and \ref Editors "editors" in a certain layout </LI>
<LI> Like a page within a book: </LI>
<UL>
<LI> Only one perspective is visible at any time </LI>
<LI> There are several perspectives inside a page </LI>
</UL>
</UL>
\imageMacro{workbench-window-perspective.png,"A Perspective",11.79}
\section Part Part
\li every \ref Views "View" or \ref Editors "Editor" is called \b Part
\subsection Editors Editors
\li the StdMultiWidget is an example for an editor in our MainApp
\li Contains the primary content, such as a document or image data, which users interact with
\li content is the primary focus of attention and a reflection of the primary task
\li primary position in the UI
\li contributes commands to the workbench's main menu bar and toolbar
\li shared in other perspectives
\imageMacro{workbench-window-editor-area.png,"Editor Area",11.79}
\subsection Views Views
<UL>
<LI> support the primary task </LI>
<UL>
<LI> navigate a hierarchy of information </LI>
<LI> open an \ref Editors "editor" </LI>
<LI> view/edit properties </LI>
</UL>
<LI> The views exist wholly within the perspective (not shared, one instance at a time) </LI>
<LI> Every functionality is a view- it supports medical image processing </LI>
</UL>
\imageMacro{workbench-window-views.png,"Views",11.79}
\section ClassDiagram Summary as class diagram
\imageMacro{workbench-class-diagram.jpg,"class diagram",13.74}
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerryExtensionPointReference.dox b/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerryExtensionPointReference.dox
index 427ca8e76e..97c148422a 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerryExtensionPointReference.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Application/BlueBerryExtensionPointReference.dox
@@ -1,1356 +1,1356 @@
/**
\page BlueBerryExtPointsIndex BlueBerry Extension-Point Reference
- \subpage BlueBerryExtPointsIndex_CoreExpr
- \subpage BlueBerryExtPointsIndex_PlatformRuntime
- \subpage BlueBerryExtPointsIndex_Workbench
\page BlueBerryExtPointsIndex_CoreExpr Platform Core Expressions
\tableofcontents
\section BlueBerryExtPointsIndex_CoreExpr_ExprDef Expression Definitions
\subsection BlueBerryExtPointsIndex_CoreExpr_ExprDef_Id Identifier
\c org.blueberry.core.expressions.definitions
\subsection BlueBerryExtPointsIndex_CoreExpr_ExprDef_Desc Description
This extension point allows you to create reusable extensions.
They can then be used in other core expression constructs.
The reference element in a core expression will evaluated the expression definition with the evaluation context that is active for the reference element.
\subsection BlueBerryExtPointsIndex_CoreExpr_ExprDef_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT extension (definition*)>
<!ATTLIST extension
point CDATA #REQUIRED
id CDATA #IMPLIED
name CDATA #IMPLIED>
\endcode
- <tt>point</tt>: a fully qualified identifier of the target extension point
- <tt>id</tt>: an optional identifier of the extension instance
- <tt>name</tt>: an optional name of the extension instance
\code{.unparsed}
<!ELEMENT definition (not | and | or | instanceof | test | systemTest | equals | count | with | resolve | adapt | iterate)>
<!ATTLIST definition
id CDATA #REQUIRED>
\endcode
Provides a global definition of an expression to be used with the \c \<reference/\> expression element.
This helps to reuse common expressions.
- <tt>id</tt>: a globally unique identifier for the expression definition
\code{.unparsed}
<!ELEMENT enablement (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
\endcode
A generic root element.
The element can be used inside an extension point to define its enablement expression.
The children of an enablement expression are combined using the and operator.
\code{.unparsed}
<!ELEMENT not (not | and | or | instanceof | test | systemTest | equals | count | with | resolve | adapt | iterate | reference)>
\endcode
This element represent a NOT operation on the result of evaluating it's sub-element expression.
\code{.unparsed}
<!ELEMENT and (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
\endcode
This element represent an AND operation on the result of evaluating all it's sub-elements expressions.
\code{.unparsed}
<!ELEMENT or (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
\endcode
This element represent an OR operation on the result of evaluating all it's sub-element expressions.
\code{.unparsed}
<!ELEMENT instanceof EMPTY>
<!ATTLIST instanceof
value CDATA #REQUIRED>
\endcode
This element is used to perform an instanceof check of the object in focus.
The expression returns \c EvaluationResult.TRUE if the object's type is a sub type of the type specified by the attribute value.
Otherwise \c EvaluationResult.FALSE is returned.
- <tt>value</tt>: a fully qualified name of a class or interface
\code{.unparsed}
<!ELEMENT test EMPTY>
<!ATTLIST test
property CDATA #REQUIRED
args CDATA #IMPLIED
value CDATA #IMPLIED
forcePluginActivation (true | false)>
\endcode
This element is used to evaluate the property state of the object in focus.
-The set of testable properties can be extended using the propery tester extension point.
+The set of testable properties can be extended using the property tester extension point.
The test expression returns \c EvaluationResult.NOT_LOADED if the property tester doing the actual testing isn't loaded yet and the attribute \c forcePluginActivation is set to \c false.
If \c forcePluginActivation is set to \c true and the evaluation context used to evaluate this expression support plug-in activation then evaluating the property will result in activating the plug-in defining the tester.
- <tt>property</tt>: The name of an object's property to test.
-- <tt>args</tt>: Additional arguments passed to the property tester. Multiple arguments are seperated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression.
+- <tt>args</tt>: Additional arguments passed to the property tester. Multiple arguments are separated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression.
- <tt>value</tt>: The expected value of the property. Can be omitted if the property is a boolean property. The test expression is supposed to return \c EvaluationResult.TRUE if the property matches the value and \c EvaluationResult.FALSE otherwise. The value attribute is converted into a Java base type using the following rules:
- The string \c "true" is converted into \c Boolean.TRUE .
- The string \c "false" is converted into \c Boolean.FALSE .
- If the string contains a dot then the interpreter tries to convert the value into a \c Float object. If this fails the string is treated as a \c java.lang.String.
- If the string only consists of numbers then the interpreter converts the value in an \c Integer object.
- In all other cases the string is treated as a \c java.lang.String .
- The conversion of the string into a \c Boolean , \c Float , or \c Integer can be suppressed by surrounding the string with single quotes. For example, the attribute \c value="'true'" is converted into the string "true".
- <tt>forcePluginActivation</tt>: A flag indicating whether the plug-in contributing the property tester should be loaded if necessary. As such, this flag should be used judiciously, in order to avoid unnecessary plug-in activations. Most clients should avoid setting this flag to \c true. This flag is only honored if the evaluation context used to evaluate this expression allows plug-in activation. Otherwise the flag is ignored and no plug-in loading takes place.
\code{.unparsed}
<!ELEMENT systemTest EMPTY>
<!ATTLIST systemTest
property CDATA #REQUIRED
value CDATA #REQUIRED>
\endcode
Tests a system property by calling the \c System.getProperty method and compares the result with the value specified through the value attribute.
- <tt>property</tt>: The name of an system property to test.
- <tt>value</tt>: The expected value of the property. The value is interpreted as a string value.
\code{.unparsed}
<!ELEMENT equals EMPTY>
<!ATTLIST equals
value CDATA #REQUIRED>
\endcode
This element is used to perform an equals check of the object in focus.
The expression returns \c EvaluationResult.TRUE if the object is equal to the value provided by the attribute value.
Otherwise \c EvaluationResult.FALSE is returned.
- <tt>value</tt>: The expected value. The value provided as a string is converted into a Java base type using the same rules as for the value attribute of the test expression.
\code{.unparsed}
<!ELEMENT count EMPTY>
<!ATTLIST count
value CDATA #REQUIRED>
\endcode
This element is used to test the number of elements in a collection.
- <tt>value</tt>: An expression to specify the number of elements in a list. Following wildcard characters can be used:
- <tt>*</tt>: any number of elements
- <tt>?</tt>: no elements or one element
- <tt>+</tt>: one or more elements
- <tt>!</tt>: no elements
- <tt>integer value</tt>: the list must contain the exact number of elements
\code{.unparsed}
<!ELEMENT with (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
<!ATTLIST with
variable CDATA #REQUIRED>
\endcode
This element changes the object to be inspected for all its child element to the object referenced by the given variable.
If the variable can not be resolved then the expression will throw an \c ExpressionException when evaluating it.
The children of a with expression are combined using the and operator.
- <tt>variable</tt>: The name of the variable to be used for further inspection. It is up to the evaluator of an extension point to provide the variable in the variable pool.
\code{.unparsed}
<!ELEMENT resolve (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
<!ATTLIST resolve
variable CDATA #REQUIRED
args CDATA #IMPLIED>
\endcode
This element changes the object to be inspected for all its child element to the object referenced by the given variable.
If the variable can not be resolved then the expression will throw an \c ExpressionException when evaluating it.
The children of a with expression are combined using the and operator.
- <tt>variable</tt>: The name of the variable to be resolved. This variable is then used as the object in focus for child element evaluation. It is up to the evaluator of an extension point to provide a corresponding variable resolver (see berry::IVariableResolver) through the evaluation context passed to the root expression element when evaluating the expression.
-- <tt>args</tt>: Additional arguments passed to the variable resolver. Multiple arguments are seperated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression.
+- <tt>args</tt>: Additional arguments passed to the variable resolver. Multiple arguments are separated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression.
\code{.unparsed}
<!ELEMENT adapt (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
<!ATTLIST adapt
type CDATA #REQUIRED>
\endcode
This element is used to adapt the object in focus to the type specified by the attribute type.
The expression returns not loaded if either the adapter or the type referenced isn't loaded yet.
It throws an \c ExpressionException during evaluation if the type name doesn't exist at all.
The children of an adapt expression are combined using the and operator.
- <tt>type</tt>: the type to which the object in focus is to be adapted
\code{.unparsed}
<!ELEMENT iterate (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
<!ATTLIST iterate
operator (or|and)
ifEmpty (true | false) >
\endcode
This element is used to iterate over a variable that is of type \c java.util.Collection.
If the object in focus is not of type \c java.util.Collection then an \c ExpressionException will be thrown while evaluating the expression.
- <tt>operator</tt>: Either "and" or "or". The operator defines how the child elements will be combined. If not specified, "and" will be used.
- <tt>ifEmpty</tt>: The value return from the iterate expression if the collection is empty. If not specified then \c true is returned when the operator equals "and" and \c false is returned if the operator equals "or".
\code{.unparsed}
<!ELEMENT reference EMPTY>
<!ATTLIST reference
definitionId CDATA #REQUIRED>
\endcode
This element is used to reference an expression from the \c org.blueberry.core.expressions.definitions extension point.
The expression definition will be evaluated within the current expression element using the current evaluation context.
- <tt>definitionId</tt>: the unique id of an expression from \c org.blueberry.core.expressions.definitions
\subsection BlueBerryExtPointsIndex_CoreExpr_ExprDef_Examples Examples
\code{.unparsed}
<extension point="org.blueberry.core.expressions.definitions">
<definition id="com.example.parts.activeProblemsView">
<with variable="activePartId">
<equals value="org.blueberry.ui.views.ProblemsView"/>
</with>
</definition>
<definition id="com.example.markers.markerSelection">
<iterate>
<instanceof value="org.blueberry.core.resources.IMarker"/>
</with>
</definition>
</extension>
\endcode
Then this expression definition can be used when composing other expressions.
\code{.unparsed}
<enabledWhen>
<reference definitionId="com.example.parts.activeProblemsView">
</enabledWhen>
<visibleWhen>
<and>
<reference definitionId="com.example.parts.activeProblemsView"/>
<reference definitionId="com.example.markers.markerSelection"/>
</and>
</visibleWhen>
\endcode
\section BlueBerryExtPointsIndex_CoreExpr_CommExpr Common Expressions
\subsection BlueBerryExtPointsIndex_CoreExpr_CommExpr_Id Identifier
\c org.blueberry.core.expressions.commonExpression
\subsection BlueBerryExtPointsIndex_CoreExpr_CommExpr_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT enablement (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
\endcode
A generic root element. The element can be used inside an extension point to define its enablement expression.
The children of an enablement expression are combined using the and operator.
\code{.unparsed}
<!ELEMENT not (not | and | or | instanceof | test | systemTest | equals | count | with | resolve | adapt | iterate | reference)>
\endcode
This element represent a NOT operation on the result of evaluating it's sub-element expression.
\code{.unparsed}
<!ELEMENT and (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
\endcode
This element represent an AND operation on the result of evaluating all it's sub-elements expressions.
\code{.unparsed}
<!ELEMENT or (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
\endcode
This element represent an OR operation on the result of evaluating all it's sub-element expressions.
\code{.unparsed}
<!ELEMENT instanceof EMPTY>
<!ATTLIST instanceof
value CDATA #REQUIRED>
\endcode
This element is used to perform an instanceof check of the object in focus.
The expression returns \c EvaluationResult.TRUE if the object's type is a sub type of the type specified by the attribute value.
Otherwise \c EvaluationResult.FALSE is returned.
- <tt>value</tt>: a fully qualified name of a class or interface
\code{.unparsed}
<!ELEMENT test EMPTY>
<!ATTLIST test
property CDATA #REQUIRED
args CDATA #IMPLIED
value CDATA #IMPLIED
forcePluginActivation (true | false) >
\endcode
This element is used to evaluate the property state of the object in focus.
-The set of testable properties can be extended using the propery tester extension point.
+The set of testable properties can be extended using the property tester extension point.
The test expression returns \c EvaluationResult.NOT_LOADED if the property tester doing the actual testing isn't loaded yet and the attribute \c forcePluginActivation is set to false.
If \c forcePluginActivation is set to \c true and the evaluation context used to evaluate this expression support plug-in activation then evaluating the property will result in activating the plug-in defining the tester.
- <tt>property</tt>: The name of an object's property to test.
-- <tt>args</tt>: Additional arguments passed to the property tester. Multiple arguments are seperated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression.
+- <tt>args</tt>: Additional arguments passed to the property tester. Multiple arguments are separated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression.
- <tt>value</tt>: The expected value of the property. Can be omitted if the property is a boolean property. The test expression is supposed to return EvaluationResult.TRUE if the property matches the value and EvaluationResult.FALSE otherwise. The value attribute is converted into a Java base type using the following rules:
- The string "true" is converted into \c Boolean.TRUE
- The string "false" is converted into \c Boolean.FALSE
- If the string contains a dot then the interpreter tries to convert the value into a \c Float object. If this fails the string is treated as a \c java.lang.String
- If the string only consists of numbers then the interpreter converts the value in an \c Integer object.
- In all other cases the string is treated as a \c java.lang.String
- The conversion of the string into a \c Boolean , \c Float , or \c Integer can be suppressed by surrounding the string with single quotes. For example, the attribute \c value="'true'" is converted into the string "true"
- <tt>forcePluginActivation</tt>: A flag indicating whether the plug-in contributing the property tester should be loaded if necessary. As such, this flag should be used judiciously, in order to avoid unnecessary plug-in activations. Most clients should avoid setting this flag to true. This flag is only honored if the evaluation context used to evaluate this expression allows plug-in activation. Otherwise the flag is ignored and no plug-in loading takes place.
\code{.unparsed}
<!ELEMENT systemTest EMPTY>
<!ATTLIST systemTest
property CDATA #REQUIRED
value CDATA #REQUIRED>
\endcode
Tests a system property by calling the \c System.getProperty method and compares the result with the value specified through the value attribute.
- <tt>property</tt>: The name of an system property to test.
- <tt>value</tt>: The expected value of the property. The value is interpreted as a string value.
\code{.unparsed}
<!ELEMENT equals EMPTY>
<!ATTLIST equals
value CDATA #REQUIRED>
\endcode
This element is used to perform an equals check of the object in focus.
The expression returns \c EvaluationResult.TRUE if the object is equal to the value provided by the attribute value.
Otherwise \c EvaluationResult.FALSE is returned.
- <tt>value</tt>: The expected value. The value provided as a string is converted into a Java base type using the same rules as for the value attribute of the test expression.
\code{.unparsed}
<!ELEMENT count EMPTY>
<!ATTLIST count
value CDATA #REQUIRED>
\endcode
This element is used to test the number of elements in a collection.
- <tt>value</tt>: An expression to specify the number of elements in a list. Following wildcard characters can be used:
- <tt>*</tt>: any number of elements
- <tt>?</tt>: no elements or one element
- <tt>+</tt>: one or more elements
- <tt>!</tt>: no elements
- <tt>integer value</tt>: the list must contain the exact number of elements
\code{.unparsed}
<!ELEMENT with (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
<!ATTLIST with
variable CDATA #REQUIRED>
\endcode
This element changes the object to be inspected for all its child element to the object referenced by the given variable.
If the variable can not be resolved then the expression will throw an \c ExpressionException when evaluating it.
The children of a with expression are combined using the and operator.
- <tt>variable</tt>: The name of the variable to be used for further inspection. It is up to the evaluator of an extension point to provide the variable in the variable pool.
\code{.unparsed}
<!ELEMENT resolve (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
<!ATTLIST resolve
variable CDATA #REQUIRED
args CDATA #IMPLIED>
\endcode
This element changes the object to be inspected for all its child element to the object referenced by the given variable.
If the variable can not be resolved then the expression will throw an \c ExpressionException when evaluating it.
The children of a with expression are combined using the and operator.
- <tt>variable</tt>: The name of the variable to be resolved. This variable is then used as the object in focus for child element evaluation. It is up to the evaluator of an extension point to provide a corresponding variable resolver (see berry::IVariableResolver) through the evaluation context passed to the root expression element when evaluating the expression.
-- <tt>args</tt>: Additional arguments passed to the variable resolver. Multiple arguments are seperated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression.
+- <tt>args</tt>: Additional arguments passed to the variable resolver. Multiple arguments are separated by commas. Each individual argument is converted into a Java base type using the same rules as defined for the value attribute of the test expression.
\code{.unparsed}
<!ELEMENT adapt (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
<!ATTLIST adapt
type CDATA #REQUIRED>
\endcode
This element is used to adapt the object in focus to the type specified by the attribute type.
The expression returns not loaded if either the adapter or the type referenced isn't loaded yet. It throws an \c ExpressionException during evaluation if the type name doesn't exist at all.
The children of an adapt expression are combined using the and operator.
- <tt>type</tt>: the type to which the object in focus is to be adapted
\code{.unparsed}
<!ELEMENT iterate (not , and , or , instanceof , test , systemTest , equals , count , with , resolve , adapt , iterate , reference)*>
<!ATTLIST iterate
operator (or|and)
ifEmpty (true | false) >
\endcode
This element is used to iterate over a variable that is of type \c java.util.Collection.
If the object in focus is not of type \c java.util.Collection then an \c ExpressionException will be thrown while evaluating the expression.
- <tt>operator</tt>: Either "and" or "or". The operator defines how the child elements will be combined. If not specified, "and" will be used.
- <tt>ifEmpty</tt>: The value return from the iterate expression if the collection is empty. If not specified then \c true is returned when the operator equals "and" and \c false is returned if the operator equals "or".
\code{.unparsed}
<!ELEMENT reference EMPTY>
<!ATTLIST reference
definitionId CDATA #REQUIRED>
\endcode
This element is used to reference an expression from the \c org.blueberry.core.expressions.definitions extension point.
The expression definition will be evaluated within the current expression element using the current evaluation context.
- <tt>definitionId</tt>: the unique id of an expression from \c org.blueberry.core.expressions.definitions
\section BlueBerryExtPointsIndex_CoreExpr_PropTest Property Testers
\subsection BlueBerryExtPointsIndex_CoreExpr_PropTest_Id Identifier
\c org.blueberry.core.expressions.propertyTesters
\subsection BlueBerryExtPointsIndex_CoreExpr_PropTest_Desc Description
This extension point allows to add properties to an already existing type.
Those properties can then be used inside the expression language's test expression element.
\subsection BlueBerryExtPointsIndex_CoreExpr_PropTest_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT extension (propertyTester*)>
<!ATTLIST extension
point CDATA #REQUIRED
id CDATA #IMPLIED
name CDATA #IMPLIED>
\endcode
- <tt>point</tt>: a fully qualified identifier of the target extension point
- <tt>id</tt>: an optional identifier of the extension instance
- <tt>name</tt>: an optional name of the extension instance
\code{.unparsed}
<!ELEMENT propertyTester EMPTY>
<!ATTLIST propertyTester
id CDATA #REQUIRED
type CDATA #REQUIRED
namespace CDATA #REQUIRED
properties CDATA #REQUIRED
class CDATA #REQUIRED>
\endcode
- <tt>id</tt>: Unique identifier for the property tester.
- <tt>type</tt>: The type to be extended by this property tester.
- <tt>namespace</tt>: A unique id determining the name space the properties are added to.
- <tt>properties</tt>: A comma separated list of properties provided by this property tester.
- <tt>class</tt>: The name of the class that implements the testing methods. The class must be public and extend \c org.blueberry.core.expressions.PropertyTester with a public 0-argument constructor.
\subsection BlueBerryExtPointsIndex_CoreExpr_PropTest_Examples Examples
\code{.unparsed}
<extension point="org.eclipse.core.expressions.propertyTesters">
<propertyTester
id="org.eclipse.jdt.ui.IResourceTester"
type="org.eclipse.core.resources.IResource"
namespace="org.eclipse.jdt.ui"
properties="canDelete"
class="org.eclipse.jdt.ui.internal.ResourceTester">
</propertyTester>
</extension>
\endcode
\page BlueBerryExtPointsIndex_PlatformRuntime Platform Runtime
\tableofcontents
\section BlueBerryExtPointsIndex_PlatformRuntime_App Applications
\subsection BlueBerryExtPointsIndex_PlatformRuntime_App_Id Identifier
\c org.blueberry.osgi.applications
\subsection BlueBerryExtPointsIndex_PlatformRuntime_App_Desc Description
The applications extension point allows plugins to contribute applications to the BlueBerry Platform.
\subsection BlueBerryExtPointsIndex_PlatformRuntime_App_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT extension (application)+>
<!ATTLIST extension
point CDATA #REQUIRED
id CDATA #IMPLIED
name CDATA #IMPLIED>
\endcode
\code{.unparsed}
<!ELEMENT application (run)>
<!ATTLIST application
id CDATA #REQUIRED
name CDATA #IMPLIED>
\endcode
\code{.unparsed}
<!ELEMENT run EMPTY>
<!ATTLIST run
class CDATA #REQUIRED>
\endcode
\subsection BlueBerryExtPointsIndex_PlatformRuntime_App_Examples Examples
\code{.unparsed}
<extension id="sampleApplication" point="org.blueberry.osgi.applications">
<application id="my.domain.application">
<run class="xyz::SampleApp"/>
</application>
</extension>
\endcode
\section BlueBerryExtPointsIndex_PlatformRuntime_Prod Products
\subsection BlueBerryExtPointsIndex_PlatformRuntime_Prod_Id Identifier
\c org.blueberry.core.runtime.products
\subsection BlueBerryExtPointsIndex_PlatformRuntime_Prod_Desc Description
Products are the BlueBerry unit of branding.
Product extensions are supplied by plug-ins wishing to define one or more products.
There must be one product per extension as the extension id is used in processing and identifying the product.
There are two possible forms of product extension, static and dynamic.
Static product extensions directly contain all relevant information about the product.
Dynamic product extensions identify a class (an \c IProductProvider) which is capable of defining one or more products when queried.
\subsection BlueBerryExtPointsIndex_PlatformRuntime_Prod_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT extension ((product | provider))>
<!ATTLIST extension
point CDATA #REQUIRED
id CDATA #IMPLIED
name CDATA #IMPLIED>
\endcode
\code{.unparsed}
<!ELEMENT product (property*)>
<!ATTLIST product
application CDATA #REQUIRED
name CDATA #REQUIRED
description CDATA #IMPLIED>
\endcode
- <tt>application</tt>: the default application to run when running this product
- <tt>name</tt>: the human-readable name of this product
- <tt>description</tt>: the human-readable description of this product
\code{.unparsed}
<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED>
\endcode
- <tt>name</tt>: the key under which this property is stored
- <tt>value</tt>: the value of this property
\code{.unparsed}
<!ELEMENT provider (run)>
\endcode
\code{.unparsed}
<!ELEMENT run EMPTY>
<!ATTLIST run
class CDATA #REQUIRED>
\endcode
- <tt>class</tt>: the fully-qualified name of a class which implements \c IProductProvider
\subsection BlueBerryExtPointsIndex_PlatformRuntime_Prod_Examples Examples
\code{.unparsed}
<extension id="coolProduct" point="org.blueberry.core.runtime.products">
<product name="%coolName" application="coolApplication" description="%coolDescription">
<property name="windowImages" value="window.gif"/>
<property name="aboutImage" value="image.gif"/>
<property name="aboutText" value="%aboutText"/>
<property name="appName" value="CoolApp"/>
<property name="welcomePage" value="$nl$/welcome.xml"/>
<property name="preferenceCustomization" value="plugin_customization.ini"/>
</product>
</extension>
\endcode
The following is an example of a dynamic product (product provider) declaration: Following is an example of an application declaration:
\code{.unparsed}
<extension id="coolProvider" point="org.blueberry.core.runtime.products">
<provider>
<run class="me::CoolProvider"/>
</provider>
</extension>
\endcode
\subsection BlueBerryExtPointsIndex_PlatformRuntime_Prod_SupplImpl Supplied Implementation
No implementations of \c IProductProvider are supplied.
\page BlueBerryExtPointsIndex_Workbench Workbench
\tableofcontents
\section BlueBerryExtPointsIndex_Workbench_Edit Internal and External Editors
\subsection BlueBerryExtPointsIndex_Workbench_Edit_Id Identifier
\c org.blueberry.ui.editors
\subsection BlueBerryExtPointsIndex_Workbench_Edit_Desc Description
This extension point is used to add new editors to the workbench.
A editor is a visual component within a workbench page.
It is typically used to edit or browse a document or input object.
To open an editor, the user will typically invoke "Open" on an \c IFile.
When this action is performed the workbench registry is consulted to determine an appropriate editor for the file type and then a new instance of the editor type is created.
The actual result depends on the type of the editor.
The workbench provides support for the creation of internal editors, which are tightly integrated into the workbench, and external editors, which are launched in a separate frame window.
There are also various levels of integration between these extremes.
In the case of an internal editor tight integration can be achieved between the workbench window and the editor part.
The workbench menu and toolbar are pre-loaded with a number of common actions, such as cut, copy, and paste.
The active part, view or editor, is expected to provide the implementation for these actions.
An internal editor may also define new actions which appear in the workbench window.
These actions only appear when the editor is active.
The integration between the workbench and external editors is more tenuous.
In this case the workbench may launch an editor but after has no way of determining the state of the external editor or collaborating with it by any means except through the file system.
\subsection BlueBerryExtPointsIndex_Workbench_Edit_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT extension (editor*)>
<!ATTLIST extension
point CDATA #REQUIRED
id CDATA #IMPLIED
name CDATA #IMPLIED>
\endcode
- <tt>point</tt>: a fully qualified identifier of the target extension point
- <tt>id</tt>: an optional identifier of the extension instance
- <tt>name</tt>: an optional name of the extension instance
\code{.unparsed}
<!ELEMENT editor (contentTypeBinding*)>
<!ATTLIST editor
id CDATA #REQUIRED
name CDATA #REQUIRED
icon CDATA #IMPLIED
extensions CDATA #IMPLIED
class CDATA #IMPLIED
command CDATA #IMPLIED
launcher CDATA #IMPLIED
contributorClass CDATA #IMPLIED
default (true | false) "false"
filenames CDATA #IMPLIED
matchingStrategy CDATA #IMPLIED>
\endcode
- <tt>id</tt>: a unique name that will be used to identify this editor
- <tt>name</tt>: a translatable name that will be used in the UI for this editor
- <tt>icon</tt>: A relative name of the icon that will be used for all resources that match the specified extensions. Editors should provide an icon to make it easy for users to distinguish between different editor types. If you specify a command rather than a class, an icon is not needed. In that case, the workbench will use the icon provided by the operating system.
- <tt>extensions</tt>: an optional field containing the list of file types understood by the editor. This is a string containing comma separate file extensions. For instance, an editor which understands hypertext documents may register for "htm, html".
- <tt>class</tt>: the name of a class that implements berry::IEditorPart. The attributes \c class , \c command , and \c launcher are mutually exclusive. If this attribute is defined then \c contributorClass should also be defined.
- <tt>command</tt>: a command to run in order to launch an external editor. The executable command must be located on the system path or in the plug-in's directory. The attributes class, command, and launcher are mutually exclusive.
- <tt>launcher</tt>: the name of a class which that implements berry::IEditorLauncher. A launcher will open an external editor. The attributes \c class , \c command , and \c launcher are mutually exclusive.
- <tt>contributorClass</tt>: the name of a class that implements berry::IEditorActionBarContributor. This attribute should only be defined if the \c class attribute is defined. This class is used to add new actions to the workbench menu and tool bar which reflect the features of the editor type.
- <tt>default</tt>: if true, this editor will be used as the default editor for the type. This is only relevant in a case where more than one editor is registered for the same type. If an editor is not the default for the type, it can still be launched using "Open with..." submenu for the selected resource.
Please note that this attribute is only honored for filename and extension associations at this time. It will not be honored for content type bindings. Content type-based resolution will occur on a first come, first serve basis and is not explicitly specified.
- <tt>filenames</tt>: an optional field containing the list of file names understood by the editor. This is a string containing comma separate file names. For instance, an editor which understands specific hypertext documents may register for "ejb.htm, ejb.html".
- <tt>matchingStrategy</tt>: the name of a class that implements berry::IEditorMatchingStrategy. This attribute should only be defined if the \c class attribute is defined. This allows the editor extension to provide its own algorithm for matching the input of one of its editors to a given editor input.
\code{.unparsed}
<!ELEMENT contentTypeBinding EMPTY>
<!ATTLIST contentTypeBinding
contentTypeId CDATA #REQUIRED>
\endcode
Advertises that the containing editor understands the given content type and is suitable for editing files of that type.
- <tt>contentTypeId</tt>: The content type identifier. This is an ID defined by the \c org.blueberry.core.runtime.contentTypes extension point.
\subsection BlueBerryExtPointsIndex_Workbench_Edit_Examples Examples
The following is an example of an internal editor extension definition:
\code{.unparsed}
<extension point="org.blueberry.ui.editors">
<editor
id="com.xyz.XMLEditor"
name="Fancy XYZ XML editor"
icon="./icons/XMLEditor.gif"
extensions="xml"
class="xyz::XMLEditor"
contributorClass="xyz::XMLEditorContributor"
default="false">
</editor>
</extension>
\endcode
\subsection BlueBerryExtPointsIndex_Workbench_Edit_SupplImpl Supplied Implementation
The workbench provides a "Default Text Editor". The end user product may contain other editors as part of the shipping bundle. In that case, editors will be registered as extensions using the syntax described above.
\section BlueBerryExtPointsIndex_Workbench_Keywords Keywords
\subsection BlueBerryExtPointsIndex_Workbench_Keywords_Id Identifier
\c org.blueberry.ui.keywords
\subsection BlueBerryExtPointsIndex_Workbench_Keywords_Desc Description
The keywords extension point defines keywords and a unique id for reference by other schemas. See \c propertyPages and \c preferencePages .
\subsection BlueBerryExtPointsIndex_Workbench_Keywords_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT extension (keyword*)>
<!ATTLIST extension
point CDATA #REQUIRED
id CDATA #IMPLIED
name CDATA #IMPLIED>
\endcode
- <tt>point</tt>: a fully qualified identifier of the target extension point
- <tt>id</tt>: an optional identifier of the extension instance
- <tt>name</tt>: an optional name of the extension instance
\code{.unparsed}
<!ELEMENT keyword EMPTY>
<!ATTLIST keyword
id CDATA #REQUIRED
label CDATA #REQUIRED>
\endcode
- <tt>id</tt>: The id is the unique id used to reference the keyword
- <tt>label</tt>: The human readable label of the keyword
\subsection BlueBerryExtPointsIndex_Workbench_Keywords_Examples Examples
The following is an example of a keyword extension:
\code{.unparsed}
<extension
point="org.blueberry.ui.keywords">
<keyword
label="presentation tab themes"
id="com.xyz.AppearanceKeywords"/>
</extension>
\endcode
\subsection BlueBerryExtPointsIndex_Workbench_Keywords_SupplImpl Supplied Implementation
Keywords are used only with preference and property pages. See the \c keywordReference element of the \c org.blueberry.ui.propertyPages and \c org.blueberry.ui.preferencePages extension points.
\section BlueBerryExtPointsIndex_Workbench_PerspExt Perspective Extensions
\subsection BlueBerryExtPointsIndex_Workbench_PerspExt_Id Identifier
\c org.blueberry.ui.perspectiveExtensions
\subsection BlueBerryExtPointsIndex_Workbench_PerspExt_Desc Description
This extension point is used to extend perspectives registered by other plug-ins.
A perspective defines the initial contents of the window action bars (menu and toolbar) and the initial set of views and their layout within a workbench page.
Other plug-ins may contribute actions or views to the perspective which appear when the perspective is selected.
Optional additions by other plug-ins are appended to the initial definition.
\subsection BlueBerryExtPointsIndex_Workbench_PerspExt_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT extension (perspectiveExtension*)>
<!ATTLIST extension
point CDATA #REQUIRED
id CDATA #IMPLIED
name CDATA #IMPLIED>
\endcode
- <tt>point</tt>: a fully qualified identifier of the target extension point
- <tt>id</tt>: an optional identifier of the extension instance
- <tt>name</tt>: an optional name of the extension instance
\code{.unparsed}
<!ELEMENT perspectiveExtension (actionSet | viewShortcut | perspectiveShortcut | newWizardShortcut | view | showInPart | hiddenMenuItem | hiddenToolBarItem)*>
<!ATTLIST perspectiveExtension
targetID IDREF #REQUIRED>
\endcode
- <tt>targetID</tt>: the unique identifier of the perspective (as specified in the registry) into which the contribution is made. If the value is set to "*" the extension is applied to all perspectives.
\code{.unparsed}
<!ELEMENT actionSet EMPTY>
<!ATTLIST actionSet
id IDREF #REQUIRED>
\endcode
- <tt>id</tt>: the unique identifier of the action set which will be added to the perspective.
\code{.unparsed}
<!ELEMENT viewShortcut EMPTY>
<!ATTLIST viewShortcut
id IDREF #REQUIRED>
\endcode
- <tt>id</tt>: the unique identifier of the view which will be added to the perspective's "Show View" submenu of the "Window" menu.
\code{.unparsed}
<!ELEMENT perspectiveShortcut EMPTY>
<!ATTLIST perspectiveShortcut
id IDREF #REQUIRED>
\endcode
- <tt>id</tt>: the unique identifier of the perspective which will be added to the perspective's "Open Perspective" submenu of the "Window" menu.
\code{.unparsed}
<!ELEMENT newWizardShortcut EMPTY>
<!ATTLIST newWizardShortcut
id IDREF #REQUIRED>
\endcode
- <tt>id</tt>: the unique identifier of the new wizard which will be added to the perspective's "New" submenu of the "File" menu.
\code{.unparsed}
<!ELEMENT showInPart EMPTY>
<!ATTLIST showInPart
id IDREF #IMPLIED>
\endcode
- <tt>id</tt>: the unique identifier of the view which will be added to the perspective's "Show In..." prompter in the Navigate menu.
\code{.unparsed}
<!ELEMENT view EMPTY>
<!ATTLIST view
id IDREF #REQUIRED
relative IDREF #IMPLIED
relationship (stack|left|right|top|bottom|fast)
ratio CDATA #IMPLIED
visible (true | false)
closeable (true | false)
moveable (true | false)
standalone (true | false)
showTitle (true | false)
minimized (true | false) "false">
\endcode
- <tt>id</tt>: the unique identifier of the view which will be added to the perspective layout.
- <tt>relative</tt>: the unique identifier of a view which already exists in the perspective. This will be used as a reference point for placement of the view. The relationship between these two views is defined by \c relationship . Ignored if relationship is "fast".
- <tt>relationship</tt>: specifies the relationship between \c id and \c relative . The following values are supported:
- <tt>fast</tt>: the view extension will be created as a fast view.
- <tt>stack</tt>: the view extension will be stacked with the relative view in a folder.
- <tt>left, right, top, bottom</tt>: the view extension will be placed beside the relative view. In this case a \c ratio must also be defined.
- <tt>ratio</tt>: the percentage of area within the relative view which will be donated to the view extension. If the view extension is a fast view, the ratio is the percentage of the workbench the fast view will cover when active. This must be defined as a floating point value and lie between 0.05 and 0.95.
- <tt>visible</tt>: whether the view is initially visible when the perspective is opened. This attribute should have a value of "true" or "false" if used. If this attribute is not used, the view will be initially visible by default.
- <tt>closeable</tt>: whether the view is closeable in the target perspective. This attribute should have a value of "true" or "false" if used. If this attribute is not used, the view will be closeable, unless the perspective itself is marked as fixed.
- <tt>moveable</tt>: whether the view is moveable. A non-moveable view cannot be moved either within the same folder, or moved between folders in the perspective. This attribute should have a value of "true" or "false" if used. If this attribute is not used, the view will be moveable, unless the perspective itself is marked as fixed.
- <tt>standalone</tt>: whether the view is a standalone view. A standalone view cannot be docked together with others in the same folder. This attribute should have a value of "true" or "false" if used. This attribute is ignored if the relationship attribute is "fast" or "stacked". If this attribute is not used, the view will be a regular view, not a standalone view (default is "false").
- <tt>showTitle</tt>: whether the view's title is shown. This attribute should have a value of "true" or "false" if used. This attribute only applies to standalone views. If this attribute is not used, the view's title will be shown (default is "true").
- <tt>minimized</tt>: If the perspective extension will result in a new view stack being created (i.e. the 'relationship' attribute is one of left, right, top or bottom) this field determines the new stack's initial display state.
\code{.unparsed}
<!ELEMENT hiddenMenuItem EMPTY>
<!ATTLIST hiddenMenuItem
id CDATA #REQUIRED>
\endcode
- <tt>id</tt>: The unique identifier of the Command which is to be removed from the menu. <b>WARNING:</b> This is considered to be a 'Product level' extension and should not be used in consumable plugins without great care.
\code{.unparsed}
<!ELEMENT hiddenToolBarItem EMPTY>
<!ATTLIST hiddenToolBarItem
id CDATA #REQUIRED>
\endcode
- <tt>id</tt>: The unique identifier of the Command which is to be removed from thetoolbar. <b>WARNING:</b> This is considered to be a 'Product level' extension and should not be used in consumable plugins without great care.
\subsection BlueBerryExtPointsIndex_Workbench_PerspExt_Examples Examples
The following is an example of a perspective extension (note the subelements and the way attributes are used):
\code{.unparsed}
<extension point="org.blueberry.ui.perspectiveExtensions">
<perspectiveExtension
targetID="org.blueberry.ui.resourcePerspective">
<actionSet id="org.xyz.MyActionSet"/>
<viewShortcut id="org.xyz.views.PackageExplorer"/>
<newWizardShortcut id="org.xyz.wizards.NewProjectCreationWizard"/>
<perspectiveShortcut id="org.xyz.MyPerspective"/>
<view id="org.xyz.views.PackageExplorer"
relative="org.blueberry.ui.views.ResourceNavigator"
relationship="stack"/>
<view id="org.xyz.views.TypeHierarchy"
relative="org.blueberry.ui.views.ResourceNavigator"
relationship="left"
ratio="0.50"/>
</perspectiveExtension>
</extension>
\endcode
In the example above, an action set, view shortcut, new wizard shortcut, and perspective shortcut are contributed to the initial contents of the Resource Perspective.
In addition, the Package Explorer view is stacked on the Resource Navigator and the Type Hierarchy View is added beside the Resource Navigator.
\section BlueBerryExtPointsIndex_Workbench_Persp Perspectives
\subsection BlueBerryExtPointsIndex_Workbench_Persp_Id Identifier
\c org.blueberry.ui.perspective
\subsection BlueBerryExtPointsIndex_Workbench_Persp_Desc Description
This extension point is used to add perspective factories to the workbench.
A perspective factory is used to define the initial layout and visible action sets for a perspective.
The user can select a perspective by invoking the "Open Perspective" submenu of the "Window" menu.
\subsection BlueBerryExtPointsIndex_Workbench_Persp_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT extension (perspective*)>
<!ATTLIST extension
point CDATA #REQUIRED
id CDATA #IMPLIED
name CDATA #IMPLIED>
\endcode
- <tt>point</tt>: a fully qualified identifier of the target extension point
- <tt>id</tt>: an optional identifier of the extension instance
- <tt>name</tt>: an optional name of the extension instance
\code{.unparsed}
<!ELEMENT perspective (description? , keywordReference*)>
<!ATTLIST perspective
id CDATA #REQUIRED
name CDATA #REQUIRED
class CDATA #REQUIRED
icon CDATA #IMPLIED
fixed (true | false)>
\endcode
- <tt>id</tt>: a unique name that will be used to identify this perspective.
- <tt>name</tt>: a translatable name that will be used in the workbench window menu bar to represent this perspective.
- <tt>class</tt>: a fully qualified name of the class that implements berry::IPerspectiveFactory interface.
- <tt>icon</tt>: a relative name of the icon that will be associated with this perspective.
- <tt>fixed</tt>: indicates whether the layout of the perspective is fixed. If true, then views created by the perspective factory are not closeable, and cannot be moved. The default is false.
\code{.unparsed}
<!ELEMENT description (#PCDATA)>
\endcode
An optional subelement whose body should contain text providing a short description of the perspective.
\subsection BlueBerryExtPointsIndex_Workbench_Persp_Examples Examples
The following is an example of a perspective extension:
\code{.unparsed}
<extension
point="org.blueberry.ui.perspectives">
<perspective
id="org.blueberry.ui.resourcePerspective"
name="Resource"
class="berry::ResourcePerspective"
icon="resources/MyIcon.gif">
</perspective>
</extension>
\endcode
\subsection BlueBerryExtPointsIndex_Workbench_Persp_SupplImpl Supplied Implementation
The workbench provides a "Resource Perspective".
Additional perspectives may be added by plug-ins.
They are selected using the "Open Perspective" submenu of the "Window" menu.
\section BlueBerryExtPointsIndex_Workbench_PrefPages Preference Pages
\subsection BlueBerryExtPointsIndex_Workbench_PrefPages_Id Identifier
\c org.blueberry.ui.preferencePages
\subsection BlueBerryExtPointsIndex_Workbench_PrefPages_Desc Description
The workbench provides one common dialog box for preferences.
The purpose of this extension point is to allow plug-ins to add pages to the preference dialog box.
When preference dialog box is opened (initiated from the menu bar), pages contributed in this way will be added to the dialog box.
\subsection BlueBerryExtPointsIndex_Workbench_PrefPages_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT extension (page*)>
<!ATTLIST extension
point CDATA #REQUIRED
id CDATA #IMPLIED
name CDATA #IMPLIED>
\endcode
- <tt>point</tt>: a fully qualified identifier of the target extension point
- <tt>id</tt>: an optional identifier of the extension instance
- <tt>name</tt>: an optional name of the extension instance
\code{.unparsed}
<!ELEMENT page (keywordReference*)>
<!ATTLIST page
id CDATA #REQUIRED
name CDATA #REQUIRED
class CDATA #REQUIRED
category IDREF #IMPLIED>
\endcode
- <tt>id</tt>: a unique name that will be used to identify this page.
- <tt>name</tt>: a translatable name that will be used in the UI for this page.
- <tt>class</tt>: a name of the fully qualified class that implements berry::IWorkbenchPreferencePage.
- <tt>category</tt>: a path indicating the location of the page in the preference tree. The path may either be a parent node ID or a sequence of IDs separated by '/', representing the full path from the root node.
\code{.unparsed}
<!ELEMENT keywordReference EMPTY>
<!ATTLIST keywordReference
id IDREF #REQUIRED>
\endcode
A reference by a preference page to a keyword. See the keywords extension point.
- <tt>id</tt>: The id of the keyword being referred to.
\subsection BlueBerryExtPointsIndex_Workbench_PrefPages_Examples Examples
The following is an example for the preference extension point:
\code{.unparsed}
<extension
point="org.blueberry.ui.preferencePages">
<page
id="com.xyz.prefpage1"
name="XYZ"
class="xyz::PrefPage1">
<keywordReference id="xyz.Keyword"/>
</page>
<page
id="com.xyz.prefpage2"
name="Keyboard Settings"
class="xyz::PrefPage2"
category="com.xyz.prefpage1">
</page>
</extension>
\endcode
\subsection BlueBerryExtPointsIndex_Workbench_PrefPages_SupplImpl Supplied Implementation
The workbench adds several pages for setting the preferences of the platform. Pages registered through this extension will be added after them according to their category information.
\section BlueBerryExtPointsIndex_Workbench_PresFact Presentation Factories
\subsection BlueBerryExtPointsIndex_Workbench_PresFact_Id Identifier
\c org.blueberry.ui.presentationFactories
\subsection BlueBerryExtPointsIndex_Workbench_PresFact_Desc Description
This extension point is used to add presentation factories to the workbench. A presentation factory defines the overall look and feel of the workbench, including how views and editors are presented.
\subsection BlueBerryExtPointsIndex_Workbench_PresFact_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT extension (factory*)>
<!ATTLIST extension
point CDATA #REQUIRED
id CDATA #IMPLIED
name CDATA #IMPLIED>
\endcode
\code{.unparsed}
<!ELEMENT factory EMPTY>
<!ATTLIST factory
class CDATA #REQUIRED
id CDATA #REQUIRED
name CDATA #REQUIRED>
\endcode
- <tt>class</tt>: Specify the fully qualified class to be used for the presentation factory. The specified value must implement the interface berry::IPresentationFactory.
- <tt>id</tt>: a unique name that will be used to identify this presentation factory
- <tt>name</tt>: a translatable name that can be used to show this presentation factory in the UI
\subsection BlueBerryExtPointsIndex_Workbench_PresFact_Examples Examples
The following is an example of a presentationFactory extension:
\code{.unparsed}
<extension point="org.blueberry.ui.presentationFactories">
<factory
class="berry::ExampleWorkbenchPresentationFactory"/>
</extension>
\endcode
\subsection BlueBerryExtPointsIndex_Workbench_PresFact_SupplImpl Supplied Implementation
If a presentation factory is not specified or is missing then the implementation in berry::QtWorkbenchPresentationFactory will be used.
\section BlueBerryExtPointsIndex_Workbench_Services Services
\subsection BlueBerryExtPointsIndex_Workbench_Services_Id Identifier
\c org.blueberry.ui.services
\subsection BlueBerryExtPointsIndex_Workbench_Services_Desc Description
Define service factories so that services can be contributed declaratively and will be available through berry::IServiceLocator::GetService(Class).
The implementation of \c AbstractServiceFactory must be able to return a global service and multiple child services (if applicable).
\subsection BlueBerryExtPointsIndex_Workbench_Services_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT extension (serviceFactory? , sourceProvider?)+>
<!ATTLIST extension
point CDATA #REQUIRED
id CDATA #IMPLIED
name CDATA #IMPLIED>
\endcode
Contribute services to the workbench.
\code{.unparsed}
<!ELEMENT serviceFactory (service+)>
<!ATTLIST serviceFactory
factoryClass CDATA #REQUIRED>
\endcode
-Match a service interface to a factory that can supply a hierachical implementation of that service.
+Match a service interface to a factory that can supply a hierarchical implementation of that service.
- <tt>factoryClass</tt>: The factory that extends \c AbstractServiceFactory and can create the implementation for the \c serviceClass.
\code{.unparsed}
<!ELEMENT service EMPTY>
<!ATTLIST service
serviceClass CDATA #IMPLIED>
\endcode
A service this factory can provide.
- <tt>serviceClass</tt>: The interface that represents a service contract.
\code{.unparsed}
<!ELEMENT sourceProvider (variable+)>
<!ATTLIST sourceProvider
provider CDATA #REQUIRED>
\endcode
A Source Provider supplies source variables to the IEvaluationService. It can also notify the IEvaluationService when one or more of the variables change.
- <tt>provider</tt>: This class must provide variables and call the appropriate \c fireSourceChanged(*) method when any of the variables change.
\code{.unparsed}
<!ELEMENT variable EMPTY>
<!ATTLIST variable
name CDATA #REQUIRED
priorityLevel (workbench|activeContexts|activeShell|activeWorkbenchWindow|activeEditorId|activePartId|activeSite) >
\endcode
A source variable from this provider.
A source provider must declare all variables that it provides.
- <tt>name</tt>: The name of a contributed source variable. It is a good practice to prepend the plugin id to the variable name to avoid collisions with other source providers.
- <tt>priorityLevel</tt>: For conflict resolution used by services like the \c IHandlerService , contributed source variables must assign a priority. workbench is the global default priority. See \c ISources for relative priority information.
\subsection BlueBerryExtPointsIndex_Workbench_Services_Examples Examples
Here is a basic definition:
\code{.unparsed}
<extension point="org.blueberry.ui.services">
<serviceFactory factoryClass="my::LevelServiceFactory">
<service serviceClass="my::ILevelService">
</service>
</serviceFactory>
</extension>
\endcode
The LevelServiceFactory can return an ILevelService when it is requested from the IServiceLocator:
\code{.unparsed}
berry::ILevelService::Pointer s = GetSite()->GetService(my::ILevelService::GetStaticClassName());
std::cout << s->GetLevel();
\endcode
In this test example, the factory would instantiate three \c ILevelService implementations during the first call to \c GetSite()->GetService(*) . The global one in the workbench, one for the workbench window, and one for the site.
\section BlueBerryExtPointsIndex_Workbench_Tweak Tweaklets
\subsection BlueBerryExtPointsIndex_Workbench_Tweak_Id Identifier
\c org.blueberry.ui.tweaklets
\subsection BlueBerryExtPointsIndex_Workbench_Tweak_Desc Description
Typically, although not required, the value of the \c definition attribute is the fully qualified name without colons of an abstract class defined by the workbench, and the value of the \c implementation attribute is the fully qualified name of a non-abstract class provided by the extending plug-in.
\subsection BlueBerryExtPointsIndex_Workbench_Tweak_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT extension (tweaklet)*>
<!ATTLIST extension
point CDATA #REQUIRED
id CDATA #IMPLIED
name CDATA #IMPLIED>
\endcode
\code{.unparsed}
<!ELEMENT tweaklet EMPTY>
<!ATTLIST tweaklet
id CDATA #REQUIRED
name CDATA #REQUIRED
description CDATA #IMPLIED
definition CDATA #IMPLIED
implementation CDATA #IMPLIED>
\endcode
- <tt>id</tt>: a unique name that will be used to identify this tweaklet
- <tt>name</tt>: a translatable name that will be used in the UI for this tweaklet
- <tt>description</tt>: a translatable short description of this tweaklet, to be used in the UI
- <tt>definition</tt>: an identifier of the tweaklet definition in the workbench, typically a fully qualified type name without colons
- <tt>implementation</tt>: an identifier of the tweaklet implementation provided by the extender, typically a fully qualified class name
\subsection BlueBerryExtPointsIndex_Workbench_Tweak_SupplImpl Supplied Implementation
Tweaklets are usually used to specialize the workbench for a specific GUI toolkit.
\section BlueBerryExtPointsIndex_Workbench_Views Views
\subsection BlueBerryExtPointsIndex_Workbench_Views_Id Identifier
\c org.blueberry.ui.views
\subsection BlueBerryExtPointsIndex_Workbench_Views_Desc Description
This extension point is used to define additional views for the workbench.
A view is a visual component within a workbench page.
It is typically used to navigate a hierarchy of information (like the workspace), open an editor, or display properties for the active editor.
The user can make a view visible from the Window > Show View menu or close it from the view local title bar.
In order to reduce the visual clutter in the Show View Dialog, views should be grouped using categories.
\subsection BlueBerryExtPointsIndex_Workbench_Views_ConfMarkup Configuration Markup
\code{.unparsed}
<!ELEMENT extension (category | view | stickyView)*>
<!ATTLIST extension
point CDATA #REQUIRED
id CDATA #IMPLIED
name CDATA #IMPLIED>
\endcode
- <tt>point</tt>: a fully qualified identifier of the target extension point
- <tt>id</tt>: an optional identifier of the extension instance
- <tt>name</tt>: an optional name of the extension instance
\code{.unparsed}
<!ELEMENT category EMPTY>
<!ATTLIST category
id CDATA #REQUIRED
name CDATA #REQUIRED
parentCategory IDREF #IMPLIED>
\endcode
- <tt>id</tt>: a unique name that will be used to identify this category
- <tt>name</tt>: a translatable name that will be used in the UI for this category
- <tt>parentCategory</tt>: an optional path composed of category IDs separated by '/'. This allows the creation of a hierarchy of categories.
\code{.unparsed}
<!ELEMENT view (description? , keywordReference*)>
<!ATTLIST view
id CDATA #REQUIRED
name CDATA #REQUIRED
category IDREF #IMPLIED
class CDATA #REQUIRED
internal (true | false)
icon CDATA #IMPLIED
fastViewWidthRatio CDATA #IMPLIED
allowMultiple (true | false)
restorable (true | false) "true">
\endcode
- <tt>id</tt>: a unique name that will be used to identify this view
- <tt>name</tt>: a translatable name that will be used in the UI for this view
- <tt>category</tt>: an optional attribute that is composed of the category IDs separated by '/'. Each referenced category must be declared in a corresponding category element.
- <tt>class</tt>: a fully qualified name of the class that implements berry::IViewPart. A common practice is to subclass berry::ViewPart or berry::QtViewPart in order to inherit the default functionality.
- <tt>internal</tt>: a hint if the view should be considered internal, e.g., in enumerations of views in the UI.
- <tt>icon</tt>: a relative name of the icon that will be associated with the view.
- <tt>fastViewWidthRatio</tt>: the percentage of the width of the workbench that the view will take up as an active fast view. This must be defined as a floating point value and lie between 0.05 and 0.95. If no value is supplied, a default ratio will be used.
- <tt>allowMultiple</tt>: flag indicating whether this view allows multiple instances to be created using IWorkbenchPage::ShowView(QString id, QString secondaryId). The default is false.
- <tt>restorable</tt>: flag indicating whether this view allows to be restored upon workbench restart. If set to false, the view will not be open after a workbench restart. The default is true.
\code{.unparsed}
<!ELEMENT description (#PCDATA)>
\endcode
An optional subelement whose body should contain text providing a short description of the view.
\code{.unparsed}
<!ELEMENT stickyView EMPTY>
<!ATTLIST stickyView
id IDREF #REQUIRED
location (RIGHT|LEFT|TOP|BOTTOM)
closeable (true | false)
moveable (true | false)>
\endcoe
A sticky view is a view that will appear by default across all perspectives in a window once it is opened.
Its initial placement is governemed by the location attribute, but nothing prevents it from being moved or closed by the user.
Use of this element will only cause a placeholder for the view to be created, it will not show the view.
-Please note that usage of this element should be done with great care and should only be applied to views that truely have a need to live across perspectives.
+Please note that usage of this element should be done with great care and should only be applied to views that truly have a need to live across perspectives.
- <tt>id</tt>: the id of the view to be made sticky.
- <tt>location</tt>: optional attribute that specifies the location of the sticky view relative to the editor area. If absent, the view will be docked to the right of the editor area.
-- <tt>closeable</tt>: optional attribute that specifies wether the view should be closeable. If absent it will be closeable.
-- <tt>moveable</tt>: optional attribute that specifies wether the view should be moveable. If absent it will be moveable.
+- <tt>closeable</tt>: optional attribute that specifies whether the view should be closeable. If absent it will be closeable.
+- <tt>moveable</tt>: optional attribute that specifies whether the view should be moveable. If absent it will be moveable.
\subsection BlueBerryExtPointsIndex_Workbench_Views_Examples Examples
The following is an example of the extension point:
\code{.unparsed}
<extension point="org.blueberry.ui.views">
<category
id="com.xyz.views.XYZviews"
name="XYZ"/>
<view
id="com.xyz.views.XYZView"
name="XYZ View"
category="com.xyz.views.XYZviews"
class="ns::XYZView"
icon="icons/XYZ.gif"/>
</extension>
\endcode
The following is an example of a sticky view declaration:
\code{.unparsed}
<extension point="org.blueberry.ui.views">
<stickyView id="com.xyz.views.XYZView" />
</extension>
\endcode
\subsection BlueBerryExtPointsIndex_Workbench_Views_SupplImpl Supplied Implementation
The BlueBerry Platform provides a number of standard views.
From the user point of view, these views are no different from any other view provided by the plug-ins.
All the views can be shown from the "Show View" submenu of the "Window" menu.
The position of a view is persistent: it is saved when the view is closed and restored when the view is reopened in a single session.
The position is also persisted between workbench sessions.
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Annotation.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Annotation.dox
index b75fbd7a25..2486231383 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Annotation.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Annotation.dox
@@ -1,51 +1,51 @@
/**
\page AnnotationPage Annotation Concept
\tableofcontents
\section AnnotationPage_Introduction Annotations
The annotations in MITK are a simple way to display additional information on the render windows. A class, deriving from mitk::Annotation represents an arbitrary
2D or 3D object that can be rendered as an Annotation. This can for example be used for the annotation of 3D points or to Annotation despriptions in the window corners.
Instances of the MicroService mitk::AbstractAnnotationRenderer are used to add the annotations to the renderwindows, updating them and depending on their implementation, organize them in a layout.
This module contains implementations for mitk::AbstractAnnotationRenderer as well as mitk::Annotation. Currently, the following features are realized within the Annotation module.
<ol>
<li>2D and 3D textelements are already defined in the Annotation module and are using VTK to create custom annotations.
<li>2D and 3D annotations can be placed freely by providing a display position
<li>2D annotations can be placed in a layout, which organizes the annotations in the display corners.
</ol>
\section AnnotationPage_ArchitectureSection General Architecture
The mitk::Annotation can be implemented using a custom rendering framework like VTK. In this diagram, the vtkAnnotation is shown as the superclass for all Annotations which use the vtk framework for rendering.
The AnnotationManager can be registered to several BaseRenderer instances in order to call the update method of each Annotation during the rendering phase of the renderer.
It also manages the respective Layouters which are used to manage the placement of a group of Annotations.
\subsection AnnotationPage_AnnotationSubsection Annotation
The mitk::Annotation is an abstract class that can manage property lists like the mitk::DataNode and provides the interfaces to the methods
AddToBaseRenderer, AddToRenderer, RemoveFromBaseRenderer RemoveFromRenderer and Update. The subclasses of the mitk::Annotation have to implement these methods
-in order to provide the functionallity of an Annotation. There are already a few implementations of mitk::Annotation which are using VTK as a rendering
+in order to provide the functionality of an Annotation. There are already a few implementations of mitk::Annotation which are using VTK as a rendering
framework to display the Annotations. In order to show an Annotation, it has to be registered as a MicroService.
\subsection AnnotationPage_AnnotationRendererSubsection AbstractAnnotationRenderer
The AbstractAnnotationRenderer is the base class for all types of AnnotationRenderers, which are used for the management of multiple annotations in a specific BaseRenderer.
-For each BaseRenderer, an AnnotationRenderer is registerered as a MicroService, using the type and the renderer name as a unique identifier. This way it is possible to keep the Annotations for a specific BaseRenderer
-even if this renderer is temporarilly not existent (e.g. when a RenderWindow was closed).
+For each BaseRenderer, an AnnotationRenderer is registered as a MicroService, using the type and the renderer name as a unique identifier. This way it is possible to keep the Annotations for a specific BaseRenderer
+even if this renderer is temporarily not existent (e.g. when a RenderWindow was closed).
Implementations of the AbstractAnnotationRenderer are using the us::ServiceTracker in order to manage all registered Annotations and to listen to the events which are thrown if a new Annotation is registered,
if an Annotation was unregistered or if it was modified.
\subsubsection AnnotationPage_ManualPlacementAnnotationRendererSubsection ManualPlacementAnnotationRenderer
Using the ManualPlacementAnnotationRenderer, allows for a simple placement of Annotations. The Annotation manages its placement internally or using the Position2D / Position3D properties.
\subsubsection AnnotationPage_LayoutAnnotationRendererSubsection LayoutAnnotationRenderer
The LayoutAnnotationRenderer allows automatic placement in the RenderWindow corners and sorts the Annotations by a specified priority.
\subsection AnnotationPage_AnnotationUtilsSubsection AnnotationUtils
mitk::AnnotationUtils is a collection of static convenience functions, for the registration of AnnotationRenderers and to request certain registered Annotations.
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox
index fec4b1577a..b469fdc6b2 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
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
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)
-# 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 possibility 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::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.
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;
\endcode
more examples of how to convert between data types can be found in the tests:
-# mitkArrayTypeConversionTest.cpp
-# mitkPointTypeConversionTest.cpp
-# mitkVectorTypeConversionTest.cpp
-# mitkMatrixTypeConversionTest.cpp
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/DICOMTesting.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/DICOMTesting.dox
index c9cacceb51..d75141f172 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/DICOMTesting.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/DICOMTesting.dox
@@ -1,154 +1,154 @@
/**
\page DICOMTesting MITK DICOM testing
\section DICOMTesting_introduction Introduction
Reading DICOM data into mitk::Images is a complicated business since DICOM and MITK have very different ideas of images.
DICOMITKSeriesGDCMReader (formerly: DicomSeriesReader) brings DICOM and MITK as close together as possible by offering methods to load DICOM CT and MR images
into mitk::Images, optionally grouping slices to 3D+t images.
Since there are so many possible sources for mistakes with any change to this loading process, testing the many
-assumptions implemented in DICOMITKSeriesGDCMReader is worthwhile. This document describes what and how theses kind of tests are implemented.
+assumptions implemented in DICOMITKSeriesGDCMReader is worthwhile. This document describes what and how these kind of tests are implemented.
\section DICOMTesting_problem Problem description
The task of loading DICOM files into mitk::Images is a challenge because of major differences
in the way that DICOM and MITK represent images:
- DICOM images
+ are mostly stored as one slice per file
- + do not describe how they can be arraged into image stacks with orthogonal axis
+ + do not describe how they can be arranged into image stacks with orthogonal axis
+ sometimes they cannot be arranged in image stacks as described above (e.g. tilted gantry)
- mitk::Image (at least its mature areas)
+ represents image stacks with orthogonal axis (nothing like a tilted gantry)
+ have a concept of a fourth dimension (time steps)
Because image processing applications based on MITK still need a way to load DICOM images into
mitk::Image objects, MITK needs a way to build image stacks and this needs to be well tested.
For more background information, see David Clunie's most valuable posts on comp.protocols.dicom, e.g.:
- https://groups.google.com/g/comp.protocols.dicom/c/bbkZcuFh8NQ
- https://groups.google.com/g/comp.protocols.dicom/c/6b0Ul76j5ms
- https://groups.google.com/g/comp.protocols.dicom/c/XYC7C3-vy4E
- https://groups.google.com/g/comp.protocols.dicom/c/RWhjXgg6P7o
\section DICOMTesting_testidea Test principle
The general idea for DICOM loaing tests is to run a set of known DICOM files through DICOMITKSeriesGDCMReader's methods
GetSeries() and LoadDicomSeries() to generate mitk::Images. These images are then compared to expected image properties,
such as the number of individual mitk::Images, positions, orientations, spacings, etc.
Stored expectations look like this (should be self-explanatory):
\verbatim
-- Image 1
Pixeltype: s
BitsPerPixel: 16
Dimension: 4
Dimensions: 64 64 6 1
Geometry:
Matrix: 5.25 0 0 0 5.2468 0.139598 0 -0.183222 3.99757
Offset: -159.672 -309.974 -69.0122
Center: 0 0 0
Translation: -159.672 -309.974 -69.0122
Scale: 1 1 1
Origin: -159.672 -309.974 -69.0122
Spacing: 5.25 5.25 4
TimeBounds: 0 1
-- Image 2
Pixeltype: s
BitsPerPixel: 16
Dimension: 4
Dimensions: 64 64 41 1
Geometry:
Matrix: 5.25 0 0 0 5.25 0 0 0 4
Offset: -160.672 -311.672 -285
Center: 0 0 0
Translation: -160.672 -311.672 -285
Scale: 1 1 1
Origin: -160.672 -311.672 -285
Spacing: 5.25 5.25 4
TimeBounds: 0 1
\endverbatim
\section DICOMTesting_implementation Implementation
\section DICOMTesting_implementation_utils Test helpers (applications and classes)
<b>Application DumpDICOMMitkImage</b>
Takes a list of DICOM images, loads them using TestDICOMLoading, then dumps information
about the resulting mitk::Images to standard output.
This application is helpful when defining reference data for tests.
<b>Application VerifyDICOMMitkImageDump</b>
Takes a list of DICOM images and loads them using TestDICOMLoading.
Takes a dump file as generated by DumpDICOMMitkImage, parses it and
compares it to actually generated mitk::Images.
This application is used to implement the majority of test cases. They all
load images, then verify the expected result structure.
<b>Class TestDICOMLoading</b>
\section PageDICOMLoadingTests_testcaseimplementation Test case implementation
Individual test cases are stored in the MITK-Data repository and constructed by Core/Code/Testing/DICOMTesting/Testing/CMakeLists.txt
The CMake file parses given directories for subdirectories containing specific test cases. Each directory contains two files:
- File "input": lists DICOM files that should be loaded for this test case
- File "expected.dump": contains the image properties in the above mentioned dump format
Each test case is translated into a CTest test which evaluates the return value of a call to VerifyDICOMMitkImageDump.
\section PageDICOMLoadingTests_testcases Implemented test cases
From test set \b TinyCTAbdomen (see description.txt in this directory for details on test images):
- singleslice : just a single slice (should work and contain meaningful spacing)
- two_slices : two slices, spacing should be calculated correctly
- all : load a "normal" series as a single 3D block
- 3D_and_T : load a small set of slices with multiple time-steps
- diff_orientation : load a set of files containing two differently oriented image blocks; at least two images (110,111) have minor errors in small decimal digits
- diff_orientation_gaps : load a set of files containing two differently oriented image blocks, each missing slices, so blocks must be split
- - diff_spacing : load a set of files containint two set of slices with different spacings
+ - diff_spacing : load a set of files containing two set of slices with different spacings
- gap : load slices that cannot form a single 3D block, because single slices are missing
- - gaps : slices missing in differnt locations, so multiple splits needed
+ - gaps : slices missing in different locations, so multiple splits needed
- unsorted_gaps : as before, just file names are unsorted
- single_negative_spacing : from reported bug related to single MR images with misleading spacing information
- tilted_gantry : slice origins do not align along first slice normal (happens with tilted gantries)
- interleaved : two volumes of slices with origins along the same line. The volumes' slices interleave in their border region. This test is meant to correctly sort apart the two blocks instead of generating many two-slices groups in the interleaved region.
- CR-MONO1-10-chest-spacing-none : CR image without spacing information - (1.0, 1.0) should be assumed
- CR-MONO1-10-chest-spacing-pixelspacing : CR image with only "Pixel Spacing" tag - this should be used as spacing
- CR-MONO1-10-chest-spacing-imagerpixelspacing : CR image with only "Imager Pixel Spacing" tag - this should be used as spacing
- CR-MONO1-10-chest-spacing-calibrated : CR image with BOTH "Imager Pixel Spacing" and "Pixel Spacing" defined - "Pixel Spacing" should be used
- OT-MONO2-8-colon : Secondary Capture image with gray value pixels - should be loaded
- OT-PAL-8-face : Secondary Capture image with PALETTE pixel values - can be loaded, but display is not correct yet
From test set \b TiltHead (see description.txt in this directory for details on test images):
- head_ct_tilt : load a series with gantry tilt and check its orientation
\section DICOMTesting_othertests Specific other tests
This list is meant to provide an up-to-date list of all implemented DICOM loading tests.
<b>If you ever find this outdated, please update it or make the persons who invalidated the list update it.</b>
<b>mitkDICOMTestingSanityTest_*</b>
These tests implement basic testing of the implemented helper classes. The tests use DICOMITKSeriesGDCMReader to load
a number of DICOM image. They verify:
- DICOMITKSeriesGDCMReader recognizes all input files as DICOM images
- DICOMITKSeriesGDCMReader generates a number of mitk::Images from the DICOM images
- the number of mitk::Images matches a number given on the command line or CTest's add_test()
- helper methods in class TestDICOMLoading make minimal sense (comparison of an image dump to itself must be valid)
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/DataInteraction.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/DataInteraction.dox
index 374ae959a6..f69eec4d22 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/DataInteraction.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/DataInteraction.dox
@@ -1,230 +1,230 @@
/**
\page DataInteractionPage Interaction Concepts
\tableofcontents
\section DataInteractionPage_Introduction Introduction to Interaction in MITK
Interaction is a very important task in medical image processing software. Therefore MITK provides a special interaction concept
that provides the developer with an easy way to develop and maintain user interaction separately from the algorithms processing the input.
This allows e.g. for common interaction schemes to be re-used in different contexts.
The core of the interaction concept is based on entities called \b DataInteractors that listen for certain pre-defined events and execute
actions when such an event is triggered.\n
In the following the different components of the interaction concept are explained.
First a a high-level overview about how the different components interact is given, then some parts are explained in more detail.
\subsection FurtherReadingInteraction Topics related to interaction - further information:
See the \ref DataInteractionTechnicalPage page for a more technical explanation. \n
Consult \ref HowToUseDataInteractor for usage information.\n
See \ref SectionImplementationDataInteractor for an example on how to implement a new mitk::DataInteractor \n
for information about how to create new events refer to ImplementNewEventsPage.\n
-The documentation of the depricated former concept can be found at \ref InteractionPage.
+The documentation of the deprecated former concept can be found at \ref InteractionPage.
\n
For a list of changes with respect to the previous interaction concept please refer to the \ref InteractionMigration
\section DataInteractionPage_HandlingSection Event Handling & GUI Toolkit Abstraction
The following sequence diagram gives an exemplary overview of the process from creating an event until executing an action in the mitk::DataInteractor.
This diagram assumes the usage of the Qt framework, but also shows that the interaction concept itself is implemented independent of any specific
graphical user interface toolkit.
\imageMacro{event_handling.png,"",16}
<ol>
<li>a user event is triggered and send to MITK
<li>this layer serves as an adapter from the GUI toolkit (here Qt) events to MITK internal events (later referred to as \link mitk::InteractionEvent InteractionEvents\endlink).
<li>once the event is adapted it is send to a mitk::Dispatcher, which is linked to a render window, to be handled.
<li>on the mitk::Dispatcher level all objects are known that can react to incoming events (mitk::DataInteractor and mitk::InteractionEventObserver instances)
<li>a mitk::DataInteractor is offered an event and checks its mitk::EventConfig object, which returns if a variant of this event has been defined for this DataInteractor.
<li>if the DataInteractor has a variant for the event, it consults its state machine to check if the input can be handled in the current state
<li>the actions associated with a state change (transition) are executed and the event is successfully handled.
</ol>
\section DataInteractionPage_EventPage Events
Events can describe any sort of user input, such as key strokes, mouse clicks or touch gestures.
These events are mapped from an UI framework like Qt to an MITK internal representation
and send to the mitk::Dispatcher which in turn deals with further processing of the event.
These events are not limited to classical input devices but can be extended at will, by introducing new classes which e.g. describe
events from tracking devices, etc. Refer to \subpage ImplementNewEventsPage to see how new events and thereby input devices can be integrated.
For an overview of available Events see mitk::InteractionEvent, for on overview of parameters see the \subpage DataInteractionTechnicalPage.
\section DataInteractionPage_InteractionEventHandlerSection InteractionEventHandler
Is the term describing objects in general that can handle events. These objects can be divided into two groups, namely
\link mitk::DataInteractor DataInteractors\endlink and mitk::InteractionEventObserver. Their difference is that mitk::DataInteractor instances are
linked with a mitk::DataNode which they manipulate, whereas mitk::InteractionEventObserver instances do not have a mitk::DataNode and therefore
are not supposed to manipulate any data.
\dot
digraph linker_deps {
node [shape=record, fontname=Helvetica, fontsize=10];
a [ label="InteractionEventHandler" ];
d [ label="{EventStateMachine|HandleEvent()}" ];
b [ label="{DataInteractor|PerformAction()}" ];
a -> d;
d -> b;
}
\enddot
\subsection DataInteractionPage_DataInteractorsSection DataInteractors
-DataInteractors are specialized mitk::InteractionEventHandler which handle events for one spefific DataNode. They are implemented following a concept called state machines
+DataInteractors are specialized mitk::InteractionEventHandler which handle events for one specific DataNode. They are implemented following a concept called state machines
(see e.g. <a href="https://en.wikipedia.org/wiki/Mealy_machine"> Wikipedia </a>).
\subsubsection DataInteractionPage_StateMachinesSection StateMachines
A specific events action is usually desired to depend on the content of the data object and the state of the interaction.
For example when adding a line by clicking with the mouse, the first two clicks are supposed to add a point.
But the second click should additionally finish the interaction and a subsequent third click should be ignored.
State machines provide a great way to model such interaction in which the same user interaction can trigger different actions
depending on the current state. Therefore DataInteractors work with so called state machine patterns.
The basic idea here is that each interaction can be described by states
and transitions which in turn trigger actions.
These patterns define a workflow and different patterns can be applied to the same mitk::DataInteractor and cause this mitk::DataInteractor
to perform different user interactions.
This principle is best described by an example.
Imagine a mitk::DataInteractor with the functionality (1) to add Points at a given mouse position and connect them by a line and (2) check if two
points are on the same position. Using this mitk::DataInteractor, different mitk::StateMachine patterns/descriptions
can be given which each cause the mitk::DataInteractor to perform different interaction schemes.
<b>State machine pattern 1:
We want the user to draw a line. A simple state machine could express this by three states like this:</b>
\dot
digraph linker_deps {
node [shape=circle, fontname=Helvetica, fontsize=10];
a [ label="NoPoints" ];
b [ label="OnePoint" ];
c [ label="TwoPoints" ];
a -> b [label="MousePress/AddPoint",fontname=Helvetica, fontsize=10];
b -> c [label="MousePress/AddPoint",fontname=Helvetica, fontsize=10];
{ rank=same; a b c }
}
\enddot
With each MousePress event the AddPoint function is called and adds a point at the mouse position, unless two points already exist.
<b>State machine pattern 2:
The same mitk::DataInteractor can also operate after the following state machine, which models the interaction to input a closed contour.
The mitk::DataInteractor can detect an AddPoint event on an already existing point and will trigger a PointsMatch event.</b>
\dot
digraph {
node [shape=circle, fontname=Helvetica, fontsize=10];
a [ label="StartState" ];
b [ label="ClosedContour"];
a -> a [label="MousePress/AddPoint",fontname=Helvetica, fontsize=10];
a -> b [label="PointsMatch/AddPoint",fontname=Helvetica, fontsize=10];
}
\enddot
In this way state machines provide both, a nice and structured way to represent interaction tasks and description of the interaction which is separated from the code.
One DataInteractor can be re-used for different tasks by simply exchanging the state machine pattern. These patterns are described in XML files.
\subsubsection DataInteractionPage_DefinitionStateMachine Definition of a State Machine
The definition is made up out of four components.
<ul>
<li> States - represent the current status of the interaction
<li> Transitions - describe the events needed to change from one state to another
<li> Conditions - are executed, before a transition is taken
<li> Actions - are executed, when a transition is taken and conditions for that transition have passed
</ul>
Each state machine needs exactly one designated start state into which the state machine is set in the beginning.
An example of a state machine describing the interaction of example 2 looks like this:
\code
<statemachine>
<state name="StartState" startstate="true" >
<transition event_class="MousePressEvent" event_variant="MousePress" target="StartState">
<condition name="CheckPoint"/>
<action name="AddPoint"/>
</transition>
<transition event_class="InternalEvent" event_variant="PointsMatch" target="ClosedContour">
<action name="AddPoint"/>
</transition>
</state>
<state name="ClosedContour"/>
</statemachine>
\endcode
<b>Example 1: State machine pattern, that describes adding points to a contour until the PointsMatch event is triggered.</b>
For a more detailed description of state machine patterns see here.
\subsection DataInteractionPage_InteractionEventObserverSection InteractionEventObserver
mitk::InteractionEventObserver instances are objects which will receive all user input and are intended for observation only,
they should never modify any DataNodes.
For mitk::InteractionEventObserver it is optional to use the state machine functionality, the default is without. How to use the state machine functionality
is described in the documentation of mitk::InteractionEventObserver::Notify.
\dot
digraph event_observer {
node [shape=record, fontname=Helvetica, fontsize=10];
c [ label="{InteractionEventObserver|Notify()}" ];
a [ label="InteractionEventHandler" ];
b [ label="{EventStateMachine|HandleEvent()}" ];
d [ label="{MyCustomObserver|PerformAction()}" ];
c -> d;
a -> b;
b -> d [style="dashed",label="optional"];
}
\enddot
\subsection DataInteractionPage_ConfigurationSection Configuration
In a lot of cases it is preferable to implement interactions independent of a specific event (e.g. left click with mouse), such that it is possible
to easily change this. This is achieved through configuration of \link mitk::InteractionEventHandler InteractionEventHandlers\endlink.
This allows to change the behavior at runtime.
The mitk::InteractionEventHandler class provides an interface to easily modify the user input that triggers an action by loading a different
configuration. This allows to implement
user-specific behavior of the software on an abstract level and to switch it at runtime.
This is achieved through XML files describing a configuration. These files can be loaded by the mitk::InteractionEventHandler and will lead to an internal mapping
from specific user input to an abstract description of the event given in the config file.
In order to do this we distinguish between a specific event and an event variant. A specific event is described by its event class, which determines the
category of an event, e.g. the class mitk::MousePressEvent, and its parameter which make this event unique, e.g. LeftMouseButton pressed and no modifier keys pressed.
The event variant is a name that is assigned to a specific event, and to which an mitk::InteractionEventHandler listens.
To illustrate this, an example is given here for two different configuration files. We assume that a mitk::InteractionEventHandler listens to the
event variant 'AddPoint', two possible config files could then look like this:
\code
<config name="one">
<event_variant name="MousePress" class="MousePressEvent">
<attribute name="EventButton" value="LeftMouseButton"/>
</event_variant>
</config>
\endcode
<b>Example 2: Event description of a left click with the mouse</b>
and
\code
<config name="two">
<event_variant name="MousePress" class="MousePressEvent">
<attribute name="EventButton" value="RightMouseButton"/>
<attribute name="Modifiers" value="shift"/>
</event_variant>
</config>
\endcode
<b>Example 3: Event description of a left click with the mouse while pressing the shift-key</b>
If the mitk::InteractionEventHandler is loaded with the first configuration the event variant 'MousePress' is triggered when the user performs a mouse click,
while when the second configuration is loaded 'MousePress' is triggered when the user performs a right click while pressing the shift button.
In this way all objects derived from mitk::InteractionEventHandler can be configured. For a detailed description about how to create the XML file see the
\ref DataInteractionTechnicalPage page.
\section DataInteractionPage_DispatcherSection Dispatcher
Instances of mitk::Dispatcher receive all events and distribute them to their related mitk::DataInteractor instances. This is done by ordering
the DataInteractors according to the layer of their
mitk::DataNode in descending order. Then the event is offered to the first mitk::DataInteractor, which in turn checks if it can handle the event. This is done
for each mitk::DataInteractor until the first processes the event, after this the other DataInteractors are skipped and all InteractionEventObservers are notified.
\section DataInteractionPage_Examples Examples
Examples can be found at \subpage InteractionHowTo
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Exceptions.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Exceptions.dox
index 53e2c839eb..f796e2bafc 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Exceptions.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Exceptions.dox
@@ -1,90 +1,90 @@
/**
\page ExceptionPage Error Handling and Exception Concept
\tableofcontents
\section ExceptionHandling General Exception Handling
In MITK, errors during program execution are handled by the well known exception handling concept which is part of the C++ language. In case of unexpected exceptional behaviour or errors during program execution MITK classes throw exceptions. MITK exceptions are always objects of the class mitk::Exception or of one of its subclasses.
\subsection Throw Throwing of exceptions
Exceptions should always be thrown by using the predefined exception macros. If you want to throw a mitk::Exception in your code, simply use the following macro.
\verbatim
//This command will throw a mitk::Exception and add a message.
//The macro will also add filename and line number to the exception
//object.
mitkThrow() << "Here comes your exception message";
\endverbatim
You can also stream more complex messages, e.g. adding integers or other variables to your exception messages, like shown in the following example.
\verbatim
mitkThrow() << "This time we show the values of some variables:" << m_MyObject->GetSize() << m_MyInteger;
\endverbatim
\subsection Doc Documentation of exceptions
If you throw exceptions in your code, please also document this in your doxygen comments by using the "@throws " tag. This will help users of your class to catch and handle the exceptions in a proper way. An example how to document exception throwing is given below.
\verbatim
class myExampleClass
{
/** Documentation
* @brief This method does [...]
- * @throws mitk::Exception This exception is thrown, when the following error occures: [...]
+ * @throws mitk::Exception This exception is thrown, when the following error occurs: [...]
*/
void MyExampleMethod()
{
//here comes your code
//here happens an exception
- mitkThrow() << "An exception occured because [...], method can't continue.";
+ mitkThrow() << "An exception occurred because [...], method can't continue.";
}
}
\endverbatim
In general, exceptions emit no logging messages by default because they are intended to be catched by overlying classes. This classes should then decide what to do, e.g. to log an error message or handle the exception in another way. See the logging documentation for more details on error logging.
\subsection Catch Catching exceptions
Exceptions should be caught by overlying classes, if they can handle them in a proper way. Catching exceptions is very simple, use the standard try-catch block to do so. An example is given below.
\verbatim
try
{
//call of a method which may throw an exception
myObject->MyExampleMethod();
}
catch (const mitk::Exception& e)
{
//This code is executed if an exception of the given type was thrown above.
//For example log an error message here or do some other proper handling of the exception.
}
\endverbatim
-Please do not use "catch (...)" because normally your class can't garantee to handle all exceptions in a proper way without differentiating them.
+Please do not use "catch (...)" because normally your class can't guarantee to handle all exceptions in a proper way without differentiating them.
\section SpecializedExceptionHandling Defining and Using more Specialized Exceptions
-The basic MITK exception concept was kept very simple and should suffice in many cases. But you can also use more specialized exceptions, if needed. Nevertheless all MITK exceptions should be subclasses of mitk::exception. You can define your own exception classes by simply implementing new classes which derive from mitk::exception. Thus, you can catch your exception seperately when needed. The mitkExceptionClassMacro helps to keep implementing new exception classes as simple as possible, like shown in the following code example.
+The basic MITK exception concept was kept very simple and should suffice in many cases. But you can also use more specialized exceptions, if needed. Nevertheless all MITK exceptions should be subclasses of mitk::exception. You can define your own exception classes by simply implementing new classes which derive from mitk::exception. Thus, you can catch your exception separately when needed. The mitkExceptionClassMacro helps to keep implementing new exception classes as simple as possible, like shown in the following code example.
\verbatim
#include <mitkCommon.h>
class mitk::MySpecializedException : public mitk::Exception
{
public:
mitkExceptionClassMacro(mitk::MySpecializedException,mitk::Exception);
};
\endverbatim
-To throw your specialized exception you should use the corresponing macro, which is shown in the next code snippet.
+To throw your specialized exception you should use the corresponding macro, which is shown in the next code snippet.
\verbatim
mitkThrowException(mitk::MySpecializedException) << "this is error info";
\endverbatim
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/GeometryOverview.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/GeometryOverview.dox
index f7bbcb984b..efe242ee3c 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/GeometryOverview.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/GeometryOverview.dox
@@ -1,160 +1,160 @@
/**
\page GeometryOverviewPage Geometry Overview
\tableofcontents
\section GeometryOverviewPage_Introduction Introduction to Geometries
Geometries are used to describe the geometrical properties of data objects in space and time.
To use the geometry classes in the right way you have to understand the three different coordinate types present in MITK:
\imageMacro{CoordinateTypes.png,"",16}
\section GeometryOverviewPage_CoordTypes The different coordinate types
\subsection GeometryOverviewPage_WorldCoord
-- World coordinates are describing the actual spacial position of all MITK objects regarding a global coordinate system, normally specified by the imaging modality
+- World coordinates are describing the actual spatial position of all MITK objects regarding a global coordinate system, normally specified by the imaging modality
- World coordinates are represented by mitk::Point3D objects.
- The geometry defines the offset, orientation, and scale of the considered data objects in reference to the world coordinate systems.
- World coordinates are always measured in mm
- If you are dealing with an image geometry, the origin of an image is pointing to the CENTER of the bottom-left-back voxel.
- If you are NOT dealing with an image geometry (no defined discrete Voxels), the origin is pointing to the bottom-left-back CORNER
- Index coordinates can be converted to world coordinates by calling mitk::BaseGeometry::IndexToWorld()
\imageMacro{worldcoordinateSystem.png,"",12}
\subsection GeometryOverviewPage_CornerCoord Corner-based coordinates
\imageMacro{WorldcoordinateSystemCenterBased.png,"",12}
\subsection GeometryOverviewPage_CenterCoord Center-based image-coordinates
\subsubsection GeometryOverviewPage_CenterCoord_ContIdx Continuous index coordinates
-- Dividing world coordinates through the pixel spacing and simultanously taking the offset into account leads to continuous index coordinates inside your dataobject. So continuous coordinates can be float values!
+- Dividing world coordinates through the pixel spacing and simultaneously taking the offset into account leads to continuous index coordinates inside your dataobject. So continuous coordinates can be float values!
- Continuous index coordinates are represented by mitk::Point3D objects.
- They can be obtained by calling mitk::BaseGeometry::WorldToIndex(), where &pt_mm is a point in worldcoordinates.\n
\subsubsection GeometryOverviewPage_CenterCoord_Idx Index coordinate system
- Index coordinates are discrete values that address voxels of a data object explicitly.
- Index coordinates are represented by itk::Index<3> objects.
- Basically they are continuous index coordinates which are rounded from half integer up.
- E.g. (0,0) specifies the very first pixel of a 2D image, (0,1) the pixel of the next column in the same row
- If you have world coordinates, they can be converted to discrete index coordinates by calling mitk::BaseGeometry::WorldToIndex()\n\n
\section GeometryOverviewPage_PointsAndVector Difference between Points and Vectors
-Like ITK, MITK differenciate between points and vectors.
+Like ITK, MITK differentiate between points and vectors.
A point defines a position in a coordinate system while a vector is the distance between two points.
Therefore points and vectors behave different if a coordinate transformation is applied.
-An offest in a coordinate transformation will affect a transformed point but not a vector.
+An offset in a coordinate transformation will affect a transformed point but not a vector.
An Example:
-If two systems are given, which differ by a offset of (1,0,0). The point A(2,2,2) in system one will correspont to point A'(3,2,2) in the second system.
+If two systems are given, which differ by a offset of (1,0,0). The point A(2,2,2) in system one will correspond to point A'(3,2,2) in the second system.
But a vector a(2,2,2) will correspond to the vector a'(2,2,2).
\section GeometryOverviewPage_Concept The Geometry Concept
As the superclass of all MITK geometries mitk::BaseGeometry holds:
-- a spacial bounding box which is axes-parallel in index coordinates (often discrete indices of pixels), to be accessed by BaseGeometry::GetBoundingBox()
+- a spatial bounding box which is axes-parallel in index coordinates (often discrete indices of pixels), to be accessed by BaseGeometry::GetBoundingBox()
- a time related bounding box which holds the temporal validity of the considered data object in milliseconds (start and end time), to be accessed by BaseGeometry::GetTimeBounds(). The default for 3D geometries is minus infinity to plus infinity, meaning the object is always displayed independent of displayed time in MITK.
- position information in form of a Euclidean transform in respect to world coordinates (i.e. a linear transformation matrix and offset) to convert (discrete or continuous) index coordinates to world coordinates and vice versa, to be accessed by mitk::BaseGeometry::GetIndexToWorldTransform(). See also: \ref GeometryOverviewPage_Introduction "Introduction to Geometries"
- Many other properties (e.g. origin, extent, ...) which can be found in the mitk::BaseGeometry "class documentation"
- <b>VERY IMPORTANT:</b> A flag called \c isImageGeometry, which indicates whether the coordinates are center-based or not! See also: \ref GeometryOverviewPage_Introduction "Introduction to Geometries" and \ref GeometryOverviewPage_Putting_Together . <b>IMPORTANT:</b> Putting it together for an Image.
Every data object (sub-)class of BaseData has a mitk::TimeGeometry which is accessed by mitk::BaseData::GetTimeGeometry().
This mitk::TimeGeometry holds one or more mitk::BaseGeometry objects which describes the object at specific time points, e.g. provides conversion between world and index coordinates and contains bounding boxes covering the area in which the data are placed.
There is the possibility of using different implementations of the abstract mitk::TimeGeometry class which may differ in how the time steps are saved and the times are calculated.
There are two ways to represent a time, either by a mitk::TimePointType or a mitk::TimeStepType.
-The first is similar to the continous index coordinates and defines a Time Point in milliseconds from timepoint zero.
+The first is similar to the continuous index coordinates and defines a Time Point in milliseconds from timepoint zero.
The second type is similar to index coordinates.
These are discrete values which specify the number of the current time step going from 0 to GetNumberOfTimeSteps().
The conversion between a time point and a time step is done by calling the method mitk::TimeGeometry::TimeStepToTimePoint() or mitk::TimeGeometry::TimePointToTimeStep().
Note that the duration of a time step may differ from object to object, so in general it is better to calculate the corresponding time steps by using time points.
Also the distance of the time steps does not need to be equidistant over time, it depends on the used mitk::TimeGeometry implementation.
Each mitk::TimeGeometry has a bounding box covering the whole area in which the corresponding object is situated during all time steps.
This bounding box may be accessed by calling mitk::TimeGeometry::GetBoundingBoxInWorld() and is always in world coordinates.
The bounding box is calculated from all time steps, to manually start this calculation process call mitk::TimeGeometry::Update().
The bounding box is not updated if the getter is called.
The mitk::TimeGeometry does not provide a transformation of world coordinates into image coordinates since each time step may has a different transformation.
If a conversion between image and world is needed, the mitk::BaseGeometry for a specific time step or time point must be fetched either by mitk::TimeGeometry::GetGeometryForTimeStep() or mitk::TimeGeometry::GetGeometryForTimePoint() and then the conversion is calculated by using this geometry.
The mitk::TimeGeometry class is an abstract class therefore it is not possible to instantiate it.
Instead a derived class must be used.
Currently the only class that can be chosen is mitk::ProportionalTimeGeometry which assumes that the time steps are ordered equidistant.
To initialize an object with given geometries call mitk::ProportionalTimeGeometry::Initialize() with an existing BaseGeometry and the number of time steps.
The given geometries will be copied and not referenced!
Also, the mitk::BaseGeometry is an abstract class and derived classes must be used.
The most simple implementation, i.e. the one to one implementation of the BaseGeometry class, is the class mitk::Geometry3D.
mitk::SlicedGeometry3D is a sub-class of mitk::BaseGeometry, which describes data objects consisting of slices, e.g., objects of type Image (or mitk::SlicedData, which is the super-class of mitk::Image).
Therefore, mitk::Image::GetTimeGeometry() will contain a list of mitk::SlicedGeometry3D instances. There is a special method mitk::SlicedData::GetSlicedGeometry(t) which directly returns a mitk::SlicedGeometry3D to avoid the need of casting.
The class mitk::SlicedGeometry3D contains a list of mitk::PlaneGeometry objects describing the slices in the image.
We have here spatial steps from 0 to GetSlices().
mitk::SlicedGeometry3D::InitializeEvenlySpaced(PlaneGeometry *planeGeometry, unsigned int slices) initializes a stack of slices with the same thickness, one starting at the position where the previous one ends.
mitk::PlaneGeometry provides methods for working with 2D manifolds (i.e., simply spoken, an object that can be described using a 2D coordinate-system) in 3D space.
For example it allows mapping of a 3D point on the 2D manifold using mitk::PlaneGeometry::Map().
Finally there is the mitk::AbstractTransformGeometry which describes a 2D manifold in 3D space, defined by a vtkAbstractTransform. It is a abstract superclass for arbitrary user defined geometries.
An example is the mitk::ThinPlateSplineCurvedGeometry.
\subsection GeometryOverviewPage_Putting_Together Putting it together for an Image
Please read this section carefully if you are working with Images!
The definition of the position of the corners of an image is different than the one of other data objects:
As mentioned in the previous section, world coordinates of data objects (e.g. surfaces) usually specify the bottom left back corner of an object.
In contrast to that, a geometry of an mitk::Image is center-based, which means that the world coordinates of a voxel belonging to an image points to the center of that voxel.
E.g:
\imageMacro{PixelCenterBased.png,"",6}
\subsection GeometryOverviewPage_CenterBasedVoxel Center-based voxel
If the origin of e.g. a surface lies at (15,10,0) in world coordinates, the origin`s world coordinates for an image are internally calculated like the following:
\code{.unparsed}
(15-0.5*X-Spacing
10-0.5*Y-Spacing
0-0.5*Z-Spacing)
\endcode
If the image`s spacing is (x,y,z)=(1,1,3) then the corner coordinates are (14.5,9.5,-1.5).
If your geometry describes an image, the member variable \c isImageGeometry must be changed to true.
This variable indicates also if your geometry is center-based or not.
The change can be done in two ways:
- You are sure that your origin is already center-based. Whether because you adjusted it manually or you copied it from another image. In that case, you can call the function \c setImageGeometry(true) or \c imageGeometryOn() to set the bool variable to true.
- You created a new geometry, did not manually adjust the origin to be center-based and have the bool value \c isImageGeometry set to false (default). In that case, call the function \c ChangeImageGeometryConsideringOriginOffset(true) . It will adjust your origin automatically and set the bool flag to true.
If you experience displaced contours, figures or other stuff, it is an indicator that you have not considered the origin offset mentioned above.
An image has a mitk::TimeGeometry, which contains one or more mitk::SlicedGeometry3D instances (one for each time step), all of which contain one or more instances of (sub-classes of) mitk::PlaneGeometry.
As a reminder: Geometry instances referring to images need a slightly different definition of corners, see mitk::BaseGeometry::SetImageGeometry.
-This is usualy automatically called by Image.
+This is usually automatically called by Image.
\section GeometryOverviewPage_Connection Connection between MITK, ITK and VTK Geometries
\imageMacro{ITK_VTK_MITK_Geometries.png,"",10}
- VTK transformation for rendering
- ITK transformation for calculations
- Both automatically updated when one is changed
Attention: <b>Not</b> automatically updated when changed hardcoded. Example: \c geometry->GetVtkMatrix()->Rotate(...)
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Interaction.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Interaction.dox
index 4c4b6290ad..bfef846c53 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Interaction.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Interaction.dox
@@ -1,128 +1,128 @@
/**
\deprecated
\page InteractionPage Interaction and Undo/Redo Concepts
\note The following page refers to the deprecated interaction frame work. Please refer to \ref DataInteractionPage for information about the current one.
\tableofcontents
\section InteractionPage_Introduction Interaction in MITK
\b Interaction is one of the most important tasks in clinically useful image processing software.
Due to that, MITK has a special interaction concept, with which the developer can map the desired interaction.
For a simple change in interaction he doesn't have to change the code. All information about the sequence
of the interaction is stored in an XML-file that is loaded by the application during startup procedure at runtime.
That even allows the storage of different interaction patterns, e.g. an interaction behaviour like in MS PowerPoint,
in Adobe Photoshop or like the interaction behaviour on a medical image retrieval system.
\section InteractionPage_Statemachines_Implementation Statemachines to implement Interaction
The interaction in MITK is implemented with the concept of state machines (by Mealy).
This concept allows to build the steps of interaction with different states, which each have different conditions, very alike the different interactions that may have to be build to develop medical imaging applications.
Furthermore state machines can be implemented using object oriented programming (OOP). Due to that we can abstract from the section of code, that implements the interaction and focus on the sequence of interaction. What steps must the user do first before the program can compute a result? For example he has to declare three points in space first and these points are the input of a filter so only after the definition of the points, the filter can produce a result. The according interaction sequence will inform the filter after the third point is set and not before that. Now the filter after an adaption only needs two points as an input. The sequence of the interaction can be easily changed if it is build up as a sequence of objects and not hard implemented in a e.g. switch/case block. Or the user wants to add a point in the scene with the right mouse button instead of the left. Wouldn't it be nice to only change the definition of an interaction sequence rather than having to search through the code and changing every single if/else condition?
\subsection InteractionPage_Statemachine State Machine
So a separation of the definition of a sequence in interaction and its implementation is a useful step in the development of an
interactive application.
To be able to do that, we implemented the concept of state machines with several classes: States, Transitions and Actions define the interaction pattern. The state machine itself adds the handling of events, that are sent to it.
\imageMacro{statemachine.jpg,"",10}
\subsubsection InteractionPage_ExampleA Example A:
A deterministic Mealy state machine has always one current state (here state 1). If an event 1 is sent to the state machine, it searches in its current state for a transition that waits for event 1 (here transition 1). The state machine finds transition 1, changes the current state to state2, as the transition points to it and executes actions 1 and 2. Now state 2 is the current state. The state machine receives an event 2 and searches for an according transition. Transition 2 waits for event 2, and since the transition leads to state 2 the current state is not changed. Action 3 and 4 are executed. Now Event 3 gets send to the state machine but the state machine can't find an according transition in state 2. Only transition 2 , that waits for event 2 and transition 4, that waits for event 4 are defined in that state. So the state machine ignores the event and doesn't change the state or execute an action. Now the state machine receives an event 4 and finds transition 3. So now the current state changes from state 2 to state 1 and actions 5 and 1 are executed.
Several actions can be defined in one transition. The execution of an action is the active part of the state machine. Here is where the state machine can make changes in data, e.g. add a Point into a list.
See mitk::StateMachine, mitk::State, mitk::Event, mitk::Action, mitk::Transition, mitk::Interactor
\subsection InteractionPage_GuardState Guard States
Guard States are a special kind of states. The action, that is executed after the state is set as current state, sends a new event to the state machine, which leads out of the guard state. So the state machine will only stay in a guard state for a short time. This kind of state is used to check different conditions, e.g. if an Object is picked or whether a set of points will be full after the addition of one point.
\imageMacro{statemachine_guard.jpg,"",10}
\subsubsection InteractionPage_ExampleB Example B:
Event 1 is sent to the state machine. This leads the current state from state 1 into state check. The action 1 is executed. This action checks a condition and puts the result into a new event, that is sent and handled by the same (this) state machine. E.g. is the object picked with the received mouse-coordinate? The event, that is generated, will be Yes or No. In case of event No, the state machine sets the current state back to state 1 and executes action 2. In case of event Yes, the state machine changes the state from state check into state 2 and executes action 3, which e.g. can select said object.
\subsection InteractionPage_XMLDefinitionStatemachine Definition of a State machine
Due to the separation of the definition of an interaction sequence and its implementation, the definition has to be archived somewhere, where the application can reach it during startup and build up all the objects (states, transitions and actions) that represent the sequence of a special interaction.
-In MITK, these informations are defined in an XML-file (i.e. DisplayInteraction.xml or BoundingShapeInteraction.xml)
+In MITK, this information is defined in an XML-file (i.e. DisplayInteraction.xml or BoundingShapeInteraction.xml)
\note Please note that since this is a resource which is compiled into the executable, changes you make to this file will only be reflected in application behavior after you recompile your code.
The structure is the following (from \ref InteractionPage_ExampleA) :
\code
<stateMachine NAME="statemachine1"><!-- defining a new state machine with its name -->
<state NAME="State1" ID="1" START_STATE="TRUE"><!-- new state tag; start state of the state machine; ID=1 -->
<transition NAME="transition1" NEXT_STATE_ID="2" EVENT_ID="1"><!-- transition, waits for event ID=1 and leads to state 2 -->
<action ID="1" /><!-- action ID = 1 shall be executed -->
<action ID="2" /><!-- action ID = 2 shall be executed -->
</transition><!-- end of transition -->
</state><!-- end of state ID = 1 -->
<state NAME="state2" ID="2">
<transition NAME="transition2" NEXT_STATE_ID="2" EVENT_ID="2">
<action ID="3" />
<action ID="4" />
</transition>
<transition NAME="transition3" NEXT_STATE_ID="1" EVENT_ID="4" >
<action ID="5" />
<action ID="1" />
</transition>
</state>
</stateMachine>
\endcode
The identification numbers (ID) inside a state machine have to be unique. Each state machine has to have one state, that is defined as the start-state of that state machine. This means, initially, the current state of the state machine is the start-state.
-The Event-Ids seen above are also defined in the statemachine.xml file. They specify a unique number for a combination of input-conditions (key, mouse and so on). See \ref InteractionPage_InteractionEvents for further informations.
+The Event-Ids seen above are also defined in the statemachine.xml file. They specify a unique number for a combination of input-conditions (key, mouse and so on). See \ref InteractionPage_InteractionEvents for further information.
The statemachine is compiled into an application at compile time.
The definition of one single state machine is called the \a statemachine-pattern. Since this pattern is build up during startup with objects (states, transitions and actions) and these objects only hold information about what interaction may be done at the current state, we can also reuse the pattern.
\note You as a developer don't necessarily have to implement your own XML-File! We already have defined some interaction-patterns (e.g. for setting Points in 2D or 3D) which you can use and adapt.
\subsubsection InteractionPage_ReusePattern Reuse of Interaction Patterns
If we for example have a pattern called "pointset", which defines how the user can set different points into the scene and there is an instance of a state machine called "PointSetInteractor". This state machine has a pointer pointing to the current state in its assigned state machine pattern. Several events are send to the state machine, which moves the pointer from one state to the next, according to the transitions, and executes the actions, referenced in the transitions.
But now a new instance of the class "PointSetInteractor" has to be build. So we reuse the pattern and let the current state pointer of the new object point to the start state of the pattern "pointset". The implementation of the actions is \b not done inside a class of the pattern (\a state, \a transition, \a action), it is done inside a state machine class (see the reference for mitkStatemachine).
\subsection InteractionPage_InteractionEvents Events
During runtime, events are thrown from e.g. the mouse to the operating system, are then send to your graphical user interface and from there it has to be send to the MITK-object called \a mitkEventMapper. This class maps the events received with an internal list of all events that can be understood in MITK. The definition of all understandable events is also located in the XML-File the state machines are defined in. If the received event can be found in the list, an internal mitk-eventnumber is added to the event and send to the object \a mitkGlobalInteraction.
See mitk::Event, mitk::GlobalInteraction
\subsection InteractionPage_GlobalInteraction GlobalInteraction
This object administers the transmission of events to registered state machines. There can be two kinds of state machines, the ones that are only listening and ones that also change data. Listening state machines are here called Listeners and state machines that also change data are called Interactors.
\note The discrimination between \a Listener and \a Interactor is only made in mitkGlobalInteraction.
As Listener an object derived from class StateMachine can be added and removed from GlobalInteraction and as Interactor an object derived from class Interactor can be added and removed. See the interaction class diagram for further information.
To add or remove a state machine to the list of registered interactors, call \a AddInteractor or \a RemoveInteractor of \a GlobalInteraction or to add or remove a listener call \a AddListener of \a RemoveListener. Listeners are always provided with the events. Interactors shall only be provided with an event, if they can handle the event. Because of that the method CanHandleEvent is called, which is implemented in each Interactor. This method analyses the event and returns a value between 0 (can't handle event) and 1 (Best choice to handle the event). Information, that can help to calculate this jurisdiction can be the bounding box of the interacted data and the picked mouse-position stored in the event.
So after the object \a GlobalInteraction has received an event, it sends this event to all registered Listeners and then asks all registered Interactors through the method \a CanHandleEvent how good each Interactor can handle this event. The Interactor which can handle the event the best receives the event. Also see the documented code in \a mitkGlobalInteraction.
To not ask all registered interactors on a new event, the class \a Interactor also has a mode, which can be one of the following: deselected, subselected (deprecated since HierarchicalInteraction has been removed), selected. These modes are also used for the event mechanism.
If an interactor is in a state, where the user builds up a graphical object, it is likely that the following events are also for the build of the object. Here the interactor is in mode selected as long as the interactor couldn't handle an event. Then it changes to mode deselected. The mode changes are done in the actions through operations (described further down) and so declared inside the interaction pattern.
See mitk::GlobalInteraction
\subsection InteractionPage_Interactors Interactors
The class \a Interactor is the superclass for all state machines, that solve the interaction for a single data-object.
An example is the class \a mitkPointSetInteractor which handles the interaction of the data \a mitkPointSet. Inside the class \a mitkPointSetInteractor all actions, defined in the interaction-pattern "pointsetinteractor", are implemented. Inside the implementation of these actions (\a ExecuteAction(...) ), so called \a mitkOperations are created, filled with information and send to the \a mitkUndoController and to \a mitkOperactionActor (the data, the interaction is handled for).
See mitk::Interactor
\subsection InteractionPage_ExecOperations Executing Operations
The class mitkOperation and its subclasses basically holds all information needed to execute a certain change of data.
-This change of data is only done inside the data-class itself, which is derived from the interface \a mitkOperationActor. Interactors handle the interaction through state-differentiation and combine all informations about the change in a \a mitkOperation and send this operation-object to the method ExecuteOperation (of data-class). Here the necessary data is extracted and then the change of data is performed.
+This change of data is only done inside the data-class itself, which is derived from the interface \a mitkOperationActor. Interactors handle the interaction through state-differentiation and combine all information about the change in a \a mitkOperation and send this operation-object to the method ExecuteOperation (of data-class). Here the necessary data is extracted and then the change of data is performed.
When the operation-object, here called do-operation, is created inside the method \a ExecuteAction (in class \a mitkInteractor), an undo-operation is also created and together with the do-operation stored in an object called \a OperationEvent. After the Interactor has sent the do-operation to the data, the operation-event-object then is sent to the instance of class \a mitkUndoController, which administrates the undo-mechanism.
See mitk::Operation, mitk::OperationActor
\subsection InteractionPage_UndoController UndoController
The instance of class \a mitkUndoController administrates different Undo-Models. Currently implemented is a limited linear Undo.
Only one Undo-Model can be activated at a time. The UndoController sends the received operation events further to the current Undo-Model, which then stores it according to the model. If the method \a Undo() of UndoController is called (e.g. Undo-Button pressed from ) the call is send to the current Undo-Model. Here the undo-operation from the last operation event in list is taken and send to the data, referenced in a pointer which is also stored in the operation-event. A call of the method \a Redo() is handled accordingly.
See mitk::UndoController, mitk::LimitedLinearUndo
\subsection InteractionPage_references References
[Bin99] Robert V. Binder. Testing Object-Oriented Systems: Models, Patterns, and Tools. Addison-Wesley, 1999
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md
index 2dac2c9742..85199dbef5 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md
@@ -1,154 +1,190 @@
# MITK ROI {#MITKROIPage}
[TOC]
## Disclaimer
Until the MITK ROI file format is going to be officially announced in a 2024 release of MITK, the file format must be considered experimental and is prone to change without any prior warning.
## Overview
MITK ROI is a JSON-based file format defining a collection of region of interests (ROIs).
ROIs must have an ID (unsigned integer) and their shape is currently considered to be an axis-aligned bounding box.
Its bounds are defined by minimum and maximum index coordinates, typically relative to an image.
Custom properties of various known types can be optionally attached to a ROI.
A few of these properties are used by MITK to define the appearance of a rendered ROI, for example:
- "color" (mitk::ColorProperty): Color/RGB triplet of the rendered ROI (default: white \[1.0, 1.0, 1.0\])
- "opacity" (mitk::FloatProperty): Opacity of the rendered ROI (default: 100% \[1.0\])
- "lineWidth" (mitk::FloatProperty): Line width of the egdes of the rendered ROI (default: 1px \[1.0\])
ROIs can be optionally time-resolved and define both coordinates and properties per time step, allowing for a dynamic appearance, position, and size over time.
ROIs also display a caption at their bottom-left corner (supporting multiple lines), that can be set once per MITK ROI file for all contained ROIs.
Placeholders enclosed by braces in the caption are substituted by their corresponding ROI property values at runtime.
The default caption is "{name} ({ID})", where ``{ID}`` is a special placeholder for the ID of a ROI (technically not a ROI property), and ``{name}`` refers to the ROI property "name" (typically an mitk::StringProperty).
Last but not least the reference (image) geometry of the ROIs in an MITK ROI file must be specified to be able to map all index coordinates to actual world coordinates.
A geometry is defined by an origin, the pixel/voxel spacing, a size, and optionally the number of time steps in case of a time-resolved MITK ROI file.
## File format
As all features are explained in the overview above, the JSON-based file format is defined here by two examples with minimal additional notes: one example for a static MITK ROI file and one example for a time-resolved MITK ROI file.
### Static MITK ROI file
This example contains two ROIs for detected tumors in an image with certain confidence.
Names and confidence values will be displayed in separate lines for each ROI.
~~~{.json}
{
"FileFormat": "MITK ROI",
"Version": 1,
"Name": "Static example",
"Caption": "{name}\nConfidence: {confidence}",
"Geometry": {
"Origin": [0, 0, 0],
"Spacing": [1, 1, 3],
"Size": [256, 256, 49]
},
"ROIs": [
{
"ID": 0,
"Min": [4, 4, 1],
"Max": [124, 124, 31],
"Properties": {
"StringProperty": {
"name": "tumor",
"comment": "Detected a tumor with 95% confidence.",
"note": "Properties are grouped by their type to reduce verbosity."
},
"ColorProperty": {
"color": [0, 1, 0]
},
"FloatProperty": {
"confidence": 0.95
}
}
},
{
"ID": 1,
"Min": [132, 4, 1],
"Max": [252, 60, 15],
"Properties": {
"StringProperty": {
"name": "Another tumor",
"comment": "Maybe another tumor (confidence only 25%)."
},
"ColorProperty": {
"color": [1, 0, 0]
},
"FloatProperty": {
"confidence": 0.25
}
}
}
]
}
~~~
Further hints:
- "FileFormat" ("MITK ROI"), "Version" (1), and "Geometry" are mandatory.
- "Name" is optional. If not set, the file name is used by MITK instead.
- ROIs are defined by JSON objects in the "ROIs" JSON array.
- See the derived classes of mitk::BaseProperty for an overview of known property types.
### Time-resolved MITK ROI file
This example only contains a single ROI but it is defined for several time steps.
Fallbacks of time step properties to default properties are demonstrated as well.
~~~{.json}
{
"FileFormat": "MITK ROI",
"Version": 1,
"Name": "Time-resolved example",
"Geometry": {
"Origin": [0, 0, 0],
"Spacing": [1, 1, 3],
"Size": [256, 256, 49],
"TimeSteps": 3
},
"ROIs": [
{
"ID": 0,
"Properties": {
"ColorProperty": {
"color": [1, 0, 0]
},
"StringProperty": {
"name": "Color-changing ROI"
}
},
"TimeSteps": [
{
"t": 0,
"Min": [4, 4, 1],
"Max": [124, 124, 31]
},
{
"t": 2,
"Min": [14, 14, 11],
"Max": [121, 121, 28],
"Properties": {
"ColorProperty": {
"color": [0, 1, 0]
}
}
}
]
}
]
}
~~~
Further hints:
- The geometry defines 3 time steps.
- The "Properties" directly in the ROI function as fallbacks, if they are not defined for a certain time step.
- Time time step indices "t" are mandatory. The ROI is only present at time steps 0 and 2.
- The ROI is red (fallback) at time step 0 and green at time step 2.
- Its extents are larger at time step 0 than at time step 2.
+
+### New features in version 2
+
+In version 2 of the file format, geometries can be optionally defined by a linear transformation instead of origin and spacing.
+Such a transformation is written as 4x4 matrix in the form of a contiguous array of 16 elements.
+For example, the following two geometries are identical:
+
+~~~{.json}
+"Geometry": {
+ "Origin": [10, 20, 30],
+ "Spacing": [1, 2, 3],
+ "Size": [100, 100, 100]
+}
+~~~
+
+~~~{.json}
+"Geometry": {
+ "Transform": [
+ 1, 0, 0, 0,
+ 0, 2, 0, 0,
+ 0, 0, 3, 0,
+ 10, 20, 30, 1
+ ],
+ "Size": [100, 100, 100]
+}
+~~~
+
+The latter format is more powerful as it is able to express rotated geometries.
+
+Further hints:
+
+ - The upper left 3x3 matrix is a rotation matrix.
+ - The column vectors of the rotation matrix represent the three space directions.
+ - The norms of the space directions are equivalent to the spacing values.
+ - The rightmost column vector is always `[0, 0, 0, 1]`.
+ - The bottom row vector corresonds to the origin (with 1 as fourth element).
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/NewEvents.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/NewEvents.dox
index 9a3545d9cf..e29e638d01 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/NewEvents.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/NewEvents.dox
@@ -1,67 +1,67 @@
/**
\page ImplementNewEventsPage What needs to be done to implement a new event?
\tableofcontents
\section WhenUseEvents When to use this guide
-In general there are no restriction about how events are created and processed. The intention of this page is to provide how to contruct Events
+In general there are no restriction about how events are created and processed. The intention of this page is to provide how to construct Events
in order to use them with the MITK Interaction Concept.
\section OriginEvents Origin and processing of Events
-\ref DataInteractionPage gives a brief description of how events are generated and proccessed. After events are created they need to be send
+\ref DataInteractionPage gives a brief description of how events are generated and processed. After events are created they need to be send
via mitk::RenderWindowBase::HandleEvent() so that they are relayed to a Dispatcher which handles the event distribution to the DataInteractors.
\section EventImplementation Event Implementation
The only prerequisite for a new event to be accepted is that it is derived from the mitk::InteractionEvent class.
The new event must implement at least two functions,
as shown in the example of the MousePressEvent.
Implementation of equality for each event class by implementing IsEqual.
Equality does \b not mean an exact copy or pointer equality.
Equality is determined by agreement in all attributes that are necessary to describe
the event for a state machine transition.
Here, for example, for the a mouse event press event, it is important which modifiers are used,
which mouse button was used to triggered the event, but the mouse position is irrelevant.
\code
bool mitk::MousePressEvent::IsEqual(const mitk::InteractionEvent& interactionEvent)
{
// cast to your event class here (this is guaranteed to work
const mitk::MousePressEvent& mpe = static_cast<const mitk::MousePressEvent&>(interactionEvent);
// Checking relevant attributes, such as EventButton and Modifiers, but omitting the mouse coordinates
// Replace this to check for your event class' attributes
return (this->GetEventButton() == mpe.GetEventButton() && this->GetModifiers() == mpe.GetModifiers()
&& this->GetButtonStates() == mpe.GetButtonStates()) && Superclass::IsEqual(interactionEvent);
}
\endcode
The function IsSuperClassOf() implements an up cast to check if the provided baseClass object is derived from this class.
This function is used to support polymorphism on state machine pattern (XML) level.
\code
bool mitk::MousePressEvent::IsSuperClassOf(InteractionEvent::Pointer baseClass)
{
// Replace with your class, or implement some rules which classes are accepted.
MousePressEvent* event = dynamic_cast<MousePressEvent*>(baseClass.GetPointer());
if (event != NULL)
{
return true;
}
return false;
}
\endcode
\section EventConstruction Configuration File & Event Factory
Regarding the state machine and the state machine pattern parser there are no changes necessary. To be able to parse the new event,
changes have to be made in the mitk::EventConfig and mitk::EventFactory classes.
The mitk::EventConfig class has to be adapted in a way such that the parameters that describe the event are parsed. The parsing can be easily extended,
and should provide a mitk::PropertyList for each event parsed.
Lastly the mitk::EventFactory has to be extended to use this mitk::PropertyList and construct an event based on it.
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Persistence.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Persistence.dox
index 125ad9b03c..fd29abfb07 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Persistence.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Persistence.dox
@@ -1,65 +1,65 @@
/**
\page PersistenceConceptPage Persistence Concept
\tableofcontents
-In general, persistence referes to the capability of an application to permanently store data, settings, states, etc. so that they outlive a restart. Normal data objects, such as images, surfaces, etc., which are visible in the data storage of MITK can be saved easily by the save/load functions, see \ref DataManagementPage for more details. This page focus on the persistence of settings, configurations, object states and parameters of the application. Depending on the component, e.g. plugin, module, etc., where you want to store your settings, MITK offers various ways to do so:
+In general, persistence refers to the capability of an application to permanently store data, settings, states, etc. so that they outlive a restart. Normal data objects, such as images, surfaces, etc., which are visible in the data storage of MITK can be saved easily by the save/load functions, see \ref DataManagementPage for more details. This page focus on the persistence of settings, configurations, object states and parameters of the application. Depending on the component, e.g. plugin, module, etc., where you want to store your settings, MITK offers various ways to do so:
-# For code inside UI independent modules with low dependencies to other libraries the mitk::PersistenceService inside the MITK persistence module can be used to store parameters by using the mitk::PropertyList class: \ref PersistenceMITKService
-# UI dependent code where the Qt library is available can use QSettings: \ref PersistenceQTSetting
-# If the UI setting of Plugins should be stored permanently, the persistence features of blueberry can be used: \ref PersistenceBlueberry
-# Extended features, which include persistence, are provided by the configuration service. \ref PersistenceConfigurationService
\section PersistenceMITKService MITK Persistence Service
For persistence of member variables and parameters, the interface mitk::IPersistenceService in the MITK core provides an API to store variables permanently by means of mitk::PropertyList objects. These properties can be stored inside a MITK scene together with the data so that the user loads data and restores the application state at once. The current actual implementation of this interface can be found in the module Persistence. The class mitk::PersistenceService offers a rather simple way to store variables permanently.
To enable MITK persistence inside your class, simply include the persistence interface and use PERSISTENCE_GET_SERVICE_METHOD_MACRO in the private part of your class, as shown in the next code snippet:
\code
#include <mitkIPersistenceService.h>
//[...]
private:
PERSISTENCE_GET_SERVICE_METHOD_MACRO
\endcode
You can then access a persistent property list by using a unique id, e.g. the name of your plugin. Variables can be added to this property list and will be available after restarting the application.
\code
mitk::PropertyList::Pointer propList = this->GetPersistenceService()->GetPropertyList("org.mitk.myUniqueModule");
//set a variable:
propList->Set("deviceNumber", m_Controls->GrabbingDeviceNumber->value());
//get a variable:
int grabbingDeviceNumber = 0;
propList->Get("deviceNumber", deviceNumber);
\endcode
When a MITK scene with stored property list is loaded within MITK the list will change automatically. However a class can be informed by the service object by when the list is changed by adding it as observer to the service object.
\code
this->GetPersistenceService()->AddPropertyListReplacedObserver(this);
\endcode
An example implementation for the use of the mitk::PersistenceService can be found in the corresponding unit test mitkPersistenceTest in the Persistence module.
\section PersistenceQTSetting Qt Settings
Within the UI dependent modules inside MITK, the Qt::settings class can also be used to store variables permanently. The following code snippet shows an example:
\code
//in the header:
#include <QSettings>
//[...}
QSettings m_MySettings;
//in the cpp file:
//initialize the settings object (e.g. constructor):
m_MySettings("MyClass","MyDescription")
//store settings:
m_MySettings.setValue("identifier",value);
//load settings:
int intExample = m_MySettings.value("identifier").toInt();
\endcode
However, integration into a MITK scene file is not possible with Qt::settings.
\section PersistenceBlueberry Persistence Features of Blueberry
In blueberry, the view states can be saved and restored which is described here: <a href="https://www.mitk.org/wiki/Article_Save_and_Restore_your_View_State">MITK.org: Save and Restore your View State</a>. Additionally, there is a possibility to make the preferences of a view persistent, which is documented in the <a href="https://docs.mitk.org/nightly/structberry_1_1IPreferencesService.html">class API documentation of the persistence service</a>.
\section PersistenceConfigurationService Configuration Service
An implementation of a configuration service is planned for MITK in near future but not available yet.
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Pipelining.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Pipelining.dox
index 1ff6d4f4f8..95e8b944ca 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Pipelining.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Pipelining.dox
@@ -1,85 +1,85 @@
/**
\page PipelineingConceptPage Pipelining Concept
\tableofcontents
\section PipelineingConceptPage_Introduction Introduction to Pipelining
Image processing in MITK draws heavily from the pipelining concept, and a clear understaning of it is crucial when developing with MITK. This document will first clarify the general idea behind pipelining and then discuss some MITK specifics that you should know about.
In the real world, a pipeline connects a source of some kind with a consumer of another. So we identify three key concepts:
1. The source, which generates data of some kind.
2. The pipeline, which transports the data. Many different pipeline segments can be switched in line to achieve this.
3. The consumer, which uses the data to do something of interest.
-The analogy to real pipelines falls a little short in one point: A physical pipeline would never process it's contents, while in software development a pipeline usually does (this is why they are often dubbed filters as well). One might ask why one shouldn't just implement the processing logic in the consumer onject itself, since it onviously knows best what to do with it's data. The two main reasons for this are reusability and flexibility. Say, one wants to display a bone segmentation from a CT-image. Let's also assume for the sake of this introduction, that this is a simple task. One could build a monolithic class that solves the problem. Or one builds a pipeline between the displaying class and the source. We know that bones are very bright in a CT Scan, so we use a treshold filter, and then a segmentation Filter to solve the problem.
+The analogy to real pipelines falls a little short in one point: A physical pipeline would never process it's contents, while in software development a pipeline usually does (this is why they are often dubbed filters as well). One might ask why one shouldn't just implement the processing logic in the consumer onject itself, since it onviously knows best what to do with it's data. The two main reasons for this are reusability and flexibility. Say, one wants to display a bone segmentation from a CT-image. Let's also assume for the sake of this introduction, that this is a simple task. One could build a monolithic class that solves the problem. Or one builds a pipeline between the displaying class and the source. We know that bones are very bright in a CT Scan, so we use a threshold filter, and then a segmentation Filter to solve the problem.
\imageMacro{pipelining_example_ct.png,"",10}
Now let's further assume that after successfully selling this new technology to a large firm, we plan to do the same with ultrasound imaging technology. The brithness relations in Ultrasound images are basically the same, but ultrasound images are very noisy, and the contrast is significantly lower. Since we used pipelining, this is no problem: We don't need to change our old segmentation class - we just plug two new filters in front of the pipeline:
\imageMacro{pipelining_example_us.png,"",16}
This may seem trivial, but when working with several input streams from many different devices that themselves stem from many different vendors, pipelining can save the day when it comes to broad range support of different specifications.
\section PipelineingConceptPage_InMITK Pipelining in MITK
\subsection PipelineingConceptPage_Update The Update() Mechanism
The flow of data inside a pipeline is triggered by only one function call to the consumer, which is Update(). Each part of the pipeline then triggers the Update() method of it's antecessor. Finally, the source creates a new batch of data using it's own GenerateData() method, and notifies its successor that new data is available. The pipeline can then start to process the data until the finished data batch is available as an output of the last Filter.
\imageMacro{pipelining_update.png,"",12}
\subsection PipelineingConceptPage_Hierarchy The Pipeline Hierarchy
-Tha base class for all parts of the pipeline except the consumer (which can be of any class) is mitk::Baseprocess. This class introduces the ability to process data, has an output and may have an input as well. You will however rarly work with this class directly.
+The base class for all parts of the pipeline except the consumer (which can be of any class) is mitk::Baseprocess. This class introduces the ability to process data, has an output and may have an input as well. You will however rarly work with this class directly.
\imageMacro{pipelining_hierarchy.png,"",16}
Several source classes extend BaseProcess. Depending on the type of data they deliver, these are ImageSource, PointSetSource and SurfaceSource. All of these mark the start of a pipeline.
The filters themselves extend one of the source classes. This may not immediately make sense, but remember that a filter basically is a source with an additional input.
\section PipelineingConceptPage_WorkWith Working with Filter
\subsection PipelineingConceptPage_Setup Setting Up a Pipeline
\verbatim
// Create Participants
mitk::USVideoDevice::Pointer videoDevice = mitk::USVideoDevice::New("-1", "Manufacturer", "Model");
TestUSFilter::Pointer filter = TestUSFilter::New();
// Make Videodevice produce it's first set of Data, so it's output isn't empty
videoDevice->Update();
- // attacht filter input to device output
+ // attach filter input to device output
filter->SetInput(videoDevice->GetOutput());
// Pipeline is now functional
filter->Update();
\endverbatim
\subsection PipelineingConceptPage_Implement Writing Your Own Filter
When writing your first Filter, this is the recommended way to go about:
- Identify which kinds of Data you require for input, and which for output
- According to the information from step one, extend the most specific subclass of BaseProcess available. E.g. a filter that processes images, should extend ImageToImageFilter.
- Identify how many inputs and how many outputs you require.
- In the constructor, define the number of outputs, and create an output.
\verbatim
//set number of outputs
this->SetNumberOfOutputs(1);
//create a new output
mitk::Image::Pointer newOutput = mitk::Image::New();
this->SetNthOutput(0, newOutput);
\endverbatim
-- Implement MakeOutput(). This Method creats a new, clean Output that can be written to. Refer to Filters with similiar task for this.
+- Implement MakeOutput(). This Method creates a new, clean Output that can be written to. Refer to Filters with similar task for this.
- Implement GenerateData(). This Method will generate the output based on the input it. At time of execution you can assume that the Data in input is a new set.
*/
\ No newline at end of file
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Properties.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Properties.dox
index e40b17a393..85cfbda276 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Properties.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Properties.dox
@@ -1,236 +1,236 @@
/**
\page PropertiesPage Properties
\tableofcontents
\section PropertyConcept The Concept Behind MITK Properties
Properties belong to a datanode and contain information relevant to the handling of the node by MITK. They provide a place to store additional information which is not part of the actual data, and as such have no reason to be contained within the data/file itself, but might be needed for such things as rendering (e.g. transfer functions) or interaction (e.g. the name of the node).
-Propteries can be read and set:
+Properties can be read and set:
\code
//1: Read a property
mitk::Property::Pointer readProperty = node->GetProperty("color"); //read the property "color" from the data node
mitk::ColorProperty::Pointer colorProperty = dynamic_cast<mitk::ColorProperty*>(readProperty); //cast this property to the subtype ColorProperty
//2: Write a property
mitk::BoolProperty::Pointer newProperty = mitk::BoolProperty::New( true ); //create a new property, in this case a boolean property
node->SetProperty( "IsTensorVolume", newProperty ); //add this property to node with the key "IsTensorVolume"
\endcode
\section PropertiesPage_PropertyServices Property Services
Property services enable you to extend the capabilities of the basic property system of MITK.
Currently there are four property services, i.e., aliases, descriptions, extensions, and filters.
All of them are briefly described in the following.
More detailed information can be found in the corresponding API documentation, as all of the services are completely documented.
All property services are provided through the functionalities of the \ref MicroServices_UserDocs "C++ Micro Services".
Hence, you can use property services from all of your modules and plugins.
\subsection PropertiesPage_PropertyAliases Property Aliases
You can add an alias to a certain property name which is displayed by the Property View instead of the genuine property name.
A property name can have more than one alias in which case the property is displayed multiple times in the Properties View.
In addition, aliases can be easily restricted to certain data node types.
Property aliases are provided through the service interface mitk::IPropertyAliases.
\subsection PropertiesPage_PropertyDescriptions Property Descriptions
The Property View will display a description of the currently selected property at its bottom if the property has an associated description.
Descriptions are parsed and displayed as rich text.
Property descriptions are provided through the service interface mitk::IPropertyDescriptions.
\subsection PropertiesPage_PropertyExtensions Property Extensions
Property extensions are a very generic approach to attach meta data to properties.
They have the sole requirement of inheriting from mitk::PropertyExtension.
The Property View searches for known property extensions like mitk::IntPropertyExtension and mitk::FloatPropertyExtension to configure the editors of the properties, e.g., you can specify the valid range for a numeric property.
Property extensions are provided through the service interface mitk::IPropertyExtensions.
\subsection PropertiesPage_PropertyFilters Property Filters
Property filters are a rather powerful tool to filter shown properties by the Property View.
There is a single global filter and the possibility to add filters on top of it for specific data node types.
Filters consist of blacklist entries and whitelist entries, i.e., blacklisted entries are filtered out and whitelisted entries are the only properties which remain after filtering.
If both lists contain entries only whitelisted entries are displayed that are not blacklisted.
Property filters are provided through the service interface mitk::IPropertyFilters.
\section ListOfIndependentProperty A List Of Module Independent Properties
This section lists most of the known properties in MITK according to where they are used. Not every node needs each
(or even close to) of these properties.
\subsection FileManagement File Management
<ul>
<li> path - The physical path the file was loaded from
<li> name - The node name in the datamanager
<li> selected - Whether the node is selected in the datamanager
</ul>
\subsection GenericRenderingProperty Generic Rendering Properties
<ul>
<li> color - Color the surface, grey value image, whatever should be rendered in (default is usually white). There is a special mitk::ColorProperty and you can use the Getter/Setter methods to access it. The color is defined with three values (RGB) in the range between 0.0 and 1.0.
\remark If you are inside a mapper you can use the following code to access
the color:
\code
float rgb[3]={1.0f, 1.0f, 1.0f};
GetColor( rgb, BaseRenderer );
\endcode
(The BaseRenderer is usually known inside a mapper).
\warning
This property will not affect images if you set the property "LookupTable". In that case a user-defined lookuptable will be used.
<li> in plane resample extent by geometry - Toggles:
Resampling grid corresponds to the current world geometry. This
means that the spacing of the output 2D image depends on the
currently selected world geometry, and *not* on the image itself.
<li> Resampling grid corresponds to the input geometry. This means that
the spacing of the output 2D image is directly derived from the
associated input image, regardless of the currently selected world
geometry.
<li> layer - Controls which image is considered "on top" of another. In the case
that two should inhabit the same space, the higher layer occludes lower layer.
So far it works for images and pointsets. The layer property applies only for similar datatypes. Pointsets are always rendered in front of images and the layer will not have any effect.
<li> levelwindow - In general, grayscale images contain values between 0 and 255. Therefore, the default window boundaries are set respectively. For displaying the image within a certain range, ie. 50 - 200, this property can be used to adjust the minimum and maximum boundary.
<li> LookupTable - This property contains a user defined lookuptable, which can be used to map scalar values to color values. Example: If an image contains a value of 128, in the resulting image the passed lookuptable could map this value to red (255, 0, 0).
\warning
If you set the "LookupTable" property the "color" property will not longer affect your image.
Example for setting up a lookuptable in MITK:
\code
#include <vtkSmartPointer.h>
#include <vtkLookupTable.h>
#include <mitkLookupTable.h>
#include <mitkLookupTableProperty.h>
[...]
vtkSmartPointer<vtkLookupTable> vtkLUT = vtkSmartPointer<vtkLookupTable>::New();
vtkLUT->SetRange(100,200); //define your table here
vtkLUT->Build();
//pass the table to MITK
mitk::LookupTable::Pointer mitkLookupTable = mitk::LookupTable::New();
mitkLookupTable->SetVtkLookupTable(vtkLUT);
mitk::LookupTableProperty::Pointer LookupTableProp = mitk::LookupTableProperty::New( mitkLookupTable );
result->SetProperty( "LookupTable", LookupTableProp );
result->Update();
\endcode
<li> opacity - Alpha (or transparency) value of the node/image/surface etc.. The
range of the opacity is between 0.0 and 1.0.
\remark
If you are inside a mapper you can use the following code to access
the opacity:
\code
float opacity=1.0f;
GetOpacity( opacity, BaseRenderer );
\endcode
(The BaseRenderer is usually known inside a mapper).
- <li> reslice interpolation - This property takes effect in swivel mode or crosshair rotaiton only. The interpolation modes "Nearest", "Linear", and "Cubic" are available and effect the pixel outcome along the rotated plane.
+ <li> reslice interpolation - This property takes effect in swivel mode or crosshair rotation only. The interpolation modes "Nearest", "Linear", and "Cubic" are available and effect the pixel outcome along the rotated plane.
<li> texture interpolation - This property toggles interpolation of the texture. If enabled, edges between image pixels are blurred. If disabled, edges remain sharp.
<li> visible - toggle node/image/surface being rendered at all
</ul>
\subsection SurfaceRenderingProperties Surface Rendering Properties
<ul>
<li> back color - in 2D, color of the normals outside the surface
- <li> back normal lenth (px) - in 2D, length of the normals in pixels
+ <li> back normal length (px) - in 2D, length of the normals in pixels
(When decreasing it the color using the front color is shorter?)
<li> color mode - (From VTK) Control how the scalar data is mapped to colors. By default (ColorModeToDefault), unsigned char scalars are treated as colors, and NOT mapped through the lookup table, while everything else is. Setting ColorModeToMapScalars means that all scalar data will be mapped through the lookup table.
<li> draw normals 2d - in 2D, toggles the presence of normals
<li> front color - in 2D, color of the normals inside the surface
- <li> front normal lenth (px) - in 2D, length of the normals in pixels
+ <li> front normal length (px) - in 2D, length of the normals in pixels
(When decreasing it the color using the back color is shorter?)
<li> invert normals - in 2D, switch front/back normals
<li> line width - in 2D, controls the thickness of the line where the surface
intersects the plane (and normals)
<li> material.ambientCoefficient - in 3D ambient lighting
<li> material.diffuseCoefficient - in 3D scattering of light
<li> material.interpolation - Choose which interpolation algorithm to use for
surface construction
<li> material.representation - Choose the representation to draw the mesh in
(Surface, Wireframe, Point Cloud)
<li> material.specularCoefficient - in-/decrease non-scattered reflection
<li> material.specularPower - control percentage of non-scattered reflection
<li> material.wireframeLineWidth - width of the wires if wireframe representation is
<li> scalar mode - (From VTK) Control how the filter works with scalar point data and cell attribute data. By default (ScalarModeToDefault), the filter will use point data, and if no point data is available, then cell data is used. Alternatively you can explicitly set the filter to use point data (ScalarModeToUsePointData) or cell data (ScalarModeToUseCellData). You can also choose to get the scalars from an array in point field data (ScalarModeToUsePointFieldData) or cell field data (ScalarModeToUseCellFieldData). If scalars are coming from a field data array, you must call SelectColorArray before you call GetColors. When ScalarMode is set to use Field Data (ScalarModeToFieldData), you must call SelectColorArray to choose the field data array to be used to color cells. In this mode, if the poly data has triangle strips, the field data is treated as the celldata for each mini-cell formed by a triangle in the strip rather than the entire strip.
<li> scalar visibility - (From VTK) Turn on/off flag to control whether scalar data is used to color objects.
<li> selected - whether the node is selected
<li> shader - which shader to use for surface rendering, currently the options are
"fixed" and "mitkShaderLightning"
</ul>
\subsection VolumeRenderingProperties Volume Rendering Properties
<ul>
<li> TransferFunction - contains transfer function for use in coloring image
<li> volumerendering - Should the volume be rendered or not
<li> volumerendering.ambient - same as cpu with gpu
<li> volumerendering.diffuse - same as cpu with gpu
<li> volumerendering.specular - same as cpu with gpu
<li> volumerendering.specular.power - same as cpu with gpu
</ul>
\subsection PointSetProperties Point Set Properties
<ul>
<li> close contour - Toggles whether the first and the last point of a contour
(connecting pieces between following points of a pointset) are connected.
<li> contourcolor - Determines the color of the contour (connecting pieces between
following points of a pointset). Visible only if "show contour" is active.
<li> contoursize - Represents the diameter of the contour (which is kind of a tube
between the following points of a pointset). Visible only if "show contour" is
active.
<li> distance decimal digits - Sets the number of decimal places for the euclidean
point to point distance which can be displayed by activating "show distances".
<li> point 2D size - The positions of points in the 2D view are represented by
crosses. "point 2D size" determines the size of this crosses.
<li> point line width - The positions of points in the 2D view are represented by
crosses. "point line width" determines the thickness of this crosses.
<li> pointsize - The positions of points in the 3D view are represented by spheres.
"pointsize" determines the diameter (size) of this spheres.
<li> selectedcolor - Sets the color for selected points from a pointset.
<li> show angles - If "show contour" is active the angles between two contour parts
can be shown.
<li> show contour - Connects following points of a pointset by drawing connecting
pieces between this points.
<li> show distance lines - Shows all angles and lines of the contour (in 2D views)
even if they are not on the view's current slice.
<li> show distances - Draws lines between following points (in 2D views) and
displays the euclidean distance between this points.
<li> show points - Toggles if the points are visible or not in the view.
<li> updateDataOnRender - If "true" the pointset is updated before rendering. If the
pointset is part of a filter pipeline this also causes an update to the
pipeline which sometimes may be not desired so it can be switched of by setting
it to false.
</ul>
\subsection Geometry2DProperties Geometry2D Properties
<ul>
<li> draw edges - Determines if tubes should be drawn around the edges of the 2D
plane. Default is true.
</ul>
Information on properties not in this list can be found in the appropriate module.
\subsection PropertiesPageSeeAlso See Also
<ul>
<li> \subpage PlanarPropertiesPage
<li> \subpage SegmentationPropertiesPage
</ul>
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/QVTKRendering.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/QVTKRendering.dox
index beac96cfda..20900d7a8f 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/QVTKRendering.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/QVTKRendering.dox
@@ -1,128 +1,128 @@
/**
\page QVTKRendering Rendering Concept
\tableofcontents
The MITK rendering pipeline is derived from the VTK rendering pipeline.
\section QVTKRendering_Pipeline_VTK VTK Rendering Pipeline
\imageMacro{RenderingOverviewVTK.png,"Rendering in VTK",12}
In VTK, the vtkRenderWindow coordinates the rendering process. Several vtkRenderers may be associated to one vtkRenderWindow.
All visible objects, which can exist in a rendered scene (2D and 3D scene), inherit from vtkProp (or any subclass e.g. vtkActor).
A vtkPropAssembly is an assembly of several vtkProps, which appears like one single vtkProp.
MITK uses a new interface class, the "vtkMitkRenderProp", which is inherited from vtkProp. Similar to a vtkPropAssembly, all MITK rendering stuff is performed via this interface class.
Thus, the MITK rendering process is completely integrated into the VTK rendering pipeline. From VTK point of view, MITK renders like a custom vtkProp object.
More information about the VTK rendering pipeline can be found at https://vtk.org/ and in the several VTK books.
\section QVTKRendering_Pipeline_MITK MITK Rendering Pipeline
This process is tightly connected to VTK, which makes it straight forward and simple. We use the above mentioned "vtkMitkRenderProp" in conjunction with the mitk::VtkPropRenderer for integration into the VTK pipeline. The QmitkRenderWindow does not inherit from mitk::RenderWindow, but from the QVTKWidget, which is provided by VTK.
The main classes of the MITK rendering process can be illustrated like this:
\imageMacro{qVtkRenderingClassOverview.png,"Rendering in MITK",16}
A render request to the vtkRenderWindow does not only update the VTK pipeline, but also the MITK pipeline. However, the mitk::RenderingManager still coordinates the rendering update behavior.
-Update requests should be sent to the RenderingManager, which then, if needed, will request an update of the overall vtkRenderWindow. The vtkRenderWindow then starts to call the Render() function of all vtkRenderers, which are associated to the vtkRenderWindow. Currently, MITK uses specific vtkRenderers (outside the standard MITK rendering pipeline) for purposes, like displaying a gradient background (mitk::GradientBackground), displaying video sources (QmitkVideoBackround and mitk::VideoSource), or displaying a (department) logo (mitk::ManufacturerLogo), etc..
+Update requests should be sent to the RenderingManager, which then, if needed, will request an update of the overall vtkRenderWindow. The vtkRenderWindow then starts to call the Render() function of all vtkRenderers, which are associated to the vtkRenderWindow. Currently, MITK uses specific vtkRenderers (outside the standard MITK rendering pipeline) for purposes, like displaying a gradient background (mitk::GradientBackground), displaying video sources (QmitkVideoBackground and mitk::VideoSource), or displaying a (department) logo (mitk::ManufacturerLogo), etc..
Despite these specific renderers, a kind of "SceneRenderer" is member of each QmitkRenderWindow. This vtkRenderer is associated with the custom vtkMitkRenderProp and is responsible for the MITK rendering.
The vtkRenderer calls four different functions in vtkMitkRenderProp, namely RenderOpaqueGeometry(), RenderTranslucentPolygonalGeometry(), RenderVolumetricGeometry() and RenderOverlay(). These function calls are forwarded to the mitk::VtkPropRenderer.
Then, depending on the mapper type (OpenGL- or VTK-based), OpenGL is enabled or disabled. In the case of OpenGL rendering, the Paint()-method of each individual mapper is called. If the mapper is VTK-based, the four function calls are forwarded to mitk::VtkMapper and within these methods the corresponding VtkProp is evaluated.
Both strategies are illustrated in the sequence diagrams below:
\imageMacro{qVtkRenderingSequenceVTK.png,"Sequence diagram for MITK VTK rendering",16}
In MITK, VTK-based mapper are more common and we recommend on implementing VTK-based mappers. However, MITK supports OpenGL-based mappers as well.
\imageMacro{qVtkRenderingSequenceGL.png,"Sequence diagram for MITK OpenGL rendering",16}
\section QVTKRendering_Mapper MITK Mapper Architecture
Mappers are used to transform the input data in tangible primitives, such as surfaces, points, lines, etc. The base class of all mappers is mitk::Mapper. The mapper hierarchy reflects the two possible ways to render in MITK: Subclasses of mitk::Mapper control the creation of rendering primitives
that interface to the graphics library (e.g. via OpenGL, vtk). The mapper architecture is illustrated in the following UML diagram:
\imageMacro{qVtkRenderingMapper.jpg,"Mapper architecture",16}
mitk::Mapper::Update() calls the time step of the input data for the specified renderer and checks whether the time step is valid and calls method mitk::Mapper::GenerateDataForRenderer(), which is reimplemented in the individual mappers and should be used to generate primitives.
mitk::Mapper::SetDefaultProperties() should be used to define mapper-specific properties.
\section QVTKRendering_programmerGuide User Guide: Programming hints for rendering related stuff (in plugins)
\li The QmitkRenderWindow can be accessed like this: this->GetRenderWindowPart()->GetRenderWindow("axial");
\li The vtkRenderWindow can be accessed like this: this->GetRenderWindowPart()->GetRenderWindow("axial")->GetVtkRenderWindow();
\li The mitkBaseRenderer can be accessed like this: mitk::BaseRenderer* renderer = mitk::BaseRenderer::GetInstance(this->GetRenderWindowPart()->GetRenderWindow("sagittal")->GetRenderWindow());
\li An update request of the overall QmitkStdMultiWidget can be performed with: mitk::RenderingManager::GetInstance()->RequestUpdateAll() or alternatively with the shorthand this->RequestRenderWindowUpdate();
\li A single QmitkRenderWindow update request can be done like this: mitk::RenderingManager::GetInstance()->RequestUpdate(this->GetRenderWindowPart()->GetRenderWindow("axial")->GetVtkRenderWindow());
\li be aware that GetRenderWindowPart() can return null if the editor is closed. This is a common reason for nullpointer exceptions.
\note The usage of ForceImmediateUpdateAll() is not desired in most common use-cases.
\subsection QVTKRendering_distinctRenderWindow Setting up a distinct Rendering-Pipeline
It is sometimes desired to have one (or more) QmitkRenderWindows that are managed totally independent of the 'usual' renderwindows defined by the QmitkStdMultiWidget.
This may include the data that is rendered as well as possible interactions. In order to achieve this, a set of objects is needed:
\li mitk::RenderingManager -> Manages the rendering
\li mitk::DataStorage -> Manages the data that is rendered
\li mitk::GlobalInteraction -> Manages all interaction
\li QmitkRenderWindow -> Actually visualizes the data
The actual setup, respectively the connection, of these classes is rather simple:
\code
// create a new instance of mitk::RenderingManager
mitk::RenderingManager::Pointer renderingManager = mitk::RenderingManager::New();
// create new instances of DataStorage and GlobalInteraction
mitk::DataStorage::Pointer dataStorage = mitk::DataStorage::New();
mitk::GlobalInteraction::Pointer globalInteraction = mitk::GlobalInteraction::New();
// add both to the RenderingManager
renderingManager->SetDataStorage( dataStorage );
renderingManager->SetGlobalInteraction( globalInteraction );
// now create a new QmitkRenderWindow with this renderingManager as parameter
QmitkRenderWindow* renderWindow = new QmitkRenderWindow( parent, "name", renderer, renderingManager );
\endcode
That is basically all you need to setup your own rendering pipeline.
Obviously you have to add all data you want to render to your new DataStorage. If you want to interact with this renderwindow, you will also have
to add additional Interactors/Listeners.
\note Dynamic casts of a mitk::BaseRenderer class to an OpenGLRenderer (or now, to an VtkPropRenderer) should be avoided. The "MITK Scene" vtkRenderer and the vtkRenderWindow as well, are therefore now included in the mitk::BaseRenderer.
\subsection QVTKRendering_userGuideMapper How to write your own Mapper
If you want to write your own mapper, you first need to decide whether you want to write a VTK-based mapper or a GL-based mapper.
We recommend to write a VTK-based mapper, as VTK is easy to learn and some GL-based mappers can have unexpected site effects.
However, you need to derive from the respective classes.
In the following we provide some programming hints for writing a Vtk-based mapper:
\li include mitkLocalStorageHandler.h and derive from class BaseLocalStorage as a nested class in your own mapper. The LocalStorage instance should
-contain all VTK ressources such as actors, textures, mappers, polydata etc.
+contain all VTK resources such as actors, textures, mappers, polydata etc.
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.
+assigned its own LocalStorage instance so that all contained resources (actors, shaders, textures, ...) are provided individually per window.
\li GenerateDataForRenderer() should be reimplemented in order to generate the primitives that should be rendered. This method is called in each Mapper::Update() pass, thus,
all primitives that are rendered are recomputed. Employ LocalStorage::IsGenerateDataRequired() to determine whether it is necessary to generate the primitives again. It is not
necessary to generate them again in case the scene has just been translated or rotated.
\li For 2D mappers, it is necessary to determine the 3D primitives close to the current plane that should be drawn. Use
planeGeometry = renderer->GetSliceNavigationController()->GetCurrentPlaneGeometry() to get the current plane. The distance to it can be determined by using planeGeometry->DistanceFromPlane(point).
\li Reimplement GetVtkProp(), that should return the specific VtkProp generated in GenerateDataForRender() (e.g. a single actor or a propassembly, which is a combination of different actors).
The VtkProp is picked up in one of the four render passes and thus integrated into the VTK render pipeline.
\li SetDefaultProperties() should be used to define mapper-specific properties.
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/ReaderWriterPage.md b/Documentation/Doxygen/3-DeveloperManual/Concepts/ReaderWriterPage.md
index 74c6e26447..6ad588bd02 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/ReaderWriterPage.md
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/ReaderWriterPage.md
@@ -1,176 +1,176 @@
Reader and Writer {#ReaderWriterPage}
=================
This page is work in progress and will introduce you to the IO-System of MITK.
[TOC]
## Introductory slides
Several Talks have been given on the IO-System. The following list should provide you with a good starting point.
- Introduction to MimeTypes: https://www.mitk.org/images/e/e8/MimeTypes.pdf (by Caspar J. Goch)
- Introduction to the IO-System: https://www.mitk.org/images/0/0b/Newio.pdf (by Keno März)
- IO-Streams and the new IO System: https://www.mitk.org/images/9/95/Streams.pdf (by Keno März)
## Quick start: Reading and writing files using IOUtil
mitk::IOUtil class provides convenience methods for loading data into a data
storage or just returning BaseData objects without user interaction. The mitk::IOUtil::Save()
and mitk::IOUtil::Load() methods cover the typical use cases and
automatically select the best matching mitk::IFileReader and mitk::IFileWriter
instance. In most cases, this is the easiest way to read or write a file.
// load files directly into datastorage
mitk::IOUtil::Load("/path/to/my/file.nrrd",*ds);
// load basedata into local vector
std::vector< mitk::Basedata::Pointer > basedatas;
basedatas = mitk::IOUtil::Load("/path/to/my/file.nrrd");
// write basedata to file (here: surface as PLY
mitk::IOUtil::Save(mySurface, "/Save/surface/here.ply");
// write basedata to file (here: surface as STL
mitk::IOUtil::Save(mySurface, "/Save/surface/here.stl");
When reading a file using IOUtil, the IO-Framework first determines the mime-type of the given file.
Afterwards, the best reader is selected internally, instantiated, and executed. The resulting BaseData Objects are returned to the developer as the method result.
## Quick start: Creating your own reader or writer
If you implement a new BaseData, usually matching readers and writers are required. The following guide will help you to quickly set up these up and get them working in MITK.
Create new classes for reader and writers. Optimally, place them in an extra IO-Module that is configured as autoload. This way, they are available to the application from the start. If you are working with common data, the appropriate module is MitkIOExt. You can either extend mitk::AbstractFileIO, which will allow you to implement a class with reader and writer abilities, or you can extend mitk::AbstractFileReader or mitk::AbstractFileWriter specifically.
\imageMacro{reader_writer_classes.png,"",16}
Implement the given Methods. A good example on how to write a simple reader and writer is the mitkPointSetReaderService.cpp and mitkPointSetWriterService.cpp class, from which you can take implementation cues. The following is a simplified version of the header file:
namespace mitk
{
class PointSetReaderService: public AbstractFileReader // 2) Extend the Abstract File Reader
{
public:
PointSetReaderService(); // 3) Provide Constructor and Destructor
virtual ~PointSetReaderService();
// 4) Overwrite the Read Method as seen here
using AbstractFileReader::Read;
virtual std::vector< itk::SmartPointer<BaseData> > Read();
private:
// 5) Provide a clone method
PointSetReaderService(const PointSetReaderService& other);
virtual PointSetReaderService* Clone() const;
};
}
### Example
Follow these steps to implement a new Reader:
A) Create a new cpp and h file in an appropriate submodule. Usually, a reader or writer should be located in the same module as the BaseData derivate it reads/writes.
-B) Extend AbstractFileReader . This is highly recommended because it enables integration of your Reader into the Registery. It will then automatically be used by the application to load this type of files.
+B) Extend AbstractFileReader . This is highly recommended because it enables integration of your Reader into the Registry. It will then automatically be used by the application to load this type of files.
C) Provide a constructor . It should contain a minimal amount of information and might look like this:
mitk::PointSetReaderService::PointSetReaderService()
: AbstractFileReader(CustomMimeType(IOMimeTypes::POINTSET_MIMETYPE_NAME()), "MITK Point Set Reader")
{
RegisterService();
}
Note the call to the superclass constructor containing the MIME-type. You can either reuse an existent MIME type here or create your own MIME-type locally . Finally, register the service to make it available to MITK.
D) Provide a Clone Method: Readers are clones when the registry requires a new reader. Provide a clone method to accommodate for this. Use the mitkPointSetReaderService.cpp as a reference if necessary.
E) Instantiate it in the module activator. Open the module activator and make sure that the new Reader/Writer is instantiated and held somewhere in the code. Also, unregister the reader/writer in the unload function if necessary.
## Reader/Writer Options
Options are a powerful concept to modify Reader/Writer behaviour and to automate user interaction with minimal development overhead. In principal, options are represented by a simple map and a few getter and setter functions:
typedef std::map<std::string, us::Any> Options;
virtual Options GetOptions();
virtual void SetOptions(const Options& options);
virtual us::Any GetOption(const std::string& name);
virtual void SetOptions(const Options& options);
In its constructor, a reader or writer can set its options as a number map entries consisting of an human-readable option name and a default value. When a user tries to read/write a file via the interface, an options menu is generate from the map.
\imageMacro{io_options.png,"The options dialog shown by the raw reader",5}
The user can then modify these options, which are automatically set in the reader/writer. You can use raw-images to see this behaviour in action.
## Mime Types
The MimeType class provides meta-data about a specific data format.
Every mitk::IFileReader and mitk::IFileWriter instance must be associated
with exactly one mime-type via a service property.
\imageMacro{mimetypes.png,"",14}
Mime-type are used for categorizing data formats and creating filter
strings in file open and save dialogs. Hence they effectively control
the accessible set of data formats from a graphical user interface.
Minimally, mime-types should provide a name and a list of handled extensions in lower case.
Additionally, it is highly encouraged to set a category and a comment which will provide user-readable
strings for user interaction.
It is important to understand the difference between mitk::MimeType and mitk::CustomMimeType.
The former is an immutable stack object and can be pulled from mitk::MimeTypeProvider.
it should be used for all interaction with MimeTypes. mitk::CustomMimeType is the heap-object pendant
which is wrapped by mitk::MimeType and should exclusively be used for registration purposes, i.e. when you
register a new MimeType.
## Additional Capabilities
You can extend your reader writer with useful capabilities. All of these are optional
+ Priority: Reader use a ranking with Get- and SetRanking in order to signify how well they are suited to read a file. If several readers are able to read a file, the one with the highest Ranking level will be chosen.
+ ProgressCallbacks : Readers are executed in a thread automatically. If the reader implements callbacks, the progress bar will be more accurate during loading of files. Note: Progress callbacks are work in progress.
## Ranking strategies
Todo.
## Convenience classes
Developers usually do not interact with the service registry directly
to retrieve and select a matching mitk::IFileReader or mitk::IFileWriter instance.
### %QmitkIOUtil
The QmitkIOUtil class is a wrapper around mitk::IOUtil, providing
file open and save dialogues for selecting a file name from a within
a graphical user interface.
### FileReaderRegistry and FileWriterRegistry
Access to mitk::IFileReader and mitk::IFileWriter objects and their service references.
## Integrating external I/O mechanisms
The MITK I/O system integrates with several external I/O systems.
### ITK Reader and Writer
The I/O classes from ITK are automatically added to the service registry
of MITK. They can be transparently used via the mitk::IFileReader and mitk::IFileWriter
interfaces or the mitk::IOUtil methods.
### VTK Reader and Writer
VTK does not provide a mechanism to enumerate all available I/O classes.
Hence MITK provides manual integration of a specific set of VTK readers
and writers.
## Error handling and recovery
Todo.
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/RenderingTests.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/RenderingTests.dox
index 134aeec00b..cad39f548e 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/RenderingTests.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/RenderingTests.dox
@@ -1,97 +1,97 @@
namespace mitk{
/**
\page RenderingTests Automatic Rendering Tests
Available sections:
-# \ref RenderingTests_WhatIsARenderingTest "What is an automatic rendering test?"
-# \ref RenderingTests_HowToCreateATest "How to create a rendering test"
\section RenderingTests_WhatIsARenderingTest What is an automatic rendering test?
An automatic rendering test is a powerful tool to test rendering results automatically via dashboard.
Regarding rendering lots of different sources influence the output on the screen (e.g. different
-mappers, renderes, camera settings or the algorithm creating the data). Thus, during the rendering
+mappers, renderers, camera settings or the algorithm creating the data). Thus, during the rendering
process of an image many different classes are involved and can have impact on the output. A minor
change in an important class (e.g. mitkVtkPropRenderer) can have major impact on the actual rendering.
An automatic rendering test takes an arbitrary object as input (e.g. image, surface, point set),
renders this into an mitkRenderWindow, makes a screen shot of that renderwindow and finally compares
that screen shot to a given reference. Of course, the reference has to be defined by the user.
Internally, a VTK test method is used to compare both screen shots and measure differences. In case
of failure, a difference can be generated to show exactly which pixels are rendered incorrectly.
Implementing automatic rendering tests for algorithms ensures that algorithms deliver the same output
as they used to do in previous version of MITK.
\section RenderingTests_HowToCreateATest How to create your own automatic rendering test
To create an automatic rendering test you should use an existing test as example
(e.g. mitkImageVtkMapper2DTest).
\subsection RenderingTests_HowToCreateATest_Sub1 Adding the test to CMake
Like adding any test with parameters to CMake, you have to add a custom test to
the files.cmake and the corresponding CMakeLists.txt:
For instance a test for the mitkImageVtkMapper2D has to be added like this:
files.cmake
\code
set(MODULE_CUSTOM_TESTS
...
mitkImageVtkMapper2D.cpp
)
\endcode
CMakeLists.txt
\code
mitkAddCustomModuleTest(mitkImageVtkMapper2D_rgbaImage640x480 mitkImageVtkMapper2D #custom name of the test and executable
${MITK_DATA_DIR}/RenderingTestData/rgbaImage.png #input image to load in data storage
-V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/rgbaImage640x480REF.png #corresponding reference screenshot
)
\endcode
The first parameter defines a custom name for the test. This is a feature to distinguish
between tests with different inputs. In this example the test is named
<em>mitkImageVtkMapper2D_rgbaImage640x480</em> to show that this test is using the test image
<em>rbgaImage640x480</em> as input.
The next parameters sets test executable name (i.e. the name of the test class). Here: <em>mitkImageVtkMapper2D</em>.
The next parameter(s) are used to pass the input to the test. For instance, it is possible to set
multiple objects as input for a test (e.g. /path/to/img1.jpg /path/to/img2.nrrd /path/to/pointset.mps).
All test data for core tests should be placed into the MITK-DATA repository inside the folder:
${MITK_DATA_DIR}/RenderingTestData/. It is possible to create other folders for different modules or bundles.
The option -V defines the path to the reference screen shot and is internally used by VTK. The reference
screen shot is highly important and has to be proven if is correct. The
<em>mitkRenderingTestHelper</em> offers means to capture a screen shot of a renderwindow.
Capturing a reference screen shot should happen just once and NOT be a permanent part of the test.
It is also possible to set the option -T /path/to/directory/. This option is internally used by VTK
-to save a difference image. This is meant for debugging and should not be used for the final implemenation of a test.
+to save a difference image. This is meant for debugging and should not be used for the final implementation of a test.
\subsection RenderingTests_HowToCreateATest_Sub2 Coding the test
Writing the test code is pretty straight forward. In the example of the <em>mitkImageVtkMapper2DTest</em>
the input parameters are added to a datastorage and rendered into a render window via the
<em>mitkRenderingTestHelper</em>. Last, the vtkTesting macro is called to compare the given reference to
the data rendered in the renderwindow:
\code
int retVal = vtkRegressionTestImage( renderingHelper.GetVtkRenderWindow() );
//retVal meanings: (see VTK/Rendering/vtkTesting.h)
//0 = test failed
//1 = test passed
//2 = test not run
//3 = something with vtkInteraction
MITK_TEST_CONDITION( retVal == 1, "VTK test result positive" );
\endcode
If the content of the previously filled renderwindow does not equal the reference, the test will fail.
Feel free to modify the data before rendering. E.g. create a surface from the loaded image and render
that surface afterwards or add compute QBalls for an image and render those. Happy testing!
*/
}
diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Selection.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Selection.dox
index 646405bd73..6ad7e13a74 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Selection.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Selection.dox
@@ -1,302 +1,302 @@
/**
\page SelectionConceptPage Selection Concept
\tableofcontents
Selecting data nodes is frequently done when working with the MITK workbench.
Most of the views in MITK require one or more selected data nodes that will be somehow processed by the underlying algorithms, such as image segmentation, image registration, basic image processing, image statistics computation etc..
-In order to overcome some disadvantages of the old data node seletion concept, the following new selection concept has been implemented.
+In order to overcome some disadvantages of the old data node selection concept, the following new selection concept has been implemented.
<br>
This page introduces the concept and the corresponding implementation.
The document starts with a brief explanation of the disadvantages of the old concepts and justifies a need for the new concept.
\section Migration Migration idea / background
The old data node selection concept is a mixture of a \a global and a \a local selection concept:
\subsection GlobalSelection Global selection
The global selection was realized by the data manager view: Selecting one or more data nodes in the data manager view lead to a propagation of the selected nodes via the blueberry framework.
Other views that derive from QmitkAbstractView received the new selection and processed it individually. All activated views were able to receive the new selection successively, thus it is called a \a global selection.
\subsection LocalSelection Local selection
The local selection was realized by Qt elements inside a view. An example of such an element is the QmitkDataStorageComboBox. This combo box contains the nodes from the data storage and allows to select one of it.
A combo box can be included in a view multiple times. Additionally a node predicate can be set for a combo box so that only a filtered subset of all nodes in the data storage is contained in the combo box.
A selection inside a combo box does not affect the selection of another combo box (in the same or another view), thus it is called a \a local selection.
<br>
Both concepts have reached their functional limit:
-# the global selection does not offer the functionality to filter data nodes
-# the global selection does not allow to select different data nodes in different views
-# the selection order for multi-node selection might be important, which can be cumbersome to achieve via the data manager
-# the local selection does not offer enough flexibility:
-# it does not allow to propagate data node selections
-# it does not allow multi node selection (multiple combo boxes are needed)
-# it does not show the visibility / hierarchy of the data nodes
The latest trend was to change the design of the view of the MITK workbench so that the local concept (using the QmitkDataStorageComboBox) was used wherever possible.
This was done since the problems of the global selection concept where severe. In cases where the local concept was used positive feedback was received.
\section NewSelectionConcept New selection concept
The new selection concept is based on the idea of a local selection concept. This accounts for some fundamental requirements, such as individual data node selection for each view and data node filtering.
Furthermore it clearly defines the responsibility of the data manager view: the data manager is only used to manage the data to render, e.g. defining the node hierarchy (rendering order) or setting the color or transparency of the pixel data.
Additionally the new local selection concept is also able to overcome the problems of the previous local concept: It allows data node selection propagation, multi node selection and is able to show additional information such as data node hierarchy or node visibility.
How this is achieved will be explained in the following sections.
\subsection BasicIdea Basic idea
The basic idea is to provide a set of classes that define the basic functionality of the new selection concept. These classes allow the use of existing and the development of new <em>data storage inspectors</em> or <em>node selection widgets</em>.
<br>
\imageMacro{selection_concept_class_diagram.png, "", 16}
<br>
Included in this concept is a set of basic data storage inspectors and node selection widgets that are frequently needed. A developer can extend these classes to create own data storage inspectors or node selection widgets to customize a plugins behavior and design.
The following paragraphs further describe the most important classes:
\b QmitkAbstractDataStorageModel
The QmitkAbstractDataStorageModel extends the QAbstractItemModel to provide a custom item model for the data storage. This custom item model extends the Qt-model functionality in order to accept an mitk::DataStorage and an mitk::NodePredicateBase.
The data storage is used to define on which data storage the model operates. In order to update the data storage model if the data storage changes, the QmitkAbstractDataStorageModel provides four pure virtual functions,
QAbstractItemModel::DataStorageChanged, QAbstractItemModel::NodeAdded, QAbstractItemModel::NodeChanged and QAbstractItemModel::NodeRemoved.
This abstract model calls the first function if the data storage has been reset using the SetDataStorage-function (i.e. to a new data storage or to a nullptr).
The last three functions are connected to the corresponding events of the data storage (AddNodeEvent, ChangedNodeEvent and RemoveNodeEvent).
The node predicate can be set and used to filter the data storage, so that only a subset of the contained data nodes is represented by the data storage model.
In order to update the data storage model if the node predicate changes, the QmitkAbstractDataStorageModel provides a pure virtual function, QAbstractItemModel::NodePredicateChanged.
This abstract model calls this function if the node predicate has been reset using the SetNodePredicate-function (i.e. to a new node predicate or to a nullptr).
Any subclass of this abstract data storage model can define its own functionality by overriding the five corresponding functions.
The abstract data storage model does not implement any Qt model functionality, so the functions of the QAbstractItemModel have to be overwritten in the subclasses, according to the model type (e.g. list model, table model).
\b QmitkAbstractDataStorageInspector
The QmitkAbstractDataStorageInspector serves as a base class that can be derived from to provide a custom view onto the data storage using a QmitkAbstractDataStorageModel derivative.
This custom widget extends the QWidget functionality in order to accept an mitk::DataStorage and an mitk::NodePredicateBase.
The data storage is used to define which data storage the widget should inspect. The node predicate can be set and later be used to filter the data storage, so that only a subset of the contained data nodes is inspected by the data storage inspector.
The data storage and the node predicate can be set by the corresponding setter, which in turn calls the pure virtual function QmitkAbstractDataStorageInspector::Initialize.
Any subclass of this abstract data storage inspector can now define its own functionality inside the Initialize-function to define what happens if the data storage or the node predicate is (re-)set.
Typically an inspector will forward the data storage and the node predicate to the data storage model to make it aware of the data it should hold.
Additionally a data storage inspector holds a QmitkModelViewSelectionConnector and initializes it with a QAbstractItemView derived class (e.g. a QListView) and the data storage model. The idea behind this connector-class will be explained in the next section.
Furthermore the abstract class provides a QSignal, CurrentSelectionChanged(NodeList), that is emitted if the selection of the underlying data storage model has changed.
The abstract class also provides two QSlots, SetSelectOnlyVisibleNodes(bool) and SetCurrentSelection(NodeList). Calling these slots will forward the given arguments to the model view selection connector member which in turn can manipulate the data storage models selection.
\b QmitkModelViewSelectionConnector
The QmitkModelViewSelectionConnector is used to transform a model selection into a list of selected data nodes and vice versa. The class accepts a QAbstractItemView with a corresponding QmitkAbstractDataStorageModel that operates on a data storage.
The connector class provides a QSlot, QmitkModelViewSelectionConnector::ChangeModelSelection(const QItemSelection\&, const QItemSelection\&) that is called if the selectionChanged-signal of the selection model of the given QAbstractItemView is emitted. This indicates a change in the model selection.
The slot itself will emit a QSignal, QmitkModelViewSelectionConnector::CurrentSelectionChanged(QList<mitk::DataNode::Pointer>), where the selection of the item view has been transformed to a list of selected data nodes.
The transformation is done by using the member functions QmitkModelViewSelectionConnector::GetSelectedNodes and QmitkModelViewSelectionConnector::GetInternalSelectedNodes: The selected indices of the item view's selection model are used to receive the corresponding data node from the data storage model.
Additionally the connector class provides a function QmitkModelViewSelectionConnector::SetCurrentSelection(QList<mitk::DataNode::Pointer>), which can be used to change the selection of the QAbstractItemView from outside of this class.
The nodes of the list are compared to the nodes that are valid for the data storage model, according to the current node predicate. If the given nodes are valid the corresponding indices are selected in the view. If the given list is equal to the current selection, nothing happens.
Using the QmitkModelViewSelectionConnector::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) function it is possible to set a variable that defines if only those nodes should be de-/selectable that are included in the list of filtered data nodes.
This means that a new selection ca be received, which might change the current selection of the view. However, those nodes that are not visible, as defined by the node predicate of the data storage model, will not be de-/selectable.
\b \anchor QmitkSelectionServiceConnector_ref QmitkSelectionServiceConnector
The QmitkSelectionServiceConnector is used to interact with the global selection bus:
It provides a private QSlot, QmitkSelectionServiceConnector::OnServiceSelectionChanged(const berry::IWorkbenchPart::Pointer&, const berry::ISelection::ConstPointer&), which is internally called if the selection of the selection bus has been changed.
This slot transforms the berry selection into a list of selected data nodes and emits the QSignal QmitkSelectionServiceConnector::ServiceSelectionChanged(QList<mitk::DataNode::Pointer>).
Typically this signal is used to react in another class on the change of selected nodes from the selection bus, e.g. inside the QmitkModelViewSelectionConnector to change the selection of an item view via the selection bus.
Additionally the connector class provides a function, QmitkSelectionServiceConnector::SetAsSelectionProvider(QmitkDataNodeSelectionProvider*), which can be used to set the connector as a provider for data node selections that will be sent via the global selection bus.
This way a QmitkModelViewSelectionConnector can propagate its selection to all listeners of the selection bus via the QmitkSelectionServiceConnector. The following code shows how the two connector classes can be connected:
\code
void QmitkDataStorageViewerTestView::SetAsSelectionProvider(bool enable)
{
if (enable)
{
m_SelectionServiceConnector->SetAsSelectionProvider(GetSite()->GetSelectionProvider().Cast<QmitkDataNodeSelectionProvider>().GetPointer());
connect(m_ModelViewSelectionConnector.get(), SIGNAL(CurrentSelectionChanged(QList<mitk::DataNode::Pointer>)), m_SelectionServiceConnector.get(), SLOT(ChangeServiceSelection(QList<mitk::DataNode::Pointer>)));
}
else
{
m_SelectionServiceConnector->RemoveAsSelectionProvider();
disconnect(m_ModelViewSelectionConnector.get(), SIGNAL(CurrentSelectionChanged(QList<mitk::DataNode::Pointer>)), m_SelectionServiceConnector.get(), SLOT(ChangeServiceSelection(QList<mitk::DataNode::Pointer>)));
}
}
\endcode
<br>
\code
void QmitkDataStorageViewerTestView::SetAsSelectionListener(bool enable)
{
if (enable)
{
m_SelectionServiceConnector->AddPostSelectionListener(GetSite()->GetWorkbenchWindow()->GetSelectionService());
connect(m_SelectionServiceConnector.get(), SIGNAL(ServiceSelectionChanged(QList<mitk::DataNode::Pointer>)), m_ModelViewSelectionConnector.get(), SLOT(SetCurrentSelection(QList<mitk::DataNode::Pointer>)));
}
else
{
m_SelectionServiceConnector->RemovePostSelectionListener();
disconnect(m_SelectionServiceConnector.get(), SIGNAL(ServiceSelectionChanged(QList<mitk::DataNode::Pointer>)), m_ModelViewSelectionConnector.get(), SLOT(SetCurrentSelection(QList<mitk::DataNode::Pointer>)));
}
}
\endcode
\subsection DataStorageInspectors The data storage inspectors
\b \anchor QmitkNodeSelectionDialog_ref QmitkNodeSelectionDialog
The QmitkNodeSelectionDialog is the main widget to be used in order to select data nodes from the data storage. It is a pop-up-dialog that can be included in any other widget, such as the QmitkSingleNodeSelectionWidget or the QmitkMultiNodeSelectionWidget.
The node selection dialog serves as a container for different QmitkAbstractDataStorageInspector: It uses a QTabWidget that holds the different data storage inspectors. These inspectors are received by using the mitk::DataStorageInspectorGenerator class.
Each data storage inspector is then added to the tab widget and its \a CurrentSelectionChanged-signal is connected to the dialog's \a OnSelectionChanged-slot.
If the selection inside a data storage inspector changes, the slot sets the selected nodes of the dialog and propagates this selection to all its known data storage inspectors. This way all inspectors that are currently held in the tab widget show the same selection.
In order to dynamically add and receive new data storage inspectors the microservice approach is used: Each concrete implementation of a data storage inspector provider registers itself as an mitk::IDataStorageInspectorProvider via the service registry.
For this the QmitkDataStorageInspectorProviderBase can be used.
Any class can use the mitk::DataStorageInspectorGenerator to either receive all registered data storage inspector providers or a specific one.
A provider in turn provides a specific data storage inspector, which can be used to display the data nodes of the data storage.
The user can define which of the registered data storage inspectors should be available when using a QmitkNodeSelectionDialog: Using the workbench preferences one can select the <a>Node Selection</a> entry and enable or disable certain data storage inspectors.
\b mitk::IDataStorageInspectorProvider
The mitk::IDataStorageInspectorProvider declares the service interface for all data storage inspector providers. It must be implemented to override the mitk::IDataStorageInspectorProvider::CreateInspector-function.
This function should return the concrete data storage inspector created by the provider. In order to simplify the use of this interface and to add some basic functionality, the QmitkDataStorageInspectorProviderBase class should be used.
\b QmitkDataStorageInspectorProviderBase
The QmitkDataStorageInspectorProviderBase implements the mitk::IDataStorageInspectorProvider-interface and is a template implementation: It can be used with a concrete implementation of the QmitkAbstractDataStorageInspector; the data storage inspector that the provider base should create.
When the provider base is initialized it registers itself as an mitk::IDataStorageInspectorProvider service reference.
The provider base overrides the mitk::IDataStorageInspectorProvider::CreateInspector-function of the interface to create and return a concrete instance of the provided data storage inspector.
This way each concrete data storage inspector can be created by receiving the corresponding data storage inspector provider as a service and using the mitk::IDataStorageInspectorProvider::CreateInspector-function.
\b mitk::DataStorageInspectorGenerator
The mitk::DataStorageInspectorGenerator simply provides two static functions to receive all or a specific data storage provider. These providers have been registered before as an mitk::IDataStorageInspectorProvider service.
Using the microservice approach the provider can be received without any further knowledge about the concrete provider.
This way a developer can easily add their own concrete data storage inspector by creating a new QmitkDataStorageInspectorProviderBase with the concrete inspector as the template argument.
\subsection CustomDataStorageInspector Adding a custom data storage inspector
As mentioned above, the microservice approach is used inside the mitk::DataStorageInspectorGenerator to receive all known or a specific data storage provider. In order to let the microservice know about a new custom data storage inspector, it has to be provided by a data storage provider.
This data storage provider has to be registered as an mitk::IDataStorageInspectorProvider service.
The new selection concept provides the QmitkDataStorageInspectorProviderBase class (see above), which automatically registers the data storage provider as a provider service. For this the following constructor can be used:
\code
QmitkDataStorageInspectorProviderBase(const std::string& id, const std::string& displayName, const std::string& desc= "")
\endcode
The constructor can be used like this:
\code
mitk::IDataStorageInspectorProvider* dataStorageInspectorProvider = new QmitkDataStorageInspectorProviderBase<QmitkDataStorageListInspector>("org.mitk.QmitkDataStorageListInspector", "Simple list", "Displays the filtered content of the data storage in a simple list."));
\endcode
This registers a new QmitkDataStorageInspectorProviderBase instance as an mitk::IDataStorageInspectorProvider service. The data storage provider and its specific data storage inspector can later be received using the following code:
\code
mitk::IDataStorageInspectorProvider* dataStorageProvider = mitk::DataStorageInspectorGenerator::GetProvider("org.mitk.QmitkDataStorageListInspector");
QmitkAbstractDataStorageInspector* dataStorageInspector = dataStorageProvider->CreateInspector();
\endcode
In the code snippets the QmitkDataStorageListInspector class was used. This is an example of a concrete data storage inspector which is provided by the presented selection concept. It is a frequently used data storage inspector and already registered via a corresponding provider base.
This is done inside the mitk::QtWidgetsActivator class; a class that is used during module activation. Currently four frequently used custom data storage inspectors are already registered. The following sections describe these four frequently used basic inspectors.
\subsubsection QmitkDataStorageListInspector
The QmitkDataStorageListInspector is an example of a concrete data storage inspector, subclassing the QmitkAbstractDataStorageInspector. Its only purpose is to provide a custom model-view pair that represents a list view onto the data storage. The view is a simple QListView.
The model is a custom model derived from the above mentioned QmitkAbstractDataStorageModel: The QmitkDataStorageDefaultListModel overrides the five pure virtual functions to react on changes of the data storage or the node predicate:
each function calls the private UpdateModelData-function, which will simply retrieve the currently available (possibly filtered) data nodes of the data storage.
Additionally the default list model needs to override some functions of the QAbstractItemModel in order to represent a list model. One important function to override is the data(const QModelIndex\& index, int role = Qt::DisplayRole)}-function:
This function is used by Qt to define what has to be displayed in the list view. It is important that a custom model returns an mitk::DataNode::Pointer object for a model index when the role is mitkDataNodeRole.
\subsubsection QmitkDataStorageTreeInspector
The QmitkDataStorageTreeInspector is another example of a concrete data storage inspector, subclassing the QmitkAbstractDataStorageInspector. Its only purpose is to provide a custom model-view pair that represents a tree view onto the data storage. The view is a simple QTreeView.
The model is a custom model derived from the above mentioned QmitkAbstractDataStorageModel: The QmitkDataStorageSimpleTreeModel needs to override some functions of the QAbstractItemModel in order to represent a tree model.
Again, one important function to override is the data(const QModelIndex\& index, int role = Qt::DisplayRole)}-function. It is important that the simple tree model returns an mitk::DataNode::Pointer object for a model index when the role is mitkDataNodeRole.
The QmitkDataStorageTreeInspector is designed to reflect the classical data manager view.
\subsubsection QmitkDataStorageSelectionHistoryInspector
The QmitkDataStorageSelectionHistoryInspector is a concrete data storage inspector that allows to view the recently used nodes of the data storage.
It uses the same QmitkDataStorageDefaultListModel as the QmitkDataStorageListInspector. The QmitkDataStorageSelectionHistoryInspector provides a function to add nodes to the selection history.
The selection history will be displayed in chronological order and a selected node will only appear once in the history list.
\subsubsection QmitkDataStorageFavoriteNodesInspector
The QmitkDataStorageFavoriteNodesInspector is a concrete data storage inspector that allows to view a user's favorite nodes of the data storage. It extends the QmitkDataStorageListInspector.
The QmitkNodeSelectionDialog provides a function to set nodes as favorite nodes using a specific node property.
It allows the QmitkDataStorageFavoriteNodesInspector to use a customized node predicate to filter nodes according to their favorite-property-state.
This favorite-property-state is defined by the node property "org.mitk.selection.favorite"
The QmitkDataStorageFavoriteNodesInspector provides a function to unset nodes as favorite nodes by setting this specific node property to "false", so that they won't appear anymore in the list of favorite nodes.
\subsection NodeSelectionWidgets The node selection widgets
\b QmitkAbstractNodeSelectionWidget
The QmitkAbstractNodeSelectionWidget extends the QWidget to provide a custom widget to show data nodes after they have been selected using the QmitkNodeSelectionDialog with its data storage inspectors.
This custom widget extends the QWidget functionality in order to accept an mitk::DataStorage and an mitk::NodePredicateBase.
The data storage is used to define which data storage should be used by the selection dialog. The node predicate is used to filter the data storage.
Any subclass of this abstract node selection widget needs to override the following functions in order to customize its behavior:
- void QmitkAbstractNodeSelectionWidget::SetCurrentSelection(NodeList), in order to set the selection of this widget given an external list of selected nodes. This function may be customized to filter the incoming selected nodes.
- void QmitkAbstractNodeSelectionWidget::UpdateInfo(), in order to set the information text and enable / disable the control buttons according to the current selection.
- void QmitkAbstractNodeSelectionWidget::OnNodePredicateChanged(), in order to react to a change of the used node predicate. Changing the node predicate might alter the selection.
This function is called if QmitkAbstractNodeSelectionWidget::SetNodePredicate(mitk::NodePredicateBase*) was called.
- void QmitkAbstractNodeSelectionWidget::OnDataStorageChanged(), in order to react to a change of the used data storage. Changing the data storage might alter the selection.
This function is called if QmitkAbstractNodeSelectionWidget::SetDataStorage(mitk::DataStorage*) was called.
Furthermore QmitkAbstractNodeSelectionWidget::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) can be overridden to set the modus to (not) include invisible nodes in the selection. Default value is true.
\b QmitkSingleNodeSelectionWidget
The QmitkSingleNodeSelectionWidget is an example of a concrete node selection widget, subclassing the QmitkAbstractNodeSelectionWidget. It overrides the virtual functions to react on changes of the node selection.
If the QmitkNodeSelectionButton of the widget is pressed, a QmitkNodeSelectionDialog is opened as a pop-up for data node selection. This dialog is a \ref QmitkNodeSelectionDialog_ref "QmitkNodeSelectionDialog" (see \ref DataStorageInspectors).
It presents the currently known and active data storage inspectors for the user to select a single data node. If a selection is confirmed, the pop-up closes and the selected data node will be shown in the QmitkSingleNodeSelectionWidget.
The user will see a thumbnail or an icon of the data node (if available) along with an HTML text of the data node info.
The QmitkSingleNodeSelectionWidget is currently used inside plugin views to allow to select specific nodes of the data storage for further processing.
A plugin view can connect a function to the CurrentSelectionChanged-signal to immediately react on a new selection of the QmitkSingleNodeSelectionWidget.
Using QmitkSingleNodeSelectionWidget::GetSelectedNode the currently selected node of the selection widget can be retrieved anytime.
Additionally an auto-selection-mode is available that automatically selects a node from the data storage if such a data storage is set and contains a node that matches the given predicate.
The auto-selection-mode only works if the QmitkSingleNodeSelectionWidget does not already show a selected node.
\b QmitkMultiNodeSelectionWidget
The QmitkMultiNodeSelectionWidget is another example of a concrete node selection widget, subclassing the QmitkAbstractNodeSelectionWidget. It overrides the virtual functions to react on changes of the node selection.
If the 'Change selection'-button is pressed a QmitkNodeSelectionDialog is opened as a pop-up for data node selection. This dialog is a \ref QmitkNodeSelectionDialog_ref "QmitkNodeSelectionDialog" (see \ref DataStorageInspectors).
It presents the currently known and active data storage inspectors for the user to select a single data node. If a selection is confirmed, the pop-up closes and the selected data nodes will be shown in the QmitkMultiNodeSelectionWidget.
Unlike single node selection the multi node selection is presented as a list of all the selected nodes.
The QmitkMultiNodeSelectionWidget is currently used inside plugin views to allow to select a set of specific nodes of the data storage for further processing.
A plugin view can connect a function to the CurrentSelectionChanged-signal to immediately react on a new selection of the QmitkMultiNodeSelectionWidget.
Using QmitkAbstractNodeSelectionWidget::GetSelectedNodes the currently selected node of the selection widget can be retrieved anytime.
- QmitkMultiNodeSelectionWidget::SetSelectionCheckFunction allows to define a function that puts a constraint on the node selection (dialog) to only allow specific combinations or seletions of nodes.
+ QmitkMultiNodeSelectionWidget::SetSelectionCheckFunction allows to define a function that puts a constraint on the node selection (dialog) to only allow specific combinations or selections of nodes.
The result of the constraint check has to be either an empty string (indicating a valid node selection) or a string that explains the error / constraint violation.
A simple check function can look like this:
\code
auto checkFunction = [](const QmitkMultiNodeSelectionWidget::NodeList & nodes)
{
if (!(nodes.size() % 2))
{
std::stringstream ss;
ss << "<font class=\"warning\"><p>Invalid selection.<p/><p>The number of selected nodes must be uneven! the current number is " << nodes.size() << ".</p></font>";
return ss.str();
}
return std::string();
};
multNodeSelectionWidget->SetSelectionCheckFunction(checkFunction);
\endcode
This check function will be passed to the QmitkNodeSelectionDialog and is used to print an error message, if an invalid selection is made inside the node selection dialog.
This will prevent the user from confirming an even selection. If a valid selection is made no error message will be printed and the user can confirm the selection.
\b QmitkNodeSelectionButton
The QmitkNodeSelectionButton is a customized QPushButton and is used for example inside the QmitkSingleNodeSelectionWidget: It displays the thumbnail or icon of the selected data node (if available) along with an HTML text of the data node info.
\section DataStorageViewerTestPlugin The DataStorageViewerTest plugin
The \a org.mitk.gui.qt.datastorageviewertest plugin can be enabled and used inside the MITK workbench. It serves as a test / demo plugin with a single view that shows different uses of the new selection concept and its related classes. The following classes are used:
-# Top row: A QmitkDataStorageListInspector
-# Top right: A QmitkDataStorageTreeInspector
-# Bottom left: A QmitkSingleNodeSelectionWidget
-# Bottom right: A QmitkMultiNodeSelectionWidget
The top row presents the two examples of an implementation of the QmitkAbstractDataStorageInspector. They display the whole content of the given data storage in two different ways. No node predicates are used in this case.
Both inspectors come with two checkboxes to set each inspectors as a selection provider and a selection listener.
With these checkboxes turned on the QmitkSelectionServiceConnector for each inspector is connected to the QmitkModelViewSelectionConnector of each inspector (as shown in \ref QmitkSelectionServiceConnector_ref "QmitkSelectionServiceConnector").
<br>
See how the selection also changes in the inspectors if nodes are selected in the mitk::DataManager and the <a>Set as selection listener</a>-checkbox is checked for the inspector.
The bottom row presents the two examples of an implementation of the QmitkAbstractNodeSelectionWidget: They display a specific data node selection that was made by the user.
Both widgets come with several checkboxes to modify the behavior of the selection widgets.
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/DocumentationGuide.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/DocumentationGuide.dox
index 353ee16c39..28b6c80176 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/DocumentationGuide.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/DocumentationGuide.dox
@@ -1,84 +1,84 @@
/**
\page DocumentationGuide Writing Documentation
\section DocumentationGuideCodeGeneral General remarks
MITK uses <a href="https://www.doxygen.nl/">Doxygen</a> for the generation of our user manual pages as well as for the generation of the on- and offline reference manuals. So on the technical side many questions can be answered by the <a href="https://www.doxygen.nl/manual/index.html">doxygen documentation</a>, such as the list of commands or a few basic doxygen tutorials.
Therefore this document is not primarily intended as a guide to using doxygen, the doxygen manual does a much better job of that, but as a guide to use doxygen in MITK and a collection of helpful hints and advise about pittfalls.
Also, of course you need to have doxygen installed to generate documentation.
\section DocumentationGuideCode Documenting the source code
-MITK is a substantial project and encompasses many different source files by many different developers over quite a considerable timeframe. Many of them have written excellent code which can do a lot of things and is very helpful to people who might apply it in wholly unlooked for ways to completely different problems. To facilitate this sharing and reusing of ressources one first and foremost has to know what kind of ressources are already available.
+MITK is a substantial project and encompasses many different source files by many different developers over quite a considerable timeframe. Many of them have written excellent code which can do a lot of things and is very helpful to people who might apply it in wholly unlooked for ways to completely different problems. To facilitate this sharing and reusing of resources one first and foremost has to know what kind of resources are already available.
Few people write code in the intention for it to be difficult to be used by others, but unfortunately what might seem a very efficient and easily understandable piece of code to the author might be nigh unreadable for someone else. Very often it does not in fact matter whether the code itself is understandable, as long as it one can get the information what a function is supposed to do. While comments in the source file help a lot to gain this knowledge in can get quite tedious go through every file looking for the right tool.
This is where using doxygen pays off by giving a short comment in the header file a reference manual is automatically generated.
While doxygen support several different manners of documentation, the MITK documentation should keep a uniform documentation style:
\warning{
Use <b>only</b> the \verbatim /** ... */ \endverbatim style for documentation.
}
In dire emergencies you may consider commenting via the /// style, others must <b>never</b> be used.
An example:
\verbatim
/** \brief Brief description what the commented part does.
*
* More detailed description. This can be as long as you like,
* whereas the brief description should never be more than one sentence.
*/
\endverbatim
See \subpage DocumentationExample for an exemplary documentation of a class.
\subsection DocumentationGuideCodeHints Helpful hints:
<ul>
<li> Always put comments intended for doxygen in the header files.
</ul>
\section DocumentationGuideManual Writing user manuals
While the usage of your view/perspective/application might seem obvious and accessible to you, to most people it is not. Writing a good manual is key for this. It is very difficult to write a manual which is too comprehensive, most often if something can be done in a wrong way, somebody will see this as the only one.
For MITK purposes you should put your documentation in BUNDLEPATH/documentation/UserManual/QmitkMyViewName.dox .
Give them a unique name and remember to only use alphanumeric characters and underscores. Identifiers like "org.placeA.x" and "org.placeB.y" look identical to doxygen because it only parses the "org". Use "org_placeA_x" instead.
Regarding images: Doxygen looks for images globally. To avoid confusion, include the plugin name into the image. E.g. "Checkboxes.png" is a bad name while "QmitkIGTTracking_Checkboxes.png" is a good name.
Include images only via the image Macro! This way it is automatically included in the pdf export of the documentation.
\verbatim
// The Macro hast he following form (note the braces!):
\imageMacro{imagePath, "Image Description", Image size in cm}
// e.g.:
\imageMacro{QmitkIGTTracking_Checkboxes.png, "Some beautiful Checkboxes!", 5}
// If you must use commas in the description, escape them!
\imageMacro{QmitkIGTTracking_Checkboxes.png, "Some beautiful Checkboxes\, you have never seen better ones! ", 5}
\endverbatim
Image size is only used for the pdf export, but images won't be displayed at all if it is missing. Maximum is 16.
The nightly generated HTML documentation and the Qt Help System documentation can contain different content using the isHTML command.
\subsection DocumentationGuideManualHints Helpful hints:
<ul>
<li> Do not use fullstops (".") in identifiers, it throws doxygen off
<li> Think were your page should go in the MITK help page structure and declare it as a subpage accordingly
<lI> Use the imageMacro instead of the image command
<li> Use structuring elements, such as sections and subsections
<li> Use references to allow for fast navigation
<li> Images, pictures and sketches are great, use them
<li> Use visual help like remark, paragraph and warning
<li> BLUEBERRY_USE_QT_HELP should be set to ON
<li> The plug-in org.blueberry.ui.qt.help should be set to ON
</ul>
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/StyleGuideAndNotes.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/StyleGuideAndNotes.dox
index 6ad26976e2..4cb7045a6e 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/StyleGuideAndNotes.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/StyleGuideAndNotes.dox
@@ -1,466 +1,466 @@
/**
\page StyleGuideAndNotesPage The MITK Style Guide and Technical Notes
\tableofcontents
The following document is a description of the accepted coding style for the Medical Imaging Interaction Toolkit (MITK). Developers who wish to contribute code to MITK should read and adhere to the standards described here.
\section StyleGuideAndNotesPage_NameConventions Naming Conventions
\li Using case change to indicate separate words
@code
ImageFilter
PixelType
DataStorage
NodePredicateProperty
@endcode
\li Underscores are not used e.g. Image_Filer, _Node
\li Variable names should convey the meaning behind the code
@code
BoundingBox::Pointer boundingBox = BoundingBox::New();
@endcode
\li Names are generally spelled out
@code
mitk::DataNode* node;
@endcode
\li Abbreviation are allowable when in common use e.g. ROI for Region of
Interest
\subsection StyleGuideAndNotesPage_NamingClasses Naming Classes
\li Classes are named beginning with a capital letter
\li Classes are named according to the following general rule:
@code
class name = <algorithm><input><concept>
@endcode
\li Examples of concepts \n
<b>Accessor</b>: Access and convert between types e.g. NullScalarAccessor \n
<b>Container</b>: A container of objects such as points or images e.g. VectorContainer \n
<b>Filter</b>: A class that participates in the data processing pipeline e.g. AddImageFilter \n
<b>Mapper</b>: Transform data from one form into another e.g. ContourMapper2D \n
<b>Reader/Writer</b>: A class that reads/writes a single data object e.g. VtkSurfaceReader \n
\subsection StyleGuideAndNotesPage_NamingFiles Naming Files
\li MITK classes like @a ExampleClass should be in namespace @a mitk and their corresponding files should be named @a mitkExampleClass.h/.cpp.
@code
mitk::DataStorage
@endcode
\li Qt specific MITK classes like @a QmitkListView should have the prefix Qmitk in their class names and their corresponding files should be named @a QmitkListView.h/.cpp.
@code
QmitkDataStorageComboBox
@endcode
\li Header Files ends with an .h and
\li Implementation Files with an .cpp or .txx for a template class
\subsection StyleGuideAndNotesPage_NamingMethodsandFunctions Naming Methods and Functions
\li Functions and methods are named beginning with a capital letter
\li Referring to class methods in code, an explicit this-> pointer should be
used
@code
mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetAll();
@endcode
\subsection StyleGuideAndNotesPage_NamingSignalSlots Naming Signal/Slots Methods and Functions
\li Slots are named according to the following general rule
@code
On[variable name who send the signal][signal]();
@endcode
\li Example
@code
connect( loadImagePushButton, SIGNAL( clicked(bool ) ),
SLOT( OnLoadImagePushButtonClicked( bool ) ) );
void mitk::Image::OnLoadImagePushButtonClicked( bool )
{
... Do something ...
}
@endcode
\li Signals are named according to the following general rule
@code
Signal[MethodName]();
@endcode
\li Example
@code
emit SignalFinishedSegmentation();
@endcode
\subsection StyleGuideAndNotesPage_NamingClassDataMembers Naming Class Data Members
\li Class data members are prefixed with m_
@code
m_Volumes
m_OffsetTable
m_ImageMask
@endcode
\li An exception to this rule, Qt class Data members are not prefixed and begin with a lower-case letter
@code
loadImageButton
closeImageAction
@endcode
\subsection StyleGuideAndNotesPage_NamingLocalVariables Naming Local Variables
\li Local variables first letter is lower-case
@code
offset
data
slicesIt
@endcode
\subsection StyleGuideAndNotesPage_NamingQtVariables Naming Qt Variables
\li GUI variables ends with name of used QT tool.
@code
QPushButton* loadImagePushButton;
QAction* closeImageAction;
QCheckBox* hideImageCheckBox;
QRadioButton* binaryImageRadioButton;
@endcode
\subsection StyleGuideAndNotesPage_NamingTypedefs Naming Typedefs
\li Typedefs must end in the word Type
@code
typedef TPixel PixelType;
typedef itk::Image< TPixel, VImageDimension > ImageType;
typedef std::list<mitk::Image::Pointer> ImageListType;
@endcode
\section StyleGuideAndNotesPage_Pointer Pointer
\subsection StyleGuideAndNotesPage_DeclarationofPointers Declaration of Pointers
\li Position of * pointers are connected with the variable
@code
int *counter;
@endcode
\li Analog to references
@code
int &counter;
@endcode
\subsection StyleGuideAndNotesPage_SmartPointer SmartPointer
\li SmartPointers must be used for classes that have itk::Object as a base
class.
\li Assignment of a just created instance to a normal pointer results in a
crash, since the reference count is decreased immediately to zero and the
object is destroyed.
@code
itk::Object::Pointer object = itk::Object::New();
@endcode
\li Static declarations are also forbidden and result into an exception when
the scope of the variable is left, because the destructor is called while the
reference count is still greater than zero.
\li Note that using smart pointers requires using real (normal) pointers when
setting input. If you want to return a newly created smart pointer that
is not also kept within the class (e.g., if you write a Clone method), you
have to return a smart pointer on output (compare itkMacro.h). If the
smart pointer is kept within the class, returning a real (normal) pointer
is sufficient.
\li Testing a SmartPointer against NULL is done with the IsNull() and Is-
NotNull() methods. A simple ==NULL issues a warning.
\section StyleGuideAndNotesPage_Namespace Namespace
\li MITK classes should be in namespace @a mitk
@code
mitk::Image::Pointer mitk::ImageGenerator::MakeImage()
{
// already in namespace mitk here!
Image::Pointer image = mitk::Image::New();
ImageDecorator::Pointer decorator = mitk::ImageDecorator::New();
d->Decorate( image );
return image;
}
@endcode
\li Constants in MITK for mitk::Operation and mitk::Action are set in
namespace, so don't forget to add prefix mitk::
@code
switch (actionId)
{
case mitk::AcMOVESELECTED:
....Do something ...
break;
default:
break;
}
@endcode
\section StyleGuideAndNotesPage_CodeLayoutandIndentation Code Layout and Indentation
\subsection StyleGuideAndNotesPage_GeneralLayout General Layout
\li Each line of code should take no more than 120 characters.
\li Use lots of whitespace to separate logical blocks of code, intermixed with
comments
-\li <b>DO NOT USE TABS.</b> The standard indention is <b>2 spaces</b> (see <a href="https://itk.org/Wiki/images/c/c6/ITKStyle.pdf">ITK Style Guide</a>). Configure your
+\li <b>DO NOT USE TABS.</b> The standard indentation is <b>2 spaces</b> (see <a href="https://itk.org/Wiki/images/c/c6/ITKStyle.pdf">ITK Style Guide</a>). Configure your
editor accordingly.
\li DO NOT USE trailing whitespaces
\li Declaration of variables should be one declaration per line
@code
int sliceNumber;
char* stringName;
ImageType::Pointer image;
@endcode
\subsection StyleGuideAndNotesPage_ClassLayout Class Layout
\li Copyright
@code
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
@endcode
\li Includes [A .. Z]
@code
#include "... .h"
@endcode
\li Namespace
@code
namespace mitk
{
@endcode
DO NOT litter your header with "using namespace;"!
\li Class (Template)
@code
template <class TType>
class ClassName : public ImageBase<VImageDimension>
{
@endcode
\li Typedefs
@code
public:
....typedefs....
@endcode
\li Methods
@code
public:
....methods....
protected:
....methods....
private:
....methods....
@endcode
\li QT Signals
@code
signals:
Signal...();
@endcode
\li QT Slots
@code
public slots:
On...();
protected slots:
On...();
@endcode
\li Data Member
@code
private/protected:
....class data members....
};
}
#endif
@endcode
\section StyleGuideAndNotesPage_UseofBraces Use of Braces
\li Used to delimit the scope of an if, for, while, switch.
\li Braces are placed on a line by themselves:
@code
for ( unsigned int i = 0; i < 3; ++i )
{
... do something ...
}
@endcode
or
@code
if ( condition )
{
... do something ...
}
else if ( other condition )
{
... do something ...
}
else
{
... do something ...
}
@endcode
\li You can choose to use braces on a line with a code block when the block
consists of a single line:
@code
if ( condition ) { foo = 1; }
else if ( condition2 ) { foo = 3; }
else { return; }
@endcode
or
@code
for ( unsigned int i = 0; i < 3; ++i) { x[i] = 0.0; }
@endcode
\section StyleGuideAndNotesPage_IncludeGuards Include Guards
\li \#include guard is a particular construct used to avoid the problem of
double inclusion when dealing with the \#include directive.
\li Naming convention for \#include guards is: ClassName_h
\li Following example demonstrates a problem that can arise if \#include guards
are missing: Here, the file child.cpp has indirectly included two copies of the text in the
header file grandfather.h. This causes a compilation error, since the structure
type foo is apparently defined twice.
@code
grandfather.h struct foo
{
int m Member;
};
father.h #include "grandfather.h"
child.h #include "grandfather.h"
#include "father.h"
@endcode
\subsection StyleGuideAndNotesPage_Useofincludeguards Use of \#include guards
\li Here, the first inclusion of grandfather.h causes the macro grandfather h to
be defined. Then, when child.cpp includes grandfather.h the second time,
the \#ifndef test fails, and the preprocessor skips down to the \#endif, thus
avoiding the second definition of struct foo. The program compiles correctly.
@code
grandfather.h #ifndef grandfather h
#define grandfather h
struct foo
{
int m Member;
};
father.h #include "grandfather.h"
child.h #include "grandfather.h"
#include "father.h"
@endcode
\section StyleGuideAndNotesPage_TechnicalNotes Some Technical Notes
\li Use forward declarations in header files wherever possible. Only include those header files in a header file
that are really necessary. Include the rest in the implementation file.
\li For classes inheriting directly or indirectly from @a itk::LightObject (most of the MITK-classes do so), the
class definition should include the mitkClassMacro. Additionally, if the class can be instantiated (normally the case,
if the class is not abstract) and has @em only a constructor without parameters, the constructor should be declared
protected and the @a itkFactorylessNewMacro should be used to create a @a New() method for instantiation. Here is an example:
@code
class ExampleClass : public SuperClassOfTheExampleClass
{
public:
mitkClassMacro(ExampleClass, SuperClassOfTheExampleClass)
itkFactorylessNewMacro(Self)
[...]
protected:
ExampleClass();
virtual ~ExampleClass();
}
@endcode
\li Set- and Get-methods can be created with the macros @a itkSetObjectMacro(name,type) and
@a itkGetObjectMacro(name,type), respectively, if the @a type is derived from @a itk::LightObject or
@a itk::Object.
\li When using inner classes of a parent class which is templated, you have
to use the keyword @a typename for gcc 3.x and standard compliance. For example,
@a TreeChangeListener is an inner class of @a Tree, therefore use:
@code
class LinkedTree : public Tree<T>
{
public:
typedef typename LinkedTree<T>::TreeChangeListener TreeChangeListener;
[...]
}
@endcode
Another example:
@code
typename std::vector<TreeChangeListener*>::iterator pos = treeChangeListenerList.begin();
@endcode
@a iterator is an inner class of @a vector.
\li Constants in MITK for mitk::Operation and mitk::Action are set in namespace, so don't forget to add prefix @a mitk::
@code
switch (actionId)
{
case mitk::AcMOVESELECTED:
@endcode
Prefixes for the constants are to be used like corresponding others. See file @a Interactions\\mitkBaseInteraction\\mitkInteractionConst.h for further details.
\section StyleGuideAndNotesPage_AutomaticCodeFormatting Automatic Code Formatting
We offer a .clang-format file, which can be used to automatically format code acceptably.
For an explanation of the different options check out https://clang.llvm.org/docs/ClangFormatStyleOptions.html
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step05.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step05.dox
index 96ff8fc34f..ae31c5ecaa 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step05.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step05.dox
@@ -1,38 +1,38 @@
/**
\page Step05Page MITK Tutorial - Step 5: Interactively add points
\li Examples/Tutorial/Step5
\li https://www.mitk.org/download/tutorial-data/Pic3D.nrrd (image) \n
https://www.mitk.org/download/tutorial-data/lungs.vtk (surface)
In addition to Step 4 where 3 views were created on the data, we now want to interactively add points.
A node containing a PointSet as data is added to the data tree and a PointSetDataInteractor is associated with the node, which handles the interaction.
The @em interaction @em pattern is defined in a state-machine, stored in an external XML file. Thus, we need to load a state-machine.
- A state machine describes interaction pattern with different states (states beeing something like "a point is selected") and transitions to these states (e.g. "select a point").
+ A state machine describes interaction pattern with different states (states being something like "a point is selected") and transitions to these states (e.g. "select a point").
These transitions are associated with actions. In this way it is possible to model complex interaction schemes.
By what these transitions and actions are triggered is described in a configuration file. It maps user events to identifiers that are used in the state machine patterns.
In this way the user interaction can be changed by simply loading a different configuration file for a state machine, and the user may add points now with a right click instead of
left click + SHIFT, as in our case.
Therefore after loading the state machine pattern the PointSetDataInteractor is also given a event configuration file.
More information about interaction in MITK can be found \ref InteractionPage "here".
In order to add a point the shift key has to be pressed while left clicking in a render window.
You can also move points or remove them (left click while pressing ALT).
\image html step5_result.png
\dontinclude Step5.cpp
A PointSet and a node for it have to be created to be able to interactively adding points:
\skipline mitk::PointSet
\until interactor->SetDataNode(pointSetNode)
\ref Step04Page "[Previous step]" \ref Step06Page "[Next step]" \ref TutorialPage "[Main tutorial page]"
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step06.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step06.dox
index 291199950c..fb53b1fe14 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step06.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step06.dox
@@ -1,110 +1,110 @@
/**
\page Step06Page MITK Tutorial - Step 6: Use an interactive region-grower
The source is now split among several files:
\li Examples/Tutorial/Step6
\li https://www.mitk.org/download/tutorial-data/Pic3D.nrrd (image)
In this step the program is enhanced by the possibility to start a region-grower at interactively added points.
We will see how MITK images can be accessed as ITK images.
We now load the image file Pic3D.nrrd only since the surface will be the result of the region-growing.
Add points in the image by pressing SHIFT+left mouse key, then adjust the thresholds and press 'Start region growing'.
\imageMacro{step6_result.png,"",13.55}
The class Step6 inherits from QWidget and provides methods for setting up the widgets.
%Step6RegionGrowing.cpp contains a method for performing the region-growing. %Step6main.cpp contains main.
Like in ITK and VTK class member names start with m_ followed by the proper member name starting with a capital letter (e.g. m_Tree).
Function names start with capital letters. To learn more about style conventions in MITK read \ref StyleGuideAndNotesPage "The MITK Style Guide".
\dontinclude Step6.cpp
The widgets are initialized as in the previous steps but with an additional QVBox for a button to start the segmentation:
\skipline Create controlsParent
\until hlayout->addWidget(m_LineEditThresholdMax)
This creates a button to start the segmentation and its clicked() signal is connected to the method StartRegionGrowing():
\dontinclude Step6.cpp
\skipline QPushButton *startButton
\skipline connect(startButton
\section AccessMTIKImagesAsITKImagesSection Access MITK images as ITK images
ITK images are templated whereas mitk::Images are not. To use ITK filters with MITK images, we have to convert from MITK to ITK. To do so, first
define an access method, which is templated as an ITK image is:
\code
template<TPixel, VImageDimension>
MyAccessMethod(itk::Image<TPixel, VImageDimension>* itkImage)
{
...
}
\endcode
If you don't understand this template syntax, you should read any C++ text book. Understanding template syntax is crucial to successfully using ITK.
To call this templated method with an (untemplated) mitk::Image, you can use the AccessByItk macro from mitkImageAccessByItk.h. This macro checks for
-the actual image type of the mitk::Image and does any neccessary conversions. Look into "Modules / Adaptor classes" for more information.
+the actual image type of the mitk::Image and does any necessary conversions. Look into "Modules / Adaptor classes" for more information.
\code
AccessByItk(mitkImage, MyAccessMethod)
\endcode
\dontinclude Step6RegionGrowing.txx
In this step our access method is called RegionGrowing() (defined in %Step6RegionGrowing.txx ):
\skipline template
\until } //RegionGrowing()
Additionally the access function has to be instantiated for all datatypes and two/three dimensions as some compilers have memory problems without this explicit instantiation, some even need instantiations in separate files for 2D/3D: \n
For 2D in %Step6RegionGrowing1.cpp :
\dontinclude Step6RegionGrowing1.cpp
\skipline InstantiateAccessFunctionForFixedDimension
... and for 3D in %Step6RegionGrowing2.cpp :
\dontinclude Step6RegionGrowing2.cpp
\skipline InstantiateAccessFunctionForFixedDimension
\dontinclude Step6.cpp
The method StartRegionGrowing() finally calls our access method RegionGrowing():
\skipline Step6::StartRegionGrowing
\until }
\section ConvertingITKMITKSection Converting ITK images to MITK images and vice versa
In some cases it is useful to simply convert between ITK and MITK images. The direction ITK to MITK is easy, since mitk::Image can handle most data types. The direction MITK to ITK is more critical, since ITK images have to be instantiated with a fixed pixel type and fixed dimension at compile time.
\li \code mitk::Image mitk::ImportItkImage(itk::Image<...>) \endcode
\li \code mitk::CastToItkImage(mitkImage, itk::Image<...>) \endcode
\section ConnectingMITKToVTKSection Connecting MITK images to VTK
Images are not converted or copied: The data array is just accessed via an encapsulating VTK object.
\li \code vtkImageData* mitk::Image::GetVtkImageData(int time = 0) \endcode
\section SurfacesMITKToVTKSection MITK Surfaces to VTK and vice versa
Again: not a conversion, just accessing.
\li \code vtkPolyData* mitk::Surface::GetVtkPolyData(int time = 0) \endcode
\li \code mitk::Surface::SetVtkPolyData(vtkPolyData*, int time = 0) \endcode
\ref Step05Page "[Previous step]" \ref Step07Page "[Next step]" \ref TutorialPage "[Main tutorial page]"
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox
index 648fcc9361..d8f233136f 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox
@@ -1,501 +1,501 @@
/**
\page Step10Page MITK Tutorial - Step 10: Adding new Interaction
\tableofcontents
\section HowToUseDataInteractor Step 01 - How to use an existing DataInteractor in your Module/Plugin/...
MITK provides finished DataInteractors for a variety of tasks, they can be found in Core/Code/Interactions. They can be used with
state machine patterns and config files located under Core/Code/Resources/Interactions.
A mitk::DataInteractor consists of four parts. The class describing the functionality and two XML files; one describes the state machine pattern, that is the workflow of an interaction and
the second describes the user events which trigger an action. Lastly every mitk::DataInteractor works on a mitk::DataNode in which it stores and manipulates data.
To use a mitk::DataInteractor these parts have to be brought together.
** TODO add code of mitk::PointSetDataInteractor Plugin ..
<b>This code demonstrates the use of an existing mitk::DataInteractor exemplary for the mitk::PointSetDataInteractor:</b>
First we need a mitk::DataNode in which the PointSets is stored. It has to be added to the mitk::DataStorage.
\code
mitk::DataNode::Pointer dataNode = mitk::DataNode::New();
GetDataStorage()->Add(dataNode.GetPointer());
\endcode
Then we create an instance of a mitk::PointSetDataInteractor and load a predefined statemachine pattern as well as a configuration
for it:
\code
m_CurrentInteractor = mitk::PointSetDataInteractor::New();
m_CurrentInteractor->LoadStateMachine("PointSet.xml");
m_CurrentInteractor->SetEventConfig("PointSetConfig.xml");
\endcode
Lastly the mitk::DataNode is added to the mitk::DataInteractor
\code
m_CurrentInteractor->SetDataNode(dataNode);
\endcode
now the mitk::DataInteractor is ready for usage.
\section HowToModifyInteraction Step 02 - How to modify the behaviour of a DataInteractor
The behavior of a mitk::DataInteractor is determined by two aspects. One, the state machine pattern which describes the flow/order of actions
that are performed. Secondly the configuration which determines which user interaction triggers the actions.
\subsection ModifyDisplayInteractionBehavior How to modify the display interaction behavior
Sometimes it may be desirable to change the behaviour of the mitk::DisplayActionEventBroadcast which controls zooming, panning and scrolling, e.g. when a
Tool is activated that reacts to the same events. Changing the behavior of the DisplayActionEventBroadcast (or possibly any other EventHandler) can be achieved from anywhere
in the code by requesting the InteractionEventObserver and assigning an alternative configuration to it, as demonstrated in this example:
\code
std::ifstream* configStream = new std::ifstream( #path to alternative configuration file# );
mitk::EventConfig newConfig(configStream);
// Requesting all registered EventObservers
std::list<mitk::ServiceReference> listEventObserver = GetModuleContext()->GetServiceReferences<InteractionEventObserver>();
for (std::list<mitk::ServiceReference>::iterator it = listEventObserver.begin(); it != listEventObserver.end(); ++it)
{
auto* displayActionEventBroadcast = dynamic_cast<DisplayActionEventBroadcast*>(GetModuleContext()->GetService<InteractionEventObserver>(*it));
// filtering: only adjust the displayActionEventBroadcast
if (nullptr != displayActionEventBroadcast)
{
displayActionEventBroadcast->SetEventConfig(newConfig);
}
}
\endcode
\section SectionImplementationDataInteractor How to implement a new DataInteractor
This second part of the tutorial step goes beyond the activation of an interactor, that modifies data by user interaction) as shown above. It shows what needs to be implemented to add a new way of interaction within your MITK application.
Please see \ref DataInteractionPage as an introduction to the MITK interaction mechanism.
This tutorial is structured as follows: The first section deals with config files, describing all the parameters of events and how to use them
in a configuration file. In the second section the basics are described that are needed to write a state machine pattern. The last section
-deals with brining configuration, state machine pattern and code together and gives an exemplary implementation of a mitk::DataInteractor.
+deals with bringing configuration, state machine pattern and code together and gives an exemplary implementation of a mitk::DataInteractor.
\section ConfigFileDescriptionSection How to create a Config-File
\subsection EventDescriptionSection Event Description
Events are described by their parameters. Each event type has its own set of parameters that can be set in the configuration file.
If a parameter is omitted it is set to its default value. All possible parameters are listed and described below. Event parameters
are also described in the documentation of the event class itself.
Mandatory for each event description is the event class and the event variant. The parameters of an event are set by attribute tags.
\note Refer to \ref EventClassSection for the meaning of event class.
\b Mouse \b Buttons \n
mitk::InteractionEvent::MouseButtons represent the buttons. They can be used for two attributes. First the EventButton attribute which describes
the button that triggered the event,
this always is a single button. Secondly the ButtonState attribute that describes which buttons were pressed at the moment the event has been generated.
For example assume the right mouse button and the middle mouse button are already pressed, now the left mouse button is pressed too and generates a second event,
this would be described as follows:
\code
<attribute name="EventButton" value="LeftMouseButton"/>
<attribute name="ButtonState" value="RightMouseButton,MiddleMouseButton"/>
\endcode
Note: Technically the LeftMouseButton is also pressed and should be listed in the ButtonState, but this is taken care of by the mitk::EventFactory .
<b> Key Events</b> \n
mitk::InteractionKeyEvent represents a pressed key, which key is pressed is provided with the Key attribute like this
\code
<attribute name="Key" value="A"/>
\endcode
or
\code
<attribute name="Key" value="Escape"/>
\endcode
\note Key Events do not require an explicit configuration, for all key events there exists
a predefined event variant with the name 'Std' + value, that is key a is named 'StdA'.
The names for special keys are listed here:
\dontinclude mitkInteractionEvent.h
\skipline // Special Keys
\until // End special keys
<b> Modifier Keys</b> \n
mitk::InteractionEvent::ModifierKeys represent the combination of pressed modifier keys, several modifier keys pressed at the same time
are denoted by listing them all separated by commas.
\code
<!-- shift and control key are pressed -->
<attribute name="Modifiers" value="shift,ctrl"/>
\endcode
\b ScrollDirection \n
This attribute is unique to the mitk::MouseWheelEvent and describes the direction in which the mouse wheel is rotated. In the event description actual only the direction is provided,
but the event is generated with the actual value, and this value can be retrieved from the object.
\code
<attribute name="ScrollDirection" value="up"/>
<!-- or -->
<attribute name="ScrollDirection" value="down"/>
\endcode
\subsection ExamplesSection Examples
Examples for key events:
\code
<config>
<!-- Event of key 'a' pressed -->
<event_variant class="InteractionKeyEvent" name="StdA">
<attribute name="Key" value="A"/>
</event_variant>
<!-- Event of key 'b' pressed while modifiers ctrl and shift are pressed-->
<event_variant class="InteractionKeyEvent" name="StdB">
<attribute name="Key" value="B"/>
<attribute name="Modifiers" value="shift,ctrl"/>
</event_variant>
</config>
\endcode
Examples for MousePress events:
\code
<!-- Standard left click -->
<config>
<event_variant class="MousePressEvent" name="StdMousePressPrimaryButton">
<attribute name="EventButton" value="LeftMouseButton"/>
</event_variant>
<!-- right click with control key pressed-->
<event_variant class="MousePressEvent" name="RightWithCTRL">
<attribute name="EventButton" value="RightMouseButton"/>
<attribute name="Modifiers" value="ctrl"/>
</event_variant>
</config>
\endcode
There exists a standard configuration file for the most common events called GlobalConfig.xml that can be used to as a default and can be extended
by a specific definition.
\subsection ParameterDescriptionSection Parameter Description
It is also possible to store parameters in the config file. Those are stored using the param-tag, like this:
\code
<config name="example2">
<param name="property1" value="yes"/>
<param name="scrollModus" value="leftright"/>
</config>
\endcode
Within the application these properties can then be access via a mitk::PropertyList like this:
\code
// sm - state machine loaded with config file example2
mitk::PropertyList::Pointer properties = GetAttributes();
std::string prop1;
properties->GetStringProperty("property1",prop1);
\endcode
\section HowToStateMachine HowTo Write a State Machine
A state machine pattern is described in a XML file.
\subsection StateSection States
States are described using the state-tag. Each state has to have a name. Exactly one state has to be a start state in
each state machine to indicate the state in which the state machine is set when it is constructed.
So a valid, but rather useless state machine would like like this:
\code
<statemachine>
<state name="start" startstate="true"/>
</statemachine>
\endcode
Optionally a state can be assigned a special mode that influences the event distribution.
These modes are GRAB_INPUT , PREFER_INPUT and REGULAR (where REGULAR is default and does not need to be indicated).
See \ref DataInteractionTechnicalPage_DispatcherEventDistSection for a description of these modes. Use the special modes only when necessary as they prevent other DataInteractors to
receive events.
\code
<!-- example -->
<state name="someState" startstate="true" state_mode="GRAB_INPUT"/>
\endcode
\subsection TransitionSection Transitions
Transitions are part of a state and describe all possible state switches, and are therefore important for modeling an interaction scheme.
Transitions consist a part that describes the event which triggers the transition (event class and event variant) and a target which is state
to which the state machine switches after executing a transition.
An event class describes the event type (see mitk::InteractionEvent for the different classes) and the event variant is a specification thereof
and the exact description is taken from a config file. Together they determine which event can trigger this transition.
For example this state machine will switch from state A to state B when the StdMousePressPrimaryButton event (left mouse button is pressed) occurs.
\subsection EventClassSection Event Class
-The event class description supports the polymorphism of the event classes. Therefore state machine patters should be written in the most
+The event class description supports the polymorphism of the event classes. Therefore state machine patterns should be written in the most
general ways possible.
So for a given class hierarchy like this:
\dot
digraph {
node [shape=record, fontname=Helvetica, fontsize=10];
a [ label="{InteractionPositionEvent}"];
b [ label="{MousePressEvent}" ];
c [ label="MouseReleaseEvent" ];
d [ label="TouchEvent", style=dotted ];
a -> b;
a -> c;
a -> d;
}
\enddot
in the state machine pattern the mitk::InteractionPositionEvent can be declared as event class to restrict to the events which hold a position information.
The actual implementation is then given in the configuration file. In this case it allows to define events of the classes mitk::InteractionPositionEvent
itself, or mitk::MousePressEvent, mitk::MouseReleaseEvent, mitk::TouchEvent.
This has the advantage that the patterns remain the same no matter what input devices are used, and the state machine patterns can be configured
for newly added event classes as long as they match the class hierarchy (this ensures they hold the necessary properties).
\code
<statemachine>
<state name="A" startstate="true">
<transition event_class="MousePressEvent" event_variant="StdMousePressPrimaryButton" target="B"/>
<state/>
<state name="B" />
</statemachine>
\endcode
\subsection ActionSection Actions
Actions can be added to transitions and represent functions in the mitk::DataInteractor that are executed on taking a transition.
The following simple state machine will listen for left mouse clicks and execute two actions (and actually never stop).
\code
<statemachine>
<state name="start" startstate="true">
<transition event_class="MousePressEvent" event_variant="StdMousePressPrimaryButton" target="start">
<action name="addPoint"/>
<action name="countClicks"/>
</transition>
</state>
</statemachine>
\endcode
In order to tell the mitk::DataInteractor which function to execute these actions are made known to the mitk::DataInteractor using
the CONNECT_FUNCTION macro. This example assumes that there exists an ExampleInteractor which inherits from mitkDataInteractor.
This class implements the functions AddPoint and CountClicks. The actions are introduced by implementing the virtual method
ConnectActionsAndFunctions():
\code
void mitk::ExampleInteractor::ConnectActionsAndFunctions()
{
CONNECT_FUNCTION("addPoint", AddPoint);
CONNECT_FUNCTION("countClicks", CountClicks);
}
\endcode
\subsection ConditionSection Conditions
Conditions can be added to transitions and represent functions in the mitk::DataInteractor that are executed on taking a transition.
A condition is used to determine if a following action should be executed or not.
\code
<statemachine>
<state name="start" startstate="true">
<transition event_class="MousePressEvent" event_variant="StdMousePressPrimaryButton" target="start">
<condition name="checkPoint"/>
<action name="addPoint"/>
<action name="countClicks"/>
</transition>
</state>
</statemachine>
\endcode
In order to tell the mitk::DataInteractor which function to execute these conditions are made known to the mitk::DataInteractor using
the CONNECT_CONDITION macro. The ConnectActionsAndFunctions() method has to be augmented accordingly:
\code
void mitk::ExampleInteractor::ConnectActionsAndFunctions()
{
CONNECT_CONDITION("checkPoint", CheckPoint);
CONNECT_FUNCTION("addPoint", AddPoint);
CONNECT_FUNCTION("countClicks", CountClicks);
}
\endcode
\section ReferenceToIncludeFiles Integration of the pattern and configuration files
The usage of custom files slightly differs from the existing ones.
Custom pattern and config files have to be stored in the /Resources/Interactions directory of the Module that they were designed for.
When loading files from a module location into an interactor, the module has to be supplied as a parameter:
\code
m_CurrentInteractor = mitk::CustomDataInteractor::New();
m_CurrentInteractor->LoadStateMachine("CustomStateMachinePattern.xml", us::GetModuleContext()->GetModule());
m_CurrentInteractor->SetEventConfig("CustomConfig.xml", us::GetModuleContext()->GetModule());
\endcode
See \ref IncludeFiles for a description.
\section HowToDataInteractor Implementation of a new mitk::DataInteractor
DataInteractors are to inherit from mitk::DataInteractor. Their functionality is implemented in functions that follow this interface:
For Actions:
\code
bool SomeFunctionality(StateMachineAction* , InteractionEvent*);
\endcode
For Conditions:
\code
bool SomeFunctionality(const InteractionEvent*);
\endcode
Your functions are connected with actions and conditions by implementing the function ConnectActionsAndFunctions(), e.g.
\code
void mitk::ExampleInteractor::ConnectActionsAndFunctions()
{
CONNECT_CONDITION("checkPoint", CheckPoint);
CONNECT_FUNCTION("addPoint", AddPoint);
CONNECT_FUNCTION("enoughPoints", EnoughPoints);
}
\endcode
Now all that is left is to write a state machine pattern and a config file as is described in the tutorials.
To provide a useful example the mitk::PointSetDataInteractor is annotated with comments that describe the important parts for an implementation
of a mitk::DataInteractor.
This step assumes knowledge of the Interaction concept described in \ref DataInteractionPage and some background of the implementation.
Please refer to these pages before proceeding.
Now all that is left it to write a state machine pattern and a config file as is described in the tutorials.
\subsection ExampleInternalEvent Example Interactor using InternalEvent
A useful tool in creating DataInteractors is mitk::InternalEvent which allows the mitk::DataInteractor to send signals on its own.
The following will describe how to build a mitk::DataInteractor that allows to add points until a certain number of points is reached.
The number of accepted points is provided in the config file as a parameter.
So we start by writing a state machine pattern that add points until it receives an mitk::InternalEvent telling it, that enough points
have been added.
\code
<statemachine>
<state name="start" startstate="true" >
<transition event_class="MousePressEvent" event_variant="AddPointClick" target="start">
<condition name="checkPoint"/>
<action name="addPoint"/>
</transition>
<transition event_class="InternalEvent" event_variant="enoughPointsAdded" target="final">
<action name="enoughPoints"/>
</transition>
</state>
<state name="final">
<--! dead state, nothing happens any more, once we reached this -->
</state>
</statemachine>
\endcode
<b> </b>
In our config file we set the number of maximal points to 10, and define AddPointClick as a right mouse click with the
ctrl button pressed.
\code
<config>
<param name="NumberOfPoints" value="10">
<event_variant class="MousePressEvent" name="AddPointClick">
<attribute name="EventButton" value="RightMouseButton"/>
<attribute name="Modifiers" value="ctrl"/>
</event_variant>
</config>
\endcode
The implementation is described in the following. \see Step10.h \see Step10.cpp
\dontinclude Step10.h
Implementation of protected functions:
\skipline protected:
\until virtual void ConfigurationChanged();
<b> ConnectActionsAndFunctions </b> - Is inherited from mitk::InteractionStateMachine, here action strings from the xml are connected with
functions in the mitk::DataInteractor (as seen above). In our example this looks like this:
\dontinclude Step10.cpp
\skipline void mitk::ExampleInteractor::ConnectActionsAndFunctions()
\until }
<b> ConfigurationChanged </b> - Is called whenever a new configuration file is loaded (by the mitk::InteractionEventHandler super class),
this function allows to implement initialization code that depends on configuration values. In our example we want to set the limit of
allowed points:
\dontinclude Step10.cpp
\skipline void mitk::ExampleInteractor::ConfigurationChang
\until }
Next the actual functionality of the DataInteractor is implemented, by providing one function per action, following this prototype described before.
\dontinclude Step10.h
\skipline private:
\until bool CheckPoint(cons
\dontinclude Step10.cpp
\skipline bool mitk::ExampleInteractor::AddPoint(StateM
\until //-
If the conditions returns false the calling transition and the included actions will not be executed.
If a condition fails the event is considered as untreated, and will be offered to other Interactors.
\dontinclude Step10.cpp
\skipline bool mitk::ExampleInteractor::CheckPoint(
\until //end
Here we see an internal event used to signal that the point set reached the maximal number of allowed points.
The event is created and added to the Dispatchers event queue.
\dontinclude Step10.cpp
\skipline // create internal
\until positionEvent->GetSender(
\note Internal events do not need any mapping to event variants. Their signal name is equivalent with the event variant.
<b> There are also two documented classes implementing a mitk::DataInteractor and a mitk::InteractionEventObserver which can be looked at for further
understanding: </b>
\see mitk::PointSetDataInteractor
\see mitk::DisplayActionEventBroadcast
Have fun with creating your own interaction and please think about contributing it to MITK!
<b>
If you meet any difficulties during this step, don't hesitate to ask on the MITK mailing list mitk-users@lists.sourceforge.net!
People there are kind and will try to help you. </b>
\ref Step09Page "[Previous step]" \ref TutorialPage "[Main tutorial page]"
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/BuildInstructions.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/BuildInstructions.dox
index 92ddf13647..7affb69a0b 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/BuildInstructions.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/BuildInstructions.dox
@@ -1,229 +1,229 @@
/**
\page BuildInstructionsPage Build Instructions
\tableofcontents
\section BuildInstructions_Introduction Introduction
The CMake-based build system of MITK supports a "superbuild" process,
meaning that it will download, configure, and build all required third-party
libraries (except Qt) automatically. These instructions will show you how to use
the MITK superbuild.
\note This page explains explicitly how to build MITK itself. If you want to
create your own project based on MITK, the process described below is completely
automated. Please see \ref HowToNewProject.
For more advanced users, the last sections explains how to inject custom build
libraries into the superbuild process.
\section BuildInstructions_Prerequisites Prerequisites
You need:
-# <a href="https://git-scm.com/">Git</a> (there are also numerous third-party graphical
clients available). We recommend using Git, but see below for a way how to
get the current source code without using it.
-# <a href="https://cmake.org/">CMake</a> (version \minimumCMakeVersion or higher)
-# <a href="https://www.qt.io/">Qt</a> \minimumQt6Version if you plan to develop Qt-based
applications
-# If you are using <b>macOS</b> you need an XCode installation and the
- Command Line Tools as it provides the neccessary compilers and SDKs
+ Command Line Tools as it provides the necessary compilers and SDKs
To build MITK on Linux, install the following packages, e. g. with APT:
\code{.unparsed}
sudo apt install build-essential doxygen git graphviz libegl-dev libfreetype6-dev libglu1-mesa-dev libopengl-dev libssl-dev libtiff5-dev libxcomposite1 libxcursor-dev libxcb-cursor-dev libxdamage-dev libxi-dev libxkbcommon-dev libxt-dev mesa-common-dev
\endcode
For the optional and experimental (!) Python integration, install NumPy and SimpleITK v1.x, e. g.:
\code{.unparsed}
sudo apt install python3-numpy python3-pip
pip3 install SimpleITK
\endcode
\section BuildInstructions_Qt A note about Qt
As we do not provide Qt in the MITK superbuild you need to install Qt manually.
The Qt Company provides <a href="https://www.qt.io/download/">online installers
for all supported platforms</a>.
We highly recommend to install Qt to the default location of the installer as it will allow MITK to automatically find Qt without any further action needed.
Make sure to also select the following <b>required components</b>:
- Qt 5 Compatibility Module
- Qt State Machines
- Qt WebEngine
On Windows, the Qt installer offers a welcome and straight forward way to install <b>OpenSSL</b>.
You find it under the Tools node.
\section BuildInstructions_Get_Source Get a source tree
Since MITK is under active development we recommend to use Git to check out
the latest stable release from the homepage. If you decide to use the most
current nightly release, make sure to get a stable tree: Check the
<a href="https://cdash.mitk.org/index.php?project=MITK">MITK dashboard</a>
before checking out. If the build tree is not clean, you can specify an
older revision for the checkout or get a stable tar ball from
<a href="https://www.mitk.org">www.mitk.org</a>.
To clone MITK's current Git repository do:
\code
git clone https://phabricator.mitk.org/source/mitk.git MITK
\endcode
\section BuildInstructions_Build_With_CMake Build MITK with CMake
Create a new directory for the superbuild binary tree, change to it and call CMake:
In the shell (assuming your current directory is the same as the one where you
issued the git clone command):
\code
mkdir MITK-superbuild
cd MITK-superbuild
ccmake ../MITK
\endcode
If you use <b>Windows</b> or prefer to use the CMake GUI, start the CMake GUI and enter the
location of the source tree and binary tree, choose a suitable generator
and configure the project.
CMake will present you a couple of options, these are the most important ones:
- <tt><b>CMAKE_PREFIX_PATH</b></tt> The path to your Qt installation, e.g., <i>C:/Qt/5.12.9/msvc2017_64</i> or <i>/home/user/Qt/5.12.9/gcc_64</i>
- <tt><b>MITK_USE_BLUEBERRY</b></tt> Build the BlueBerry application framework
- <tt><b>MITK_USE_Boost_LIBRARIES</b></tt> If you need binary Boost libraries,
specify them here.
- <tt><b>MITK_USE_Python3</b></tt> Enables Python wrapping in MITK. This will also
configure ITK and VTK to build Python wrappers.
- <tt><b>MITK_USE_Qt6</b></tt> Build MITK code which depends on Qt 6
If you are satisfied with the configuration of your MITK superbuild, generate
the project files with CMake by pressing "Generate".
<b>Linux</b> and <b>macOS</b> users usually just enter "make" (optionally
supplying the number threads to be used for a parallel build):
\code
make -j6
\endcode
<b>Windows</b> users using Visual Studio can open the generated
<tt>MITK-superbuild.sln</tt> solution file in the <tt>MITK-superbuild</tt>
directory and start the build by building the <tt>BUILD_ALL</tt> project.
\section BuildInstructions_Customize Customize your MITK superbuild
The MITK superbuild configures MITK as well as all external libraries. The
build directories of these libraries, and of MITK itself are located inside
the <tt>MITK-superbuild</tt> directory. For example, the directory layout may
look like:
\code
MITK-superbuild
|- ep "external projects"
|-bin
|-lib
|-include
|-src
|- MITK-build
\endcode
To change the configuration of the MITK build itself, choose the MITK-build directory
as the binary directory in the CMake GUI (not the MITK-superbuild directory).
After generating the project files, build the MITK project by either issuing "make"
in the MITK-build directory (Linux, macOS), or by opening MITK-build/MITK.sln (Windows).
You may also change the configuration of any project configured via the
superbuild process. Make sure to also build the changed project and also the
projects which depend on it.
\section BuildInstructions_Running Running Applications
On Linux, just execute the application you want to run. MITK executables are
located in <tt>MITK-superbuild/MITK-build/bin</tt>
On Windows, the <tt>PATH</tt> environment variable must contain the directories
containing the third-party libraries. This is automatically done from Visual Studio.
For running the applications directly use the generated batch files in the
<tt>MITK-superbuild/MITK-build/bin</tt>.
\section BuildInstructions_Documentation Documentation
If you have the <a href="https://www.doxygen.nl/">Doxygen</a> documentation tool
installed, you get a new project (Visual Studio) or "make" target named "doc".
You can build this to generate the HTML documentation of MITK in the
Documentation/Doxygen directory of your MITK-build binary tree or in the
<tt>MITK_DOXYGEN_OUTPUT_DIR</tt> CMake variable (if specified).
\section BuildInstructions_As_Toolkit Use MITK in your own project (as a toolkit)
To use MITK in your external project, add the CMake command
<code>find_package(MITK REQUIRED)</code> to your CMakeLists.txt and make use of
the CMake macros <code>mitk_create_module()</code> and
<code>mitk_create_executable()</code> provided by MITK.
Here is a very basic example CMakeLists.txt including MITK as a project:
\code
cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
project(MyProject)
find_package(MITK 2023.12 REQUIRED)
add_executable(MyApp main.cpp)
target_link_libraries(MyApp MitkCore)
\endcode
with the main.cpp being
\code
#include <iostream>
#include <mitkLog.h>
int main()
{
MITK_INFO << "Hello world!";
return 0;
}
\endcode
\section BuildInstructions_Advanced_Customization Superbuild customization
You can inject pre-build third-party libraries into the MITK superbuild by
setting certain CMake variables before the first configure step. MITK will
then use these third-party libraries instead of downloading and building them
by itself. Note that you must take care of configuring those libraries with all options
MITK requires.
The variables listed below are provided for injecting third-party libraries.
Their occurrence in the CMake GUI or in ccmake may depend on specific
MITK_USE_* options set to ON. You may also use the variable names below without
the <tt>EXTERNAL_</tt> prefix, for example when providing their values on a
command line call to CMake.
- <tt><b>EXTERNAL_BOOST_ROOT</b></tt> Set this variable to your custom Boost
installation
- <tt><b>EXTERNAL_CTK_DIR</b></tt> Set this variable to your CTK binary tree
(the directory containing the CTKConfig.cmake file)
- <tt><b>EXTERNAL_CableSwig_DIR</b></tt> Set this variable to your CableSwig
binary tree for Python wrapping (the directory containing the
CableSwigConfig.cmake file)
- <tt><b>EXTERNAL_DCMTK_DIR</b></tt> Set this variable to your DCMTK binary
tree (the directory containing the DCMTKConfig.cmake file)
- <tt><b>EXTERNAL_GDCM_DIR</b></tt> Set this variable to your GDCM binary
tree (the directory containing the GDCMConfig.cmake file)
- <tt><b>EXTERNAL_ITK_DIR</b></tt> Set this variable to your ITK binary tree
(the directory containing the ITKConfig.cmake file)
- <tt><b>EXTERNAL_VTK_DIR</b></tt> Set this variable to your VTK binary tree
(the directory containing the VTKConfig.cmake file)
To set CMake options before the first configure step is invoked, supply them
on the command line, i.e.
\code
ccmake -DITK_DIR:PATH=/opt/ITK-release ../MITK
\endcode
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/ThirdPartyLibs.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/ThirdPartyLibs.dox
index 599bd44375..1a2190b2e0 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/ThirdPartyLibs.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/ThirdPartyLibs.dox
@@ -1,99 +1,95 @@
/**
\page thirdpartylibs Third-party libraries
The following third-party libraries can be used with MITK by default and can, in part, be automatically downloaded during superbuild.
\par ACVD
https://www.creatis.insa-lyon.fr/~valette/public/project/acvd/
\par ANN
https://www.cs.umd.edu/~mount/ANN/
\par Boost
https://www.boost.org/
\par C++ REST SDK
https://github.com/Microsoft/cpprestsdk/
\par CppUnit
https://sourceforge.net/projects/cppunit/
\par CTK
https://commontk.org/
\par DCMTK
https://dicom.offis.de/dcmtk
-\par Eigen
-
-https://eigen.tuxfamily.org/index.php?title=Main_Page
-
\par GDCM
https://gdcm.sourceforge.net/
\par HDF5
https://support.hdfgroup.org/HDF5/
\par ITK
https://itk.org/
\par JSON for Modern C++
https://github.com/nlohmann/json
\par lz4
https://github.com/lz4/lz4
\par MatchPoint
https://www.dkfz.de/en/sidt/projects/MatchPoint/info.html
\par PCRE
https://www.pcre.org/
\par POCO
https://pocoproject.org/
\par Python
https://www.python.org/
\par Qt
https://www.qt.io/
\par Qwt
https://qwt.sourceforge.io/
\par SWIG
https://www.swig.org/
\par TinyXML-2
https://www.grinninglizard.com/tinyxml2/
\par VTK
https://vtk.org/
\par zlib
https://zlib.net/
For copyright information on any of the above toolkits see the corresponding home page or the corresponding source folder.
*/
diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/Starting.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/Starting.dox
index 4bc71644b8..1f584e1fa5 100644
--- a/Documentation/Doxygen/3-DeveloperManual/Starting/Starting.dox
+++ b/Documentation/Doxygen/3-DeveloperManual/Starting/Starting.dox
@@ -1,38 +1,38 @@
/**
\page StartingDevelopment Starting your MITK Development
This introduction will acquaint you with the most important workflows to get you started with your MITK development.
First, \ref Architecture will explain the differences between the application and the toolkit.
-\ref SettingUpMITK will get you started with a working environment for MITK development. \ref GettingToKnowMITK will walk you trough the folder structure, the module system, and plugin system. This chapter also contains an extensive tutorial on how to work with MITK.
+\ref SettingUpMITK will get you started with a working environment for MITK development. \ref GettingToKnowMITK will walk you through the folder structure, the module system, and plugin system. This chapter also contains an extensive tutorial on how to work with MITK.
The \ref FirstSteps section will then show you how to extend MITK for your own project.
<ul>
<li> \subpage Architecture </li>
<li> \subpage SettingUpMITK </li>
<ul>
<li> \ref SupportedPlatformsPage </li>
<li> \ref BuildInstructionsPage </li>
<li> \ref thirdpartylibs </li>
<li> \ref HowToNewProject </li>
</ul>
<li> \subpage GettingToKnowMITK </li>
<ul>
<li> \ref DirectoryStructurePage </li>
<li> \ref TutorialPage</li>
<li> \ref CMAKE_FAQ </li>
<li> \ref StyleGuideAndNotesPage </li>
<li> \ref DocumentationGuide </li>
<li> \ref CodingPage </li>
<li> \ref KnownProblemsPage </li>
</ul>
<li> \subpage FirstSteps </li>
<ul>
<li> \ref NewViewPage </li>
<li> \ref NewModulePage</li>
<li> \ref CMAKE_FAQ </li>
</ul>
<li> \subpage AboutTestingPage </li>
</ul>
*/
diff --git a/Documentation/Doxygen/4-API/Groups.dox b/Documentation/Doxygen/4-API/Groups.dox
index e71fddd065..1437fbef52 100644
--- a/Documentation/Doxygen/4-API/Groups.dox
+++ b/Documentation/Doxygen/4-API/Groups.dox
@@ -1,35 +1,34 @@
/**
\defgroup ToolManagerEtAl Classes related to the Segmentation plugin
- \brief A couple of classes related to the Segmentation plugin. See also
- \ref QmitkSegmentationTechnicalPage
+ \brief A couple of classes related to the Segmentation plugin.
*/
/**
\defgroup MITKModules MITK Modules
\brief This group includes all MITK Modules.
*/
/**
\defgroup BlueBerryPlugins BlueBerry Plugins
\brief This group includes all BlueBerry Plugins.
\defgroup MITKPlugins MITK Plugins
\brief This group includes all MITK Plugins.
*/
/**
\defgroup MITKExamplePlugins MITK Example Plugins
\brief This group includes all MITK example plugins demonstrating specific framework features.
*/
/**
\defgroup MITKTestingAPI MITK Testing
\brief In this group, the API documentation of methods useful for testing is collected. The more general concepts of testing in MITK are described on the \ref GeneralTests page.
*/
diff --git a/Documentation/doxygen.conf.in b/Documentation/doxygen.conf.in
index 5eca799ed3..96a15a570d 100644
--- a/Documentation/doxygen.conf.in
+++ b/Documentation/doxygen.conf.in
@@ -1,2838 +1,2834 @@
# Doxyfile 1.9.6
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
#
# All text after a double hash (##) is considered a comment and is placed in
# front of the TAG it is preceding.
#
# All text after a single hash (#) is considered a comment and will be ignored.
# The format is:
# TAG = value [value, ...]
# For lists, items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (\" \").
#
# Note:
#
# Use doxygen to compare the used configuration file with the template
# configuration file:
# doxygen -x [configFile]
# Use doxygen to compare the used configuration file with the template
# configuration file without replacing the environment variables or CMake type
# replacement variables:
# doxygen -x_noenv [configFile]
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
# This tag specifies the encoding used for all characters in the configuration
# file that follow. The default is UTF-8 which is also the encoding used for all
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
# iconv built into libc) for the transcoding. See
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
# double-quotes, unless you are using Doxywizard) that should identify the
# project for which the documentation is generated. This name is used in the
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = "Medical Imaging Interaction Toolkit"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = @MITK_VERSION_STRING@
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF = "Medical Imaging Interaction Toolkit"
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
# the logo to the output directory.
PROJECT_LOGO =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = @MITK_DOXYGEN_OUTPUT_DIR@
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# sub-directories (in 2 levels) under the output directory of each output format
# and will distribute the generated files over these directories. Enabling this
# option can be useful when feeding doxygen a huge amount of source files, where
# putting all generated files in the same directory would otherwise causes
# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to
# control the number of sub-directories.
# The default value is: NO.
CREATE_SUBDIRS = NO
# Controls the number of sub-directories that will be created when
# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every
# level increment doubles the number of directories, resulting in 4096
# directories at level 8 which is the default and also the maximum value. The
# sub-directories are organized in 2 levels, the first level always has a fixed
# number of 16 directories.
# Minimum value: 0, maximum value: 8, default value: 8.
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
CREATE_SUBDIRS_LEVEL = 8
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
# characters to appear in the names of generated files. If set to NO, non-ASCII
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
# U+3044.
# The default value is: NO.
ALLOW_UNICODE_NAMES = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with
# English messages), Korean, Korean-en (Korean with English messages), Latvian,
# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,
# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,
# Swedish, Turkish, Ukrainian and Vietnamese.
# The default value is: English.
OUTPUT_LANGUAGE = English
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this.
# The default value is: YES.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
# description of a member or function before the detailed description
#
# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
# The default value is: YES.
REPEAT_BRIEF = YES
# This tag implements a quasi-intelligent brief description abbreviator that is
# used to form the text in various listings. Each string in this list, if found
# as the leading text of the brief description, will be stripped from the text
# and the result, after processing the whole list, is used as the annotated
# text. Otherwise, the brief description is used as-is. If left blank, the
# following values are used ($name is automatically replaced with the name of
# the entity):The $name class, The $name widget, The $name file, is, provides,
# specifies, contains, represents, a, an and the.
ABBREVIATE_BRIEF =
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# doxygen will generate a detailed section even if there is only a brief
# description.
# The default value is: NO.
ALWAYS_DETAILED_SEC = NO
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
# inherited members of a class in the documentation of that class as if those
# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
# The default value is: NO.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
# before files name in the file list and in the header files. If set to NO the
# shortest path that makes the file name unique will be used
# The default value is: YES.
FULL_PATH_NAMES = NO
# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
# Stripping is only done if one of the specified strings matches the left-hand
# part of the path. The tag can be used to show relative paths in the file list.
# If left blank the directory from which doxygen is run is used as the path to
# strip.
#
# Note that you can specify absolute paths here, but also relative paths, which
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH =
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
# header file to include in order to use a class. If left blank only the name of
# the header file containing the class definition is used. Otherwise one should
# specify the list of include paths that are normally passed to the compiler
# using the -I flag.
STRIP_FROM_INC_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
# less readable) file names. This can be useful is your file systems doesn't
# support long names like on DOS, Mac, or CD-ROM.
# The default value is: NO.
SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
# first line (until the first dot) of a Javadoc-style comment as the brief
# description. If set to NO, the Javadoc-style will behave just like regular Qt-
# style comments (thus requiring an explicit @brief command for a brief
# description.)
# The default value is: NO.
JAVADOC_AUTOBRIEF = NO
# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
# such as
# /***************
# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
# Javadoc-style will behave just like regular comments and it will not be
# interpreted by doxygen.
# The default value is: NO.
JAVADOC_BANNER = NO
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
# line (until the first dot) of a Qt-style comment as the brief description. If
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
# requiring an explicit \brief command for a brief description.)
# The default value is: NO.
QT_AUTOBRIEF = NO
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
# a brief description. This used to be the default behavior. The new default is
# to treat a multi-line C++ comment block as a detailed description. Set this
# tag to YES if you prefer the old behavior instead.
#
# Note that setting this tag to YES also means that rational rose comments are
# not recognized any more.
# The default value is: NO.
MULTILINE_CPP_IS_BRIEF = NO
# By default Python docstrings are displayed as preformatted text and doxygen's
# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
# doxygen's special commands can be used and the contents of the docstring
# documentation blocks is shown as doxygen documentation.
# The default value is: YES.
PYTHON_DOCSTRING = YES
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.
# The default value is: YES.
INHERIT_DOCS = YES
# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
# page for each member. If set to NO, the documentation of a member will be part
# of the file/class/namespace that contains it.
# The default value is: NO.
SEPARATE_MEMBER_PAGES = NO
# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
# uses this value to replace tabs by spaces in code fragments.
# Minimum value: 1, maximum value: 16, default value: 4.
TAB_SIZE = 8
# This tag can be used to specify a number of aliases that act as commands in
# the documentation. An alias has the form:
# name=value
# For example adding
# "sideeffect=@par Side Effects:^^"
# will allow you to put the command \sideeffect (or @sideeffect) in the
# documentation, which will result in a user-defined paragraph with heading
# "Side Effects:". Note that you cannot put \n's in the value part of an alias
# to insert newlines (in the resulting output). You can put ^^ in the value part
# of an alias to insert a newline as if a physical newline was in the original
# file. When you need a literal { or } or , in the value part of an alias you
# have to escape them by means of a backslash (\), this can lead to conflicts
# with the commands \{ and \} for these it is advised to use the version @{ and
# @} or use a double escape (\\{ and \\})
ALIASES = "FIXME=\par Fix Me's:\n" \
"BlueBerry=\if BLUEBERRY" \
"endBlueBerry=\endif" \
"bundlemainpage{1}=\page \1" \
"embmainpage{1}=\page \1" \
"github{2}=<a href=\"https://github.com/MITK/MITK/blob/master/\1\">\2</a>" \
"deprecatedSince{1}=\xrefitem deprecatedSince\1 \"\" \"Functions deprecated as of \1\" \deprecated (as of \1)" \
"minimumCMakeVersion=@MITK_CMAKE_MINIMUM_REQUIRED_VERSION@" \
"minimumQt6Version=@MITK_QT6_MINIMUM_VERSION@" \
"imageMacro{3}=\image html \1 \2 \n \image latex \1 \2 width=\3cm" \
"developersguidemainpage{1}=\page \1" \
"usersguidemainpage{1}=\page \1" \
"nondependentPluginLink{3}= \ref \1 \"\3\""
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
# members will be omitted, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_FOR_C = NO
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
# Python sources only. Doxygen will then generate output that is more tailored
# for that language. For instance, namespaces will be presented as packages,
# qualified scopes will look different, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
# sources. Doxygen will then generate output that is tailored for Fortran.
# The default value is: NO.
OPTIMIZE_FOR_FORTRAN = NO
# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
# sources. Doxygen will then generate output that is tailored for VHDL.
# The default value is: NO.
OPTIMIZE_OUTPUT_VHDL = NO
# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
# sources only. Doxygen will then generate output that is more tailored for that
# language. For instance, namespaces will be presented as modules, types will be
# separated into more groups, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_SLICE = NO
# Doxygen selects the parser to use depending on the extension of the files it
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
# default for Fortran type files). For instance to make doxygen treat .inc files
# as Fortran files (default is PHP), and .f files as C (default is Fortran),
# use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
# the files are not read by doxygen. When specifying no_extension you should add
# * to the FILE_PATTERNS.
#
# Note see also the list of default file extension mappings.
-EXTENSION_MAPPING = cmake=c++
+EXTENSION_MAPPING =
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
# documentation. See https://daringfireball.net/projects/markdown/ for details.
# The output of markdown processing is further processed by doxygen, so you can
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
# case of backward compatibilities issues.
# The default value is: YES.
MARKDOWN_SUPPORT = YES
# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
# Minimum value: 0, maximum value: 99, default value: 5.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 5
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or
# globally by setting AUTOLINK_SUPPORT to NO.
# The default value is: YES.
AUTOLINK_SUPPORT = YES
# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
# to include (a tag file for) the STL sources as input, then you should set this
# tag to YES in order to let doxygen match functions declarations and
# definitions whose arguments contain STL classes (e.g. func(std::string);
# versus func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
# The default value is: NO.
BUILTIN_STL_SUPPORT = YES
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
# The default value is: NO.
CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
# will parse them like normal C++ but will assume all classes use public instead
# of private inheritance when no explicit protection keyword is present.
# The default value is: NO.
SIP_SUPPORT = NO
# For Microsoft's IDL there are propget and propput attributes to indicate
# getter and setter methods for a property. Setting this option to YES will make
# doxygen to replace the get and set methods by a property in the documentation.
# This will only work if the methods are indeed getting or setting a simple
# type. If this is not the case, or you want to show the methods anyway, you
# should set this option to NO.
# The default value is: YES.
IDL_PROPERTY_SUPPORT = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
# The default value is: NO.
DISTRIBUTE_GROUP_DOC = YES
# If one adds a struct or class to a group and this option is enabled, then also
# any nested class or struct is added to the same group. By default this option
# is disabled and one has to add nested compounds explicitly via \ingroup.
# The default value is: NO.
GROUP_NESTED_COMPOUNDS = NO
# Set the SUBGROUPING tag to YES to allow class member groups of the same type
# (for instance a group of public functions) to be put as a subgroup of that
# type (e.g. under the Public Functions section). Set it to NO to prevent
# subgrouping. Alternatively, this can be done per class using the
# \nosubgrouping command.
# The default value is: YES.
SUBGROUPING = YES
# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
# are shown inside the group in which they are included (e.g. using \ingroup)
# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
# and RTF).
#
# Note that this feature does not work in combination with
# SEPARATE_MEMBER_PAGES.
# The default value is: NO.
INLINE_GROUPED_CLASSES = NO
# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
# with only public data fields or simple typedef fields will be shown inline in
# the documentation of the scope in which they are defined (i.e. file,
# namespace, or group documentation), provided this scope is documented. If set
# to NO, structs, classes, and unions are shown on a separate page (for HTML and
# Man pages) or section (for LaTeX and RTF).
# The default value is: NO.
INLINE_SIMPLE_STRUCTS = NO
# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
# enum is documented as struct, union, or enum with the name of the typedef. So
# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
# with name TypeT. When disabled the typedef will appear as a member of a file,
# namespace, or class. And the struct will be named TypeS. This can typically be
# useful for C code in case the coding convention dictates that all compound
# types are typedef'ed and only the typedef is referenced, never the tag name.
# The default value is: NO.
TYPEDEF_HIDES_STRUCT = NO
# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
# cache is used to resolve symbols given their name and scope. Since this can be
# an expensive process and often the same symbol appears multiple times in the
# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
# doxygen will become slower. If the cache is too large, memory is wasted. The
# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
# symbols. At the end of a run doxygen will report the cache usage and suggest
# the optimal cache size from a speed point of view.
# Minimum value: 0, maximum value: 9, default value: 0.
LOOKUP_CACHE_SIZE = 0
# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use
# during processing. When set to 0 doxygen will based this on the number of
# cores available in the system. You can set it explicitly to a value larger
# than 0 to get more control over the balance between CPU load and processing
# speed. At this moment only the input processing can be done using multiple
# threads. Since this is still an experimental feature the default is set to 1,
# which effectively disables parallel processing. Please report any issues you
# encounter. Generating dot graphs in parallel is controlled by the
# DOT_NUM_THREADS setting.
# Minimum value: 0, maximum value: 32, default value: 1.
NUM_PROC_THREADS = 1
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
# documentation are documented, even if no documentation was available. Private
# class members and static file members will be hidden unless the
# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
# Note: This will also disable the warnings about undocumented members that are
# normally produced when WARNINGS is set to YES.
# The default value is: NO.
EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
# be included in the documentation.
# The default value is: NO.
EXTRACT_PRIVATE = NO
# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
# methods of a class will be included in the documentation.
# The default value is: NO.
EXTRACT_PRIV_VIRTUAL = NO
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
# The default value is: NO.
EXTRACT_PACKAGE = NO
# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
# included in the documentation.
# The default value is: NO.
EXTRACT_STATIC = YES
# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
# locally in source files will be included in the documentation. If set to NO,
# only classes defined in header files are included. Does not have any effect
# for Java sources.
# The default value is: YES.
EXTRACT_LOCAL_CLASSES = @MITK_DOXYGEN_INTERNAL_DOCS@
# This flag is only useful for Objective-C code. If set to YES, local methods,
# which are defined in the implementation section but not in the interface are
# included in the documentation. If set to NO, only methods in the interface are
# included.
# The default value is: NO.
EXTRACT_LOCAL_METHODS = NO
# If this flag is set to YES, the members of anonymous namespaces will be
# extracted and appear in the documentation as a namespace called
# 'anonymous_namespace{file}', where file will be replaced with the base name of
# the file that contains the anonymous namespace. By default anonymous namespace
# are hidden.
# The default value is: NO.
EXTRACT_ANON_NSPACES = NO
# If this flag is set to YES, the name of an unnamed parameter in a declaration
# will be determined by the corresponding definition. By default unnamed
# parameters remain unnamed in the output.
# The default value is: YES.
RESOLVE_UNNAMED_PARAMS = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
# members will be included in the various overviews, but no documentation
# section is generated. This option has no effect if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set
# to NO, these classes will be included in the various overviews. This option
# will also hide undocumented C++ concepts if enabled. This option has no effect
# if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
# declarations. If set to NO, these declarations will be included in the
# documentation.
# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = @MITK_DOXYGEN_HIDE_FRIEND_COMPOUNDS@
# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
# documentation blocks found inside the body of a function. If set to NO, these
# blocks will be appended to the function's detailed documentation block.
# The default value is: NO.
HIDE_IN_BODY_DOCS = NO
# The INTERNAL_DOCS tag determines if documentation that is typed after a
# \internal command is included. If the tag is set to NO then the documentation
# will be excluded. Set it to YES to include the internal documentation.
# The default value is: NO.
INTERNAL_DOCS = @MITK_DOXYGEN_INTERNAL_DOCS@
# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
# able to match the capabilities of the underlying filesystem. In case the
# filesystem is case sensitive (i.e. it supports files in the same directory
# whose names only differ in casing), the option must be set to YES to properly
# deal with such files in case they appear in the input. For filesystems that
# are not case sensitive the option should be set to NO to properly deal with
# output files written for symbols that only differ in casing, such as for two
# classes, one named CLASS and the other named Class, and to also support
# references to files without having to specify the exact matching casing. On
# Windows (including Cygwin) and MacOS, users should typically set this option
# to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES.
# Possible values are: SYSTEM, NO and YES.
# The default value is: SYSTEM.
CASE_SENSE_NAMES = YES
# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
# their full class and namespace scopes in the documentation. If set to YES, the
# scope will be hidden.
# The default value is: NO.
HIDE_SCOPE_NAMES = NO
# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
# append additional text to a page's title, such as Class Reference. If set to
# YES the compound reference will be hidden.
# The default value is: NO.
HIDE_COMPOUND_REFERENCE= NO
# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
# will show which file needs to be included to use the class.
# The default value is: YES.
SHOW_HEADERFILE = YES
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
# the files that are included by a file in the documentation of that file.
# The default value is: YES.
SHOW_INCLUDE_FILES = YES
# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
# grouped member an include statement to the documentation, telling the reader
# which file to include in order to use the member.
# The default value is: NO.
SHOW_GROUPED_MEMB_INC = NO
# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
# files with double quotes in the documentation rather than with sharp brackets.
# The default value is: NO.
FORCE_LOCAL_INCLUDES = NO
# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
# documentation for inline members.
# The default value is: YES.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
# (detailed) documentation of file and class members alphabetically by member
# name. If set to NO, the members will appear in declaration order.
# The default value is: YES.
SORT_MEMBER_DOCS = YES
# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
# descriptions of file, namespace and class members alphabetically by member
# name. If set to NO, the members will appear in declaration order. Note that
# this will also influence the order of the classes in the class list.
# The default value is: NO.
SORT_BRIEF_DOCS = NO
# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
# (brief and detailed) documentation of class members so that constructors and
# destructors are listed first. If set to NO the constructors will appear in the
# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
# member documentation.
# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
# detailed member documentation.
# The default value is: NO.
SORT_MEMBERS_CTORS_1ST = NO
# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
# of group names into alphabetical order. If set to NO the group names will
# appear in their defined order.
# The default value is: NO.
SORT_GROUP_NAMES = NO
# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
# fully-qualified names, including namespaces. If set to NO, the class list will
# be sorted only by class name, not including the namespace part.
# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
# Note: This option applies only to the class list, not to the alphabetical
# list.
# The default value is: NO.
SORT_BY_SCOPE_NAME = YES
# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
# type resolution of all parameters of a function it will reject a match between
# the prototype and the implementation of a member function even if there is
# only one candidate or it is obvious which candidate to choose by doing a
# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
# accept a match between prototype and implementation in such cases.
# The default value is: NO.
STRICT_PROTO_MATCHING = NO
# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
# list. This list is created by putting \todo commands in the documentation.
# The default value is: YES.
GENERATE_TODOLIST = @MITK_DOXYGEN_GENERATE_TODOLIST@
# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
# list. This list is created by putting \test commands in the documentation.
# The default value is: YES.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
# list. This list is created by putting \bug commands in the documentation.
# The default value is: YES.
GENERATE_BUGLIST = @MITK_DOXYGEN_GENERATE_BUGLIST@
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
# the deprecated list. This list is created by putting \deprecated commands in
# the documentation.
# The default value is: YES.
GENERATE_DEPRECATEDLIST= @MITK_DOXYGEN_GENERATE_DEPRECATEDLIST@
# The ENABLED_SECTIONS tag can be used to enable conditional documentation
# sections, marked by \if <section_label> ... \endif and \cond <section_label>
# ... \endcond blocks.
ENABLED_SECTIONS = @MITK_DOXYGEN_ENABLED_SECTIONS@
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
# initial value of a variable or macro / define can have for it to appear in the
# documentation. If the initializer consists of more lines than specified here
# it will be hidden. Use a value of 0 to hide initializers completely. The
# appearance of the value of individual variables and macros / defines can be
# controlled using \showinitializer or \hideinitializer command in the
# documentation regardless of this setting.
# Minimum value: 0, maximum value: 10000, default value: 30.
MAX_INITIALIZER_LINES = 0
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
# the bottom of the documentation of classes and structs. If set to YES, the
# list will mention the files that were used to generate the documentation.
# The default value is: YES.
SHOW_USED_FILES = YES
# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
# will remove the Files entry from the Quick Index and from the Folder Tree View
# (if specified).
# The default value is: YES.
SHOW_FILES = YES
# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
# page. This will remove the Namespaces entry from the Quick Index and from the
# Folder Tree View (if specified).
# The default value is: YES.
SHOW_NAMESPACES = YES
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from
# the version control system). Doxygen will invoke the program by executing (via
# popen()) the command command input-file, where command is the value of the
# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
# by doxygen. Whatever the program writes to standard output is used as the file
# version. For an example see the documentation.
FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
# by doxygen. The layout file controls the global structure of the generated
# output files in an output format independent way. To create the layout file
# that represents doxygen's defaults, run doxygen with the -l option. You can
# optionally specify a file name after the option, if omitted DoxygenLayout.xml
# will be used as the name of the layout file. See also section "Changing the
# layout of pages" for information.
#
# Note that if you run doxygen from a directory containing a file called
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
# tag is left empty.
LAYOUT_FILE = @MITK_SOURCE_DIR@/Documentation/MITKDoxygenLayout.xml
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib
# extension is automatically appended if omitted. This requires the bibtex tool
# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references.
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated to
# standard output by doxygen. If QUIET is set to YES this implies that the
# messages are off.
# The default value is: NO.
QUIET = YES
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
# this implies that the warnings are on.
#
# Tip: Turn warnings on while writing the documentation.
# The default value is: YES.
WARNINGS = YES
# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: YES.
WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as documenting some parameters in
# a documented function twice, or documenting parameters that don't exist or
# using markup commands wrongly.
# The default value is: YES.
WARN_IF_DOC_ERROR = YES
# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
# function parameter documentation. If set to NO, doxygen will accept that some
# parameters have no documentation without warning.
# The default value is: YES.
WARN_IF_INCOMPLETE_DOC = YES
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return
# value. If set to NO, doxygen will only warn about wrong parameter
# documentation, but not about the absence of documentation. If EXTRACT_ALL is
# set to YES then this flag will automatically be disabled. See also
# WARN_IF_INCOMPLETE_DOC
# The default value is: NO.
WARN_NO_PARAMDOC = NO
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
# undocumented enumeration values. If set to NO, doxygen will accept
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: NO.
WARN_IF_UNDOC_ENUM_VAL = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
# at the end of the doxygen process doxygen will return with a non-zero status.
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
# The default value is: NO.
WARN_AS_ERROR = NO
# The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which
# will be replaced by the file and line number from which the warning originated
# and the warning text. Optionally the format may contain $version, which will
# be replaced by the version of the file (if it could be obtained via
# FILE_VERSION_FILTER)
# See also: WARN_LINE_FORMAT
# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
# In the $text part of the WARN_FORMAT command it is possible that a reference
# to a more specific place is given. To make it easier to jump to this place
# (outside of doxygen) the user can define a custom "cut" / "paste" string.
# Example:
# WARN_LINE_FORMAT = "'vi $file +$line'"
# See also: WARN_FORMAT
# The default value is: at line $line of file $file.
WARN_LINE_FORMAT = "at line $line of file $file"
# The WARN_LOGFILE tag can be used to specify a file to which warning and error
# messages should be written. If left blank the output is written to standard
# error (stderr). In case the file specified cannot be opened for writing the
# warning and error messages are written to standard error. When as file - is
# specified the warning and error messages are written to standard output
# (stdout).
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag is used to specify the files and/or directories that contain
# documented source files. You may enter file names like myfile.cpp or
# directories like /usr/src/myproject. Separate the files or directories with
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = "@MITK_SOURCE_DIR@" \
"@MITK_BINARY_DIR@" \
@MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS@
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see:
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# See also: INPUT_FILE_ENCODING
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
# This tag can be used to specify the character encoding of the source files
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
# character encoding on a per file pattern basis. Doxygen will compare the file
# name with each pattern and apply the encoding instead of the default
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
# "INPUT_ENCODING" for further information on supported encodings.
INPUT_FILE_ENCODING =
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen.
#
# Note the list of default checked file patterns might differ from the list of
# default file extension mappings.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.h \
*.dox \
*.md
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
# The default value is: NO.
RECURSIVE = YES
# The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
#
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE = "@MITK_SOURCE_DIR@/Utilities/qtsingleapplication/" \
"@MITK_SOURCE_DIR@/Modules/CppMicroServices/core/doc/snippets/" \
"@MITK_SOURCE_DIR@/Modules/CppMicroServices/core/doc/doxygen/standalone/" \
"@MITK_SOURCE_DIR@/Modules/CppMicroServices/core/test/" \
"@MITK_SOURCE_DIR@/Modules/CppMicroServices/core/examples/" \
"@MITK_SOURCE_DIR@/Modules/CppMicroServices/core/src/util/jsoncpp.cpp" \
"@MITK_SOURCE_DIR@/Modules/CppMicroServices/third_party" \
- "@MITK_SOURCE_DIR@/CMake/PackageDepends" \
- "@MITK_SOURCE_DIR@/CMakeExternals" \
"@MITK_SOURCE_DIR@/Licenses" \
"@MITK_BINARY_DIR@/Documentation/Doxygen" \
"@MITK_BINARY_DIR@/bin/" \
"@MITK_BINARY_DIR@/PT/" \
"@MITK_BINARY_DIR@/GP/" \
"@MITK_BINARY_DIR@/_CPack_Packages/" \
@MITK_DOXYGEN_ADDITIONAL_EXCLUDE_DIRS@
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
# The default value is: NO.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS = README* \
moc_* \
ui_* \
qrc_* \
wrap_* \
Register* \
- */files.cmake \
*/.git/* \
*_p.h \
*Private.* \
*/Internal/* \
*/internal/* \
*/Snippets/* \
*/snippets/* \
*/testing/* \
*/Testing/* \
*/test/* \
*/resource/* \
- "@MITK_BINARY_DIR@/*.cmake" \
@MITK_DOXYGEN_EXCLUDE_PATTERNS@
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# ANamespace::AClass, ANamespace::*Test
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS =
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
# command).
EXAMPLE_PATH = "@MITK_SOURCE_DIR@/Examples/" \
"@MITK_SOURCE_DIR@/Examples/Tutorial/" \
"@MITK_SOURCE_DIR@/Examples/Plugins/" \
"@MITK_SOURCE_DIR@/Examples/QtFreeRender/" \
"@MITK_SOURCE_DIR@/Modules/Core/" \
"@MITK_SOURCE_DIR@/Modules/CppMicroServices/core/doc/snippets/" \
"@MITK_SOURCE_DIR@/Modules/CppMicroServices/core/examples/" \
"@MITK_SOURCE_DIR@/Modules/CppMicroServices/" \
"@MITK_SOURCE_DIR@/Plugins/org.mitk.gui.common/src/" \
@MITK_DOXYGEN_ADDITIONAL_EXAMPLE_PATHS@
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank all
# files are included.
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude commands
# irrespective of the value of the RECURSIVE tag.
# The default value is: NO.
EXAMPLE_RECURSIVE = YES
# The IMAGE_PATH tag can be used to specify one or more files or directories
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH = "@MITK_SOURCE_DIR@/Documentation/Doxygen/" \
"@MITK_SOURCE_DIR@" \
@MITK_DOXYGEN_ADDITIONAL_IMAGE_PATHS@
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command:
#
# <filter> <input-file>
#
# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
# name of an input file. Doxygen will then use the output that the filter
# program writes to standard output. If FILTER_PATTERNS is specified, this tag
# will be ignored.
#
# Note that the filter must not add or remove lines; it is applied before the
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
#
# Note that doxygen will use the data processed and written to standard output
# for further processing, therefore nothing else, like debug statements or used
# commands (so in case of a Windows batch file always use @echo OFF), should be
# written to standard output.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the
# filter if there is a match. The filters are a list of the form: pattern=filter
# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
# patterns match the file name, INPUT_FILTER is applied.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
-FILTER_PATTERNS = *.cmake=@CMakeDoxygenFilter_EXECUTABLE@
+FILTER_PATTERNS =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will also be used to filter the input files that are used for
# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
# The default value is: NO.
FILTER_SOURCE_FILES = NO
# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
# it is also possible to disable source filtering for a specific pattern using
# *.ext= (so without naming a filter).
# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
FILTER_SOURCE_PATTERNS =
# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
# is part of the input, its contents will be placed on the main page
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE =
# The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common
# extension is to allow longer lines before the automatic comment starts. The
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
# be processed before the automatic comment starts.
# Minimum value: 7, maximum value: 10000, default value: 72.
FORTRAN_COMMENT_AFTER = 72
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
# generated. Documented entities will be cross-referenced with these sources.
#
# Note: To get rid of all source code in the generated output, make sure that
# also VERBATIM_HEADERS is set to NO.
# The default value is: NO.
SOURCE_BROWSER = YES
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
# classes and enums directly into the documentation.
# The default value is: NO.
INLINE_SOURCES = NO
# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
# special comment blocks from generated source code fragments. Normal C, C++ and
# Fortran comments will always remain visible.
# The default value is: YES.
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
# entity all documented functions referencing it will be listed.
# The default value is: NO.
REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES then for each documented function
# all documented entities called/used by that function will be listed.
# The default value is: NO.
REFERENCES_RELATION = YES
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
# to YES then the hyperlinks from functions in REFERENCES_RELATION and
# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
# link to the documentation.
# The default value is: YES.
REFERENCES_LINK_SOURCE = YES
# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
# source code will show a tooltip with additional information such as prototype,
# brief description and links to the definition and documentation. Since this
# will make the HTML file larger and loading of large files a bit slower, you
# can opt to disable this feature.
# The default value is: YES.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
SOURCE_TOOLTIPS = YES
# If the USE_HTAGS tag is set to YES then the references to source code will
# point to the HTML generated by the htags(1) tool instead of doxygen built-in
# source browser. The htags tool is part of GNU's global source tagging system
# (see https://www.gnu.org/software/global/global.html). You will need version
# 4.8.6 or higher.
#
# To use it do the following:
# - Install the latest version of global
# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
# - Make sure the INPUT points to the root of the source tree
# - Run doxygen as normal
#
# Doxygen will invoke htags (and that will in turn invoke gtags), so these
# tools must be available from the command line (i.e. in the search path).
#
# The result: instead of the source browser generated by doxygen, the links to
# source code will now point to the output of htags.
# The default value is: NO.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
USE_HTAGS = NO
# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
# verbatim copy of the header file for each class for which an include is
# specified. Set to NO to disable this.
# See also: Section \class.
# The default value is: YES.
VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
# clang parser (see:
# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
# performance. This can be particularly helpful with template rich C++ code for
# which doxygen's built-in parser lacks the necessary type information.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
# tag is set to YES then doxygen will add the directory of each input to the
# include path.
# The default value is: YES.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_ADD_INC_PATHS = YES
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
# If clang assisted parsing is enabled you can provide the clang parser with the
# path to the directory containing a file called compile_commands.json. This
# file is the compilation database (see:
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
# options used when the source files were built. This is equivalent to
# specifying the -p option to a clang tool, such as clang-check. These options
# will then be passed to the parser. Any options specified with CLANG_OPTIONS
# will be added as well.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
CLANG_DATABASE_PATH =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
# compounds will be generated. Enable this if the project contains a lot of
# classes, structs, unions or interfaces.
# The default value is: YES.
ALPHABETICAL_INDEX = YES
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
# that should be ignored while generating the index headers. The IGNORE_PREFIX
# tag works for classes, function and member names. The entity will be placed in
# the alphabetical list under the first letter of the entity name that remains
# after removing the prefix.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
# The default value is: YES.
GENERATE_HTML = YES
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = html
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).
# The default value is: .html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FILE_EXTENSION = .html
# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
# each generated HTML page. If the tag is left blank doxygen will generate a
# standard header.
#
# To get valid HTML the header file that includes any scripts and style sheets
# that doxygen needs, which is dependent on the configuration options used (e.g.
# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
# default header using
# doxygen -w html new_header.html new_footer.html new_stylesheet.css
# YourConfigFile
# and then modify the file new_header.html. See also section "Doxygen usage"
# for information on how to generate the default header that doxygen normally
# uses.
# Note: The header is subject to change so you typically have to regenerate the
# default header when upgrading to a newer version of doxygen. For a description
# of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
# generated HTML page. If the tag is left blank doxygen will generate a standard
# footer. See HTML_HEADER for more information on how to generate a default
# footer and what special commands can be used inside the footer. See also
# section "Doxygen usage" for information on how to generate the default footer
# that doxygen normally uses.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
# sheet that is used by each HTML page. It can be used to fine-tune the look of
# the HTML output. If left blank doxygen will generate a default style sheet.
# See also section "Doxygen usage" for information on how to generate the style
# sheet that doxygen normally uses.
# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
# it is more robust and this tag (HTML_STYLESHEET) will in the future become
# obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_STYLESHEET =
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
# created by doxygen. Using this option one can overrule certain style aspects.
# This is preferred over using HTML_STYLESHEET since it does not replace the
# standard style sheet and is therefore more robust against future updates.
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list).
# Note: Since the styling of scrollbars can currently not be overruled in
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
# one or more extra stylesheets have been specified. So if scrollbar
# customization is desired it has to be added explicitly. For an example see the
# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET = @MITK_DOXYGEN_STYLESHEET@
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
# that these files will be copied to the base HTML output directory. Use the
# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
# files will be copied as-is; there are no commands or markers available.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_FILES = "@MITK_SOURCE_DIR@/Documentation/Doxygen/mitkLogo.jpg"
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
# should be rendered with a dark or light theme.
# Possible values are: LIGHT always generate light mode output, DARK always
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
# the user preference, use light mode if no preference is set (the default),
# AUTO_DARK automatically set the mode according to the user preference, use
# dark mode if no preference is set and TOGGLE allow to user to switch between
# light and dark mode via a button.
# The default value is: AUTO_LIGHT.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE = AUTO_LIGHT
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a color-wheel, see
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again.
# Minimum value: 0, maximum value: 359, default value: 220.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_HUE = 220
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
# in the HTML output. For a value of 0 the output will use gray-scales only. A
# value of 255 will produce the most vivid colors.
# Minimum value: 0, maximum value: 255, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_SAT = 100
# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
# luminance component of the colors in the HTML output. Values below 100
# gradually make the output lighter, whereas values above 100 make the output
# darker. The value divided by 100 is the actual gamma applied, so 80 represents
# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
# change the gamma.
# Minimum value: 40, maximum value: 240, default value: 80.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
# to YES can help to show when doxygen was last run and thus if the
# documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = YES
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
# are dynamically created via JavaScript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
# page. Disable this option to support browsers that do not have JavaScript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_MENUS = YES
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_SECTIONS = @MITK_DOXYGEN_HTML_DYNAMIC_SECTIONS@
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand
# and collapse entries dynamically later on. Doxygen will expand the tree to
# such a level that at most the specified number of entries are visible (unless
# a fully collapsed tree already exceeds this amount). So setting the number of
# entries 1 will produce a full collapsed tree by default. 0 is a special value
# representing an infinite number of entries and will result in a full expanded
# tree by default.
# Minimum value: 0, maximum value: 9999, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
# environment (see:
# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
# create a documentation set, doxygen will generate a Makefile in the HTML
# output directory. Running make will produce the docset in that directory and
# running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_DOCSET = NO
# This tag determines the name of the docset feed. A documentation feed provides
# an umbrella under which multiple documentation sets from a single provider
# (such as a company or product suite) can be grouped.
# The default value is: Doxygen generated docs.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDNAME = "Doxygen generated docs"
# This tag determines the URL of the docset feed. A documentation feed provides
# an umbrella under which multiple documentation sets from a single provider
# (such as a company or product suite) can be grouped.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDURL =
# This tag specifies a string that should uniquely identify the documentation
# set bundle. This should be a reverse domain-name style string, e.g.
# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_BUNDLE_ID = org.doxygen.Project
# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
# the documentation publisher. This should be a reverse domain-name style
# string, e.g. com.mycompany.MyDocSet.documentation.
# The default value is: org.doxygen.Publisher.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
# The default value is: Publisher.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
# on Windows. In the beginning of 2021 Microsoft took the original page, with
# a.o. the download links, offline the HTML help workshop was already many years
# in maintenance mode). You can download the HTML help workshop from the web
# archives at Installation executable (see:
# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
# files are now used as the Windows 98 help format, and will replace the old
# Windows help format (.hlp) on all Windows platforms in the future. Compressed
# HTML files also contain an index, a table of contents, and you can search for
# words in the documentation. The HTML workshop also contains a viewer for
# compressed HTML files.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_HTMLHELP = NO
# The CHM_FILE tag can be used to specify the file name of the resulting .chm
# file. You can add a path in front of the file if the result should not be
# written to the html output directory.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_FILE =
# The HHC_LOCATION tag can be used to specify the location (absolute path
# including file name) of the HTML help compiler (hhc.exe). If non-empty,
# doxygen will try to run the HTML help compiler on the generated index.hhp.
# The file has to be specified with full path.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated
# (YES) or that it should be included in the main .chm file (NO).
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
GENERATE_CHI = NO
# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
# and project file content.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_INDEX_ENCODING =
# The BINARY_TOC flag controls whether a binary table of contents is generated
# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
# enables the Previous and Next buttons.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members to
# the table of contents of the HTML help documentation and to the tree view.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
TOC_EXPAND = NO
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
# (.qch) of the generated HTML documentation.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_QHP = @MITK_DOXYGEN_GENERATE_QHP@
# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
# the file name of the resulting .qch file. The path specified is relative to
# the HTML output folder.
# This tag requires that the tag GENERATE_QHP is set to YES.
QCH_FILE = @MITK_DOXYGEN_QCH_FILE@
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
# (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_NAMESPACE = org.mitk
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_VIRTUAL_FOLDER = MITK
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
# Filters (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
# The QHG_LOCATION tag can be used to specify the location (absolute path
# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
# run qhelpgenerator on the generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION = @QT_HELPGENERATOR_EXECUTABLE@
# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
# generated, together with the HTML files, they form an Eclipse help plugin. To
# install this plugin and make it available under the help contents menu in
# Eclipse, the contents of the directory containing the HTML and XML files needs
# to be copied into the plugins directory of eclipse. The name of the directory
# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
# After copying Eclipse needs to be restarted before the help appears.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_ECLIPSEHELP = NO
# A unique identifier for the Eclipse help plugin. When installing the plugin
# the directory name containing the HTML and XML files should also have this
# name. Each documentation set should have its own identifier.
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
ECLIPSE_DOC_ID = org.doxygen.Project
# If you want full control over the layout of the generated HTML pages it might
# be necessary to disable the index and replace it with your own. The
# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
# of each HTML page. A value of NO enables the index and the value YES disables
# it. Since the tabs in the index contain the same information as the navigation
# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
DISABLE_INDEX = NO
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
# structure should be generated to display hierarchical information. If the tag
# value is set to YES, a side panel will be generated containing a tree-like
# index structure (just like the one that is generated for HTML Help). For this
# to work a browser that supports JavaScript, DHTML, CSS and frames is required
# (i.e. any modern browser). Windows users are probably better off using the
# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
# further fine tune the look of the index (see "Fine-tuning the output"). As an
# example, the default style sheet generated by doxygen has an example that
# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
# Since the tree basically has the same information as the tab index, you could
# consider setting DISABLE_INDEX to YES when enabling this option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = YES
# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
# area (value NO) or if it should extend to the full height of the window (value
# YES). Setting this to YES gives a layout similar to
# https://docs.readthedocs.io with more room for contents, but less room for the
# project logo, title, and description. If either GENERATE_TREEVIEW or
# DISABLE_INDEX is set to NO, this option has no effect.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
FULL_SIDEBAR = NO
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
# doxygen will group on one line in the generated HTML documentation.
#
# Note that a value of 0 will completely suppress the enum values from appearing
# in the overview section.
# Minimum value: 0, maximum value: 20, default value: 4.
# This tag requires that the tag GENERATE_HTML is set to YES.
ENUM_VALUES_PER_LINE = 4
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
# to set the initial width (in pixels) of the frame in which the tree is shown.
# Minimum value: 0, maximum value: 1500, default value: 250.
# This tag requires that the tag GENERATE_HTML is set to YES.
TREEVIEW_WIDTH = 300
# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
# external symbols imported via tag files in a separate window.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
EXT_LINKS_IN_WINDOW = NO
# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
# addresses.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
OBFUSCATE_EMAILS = YES
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
# the HTML output. These images will generally look nicer at scaled resolutions.
# Possible values are: png (the default) and svg (looks nicer but requires the
# pdf2svg or inkscape tool).
# The default value is: png.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FORMULA_FORMAT = png
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# doxygen run you need to manually remove any form_*.png images from the HTML
# output directory to force them to be regenerated.
# Minimum value: 8, maximum value: 50, default value: 10.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_FONTSIZE = 10
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details.
FORMULA_MACROFILE =
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# https://www.mathjax.org) which uses client side JavaScript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
# to it using the MATHJAX_RELPATH option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
USE_MATHJAX = YES
# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
# Note that the different versions of MathJax have different requirements with
# regards to the different settings, so it is possible that also other MathJax
# settings have to be changed when switching between the different MathJax
# versions.
# Possible values are: MathJax_2 and MathJax_3.
# The default value is: MathJax_2.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_VERSION = MathJax_2
# When MathJax is enabled you can set the default output format to be used for
# the MathJax output. For more details about the output format see MathJax
# version 2 (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
# (see:
# http://docs.mathjax.org/en/latest/web/components/output.html).
# Possible values are: HTML-CSS (which is slower, but has the best
# compatibility. This is the name for Mathjax version 2, for MathJax version 3
# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
# is the name for Mathjax version 3, for MathJax version 2 this will be
# translated into HTML-CSS) and SVG.
# The default value is: HTML-CSS.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_FORMAT = HTML-CSS
# When MathJax is enabled you need to specify the location relative to the HTML
# output directory using the MATHJAX_RELPATH option. The destination directory
# should contain the MathJax.js script. For instance, if the mathjax directory
# is located at the same level as the HTML output directory, then
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from https://www.mathjax.org before deployment. The default value is:
# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://www.mathjax.org/mathjax
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example
# for MathJax version 2 (see
# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# For example for MathJax version 3 (see
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
# MATHJAX_EXTENSIONS = ams
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
# (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_CODEFILE =
# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
# the HTML output. The underlying search engine uses javascript and DHTML and
# should work on any modern browser. Note that when using HTML help
# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
# there is already a search function so this one should typically be disabled.
# For large projects the javascript based search engine can be slow, then
# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
# search using the keyboard; to jump to the search box use <access key> + S
# (what the <access key> is depends on the OS and browser, but it is typically
# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
# key> to jump into the search results window, the results can be navigated
# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
# the search. The filter options can be selected when the cursor is inside the
# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
# to select a filter and <Enter> or <escape> to activate or cancel the filter
# option.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
# implemented using a web server instead of a web client using JavaScript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
# and searching needs to be provided by external tools. See the section
# "External Indexing and Searching" for details.
# The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES.
SERVER_BASED_SEARCH = NO
# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
# script for searching. Instead the search results are written to an XML file
# which needs to be processed by an external indexer. Doxygen will invoke an
# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
# search results.
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see:
# https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH = NO
# The SEARCHENGINE_URL should point to a search engine hosted by a web server
# which will return the search results when EXTERNAL_SEARCH is enabled.
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see:
# https://xapian.org/). See the section "External Indexing and Searching" for
# details.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
# search data is written to a file for indexing by an external tool. With the
# SEARCHDATA_FILE tag the name of this file can be specified.
# The default file is: searchdata.xml.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHDATA_FILE = searchdata.xml
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
# projects and redirect the results back to the right project.
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH_ID =
# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
# projects other than the one defined by this configuration file, but that are
# all added to the same external search index. Each project needs to have a
# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
# to a relative location where the documentation can be found. The format is:
# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
# The default value is: YES.
GENERATE_LATEX = NO
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: latex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked.
#
# Note that when not enabling USE_PDFLATEX the default is latex when enabling
# USE_PDFLATEX the default is pdflatex and when in the later case latex is
# chosen this is overwritten by pdflatex. For specific output languages the
# default can have been set differently, this depends on the implementation of
# the output language.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
# index for LaTeX.
# Note: This tag is used in the Makefile / make.bat.
# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
# (.tex).
# The default file is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
# generate index for LaTeX. In case there is no backslash (\) as first character
# it will be automatically added in the LaTeX code.
# Note: This tag is used in the generated output file (.tex).
# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
# The default value is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_MAKEINDEX_CMD = makeindex
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used by the
# printer.
# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
# 14 inches) and executive (7.25 x 10.5 inches).
# The default value is: a4.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. The package can be specified just
# by its name or with the correct syntax as to be used with the LaTeX
# \usepackage command. To get the times font for instance you can specify :
# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
# To use the option intlimits with the amsmath package you can specify:
# EXTRA_PACKAGES=[intlimits]{amsmath}
# If left blank no extra packages will be included.
# This tag requires that the tag GENERATE_LATEX is set to YES.
EXTRA_PACKAGES = amssymb
# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
# the generated LaTeX document. The header should contain everything until the
# first chapter. If it is left blank doxygen will generate a standard header. It
# is highly recommended to start with a default header using
# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
# and then modify the file new_header.tex. See also section "Doxygen usage" for
# information on how to generate the default header that doxygen normally uses.
#
# Note: Only use a user-defined header if you know what you are doing!
# Note: The header is subject to change so you typically have to regenerate the
# default header when upgrading to a newer version of doxygen. The following
# commands have a special meaning inside the header (and footer): For a
# description of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
# the generated LaTeX document. The footer should contain everything after the
# last chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer. See also section "Doxygen
# usage" for information on how to generate the default footer that doxygen
# normally uses. Note: Only use a user-defined footer if you know what you are
# doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# LaTeX style sheets that are included after the standard style sheets created
# by doxygen. Using this option one can overrule certain style aspects. Doxygen
# will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list).
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_STYLESHEET =
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the LATEX_OUTPUT output
# directory. Note that the files will be copied as-is; there are no commands or
# markers available.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_FILES =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
# contain links (just like the HTML output) instead of page references. This
# makes the output suitable for online browsing using a PDF viewer.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = NO
# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
# files. Set this option to YES, to get a higher quality PDF documentation.
#
# See also section LATEX_CMD_NAME for selecting the engine.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = NO
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
# if errors occur, instead of asking the user for help.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BATCHMODE = NO
# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
# index chapters (such as File Index, Compound Index, etc.) in the output.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HIDE_INDICES = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# The default value is: plain.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered,
# it will be relative to the LATEX_OUTPUT directory. If left blank the
# LATEX_OUTPUT directory will be used.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EMOJI_DIRECTORY =
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
# RTF output is optimized for Word 97 and may not look too pretty with other RTF
# readers/editors.
# The default value is: NO.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: rtf.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
# contain hyperlink fields. The RTF file will contain links (just like the HTML
# output) instead of page references. This makes the output suitable for online
# browsing using Word or some other Word compatible readers that support those
# fields.
#
# Note: WordPad (write) and others do not support links.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# configuration file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
#
# See also section "Doxygen usage" for information on how to generate the
# default style sheet that doxygen normally uses.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
# similar to doxygen's configuration file. A template extensions file can be
# generated using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
# classes and files.
# The default value is: NO.
GENERATE_MAN = NO
# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it. A directory man3 will be created inside the directory specified by
# MAN_OUTPUT.
# The default directory is: man.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_OUTPUT = man
# The MAN_EXTENSION tag determines the extension that is added to the generated
# man pages. In case the manual section does not start with a number, the number
# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
# optional.
# The default value is: .3.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_EXTENSION = .3
# The MAN_SUBDIR tag determines the name of the directory created within
# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
# MAN_EXTENSION with the initial . removed.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_SUBDIR =
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
# will generate one additional man file for each entity documented in the real
# man page(s). These additional files only source the real man page, but without
# them the man command would be unable to find the correct page.
# The default value is: NO.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_LINKS = NO
#---------------------------------------------------------------------------
# Configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
# captures the structure of the code including all documentation.
# The default value is: NO.
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: xml.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_OUTPUT = xml
# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
# listings (including syntax highlighting and cross-referencing information) to
# the XML output. Note that enabling this will significantly increase the size
# of the XML output.
# The default value is: YES.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_PROGRAMLISTING = YES
# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
# namespace members in file scope as well, matching the HTML output.
# The default value is: NO.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_NS_MEMB_FILE_SCOPE = NO
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
# that can be used to generate PDF.
# The default value is: NO.
GENERATE_DOCBOOK = NO
# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
# front of it.
# The default directory is: docbook.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_OUTPUT = docbook
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
# the structure of the code including all documentation. Note that this feature
# is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
# file that captures the structure of the code including all documentation.
#
# Note that this feature is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_PERLMOD = NO
# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
# output from the Perl module output.
# The default value is: NO.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
# formatted so it can be parsed by a human reader. This is useful if you want to
# understand what is going on. On the other hand, if this tag is set to NO, the
# size of the Perl module output will be much smaller and Perl will parse it
# just the same.
# The default value is: YES.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_PRETTY = YES
# The names of the make variables in the generated doxyrules.make file are
# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
# so different doxyrules.make files included by the same Makefile don't
# overwrite each other's variables.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
# C-preprocessor directives found in the sources and include files.
# The default value is: YES.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
# in the source code. If set to NO, only conditional compilation will be
# performed. Macro expansion can be done in a controlled way by setting
# EXPAND_ONLY_PREDEF to YES.
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
# EXPAND_AS_DEFINED tags.
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = NO
# If the SEARCH_INCLUDES tag is set to YES, the include files in the
# INCLUDE_PATH will be searched if a #include is found.
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by the
# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
# RECURSIVE has no effect here.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will be
# used.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names that are
# defined before the preprocessor is started (similar to the -D option of e.g.
# gcc). The argument of the tag is a list of macros of the form: name or
# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
# is assumed. To prevent a macro definition from being undefined via #undef or
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = itkNotUsed(x)= \
"itkSetMacro(name,type)=virtual void Set##name (type _arg);" \
"itkGetMacro(name,type)=virtual type Get##name ();" \
"itkGetConstMacro(name,type)=virtual type Get##name () const;" \
"itkSetStringMacro(name)=virtual void Set##name (const char* _arg);" \
"itkGetStringMacro(name)=virtual const char* Get##name () const;" \
"itkSetClampMacro(name,type,min,max)=virtual void Set##name (type _arg);" \
"itkSetObjectMacro(name,type)=virtual void Set##name (type* _arg);" \
"itkGetObjectMacro(name,type)=virtual type* Get##name ();" \
"itkSetConstObjectMacro(name,type)=virtual void Set##name ( const type* _arg);" \
"itkGetConstObjectMacro(name,type)=virtual const type* Get##name ();" \
"itkGetConstReferenceMacro(name,type)=virtual const type& Get##name ();" \
"itkGetConstReferenceObjectMacro(name,type)=virtual const type::Pointer& Get##name () const;" \
"itkBooleanMacro(name)=virtual void name##On (); virtual void name##Off ();" \
"itkSetVector2Macro(name,type)=virtual void Set##name (type _arg1, type _arg2) virtual void Set##name (type _arg[2]);" \
"itkGetVector2Macro(name,type)=virtual type* Get##name () const; virtual void Get##name (type& _arg1, type& _arg2) const; virtual void Get##name (type _arg[2]) const;" \
"itkSetVector3Macro(name,type)=virtual void Set##name (type _arg1, type _arg2, type _arg3) virtual void Set##name (type _arg[3]);" \
"itkGetVector3Macro(name,type)=virtual type* Get##name () const; virtual void Get##name (type& _arg1, type& _arg2, type& _arg3) const; virtual void Get##name (type _arg[3]) const;" \
"itkSetVector4Macro(name,type)=virtual void Set##name (type _arg1, type _arg2, type _arg3, type _arg4) virtual void Set##name (type _arg[4]);" \
"itkGetVector4Macro(name,type)=virtual type* Get##name () const; virtual void Get##name (type& _arg1, type& _arg2, type& _arg3, type& _arg4) const; virtual void Get##name (type _arg[4]) const;" \
"itkSetVector6Macro(name,type)=virtual void Set##name (type _arg1, type _arg2, type _arg3, type _arg4, type _arg5, type _arg6) virtual void Set##name (type _arg[6]);" \
"itkGetVector6Macro(name,type)=virtual type* Get##name () const; virtual void Get##name (type& _arg1, type& _arg2, type& _arg3, type& _arg4, type& _arg5, type& _arg6) const; virtual void Get##name (type _arg[6]) const;" \
"itkSetVectorMacro(name,type,count)=virtual void Set##name(type data[]);" \
"itkGetVectorMacro(name,type,count)=virtual type* Get##name () const;" \
"itkNewMacro(type)=static Pointer New();" \
"itkFactorylessNewMacro(type)=static Pointer New();" \
"itkCloneMacro(type)=Pointer Clone() const;" \
"itkTypeMacro(thisClass,superclass)=virtual const char *GetClassName() const;" \
"itkConceptMacro(name,concept)=enum { name = 0 };" \
"ITK_NUMERIC_LIMITS=std::numeric_limits" \
"ITK_TYPENAME=typename" \
"FEM_ABSTRACT_CLASS(thisClass,parentClass)=public: /** Standard Self typedef.*/ typedef thisClass Self; /** Standard Superclass typedef. */ typedef parentClass Superclass; /** Pointer or SmartPointer to an object. */ typedef Self* Pointer; /** Const pointer or SmartPointer to an object. */ typedef const Self* ConstPointer; private:" \
"FEM_CLASS(thisClass,parentClass)=FEM_ABSTRACT_CLASS(thisClass,parentClass) public: /** Create a new object from the existing one */ virtual Baseclass::Pointer Clone() const; /** Class ID for FEM object factory */ static const int CLID; /** Virtual function to access the class ID */ virtual int ClassID() const { return CLID; /** Object creation in an itk compatible way */ static Self::Pointer New() { return new Self(); } private:" \
FREEVERSION \
ERROR_CHECKING \
HAS_TIFF \
HAS_JPEG \
HAS_NETLIB \
HAS_PNG \
HAS_ZLIB \
HAS_GLUT \
HAS_QT \
VCL_USE_NATIVE_STL=1 \
VCL_USE_NATIVE_COMPLEX=1 \
VCL_HAS_BOOL=1 \
VXL_BIG_ENDIAN=1 \
VXL_LITTLE_ENDIAN=0 \
VNL_DLL_DATA= \
"US_PREPEND_NAMESPACE(x)=us::x" \
"US_BEGIN_NAMESPACE= namespace us {" \
"US_END_NAMESPACE=}" \
"US_BASECLASS_NAME=itk::LightObject" \
US_EXPORT= \
"DEPRECATED(func)=func" \
"mitkSegmentationTaskValueMacro(type, name)=bool Has##name() const; type Get##name() const; void Set##name(const type& value);" \
"mitkSegmentationTaskListValueMacro(type, name)=bool Has##name(size_t index) const; type Get##name(size_t index) const; void SetDefault##name(const type& value);" \
@US_PLATFORM@
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
# macro definition that is found in the sources will be used. Use the PREDEFINED
# tag if you want to use a different macro definition that overrules the
# definition found in the source code.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all references to function-like macros that are alone on a line, have
# an all uppercase name, and do not end with a semicolon. Such function macros
# are typically used for boiler-plate code, and will confuse the parser if not
# removed.
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration options related to external references
#---------------------------------------------------------------------------
# The TAGFILES tag can be used to specify one or more tag files. For each tag
# file the location of the external documentation should be added. The format of
# a tag file without this location is as follows:
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
# TAGFILES = file1=loc1 "file2 = loc2" ...
# where loc1 and loc2 can be relative or absolute paths or URLs. See the
# section "Linking to external documentation" for more information about the use
# of tag files.
# Note: Each tag file must have a unique name (where the name does NOT include
# the path). If a tag file is not located in the directory in which doxygen is
# run, you must also specify the path to the tagfile here.
TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
# tag file that is based on the input files it reads. See section "Linking to
# external documentation" for more information about the usage of tag files.
GENERATE_TAGFILE = @MITK_DOXYGEN_TAGFILE_NAME@
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# the class index. If set to NO, only the inherited external classes will be
# listed.
# The default value is: NO.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will be
# listed.
# The default value is: YES.
EXTERNAL_GROUPS = NO
# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
# the related pages index. If set to NO, only the current project's pages will
# be listed.
# The default value is: YES.
EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
DIA_PATH =
# If set to YES the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class.
# The default value is: YES.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz (see:
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: NO.
HAVE_DOT = @HAVE_DOT@
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of
# processors available in the system. You can set it explicitly to a value
# larger than 0 to get control over the balance between CPU load and processing
# speed.
# Minimum value: 0, maximum value: 32, default value: 0.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NUM_THREADS = @MITK_DOXYGEN_DOT_NUM_THREADS@
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
# subgraphs. When you want a differently looking font in the dot files that
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
# Edge and Graph Attributes specification</a> You need to make sure dot is able
# to find the font, which can be done by putting it in a standard location or by
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
# directory containing the font. Default graphviz fontsize is 14.
# The default value is: fontname=Helvetica,fontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
# arrows shapes.</a>
# The default value is: labelfontname=Helvetica,labelfontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
# around nodes set 'shape=plain' or 'shape=plaintext' <a
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
# The default value is: shape=box,height=0.2,width=0.4.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
# You can set the path where dot can find font specified with fontname in
# DOT_COMMON_ATTR and others dot attributes.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
# graph for each documented class showing the direct and indirect inheritance
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
# to TEXT the direct and indirect inheritance relations will be shown as texts /
# links.
# Possible values are: NO, YES, TEXT and GRAPH.
# The default value is: YES.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
# graph for each documented class showing the direct and indirect implementation
# dependencies (inheritance, containment, and class references variables) of the
# class with other documented classes.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
# groups, showing the direct groups dependencies. See also the chapter Grouping
# in the manual.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GROUP_GRAPHS = YES
# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
UML_LOOK = @MITK_DOXYGEN_UML_LOOK@
# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
# class node. If there are many fields or methods and many nodes the graph may
# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
# number of items for each type to make the size more manageable. Set this to 0
# for no limit. Note that the threshold may be exceeded by 50% before the limit
# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
# but if the number exceeds 15, the total amount of fields shown is limited to
# 10.
# Minimum value: 0, maximum value: 100, default value: 10.
# This tag requires that the tag UML_LOOK is set to YES.
UML_LIMIT_NUM_FIELDS = 10
# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
# tag is set to YES, doxygen will add type and arguments for attributes and
# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
# will not generate fields with class member information in the UML graphs. The
# class diagrams will look similar to the default class diagrams but using UML
# notation for the relationships.
# Possible values are: NO, YES and NONE.
# The default value is: NO.
# This tag requires that the tag UML_LOOK is set to YES.
DOT_UML_DETAILS = NO
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold
# significantly it will wrapped across multiple lines. Some heuristics are apply
# to avoid ugly line breaks.
# Minimum value: 0, maximum value: 1000, default value: 17.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_WRAP_THRESHOLD = 17
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
# collaboration graphs will show the relations between templates and their
# instances.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
TEMPLATE_RELATIONS = YES
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
# YES then doxygen will generate a graph for each documented file showing the
# direct and indirect include dependencies of the file with other documented
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDE_GRAPH = NO
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
# set to YES then doxygen will generate a graph for each documented file showing
# the direct and indirect include dependencies of the file with other documented
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDED_BY_GRAPH = NO
# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
# dependency graph for every global function or class method.
#
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable call graphs for selected
# functions only using the \callgraph command. Disabling a call graph can be
# accomplished by means of the command \hidecallgraph.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
CALL_GRAPH = NO
# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
# dependency graph for every global function or class method.
#
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable caller graphs for selected
# functions only using the \callergraph command. Disabling a caller graph can be
# accomplished by means of the command \hidecallergraph.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
CALLER_GRAPH = NO
# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
# hierarchy of all classes instead of a textual one.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GRAPHICAL_HIERARCHY = NO
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
# dependencies a directory has on other directories in a graphical way. The
# dependency relations are determined by the #include relations between the
# files in the directories.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
DIRECTORY_GRAPH = YES
# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels
# of child directories generated in directory dependency graphs by dot.
# Minimum value: 1, maximum value: 25, default value: 1.
# This tag requires that the tag DIRECTORY_GRAPH is set to YES.
DIR_GRAPH_MAX_DEPTH = 1
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see:
# http://www.graphviz.org/)).
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus.
# The default value is: png.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_IMAGE_FORMAT = png
# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
# enable generation of interactive SVG images that allow zooming and panning.
#
# Note that this requires a modern browser other than Internet Explorer. Tested
# and working are Firefox, Chrome, Safari, and Opera.
# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
# the SVG files visible. Older versions of IE do not have SVG support.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
INTERACTIVE_SVG = NO
# The DOT_PATH tag can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found in the path.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_PATH = @DOXYGEN_DOT_PATH@
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the \dotfile
# command).
# This tag requires that the tag HAVE_DOT is set to YES.
DOTFILE_DIRS =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
MSCFILE_DIRS =
# The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile
# command).
DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
# path where java can find the plantuml.jar file or to the filename of jar file
# to be used. If left blank, it is assumed PlantUML is not used or called during
# a preprocessing step. Doxygen will generate a warning when it encounters a
# \startuml command in this case and will not generate output for the diagram.
PLANTUML_JAR_PATH =
# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
# configuration file for plantuml.
PLANTUML_CFG_FILE =
# When using plantuml, the specified paths are searched for files specified by
# the !include statement in a plantuml block.
PLANTUML_INCLUDE_PATH =
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
# that will be shown in the graph. If the number of nodes in a graph becomes
# larger than this value, doxygen will truncate the graph, which is visualized
# by representing a node as a red box. Note that doxygen if the number of direct
# children of the root node in a graph is already larger than
# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
# Minimum value: 0, maximum value: 10000, default value: 50.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_GRAPH_MAX_NODES = 120
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
# generated by dot. A depth value of 3 means that only nodes reachable from the
# root by following a path via at most 3 edges will be shown. Nodes that lay
# further from the root node will be omitted. Note that setting this option to 1
# or 2 may greatly reduce the computation time needed for large code bases. Also
# note that the size of a graph can be further restricted by
# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
# Minimum value: 0, maximum value: 1000, default value: 0.
# This tag requires that the tag HAVE_DOT is set to YES.
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
# this, this feature is disabled by default.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_MULTI_TARGETS = NO
# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
# explaining the meaning of the various boxes and arrows in the dot generated
# graphs.
# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal
# graphical representation for inheritance and collaboration diagrams is used.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs.
#
# Note: This setting is not only used for dot files but also for msc temporary
# files.
# The default value is: YES.
DOT_CLEANUP = YES
diff --git a/Documentation/doxygen_developers_guide.conf.in b/Documentation/doxygen_developers_guide.conf.in
index e611290891..d7d5fd63ef 100644
--- a/Documentation/doxygen_developers_guide.conf.in
+++ b/Documentation/doxygen_developers_guide.conf.in
@@ -1,2739 +1,2739 @@
# Doxyfile 1.9.6
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
#
# All text after a double hash (##) is considered a comment and is placed in
# front of the TAG it is preceding.
#
# All text after a single hash (#) is considered a comment and will be ignored.
# The format is:
# TAG = value [value, ...]
# For lists, items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (\" \").
#
# Note:
#
# Use doxygen to compare the used configuration file with the template
# configuration file:
# doxygen -x [configFile]
# Use doxygen to compare the used configuration file with the template
# configuration file without replacing the environment variables or CMake type
# replacement variables:
# doxygen -x_noenv [configFile]
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
# This tag specifies the encoding used for all characters in the configuration
# file that follow. The default is UTF-8 which is also the encoding used for all
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
# iconv built into libc) for the transcoding. See
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
# double-quotes, unless you are using Doxywizard) that should identify the
# project for which the documentation is generated. This name is used in the
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = MITK
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = @MITK_VERSION_STRING@
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF = "Medical Imaging Interaction Toolkit"
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
# the logo to the output directory.
PROJECT_LOGO =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = @MITK_DOXYGEN_OUTPUT_DIR@/Guides/Developers_Guide/
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# sub-directories (in 2 levels) under the output directory of each output format
# and will distribute the generated files over these directories. Enabling this
# option can be useful when feeding doxygen a huge amount of source files, where
# putting all generated files in the same directory would otherwise causes
# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to
# control the number of sub-directories.
# The default value is: NO.
CREATE_SUBDIRS = NO
# Controls the number of sub-directories that will be created when
# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every
# level increment doubles the number of directories, resulting in 4096
# directories at level 8 which is the default and also the maximum value. The
# sub-directories are organized in 2 levels, the first level always has a fixed
# number of 16 directories.
# Minimum value: 0, maximum value: 8, default value: 8.
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
CREATE_SUBDIRS_LEVEL = 8
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
# characters to appear in the names of generated files. If set to NO, non-ASCII
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
# U+3044.
# The default value is: NO.
ALLOW_UNICODE_NAMES = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with
# English messages), Korean, Korean-en (Korean with English messages), Latvian,
# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,
# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,
# Swedish, Turkish, Ukrainian and Vietnamese.
# The default value is: English.
OUTPUT_LANGUAGE = English
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this.
# The default value is: YES.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
# description of a member or function before the detailed description
#
# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
# The default value is: YES.
REPEAT_BRIEF = YES
# This tag implements a quasi-intelligent brief description abbreviator that is
# used to form the text in various listings. Each string in this list, if found
# as the leading text of the brief description, will be stripped from the text
# and the result, after processing the whole list, is used as the annotated
# text. Otherwise, the brief description is used as-is. If left blank, the
# following values are used ($name is automatically replaced with the name of
# the entity):The $name class, The $name widget, The $name file, is, provides,
# specifies, contains, represents, a, an and the.
ABBREVIATE_BRIEF =
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# doxygen will generate a detailed section even if there is only a brief
# description.
# The default value is: NO.
ALWAYS_DETAILED_SEC = NO
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
# inherited members of a class in the documentation of that class as if those
# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
# The default value is: NO.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
# before files name in the file list and in the header files. If set to NO the
# shortest path that makes the file name unique will be used
# The default value is: YES.
FULL_PATH_NAMES = NO
# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
# Stripping is only done if one of the specified strings matches the left-hand
# part of the path. The tag can be used to show relative paths in the file list.
# If left blank the directory from which doxygen is run is used as the path to
# strip.
#
# Note that you can specify absolute paths here, but also relative paths, which
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH =
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
# header file to include in order to use a class. If left blank only the name of
# the header file containing the class definition is used. Otherwise one should
# specify the list of include paths that are normally passed to the compiler
# using the -I flag.
STRIP_FROM_INC_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
# less readable) file names. This can be useful is your file systems doesn't
# support long names like on DOS, Mac, or CD-ROM.
# The default value is: NO.
SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
# first line (until the first dot) of a Javadoc-style comment as the brief
# description. If set to NO, the Javadoc-style will behave just like regular Qt-
# style comments (thus requiring an explicit @brief command for a brief
# description.)
# The default value is: NO.
JAVADOC_AUTOBRIEF = NO
# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
# such as
# /***************
# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
# Javadoc-style will behave just like regular comments and it will not be
# interpreted by doxygen.
# The default value is: NO.
JAVADOC_BANNER = NO
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
# line (until the first dot) of a Qt-style comment as the brief description. If
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
# requiring an explicit \brief command for a brief description.)
# The default value is: NO.
QT_AUTOBRIEF = NO
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
# a brief description. This used to be the default behavior. The new default is
# to treat a multi-line C++ comment block as a detailed description. Set this
# tag to YES if you prefer the old behavior instead.
#
# Note that setting this tag to YES also means that rational rose comments are
# not recognized any more.
# The default value is: NO.
MULTILINE_CPP_IS_BRIEF = NO
# By default Python docstrings are displayed as preformatted text and doxygen's
# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
# doxygen's special commands can be used and the contents of the docstring
# documentation blocks is shown as doxygen documentation.
# The default value is: YES.
PYTHON_DOCSTRING = YES
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.
# The default value is: YES.
INHERIT_DOCS = YES
# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
# page for each member. If set to NO, the documentation of a member will be part
# of the file/class/namespace that contains it.
# The default value is: NO.
SEPARATE_MEMBER_PAGES = NO
# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
# uses this value to replace tabs by spaces in code fragments.
# Minimum value: 1, maximum value: 16, default value: 4.
TAB_SIZE = 8
# This tag can be used to specify a number of aliases that act as commands in
# the documentation. An alias has the form:
# name=value
# For example adding
# "sideeffect=@par Side Effects:^^"
# will allow you to put the command \sideeffect (or @sideeffect) in the
# documentation, which will result in a user-defined paragraph with heading
# "Side Effects:". Note that you cannot put \n's in the value part of an alias
# to insert newlines (in the resulting output). You can put ^^ in the value part
# of an alias to insert a newline as if a physical newline was in the original
# file. When you need a literal { or } or , in the value part of an alias you
# have to escape them by means of a backslash (\), this can lead to conflicts
# with the commands \{ and \} for these it is advised to use the version @{ and
# @} or use a double escape (\\{ and \\})
ALIASES = "FIXME=\par Fix Me's:\n" \
"BlueBerry=\if BLUEBERRY" \
"endBlueBerry=\endif" \
"bundlemainpage{1}=\page \1" \
"embmainpage{1}=\page \1" \
"github{2}=<a href=\"https://github.com/MITK/MITK/blob/master/\1\">\2</a>" \
"deprecatedSince{1}=\xrefitem deprecatedSince\1 \" Deprecated as of \1\" \"Functions deprecated as of \1\"" \
"minimumCMakeVersion=@MITK_CMAKE_MINIMUM_REQUIRED_VERSION@" \
"minimumQt6Version=@MITK_QT6_MINIMUM_VERSION@" \
"imageMacro{3}=\image html \1 \2 \n \image latex \1 \2 width=\3cm" \
"developersguidemainpage{1}=\mainpage" \
"usersguidemainpage{1}=\page \1" \
"nondependentPluginLink{3}= \ref \1 \"\3\""
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
# members will be omitted, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_FOR_C = NO
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
# Python sources only. Doxygen will then generate output that is more tailored
# for that language. For instance, namespaces will be presented as packages,
# qualified scopes will look different, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
# sources. Doxygen will then generate output that is tailored for Fortran.
# The default value is: NO.
OPTIMIZE_FOR_FORTRAN = NO
# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
# sources. Doxygen will then generate output that is tailored for VHDL.
# The default value is: NO.
OPTIMIZE_OUTPUT_VHDL = NO
# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
# sources only. Doxygen will then generate output that is more tailored for that
# language. For instance, namespaces will be presented as modules, types will be
# separated into more groups, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_SLICE = NO
# Doxygen selects the parser to use depending on the extension of the files it
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
# default for Fortran type files). For instance to make doxygen treat .inc files
# as Fortran files (default is PHP), and .f files as C (default is Fortran),
# use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
# the files are not read by doxygen. When specifying no_extension you should add
# * to the FILE_PATTERNS.
#
# Note see also the list of default file extension mappings.
EXTENSION_MAPPING =
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
# documentation. See https://daringfireball.net/projects/markdown/ for details.
# The output of markdown processing is further processed by doxygen, so you can
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
# case of backward compatibilities issues.
# The default value is: YES.
MARKDOWN_SUPPORT = YES
# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
# Minimum value: 0, maximum value: 99, default value: 5.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 5
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or
# globally by setting AUTOLINK_SUPPORT to NO.
# The default value is: YES.
AUTOLINK_SUPPORT = YES
# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
# to include (a tag file for) the STL sources as input, then you should set this
# tag to YES in order to let doxygen match functions declarations and
# definitions whose arguments contain STL classes (e.g. func(std::string);
# versus func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
# The default value is: NO.
BUILTIN_STL_SUPPORT = YES
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
# The default value is: NO.
CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
# will parse them like normal C++ but will assume all classes use public instead
# of private inheritance when no explicit protection keyword is present.
# The default value is: NO.
SIP_SUPPORT = NO
# For Microsoft's IDL there are propget and propput attributes to indicate
# getter and setter methods for a property. Setting this option to YES will make
# doxygen to replace the get and set methods by a property in the documentation.
# This will only work if the methods are indeed getting or setting a simple
# type. If this is not the case, or you want to show the methods anyway, you
# should set this option to NO.
# The default value is: YES.
IDL_PROPERTY_SUPPORT = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
# The default value is: NO.
DISTRIBUTE_GROUP_DOC = YES
# If one adds a struct or class to a group and this option is enabled, then also
# any nested class or struct is added to the same group. By default this option
# is disabled and one has to add nested compounds explicitly via \ingroup.
# The default value is: NO.
GROUP_NESTED_COMPOUNDS = NO
# Set the SUBGROUPING tag to YES to allow class member groups of the same type
# (for instance a group of public functions) to be put as a subgroup of that
# type (e.g. under the Public Functions section). Set it to NO to prevent
# subgrouping. Alternatively, this can be done per class using the
# \nosubgrouping command.
# The default value is: YES.
SUBGROUPING = YES
# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
# are shown inside the group in which they are included (e.g. using \ingroup)
# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
# and RTF).
#
# Note that this feature does not work in combination with
# SEPARATE_MEMBER_PAGES.
# The default value is: NO.
INLINE_GROUPED_CLASSES = NO
# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
# with only public data fields or simple typedef fields will be shown inline in
# the documentation of the scope in which they are defined (i.e. file,
# namespace, or group documentation), provided this scope is documented. If set
# to NO, structs, classes, and unions are shown on a separate page (for HTML and
# Man pages) or section (for LaTeX and RTF).
# The default value is: NO.
INLINE_SIMPLE_STRUCTS = NO
# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
# enum is documented as struct, union, or enum with the name of the typedef. So
# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
# with name TypeT. When disabled the typedef will appear as a member of a file,
# namespace, or class. And the struct will be named TypeS. This can typically be
# useful for C code in case the coding convention dictates that all compound
# types are typedef'ed and only the typedef is referenced, never the tag name.
# The default value is: NO.
TYPEDEF_HIDES_STRUCT = NO
# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
# cache is used to resolve symbols given their name and scope. Since this can be
# an expensive process and often the same symbol appears multiple times in the
# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
# doxygen will become slower. If the cache is too large, memory is wasted. The
# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
# symbols. At the end of a run doxygen will report the cache usage and suggest
# the optimal cache size from a speed point of view.
# Minimum value: 0, maximum value: 9, default value: 0.
LOOKUP_CACHE_SIZE = 0
# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use
# during processing. When set to 0 doxygen will based this on the number of
# cores available in the system. You can set it explicitly to a value larger
# than 0 to get more control over the balance between CPU load and processing
# speed. At this moment only the input processing can be done using multiple
# threads. Since this is still an experimental feature the default is set to 1,
# which effectively disables parallel processing. Please report any issues you
# encounter. Generating dot graphs in parallel is controlled by the
# DOT_NUM_THREADS setting.
# Minimum value: 0, maximum value: 32, default value: 1.
NUM_PROC_THREADS = 1
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
# documentation are documented, even if no documentation was available. Private
# class members and static file members will be hidden unless the
# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
# Note: This will also disable the warnings about undocumented members that are
# normally produced when WARNINGS is set to YES.
# The default value is: NO.
EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
# be included in the documentation.
# The default value is: NO.
EXTRACT_PRIVATE = NO
# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
# methods of a class will be included in the documentation.
# The default value is: NO.
EXTRACT_PRIV_VIRTUAL = NO
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
# The default value is: NO.
EXTRACT_PACKAGE = NO
# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
# included in the documentation.
# The default value is: NO.
EXTRACT_STATIC = YES
# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
# locally in source files will be included in the documentation. If set to NO,
# only classes defined in header files are included. Does not have any effect
# for Java sources.
# The default value is: YES.
EXTRACT_LOCAL_CLASSES = @MITK_DOXYGEN_INTERNAL_DOCS@
# This flag is only useful for Objective-C code. If set to YES, local methods,
# which are defined in the implementation section but not in the interface are
# included in the documentation. If set to NO, only methods in the interface are
# included.
# The default value is: NO.
EXTRACT_LOCAL_METHODS = NO
# If this flag is set to YES, the members of anonymous namespaces will be
# extracted and appear in the documentation as a namespace called
# 'anonymous_namespace{file}', where file will be replaced with the base name of
# the file that contains the anonymous namespace. By default anonymous namespace
# are hidden.
# The default value is: NO.
EXTRACT_ANON_NSPACES = NO
# If this flag is set to YES, the name of an unnamed parameter in a declaration
# will be determined by the corresponding definition. By default unnamed
# parameters remain unnamed in the output.
# The default value is: YES.
RESOLVE_UNNAMED_PARAMS = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
# members will be included in the various overviews, but no documentation
# section is generated. This option has no effect if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set
# to NO, these classes will be included in the various overviews. This option
# will also hide undocumented C++ concepts if enabled. This option has no effect
# if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
# declarations. If set to NO, these declarations will be included in the
# documentation.
# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = @MITK_DOXYGEN_HIDE_FRIEND_COMPOUNDS@
# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
# documentation blocks found inside the body of a function. If set to NO, these
# blocks will be appended to the function's detailed documentation block.
# The default value is: NO.
HIDE_IN_BODY_DOCS = NO
# The INTERNAL_DOCS tag determines if documentation that is typed after a
# \internal command is included. If the tag is set to NO then the documentation
# will be excluded. Set it to YES to include the internal documentation.
# The default value is: NO.
INTERNAL_DOCS = @MITK_DOXYGEN_INTERNAL_DOCS@
# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
# able to match the capabilities of the underlying filesystem. In case the
# filesystem is case sensitive (i.e. it supports files in the same directory
# whose names only differ in casing), the option must be set to YES to properly
# deal with such files in case they appear in the input. For filesystems that
# are not case sensitive the option should be set to NO to properly deal with
# output files written for symbols that only differ in casing, such as for two
# classes, one named CLASS and the other named Class, and to also support
# references to files without having to specify the exact matching casing. On
# Windows (including Cygwin) and MacOS, users should typically set this option
# to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES.
# Possible values are: SYSTEM, NO and YES.
# The default value is: SYSTEM.
CASE_SENSE_NAMES = YES
# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
# their full class and namespace scopes in the documentation. If set to YES, the
# scope will be hidden.
# The default value is: NO.
HIDE_SCOPE_NAMES = NO
# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
# append additional text to a page's title, such as Class Reference. If set to
# YES the compound reference will be hidden.
# The default value is: NO.
HIDE_COMPOUND_REFERENCE= NO
# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
# will show which file needs to be included to use the class.
# The default value is: YES.
SHOW_HEADERFILE = YES
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
# the files that are included by a file in the documentation of that file.
# The default value is: YES.
SHOW_INCLUDE_FILES = YES
# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
# grouped member an include statement to the documentation, telling the reader
# which file to include in order to use the member.
# The default value is: NO.
SHOW_GROUPED_MEMB_INC = NO
# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
# files with double quotes in the documentation rather than with sharp brackets.
# The default value is: NO.
FORCE_LOCAL_INCLUDES = NO
# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
# documentation for inline members.
# The default value is: YES.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
# (detailed) documentation of file and class members alphabetically by member
# name. If set to NO, the members will appear in declaration order.
# The default value is: YES.
SORT_MEMBER_DOCS = YES
# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
# descriptions of file, namespace and class members alphabetically by member
# name. If set to NO, the members will appear in declaration order. Note that
# this will also influence the order of the classes in the class list.
# The default value is: NO.
SORT_BRIEF_DOCS = NO
# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
# (brief and detailed) documentation of class members so that constructors and
# destructors are listed first. If set to NO the constructors will appear in the
# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
# member documentation.
# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
# detailed member documentation.
# The default value is: NO.
SORT_MEMBERS_CTORS_1ST = NO
# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
# of group names into alphabetical order. If set to NO the group names will
# appear in their defined order.
# The default value is: NO.
SORT_GROUP_NAMES = NO
# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
# fully-qualified names, including namespaces. If set to NO, the class list will
# be sorted only by class name, not including the namespace part.
# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
# Note: This option applies only to the class list, not to the alphabetical
# list.
# The default value is: NO.
SORT_BY_SCOPE_NAME = YES
# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
# type resolution of all parameters of a function it will reject a match between
# the prototype and the implementation of a member function even if there is
# only one candidate or it is obvious which candidate to choose by doing a
# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
# accept a match between prototype and implementation in such cases.
# The default value is: NO.
STRICT_PROTO_MATCHING = NO
# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
# list. This list is created by putting \todo commands in the documentation.
# The default value is: YES.
GENERATE_TODOLIST = @MITK_DOXYGEN_GENERATE_TODOLIST@
# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
# list. This list is created by putting \test commands in the documentation.
# The default value is: YES.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
# list. This list is created by putting \bug commands in the documentation.
# The default value is: YES.
GENERATE_BUGLIST = @MITK_DOXYGEN_GENERATE_BUGLIST@
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
# the deprecated list. This list is created by putting \deprecated commands in
# the documentation.
# The default value is: YES.
GENERATE_DEPRECATEDLIST= @MITK_DOXYGEN_GENERATE_DEPRECATEDLIST@
# The ENABLED_SECTIONS tag can be used to enable conditional documentation
# sections, marked by \if <section_label> ... \endif and \cond <section_label>
# ... \endcond blocks.
ENABLED_SECTIONS = @MITK_DOXYGEN_ENABLED_SECTIONS@
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
# initial value of a variable or macro / define can have for it to appear in the
# documentation. If the initializer consists of more lines than specified here
# it will be hidden. Use a value of 0 to hide initializers completely. The
# appearance of the value of individual variables and macros / defines can be
# controlled using \showinitializer or \hideinitializer command in the
# documentation regardless of this setting.
# Minimum value: 0, maximum value: 10000, default value: 30.
MAX_INITIALIZER_LINES = 0
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
# the bottom of the documentation of classes and structs. If set to YES, the
# list will mention the files that were used to generate the documentation.
# The default value is: YES.
SHOW_USED_FILES = YES
# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
# will remove the Files entry from the Quick Index and from the Folder Tree View
# (if specified).
# The default value is: YES.
SHOW_FILES = NO
# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
# page. This will remove the Namespaces entry from the Quick Index and from the
# Folder Tree View (if specified).
# The default value is: YES.
SHOW_NAMESPACES = NO
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from
# the version control system). Doxygen will invoke the program by executing (via
# popen()) the command command input-file, where command is the value of the
# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
# by doxygen. Whatever the program writes to standard output is used as the file
# version. For an example see the documentation.
FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
# by doxygen. The layout file controls the global structure of the generated
# output files in an output format independent way. To create the layout file
# that represents doxygen's defaults, run doxygen with the -l option. You can
# optionally specify a file name after the option, if omitted DoxygenLayout.xml
# will be used as the name of the layout file. See also section "Changing the
# layout of pages" for information.
#
# Note that if you run doxygen from a directory containing a file called
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
# tag is left empty.
LAYOUT_FILE =
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib
# extension is automatically appended if omitted. This requires the bibtex tool
# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references.
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated to
# standard output by doxygen. If QUIET is set to YES this implies that the
# messages are off.
# The default value is: NO.
QUIET = YES
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
# this implies that the warnings are on.
#
# Tip: Turn warnings on while writing the documentation.
# The default value is: YES.
WARNINGS = YES
# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: YES.
WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as documenting some parameters in
# a documented function twice, or documenting parameters that don't exist or
# using markup commands wrongly.
# The default value is: YES.
WARN_IF_DOC_ERROR = YES
# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
# function parameter documentation. If set to NO, doxygen will accept that some
# parameters have no documentation without warning.
# The default value is: YES.
WARN_IF_INCOMPLETE_DOC = YES
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return
# value. If set to NO, doxygen will only warn about wrong parameter
# documentation, but not about the absence of documentation. If EXTRACT_ALL is
# set to YES then this flag will automatically be disabled. See also
# WARN_IF_INCOMPLETE_DOC
# The default value is: NO.
WARN_NO_PARAMDOC = NO
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
# undocumented enumeration values. If set to NO, doxygen will accept
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: NO.
WARN_IF_UNDOC_ENUM_VAL = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
# at the end of the doxygen process doxygen will return with a non-zero status.
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
# The default value is: NO.
WARN_AS_ERROR = NO
# The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which
# will be replaced by the file and line number from which the warning originated
# and the warning text. Optionally the format may contain $version, which will
# be replaced by the version of the file (if it could be obtained via
# FILE_VERSION_FILTER)
# See also: WARN_LINE_FORMAT
# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
# In the $text part of the WARN_FORMAT command it is possible that a reference
# to a more specific place is given. To make it easier to jump to this place
# (outside of doxygen) the user can define a custom "cut" / "paste" string.
# Example:
# WARN_LINE_FORMAT = "'vi $file +$line'"
# See also: WARN_FORMAT
# The default value is: at line $line of file $file.
WARN_LINE_FORMAT = "at line $line of file $file"
# The WARN_LOGFILE tag can be used to specify a file to which warning and error
# messages should be written. If left blank the output is written to standard
# error (stderr). In case the file specified cannot be opened for writing the
# warning and error messages are written to standard error. When as file - is
# specified the warning and error messages are written to standard output
# (stdout).
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag is used to specify the files and/or directories that contain
# documented source files. You may enter file names like myfile.cpp or
# directories like /usr/src/myproject. Separate the files or directories with
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = "@MITK_SOURCE_DIR@/Core/Documentation/Doxygen/Concepts/" \
"@MITK_SOURCE_DIR@/Documentation/Doxygen/DeveloperManual/" \
"@MITK_SOURCE_DIR@/Modules/"
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see:
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# See also: INPUT_FILE_ENCODING
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
# This tag can be used to specify the character encoding of the source files
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
# character encoding on a per file pattern basis. Doxygen will compare the file
# name with each pattern and apply the encoding instead of the default
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
# "INPUT_ENCODING" for further information on supported encodings.
INPUT_FILE_ENCODING =
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen.
#
# Note the list of default checked file patterns might differ from the list of
# default file extension mappings.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.dox \
*.md
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
# The default value is: NO.
RECURSIVE = YES
# The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
#
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
# The default value is: NO.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS = modules.dox \
*/Plugins/*/documentation/doxygen/*
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# ANamespace::AClass, ANamespace::*Test
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS =
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
# command).
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank all
# files are included.
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude commands
# irrespective of the value of the RECURSIVE tag.
# The default value is: NO.
EXAMPLE_RECURSIVE = YES
# The IMAGE_PATH tag can be used to specify one or more files or directories
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH = "@MITK_SOURCE_DIR@/Core/Documentation/Doxygen/Concepts/" \
"@MITK_SOURCE_DIR@/Documentation/Doxygen/DeveloperManual/" \
"@MITK_SOURCE_DIR@/Modules/"
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command:
#
# <filter> <input-file>
#
# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
# name of an input file. Doxygen will then use the output that the filter
# program writes to standard output. If FILTER_PATTERNS is specified, this tag
# will be ignored.
#
# Note that the filter must not add or remove lines; it is applied before the
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
#
# Note that doxygen will use the data processed and written to standard output
# for further processing, therefore nothing else, like debug statements or used
# commands (so in case of a Windows batch file always use @echo OFF), should be
# written to standard output.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the
# filter if there is a match. The filters are a list of the form: pattern=filter
# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
# patterns match the file name, INPUT_FILTER is applied.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
-FILTER_PATTERNS = *.cmake=@CMakeDoxygenFilter_EXECUTABLE@
+FILTER_PATTERNS =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will also be used to filter the input files that are used for
# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
# The default value is: NO.
FILTER_SOURCE_FILES = NO
# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
# it is also possible to disable source filtering for a specific pattern using
# *.ext= (so without naming a filter).
# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
FILTER_SOURCE_PATTERNS =
# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
# is part of the input, its contents will be placed on the main page
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE =
# The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common
# extension is to allow longer lines before the automatic comment starts. The
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
# be processed before the automatic comment starts.
# Minimum value: 7, maximum value: 10000, default value: 72.
FORTRAN_COMMENT_AFTER = 72
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
# generated. Documented entities will be cross-referenced with these sources.
#
# Note: To get rid of all source code in the generated output, make sure that
# also VERBATIM_HEADERS is set to NO.
# The default value is: NO.
SOURCE_BROWSER = YES
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
# classes and enums directly into the documentation.
# The default value is: NO.
INLINE_SOURCES = NO
# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
# special comment blocks from generated source code fragments. Normal C, C++ and
# Fortran comments will always remain visible.
# The default value is: YES.
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
# entity all documented functions referencing it will be listed.
# The default value is: NO.
REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES then for each documented function
# all documented entities called/used by that function will be listed.
# The default value is: NO.
REFERENCES_RELATION = YES
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
# to YES then the hyperlinks from functions in REFERENCES_RELATION and
# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
# link to the documentation.
# The default value is: YES.
REFERENCES_LINK_SOURCE = YES
# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
# source code will show a tooltip with additional information such as prototype,
# brief description and links to the definition and documentation. Since this
# will make the HTML file larger and loading of large files a bit slower, you
# can opt to disable this feature.
# The default value is: YES.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
SOURCE_TOOLTIPS = YES
# If the USE_HTAGS tag is set to YES then the references to source code will
# point to the HTML generated by the htags(1) tool instead of doxygen built-in
# source browser. The htags tool is part of GNU's global source tagging system
# (see https://www.gnu.org/software/global/global.html). You will need version
# 4.8.6 or higher.
#
# To use it do the following:
# - Install the latest version of global
# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
# - Make sure the INPUT points to the root of the source tree
# - Run doxygen as normal
#
# Doxygen will invoke htags (and that will in turn invoke gtags), so these
# tools must be available from the command line (i.e. in the search path).
#
# The result: instead of the source browser generated by doxygen, the links to
# source code will now point to the output of htags.
# The default value is: NO.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
USE_HTAGS = NO
# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
# verbatim copy of the header file for each class for which an include is
# specified. Set to NO to disable this.
# See also: Section \class.
# The default value is: YES.
VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
# clang parser (see:
# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
# performance. This can be particularly helpful with template rich C++ code for
# which doxygen's built-in parser lacks the necessary type information.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
# tag is set to YES then doxygen will add the directory of each input to the
# include path.
# The default value is: YES.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_ADD_INC_PATHS = YES
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
# If clang assisted parsing is enabled you can provide the clang parser with the
# path to the directory containing a file called compile_commands.json. This
# file is the compilation database (see:
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
# options used when the source files were built. This is equivalent to
# specifying the -p option to a clang tool, such as clang-check. These options
# will then be passed to the parser. Any options specified with CLANG_OPTIONS
# will be added as well.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
CLANG_DATABASE_PATH =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
# compounds will be generated. Enable this if the project contains a lot of
# classes, structs, unions or interfaces.
# The default value is: YES.
ALPHABETICAL_INDEX = YES
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
# that should be ignored while generating the index headers. The IGNORE_PREFIX
# tag works for classes, function and member names. The entity will be placed in
# the alphabetical list under the first letter of the entity name that remains
# after removing the prefix.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
# The default value is: YES.
GENERATE_HTML = NO
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = html
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).
# The default value is: .html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FILE_EXTENSION = .html
# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
# each generated HTML page. If the tag is left blank doxygen will generate a
# standard header.
#
# To get valid HTML the header file that includes any scripts and style sheets
# that doxygen needs, which is dependent on the configuration options used (e.g.
# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
# default header using
# doxygen -w html new_header.html new_footer.html new_stylesheet.css
# YourConfigFile
# and then modify the file new_header.html. See also section "Doxygen usage"
# for information on how to generate the default header that doxygen normally
# uses.
# Note: The header is subject to change so you typically have to regenerate the
# default header when upgrading to a newer version of doxygen. For a description
# of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
# generated HTML page. If the tag is left blank doxygen will generate a standard
# footer. See HTML_HEADER for more information on how to generate a default
# footer and what special commands can be used inside the footer. See also
# section "Doxygen usage" for information on how to generate the default footer
# that doxygen normally uses.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
# sheet that is used by each HTML page. It can be used to fine-tune the look of
# the HTML output. If left blank doxygen will generate a default style sheet.
# See also section "Doxygen usage" for information on how to generate the style
# sheet that doxygen normally uses.
# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
# it is more robust and this tag (HTML_STYLESHEET) will in the future become
# obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_STYLESHEET =
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
# created by doxygen. Using this option one can overrule certain style aspects.
# This is preferred over using HTML_STYLESHEET since it does not replace the
# standard style sheet and is therefore more robust against future updates.
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list).
# Note: Since the styling of scrollbars can currently not be overruled in
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
# one or more extra stylesheets have been specified. So if scrollbar
# customization is desired it has to be added explicitly. For an example see the
# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET = @MITK_DOXYGEN_STYLESHEET@
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
# that these files will be copied to the base HTML output directory. Use the
# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
# files will be copied as-is; there are no commands or markers available.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_FILES =
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
# should be rendered with a dark or light theme.
# Possible values are: LIGHT always generate light mode output, DARK always
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
# the user preference, use light mode if no preference is set (the default),
# AUTO_DARK automatically set the mode according to the user preference, use
# dark mode if no preference is set and TOGGLE allow to user to switch between
# light and dark mode via a button.
# The default value is: AUTO_LIGHT.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE = AUTO_LIGHT
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a color-wheel, see
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again.
# Minimum value: 0, maximum value: 359, default value: 220.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_HUE = 220
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
# in the HTML output. For a value of 0 the output will use gray-scales only. A
# value of 255 will produce the most vivid colors.
# Minimum value: 0, maximum value: 255, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_SAT = 100
# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
# luminance component of the colors in the HTML output. Values below 100
# gradually make the output lighter, whereas values above 100 make the output
# darker. The value divided by 100 is the actual gamma applied, so 80 represents
# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
# change the gamma.
# Minimum value: 40, maximum value: 240, default value: 80.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
# to YES can help to show when doxygen was last run and thus if the
# documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = YES
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
# are dynamically created via JavaScript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
# page. Disable this option to support browsers that do not have JavaScript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_MENUS = YES
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_SECTIONS = @MITK_DOXYGEN_HTML_DYNAMIC_SECTIONS@
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand
# and collapse entries dynamically later on. Doxygen will expand the tree to
# such a level that at most the specified number of entries are visible (unless
# a fully collapsed tree already exceeds this amount). So setting the number of
# entries 1 will produce a full collapsed tree by default. 0 is a special value
# representing an infinite number of entries and will result in a full expanded
# tree by default.
# Minimum value: 0, maximum value: 9999, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
# environment (see:
# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
# create a documentation set, doxygen will generate a Makefile in the HTML
# output directory. Running make will produce the docset in that directory and
# running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_DOCSET = NO
# This tag determines the name of the docset feed. A documentation feed provides
# an umbrella under which multiple documentation sets from a single provider
# (such as a company or product suite) can be grouped.
# The default value is: Doxygen generated docs.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDNAME = "Doxygen generated docs"
# This tag determines the URL of the docset feed. A documentation feed provides
# an umbrella under which multiple documentation sets from a single provider
# (such as a company or product suite) can be grouped.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDURL =
# This tag specifies a string that should uniquely identify the documentation
# set bundle. This should be a reverse domain-name style string, e.g.
# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_BUNDLE_ID = org.doxygen.Project
# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
# the documentation publisher. This should be a reverse domain-name style
# string, e.g. com.mycompany.MyDocSet.documentation.
# The default value is: org.doxygen.Publisher.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
# The default value is: Publisher.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
# on Windows. In the beginning of 2021 Microsoft took the original page, with
# a.o. the download links, offline the HTML help workshop was already many years
# in maintenance mode). You can download the HTML help workshop from the web
# archives at Installation executable (see:
# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
# files are now used as the Windows 98 help format, and will replace the old
# Windows help format (.hlp) on all Windows platforms in the future. Compressed
# HTML files also contain an index, a table of contents, and you can search for
# words in the documentation. The HTML workshop also contains a viewer for
# compressed HTML files.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_HTMLHELP = NO
# The CHM_FILE tag can be used to specify the file name of the resulting .chm
# file. You can add a path in front of the file if the result should not be
# written to the html output directory.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_FILE =
# The HHC_LOCATION tag can be used to specify the location (absolute path
# including file name) of the HTML help compiler (hhc.exe). If non-empty,
# doxygen will try to run the HTML help compiler on the generated index.hhp.
# The file has to be specified with full path.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated
# (YES) or that it should be included in the main .chm file (NO).
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
GENERATE_CHI = NO
# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
# and project file content.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_INDEX_ENCODING =
# The BINARY_TOC flag controls whether a binary table of contents is generated
# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
# enables the Previous and Next buttons.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members to
# the table of contents of the HTML help documentation and to the tree view.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
TOC_EXPAND = NO
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
# (.qch) of the generated HTML documentation.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_QHP = @MITK_DOXYGEN_GENERATE_QHP@
# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
# the file name of the resulting .qch file. The path specified is relative to
# the HTML output folder.
# This tag requires that the tag GENERATE_QHP is set to YES.
QCH_FILE = @MITK_DOXYGEN_QCH_FILE@
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
# (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_NAMESPACE = org.mitk
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_VIRTUAL_FOLDER = MITK
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
# Filters (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
# The QHG_LOCATION tag can be used to specify the location (absolute path
# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
# run qhelpgenerator on the generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION = @QT_HELPGENERATOR_EXECUTABLE@
# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
# generated, together with the HTML files, they form an Eclipse help plugin. To
# install this plugin and make it available under the help contents menu in
# Eclipse, the contents of the directory containing the HTML and XML files needs
# to be copied into the plugins directory of eclipse. The name of the directory
# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
# After copying Eclipse needs to be restarted before the help appears.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_ECLIPSEHELP = NO
# A unique identifier for the Eclipse help plugin. When installing the plugin
# the directory name containing the HTML and XML files should also have this
# name. Each documentation set should have its own identifier.
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
ECLIPSE_DOC_ID = org.doxygen.Project
# If you want full control over the layout of the generated HTML pages it might
# be necessary to disable the index and replace it with your own. The
# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
# of each HTML page. A value of NO enables the index and the value YES disables
# it. Since the tabs in the index contain the same information as the navigation
# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
DISABLE_INDEX = NO
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
# structure should be generated to display hierarchical information. If the tag
# value is set to YES, a side panel will be generated containing a tree-like
# index structure (just like the one that is generated for HTML Help). For this
# to work a browser that supports JavaScript, DHTML, CSS and frames is required
# (i.e. any modern browser). Windows users are probably better off using the
# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
# further fine tune the look of the index (see "Fine-tuning the output"). As an
# example, the default style sheet generated by doxygen has an example that
# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
# Since the tree basically has the same information as the tab index, you could
# consider setting DISABLE_INDEX to YES when enabling this option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = YES
# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
# area (value NO) or if it should extend to the full height of the window (value
# YES). Setting this to YES gives a layout similar to
# https://docs.readthedocs.io with more room for contents, but less room for the
# project logo, title, and description. If either GENERATE_TREEVIEW or
# DISABLE_INDEX is set to NO, this option has no effect.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
FULL_SIDEBAR = NO
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
# doxygen will group on one line in the generated HTML documentation.
#
# Note that a value of 0 will completely suppress the enum values from appearing
# in the overview section.
# Minimum value: 0, maximum value: 20, default value: 4.
# This tag requires that the tag GENERATE_HTML is set to YES.
ENUM_VALUES_PER_LINE = 4
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
# to set the initial width (in pixels) of the frame in which the tree is shown.
# Minimum value: 0, maximum value: 1500, default value: 250.
# This tag requires that the tag GENERATE_HTML is set to YES.
TREEVIEW_WIDTH = 300
# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
# external symbols imported via tag files in a separate window.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
EXT_LINKS_IN_WINDOW = NO
# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
# addresses.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
OBFUSCATE_EMAILS = YES
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
# the HTML output. These images will generally look nicer at scaled resolutions.
# Possible values are: png (the default) and svg (looks nicer but requires the
# pdf2svg or inkscape tool).
# The default value is: png.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FORMULA_FORMAT = png
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# doxygen run you need to manually remove any form_*.png images from the HTML
# output directory to force them to be regenerated.
# Minimum value: 8, maximum value: 50, default value: 10.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_FONTSIZE = 10
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details.
FORMULA_MACROFILE =
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# https://www.mathjax.org) which uses client side JavaScript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
# to it using the MATHJAX_RELPATH option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
USE_MATHJAX = NO
# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
# Note that the different versions of MathJax have different requirements with
# regards to the different settings, so it is possible that also other MathJax
# settings have to be changed when switching between the different MathJax
# versions.
# Possible values are: MathJax_2 and MathJax_3.
# The default value is: MathJax_2.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_VERSION = MathJax_2
# When MathJax is enabled you can set the default output format to be used for
# the MathJax output. For more details about the output format see MathJax
# version 2 (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
# (see:
# http://docs.mathjax.org/en/latest/web/components/output.html).
# Possible values are: HTML-CSS (which is slower, but has the best
# compatibility. This is the name for Mathjax version 2, for MathJax version 3
# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
# is the name for Mathjax version 3, for MathJax version 2 this will be
# translated into HTML-CSS) and SVG.
# The default value is: HTML-CSS.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_FORMAT = HTML-CSS
# When MathJax is enabled you need to specify the location relative to the HTML
# output directory using the MATHJAX_RELPATH option. The destination directory
# should contain the MathJax.js script. For instance, if the mathjax directory
# is located at the same level as the HTML output directory, then
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from https://www.mathjax.org before deployment. The default value is:
# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://www.mathjax.org/mathjax
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example
# for MathJax version 2 (see
# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# For example for MathJax version 3 (see
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
# MATHJAX_EXTENSIONS = ams
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
# (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_CODEFILE =
# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
# the HTML output. The underlying search engine uses javascript and DHTML and
# should work on any modern browser. Note that when using HTML help
# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
# there is already a search function so this one should typically be disabled.
# For large projects the javascript based search engine can be slow, then
# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
# search using the keyboard; to jump to the search box use <access key> + S
# (what the <access key> is depends on the OS and browser, but it is typically
# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
# key> to jump into the search results window, the results can be navigated
# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
# the search. The filter options can be selected when the cursor is inside the
# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
# to select a filter and <Enter> or <escape> to activate or cancel the filter
# option.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
# implemented using a web server instead of a web client using JavaScript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
# and searching needs to be provided by external tools. See the section
# "External Indexing and Searching" for details.
# The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES.
SERVER_BASED_SEARCH = NO
# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
# script for searching. Instead the search results are written to an XML file
# which needs to be processed by an external indexer. Doxygen will invoke an
# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
# search results.
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see:
# https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH = NO
# The SEARCHENGINE_URL should point to a search engine hosted by a web server
# which will return the search results when EXTERNAL_SEARCH is enabled.
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see:
# https://xapian.org/). See the section "External Indexing and Searching" for
# details.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
# search data is written to a file for indexing by an external tool. With the
# SEARCHDATA_FILE tag the name of this file can be specified.
# The default file is: searchdata.xml.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHDATA_FILE = searchdata.xml
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
# projects and redirect the results back to the right project.
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH_ID =
# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
# projects other than the one defined by this configuration file, but that are
# all added to the same external search index. Each project needs to have a
# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
# to a relative location where the documentation can be found. The format is:
# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
# The default value is: YES.
GENERATE_LATEX = YES
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: latex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked.
#
# Note that when not enabling USE_PDFLATEX the default is latex when enabling
# USE_PDFLATEX the default is pdflatex and when in the later case latex is
# chosen this is overwritten by pdflatex. For specific output languages the
# default can have been set differently, this depends on the implementation of
# the output language.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
# index for LaTeX.
# Note: This tag is used in the Makefile / make.bat.
# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
# (.tex).
# The default file is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
# generate index for LaTeX. In case there is no backslash (\) as first character
# it will be automatically added in the LaTeX code.
# Note: This tag is used in the generated output file (.tex).
# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
# The default value is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_MAKEINDEX_CMD = makeindex
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used by the
# printer.
# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
# 14 inches) and executive (7.25 x 10.5 inches).
# The default value is: a4.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. The package can be specified just
# by its name or with the correct syntax as to be used with the LaTeX
# \usepackage command. To get the times font for instance you can specify :
# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
# To use the option intlimits with the amsmath package you can specify:
# EXTRA_PACKAGES=[intlimits]{amsmath}
# If left blank no extra packages will be included.
# This tag requires that the tag GENERATE_LATEX is set to YES.
EXTRA_PACKAGES = amssymb
# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
# the generated LaTeX document. The header should contain everything until the
# first chapter. If it is left blank doxygen will generate a standard header. It
# is highly recommended to start with a default header using
# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
# and then modify the file new_header.tex. See also section "Doxygen usage" for
# information on how to generate the default header that doxygen normally uses.
#
# Note: Only use a user-defined header if you know what you are doing!
# Note: The header is subject to change so you typically have to regenerate the
# default header when upgrading to a newer version of doxygen. The following
# commands have a special meaning inside the header (and footer): For a
# description of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
# the generated LaTeX document. The footer should contain everything after the
# last chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer. See also section "Doxygen
# usage" for information on how to generate the default footer that doxygen
# normally uses. Note: Only use a user-defined footer if you know what you are
# doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# LaTeX style sheets that are included after the standard style sheets created
# by doxygen. Using this option one can overrule certain style aspects. Doxygen
# will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list).
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_STYLESHEET =
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the LATEX_OUTPUT output
# directory. Note that the files will be copied as-is; there are no commands or
# markers available.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_FILES =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
# contain links (just like the HTML output) instead of page references. This
# makes the output suitable for online browsing using a PDF viewer.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = YES
# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
# files. Set this option to YES, to get a higher quality PDF documentation.
#
# See also section LATEX_CMD_NAME for selecting the engine.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
# if errors occur, instead of asking the user for help.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BATCHMODE = YES
# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
# index chapters (such as File Index, Compound Index, etc.) in the output.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HIDE_INDICES = YES
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# The default value is: plain.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered,
# it will be relative to the LATEX_OUTPUT directory. If left blank the
# LATEX_OUTPUT directory will be used.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EMOJI_DIRECTORY =
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
# RTF output is optimized for Word 97 and may not look too pretty with other RTF
# readers/editors.
# The default value is: NO.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: rtf.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
# contain hyperlink fields. The RTF file will contain links (just like the HTML
# output) instead of page references. This makes the output suitable for online
# browsing using Word or some other Word compatible readers that support those
# fields.
#
# Note: WordPad (write) and others do not support links.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# configuration file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
#
# See also section "Doxygen usage" for information on how to generate the
# default style sheet that doxygen normally uses.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
# similar to doxygen's configuration file. A template extensions file can be
# generated using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
# classes and files.
# The default value is: NO.
GENERATE_MAN = NO
# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it. A directory man3 will be created inside the directory specified by
# MAN_OUTPUT.
# The default directory is: man.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_OUTPUT = man
# The MAN_EXTENSION tag determines the extension that is added to the generated
# man pages. In case the manual section does not start with a number, the number
# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
# optional.
# The default value is: .3.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_EXTENSION = .3
# The MAN_SUBDIR tag determines the name of the directory created within
# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
# MAN_EXTENSION with the initial . removed.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_SUBDIR =
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
# will generate one additional man file for each entity documented in the real
# man page(s). These additional files only source the real man page, but without
# them the man command would be unable to find the correct page.
# The default value is: NO.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_LINKS = NO
#---------------------------------------------------------------------------
# Configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
# captures the structure of the code including all documentation.
# The default value is: NO.
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: xml.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_OUTPUT = xml
# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
# listings (including syntax highlighting and cross-referencing information) to
# the XML output. Note that enabling this will significantly increase the size
# of the XML output.
# The default value is: YES.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_PROGRAMLISTING = YES
# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
# namespace members in file scope as well, matching the HTML output.
# The default value is: NO.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_NS_MEMB_FILE_SCOPE = NO
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
# that can be used to generate PDF.
# The default value is: NO.
GENERATE_DOCBOOK = NO
# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
# front of it.
# The default directory is: docbook.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_OUTPUT = docbook
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
# the structure of the code including all documentation. Note that this feature
# is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
# file that captures the structure of the code including all documentation.
#
# Note that this feature is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_PERLMOD = NO
# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
# output from the Perl module output.
# The default value is: NO.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
# formatted so it can be parsed by a human reader. This is useful if you want to
# understand what is going on. On the other hand, if this tag is set to NO, the
# size of the Perl module output will be much smaller and Perl will parse it
# just the same.
# The default value is: YES.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_PRETTY = YES
# The names of the make variables in the generated doxyrules.make file are
# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
# so different doxyrules.make files included by the same Makefile don't
# overwrite each other's variables.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
# C-preprocessor directives found in the sources and include files.
# The default value is: YES.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
# in the source code. If set to NO, only conditional compilation will be
# performed. Macro expansion can be done in a controlled way by setting
# EXPAND_ONLY_PREDEF to YES.
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
# EXPAND_AS_DEFINED tags.
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = NO
# If the SEARCH_INCLUDES tag is set to YES, the include files in the
# INCLUDE_PATH will be searched if a #include is found.
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by the
# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
# RECURSIVE has no effect here.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will be
# used.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names that are
# defined before the preprocessor is started (similar to the -D option of e.g.
# gcc). The argument of the tag is a list of macros of the form: name or
# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
# is assumed. To prevent a macro definition from being undefined via #undef or
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
# macro definition that is found in the sources will be used. Use the PREDEFINED
# tag if you want to use a different macro definition that overrules the
# definition found in the source code.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all references to function-like macros that are alone on a line, have
# an all uppercase name, and do not end with a semicolon. Such function macros
# are typically used for boiler-plate code, and will confuse the parser if not
# removed.
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration options related to external references
#---------------------------------------------------------------------------
# The TAGFILES tag can be used to specify one or more tag files. For each tag
# file the location of the external documentation should be added. The format of
# a tag file without this location is as follows:
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
# TAGFILES = file1=loc1 "file2 = loc2" ...
# where loc1 and loc2 can be relative or absolute paths or URLs. See the
# section "Linking to external documentation" for more information about the use
# of tag files.
# Note: Each tag file must have a unique name (where the name does NOT include
# the path). If a tag file is not located in the directory in which doxygen is
# run, you must also specify the path to the tagfile here.
TAGFILES = @BLUEBERRY_DOXYGEN_TAGFILE@
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
# tag file that is based on the input files it reads. See section "Linking to
# external documentation" for more information about the usage of tag files.
GENERATE_TAGFILE = @MITK_DOXYGEN_TAGFILE_NAME@
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# the class index. If set to NO, only the inherited external classes will be
# listed.
# The default value is: NO.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will be
# listed.
# The default value is: YES.
EXTERNAL_GROUPS = NO
# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
# the related pages index. If set to NO, only the current project's pages will
# be listed.
# The default value is: YES.
EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
DIA_PATH =
# If set to YES the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class.
# The default value is: YES.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz (see:
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: NO.
HAVE_DOT = @HAVE_DOT@
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of
# processors available in the system. You can set it explicitly to a value
# larger than 0 to get control over the balance between CPU load and processing
# speed.
# Minimum value: 0, maximum value: 32, default value: 0.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NUM_THREADS = @MITK_DOXYGEN_DOT_NUM_THREADS@
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
# subgraphs. When you want a differently looking font in the dot files that
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
# Edge and Graph Attributes specification</a> You need to make sure dot is able
# to find the font, which can be done by putting it in a standard location or by
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
# directory containing the font. Default graphviz fontsize is 14.
# The default value is: fontname=Helvetica,fontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
# arrows shapes.</a>
# The default value is: labelfontname=Helvetica,labelfontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
# around nodes set 'shape=plain' or 'shape=plaintext' <a
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
# The default value is: shape=box,height=0.2,width=0.4.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
# You can set the path where dot can find font specified with fontname in
# DOT_COMMON_ATTR and others dot attributes.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
# graph for each documented class showing the direct and indirect inheritance
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
# to TEXT the direct and indirect inheritance relations will be shown as texts /
# links.
# Possible values are: NO, YES, TEXT and GRAPH.
# The default value is: YES.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
# graph for each documented class showing the direct and indirect implementation
# dependencies (inheritance, containment, and class references variables) of the
# class with other documented classes.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
# groups, showing the direct groups dependencies. See also the chapter Grouping
# in the manual.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GROUP_GRAPHS = YES
# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
UML_LOOK = @MITK_DOXYGEN_UML_LOOK@
# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
# class node. If there are many fields or methods and many nodes the graph may
# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
# number of items for each type to make the size more manageable. Set this to 0
# for no limit. Note that the threshold may be exceeded by 50% before the limit
# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
# but if the number exceeds 15, the total amount of fields shown is limited to
# 10.
# Minimum value: 0, maximum value: 100, default value: 10.
# This tag requires that the tag UML_LOOK is set to YES.
UML_LIMIT_NUM_FIELDS = 10
# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
# tag is set to YES, doxygen will add type and arguments for attributes and
# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
# will not generate fields with class member information in the UML graphs. The
# class diagrams will look similar to the default class diagrams but using UML
# notation for the relationships.
# Possible values are: NO, YES and NONE.
# The default value is: NO.
# This tag requires that the tag UML_LOOK is set to YES.
DOT_UML_DETAILS = NO
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold
# significantly it will wrapped across multiple lines. Some heuristics are apply
# to avoid ugly line breaks.
# Minimum value: 0, maximum value: 1000, default value: 17.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_WRAP_THRESHOLD = 17
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
# collaboration graphs will show the relations between templates and their
# instances.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
TEMPLATE_RELATIONS = YES
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
# YES then doxygen will generate a graph for each documented file showing the
# direct and indirect include dependencies of the file with other documented
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDE_GRAPH = NO
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
# set to YES then doxygen will generate a graph for each documented file showing
# the direct and indirect include dependencies of the file with other documented
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDED_BY_GRAPH = NO
# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
# dependency graph for every global function or class method.
#
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable call graphs for selected
# functions only using the \callgraph command. Disabling a call graph can be
# accomplished by means of the command \hidecallgraph.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
CALL_GRAPH = NO
# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
# dependency graph for every global function or class method.
#
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable caller graphs for selected
# functions only using the \callergraph command. Disabling a caller graph can be
# accomplished by means of the command \hidecallergraph.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
CALLER_GRAPH = NO
# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
# hierarchy of all classes instead of a textual one.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GRAPHICAL_HIERARCHY = NO
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
# dependencies a directory has on other directories in a graphical way. The
# dependency relations are determined by the #include relations between the
# files in the directories.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
DIRECTORY_GRAPH = YES
# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels
# of child directories generated in directory dependency graphs by dot.
# Minimum value: 1, maximum value: 25, default value: 1.
# This tag requires that the tag DIRECTORY_GRAPH is set to YES.
DIR_GRAPH_MAX_DEPTH = 1
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see:
# http://www.graphviz.org/)).
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus.
# The default value is: png.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_IMAGE_FORMAT = png
# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
# enable generation of interactive SVG images that allow zooming and panning.
#
# Note that this requires a modern browser other than Internet Explorer. Tested
# and working are Firefox, Chrome, Safari, and Opera.
# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
# the SVG files visible. Older versions of IE do not have SVG support.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
INTERACTIVE_SVG = NO
# The DOT_PATH tag can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found in the path.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_PATH = @DOXYGEN_DOT_PATH@
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the \dotfile
# command).
# This tag requires that the tag HAVE_DOT is set to YES.
DOTFILE_DIRS =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
MSCFILE_DIRS =
# The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile
# command).
DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
# path where java can find the plantuml.jar file or to the filename of jar file
# to be used. If left blank, it is assumed PlantUML is not used or called during
# a preprocessing step. Doxygen will generate a warning when it encounters a
# \startuml command in this case and will not generate output for the diagram.
PLANTUML_JAR_PATH =
# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
# configuration file for plantuml.
PLANTUML_CFG_FILE =
# When using plantuml, the specified paths are searched for files specified by
# the !include statement in a plantuml block.
PLANTUML_INCLUDE_PATH =
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
# that will be shown in the graph. If the number of nodes in a graph becomes
# larger than this value, doxygen will truncate the graph, which is visualized
# by representing a node as a red box. Note that doxygen if the number of direct
# children of the root node in a graph is already larger than
# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
# Minimum value: 0, maximum value: 10000, default value: 50.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_GRAPH_MAX_NODES = 50
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
# generated by dot. A depth value of 3 means that only nodes reachable from the
# root by following a path via at most 3 edges will be shown. Nodes that lay
# further from the root node will be omitted. Note that setting this option to 1
# or 2 may greatly reduce the computation time needed for large code bases. Also
# note that the size of a graph can be further restricted by
# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
# Minimum value: 0, maximum value: 1000, default value: 0.
# This tag requires that the tag HAVE_DOT is set to YES.
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
# this, this feature is disabled by default.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_MULTI_TARGETS = NO
# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
# explaining the meaning of the various boxes and arrows in the dot generated
# graphs.
# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal
# graphical representation for inheritance and collaboration diagrams is used.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs.
#
# Note: This setting is not only used for dot files but also for msc temporary
# files.
# The default value is: YES.
DOT_CLEANUP = YES
diff --git a/Documentation/doxygen_users_guide.conf.in b/Documentation/doxygen_users_guide.conf.in
index ce6a6ed84a..5cdee52de3 100644
--- a/Documentation/doxygen_users_guide.conf.in
+++ b/Documentation/doxygen_users_guide.conf.in
@@ -1,2737 +1,2737 @@
# Doxyfile 1.9.6
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
#
# All text after a double hash (##) is considered a comment and is placed in
# front of the TAG it is preceding.
#
# All text after a single hash (#) is considered a comment and will be ignored.
# The format is:
# TAG = value [value, ...]
# For lists, items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (\" \").
#
# Note:
#
# Use doxygen to compare the used configuration file with the template
# configuration file:
# doxygen -x [configFile]
# Use doxygen to compare the used configuration file with the template
# configuration file without replacing the environment variables or CMake type
# replacement variables:
# doxygen -x_noenv [configFile]
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
# This tag specifies the encoding used for all characters in the configuration
# file that follow. The default is UTF-8 which is also the encoding used for all
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
# iconv built into libc) for the transcoding. See
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
# double-quotes, unless you are using Doxywizard) that should identify the
# project for which the documentation is generated. This name is used in the
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = MITK
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = @MITK_VERSION_STRING@
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF = "Medical Imaging Interaction Toolkit"
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
# the logo to the output directory.
PROJECT_LOGO =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = @MITK_DOXYGEN_OUTPUT_DIR@/Guides/Users_Guide/
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# sub-directories (in 2 levels) under the output directory of each output format
# and will distribute the generated files over these directories. Enabling this
# option can be useful when feeding doxygen a huge amount of source files, where
# putting all generated files in the same directory would otherwise causes
# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to
# control the number of sub-directories.
# The default value is: NO.
CREATE_SUBDIRS = NO
# Controls the number of sub-directories that will be created when
# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every
# level increment doubles the number of directories, resulting in 4096
# directories at level 8 which is the default and also the maximum value. The
# sub-directories are organized in 2 levels, the first level always has a fixed
# number of 16 directories.
# Minimum value: 0, maximum value: 8, default value: 8.
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
CREATE_SUBDIRS_LEVEL = 8
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
# characters to appear in the names of generated files. If set to NO, non-ASCII
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
# U+3044.
# The default value is: NO.
ALLOW_UNICODE_NAMES = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with
# English messages), Korean, Korean-en (Korean with English messages), Latvian,
# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,
# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,
# Swedish, Turkish, Ukrainian and Vietnamese.
# The default value is: English.
OUTPUT_LANGUAGE = English
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this.
# The default value is: YES.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
# description of a member or function before the detailed description
#
# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
# The default value is: YES.
REPEAT_BRIEF = YES
# This tag implements a quasi-intelligent brief description abbreviator that is
# used to form the text in various listings. Each string in this list, if found
# as the leading text of the brief description, will be stripped from the text
# and the result, after processing the whole list, is used as the annotated
# text. Otherwise, the brief description is used as-is. If left blank, the
# following values are used ($name is automatically replaced with the name of
# the entity):The $name class, The $name widget, The $name file, is, provides,
# specifies, contains, represents, a, an and the.
ABBREVIATE_BRIEF =
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# doxygen will generate a detailed section even if there is only a brief
# description.
# The default value is: NO.
ALWAYS_DETAILED_SEC = NO
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
# inherited members of a class in the documentation of that class as if those
# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
# The default value is: NO.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
# before files name in the file list and in the header files. If set to NO the
# shortest path that makes the file name unique will be used
# The default value is: YES.
FULL_PATH_NAMES = NO
# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
# Stripping is only done if one of the specified strings matches the left-hand
# part of the path. The tag can be used to show relative paths in the file list.
# If left blank the directory from which doxygen is run is used as the path to
# strip.
#
# Note that you can specify absolute paths here, but also relative paths, which
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH =
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
# header file to include in order to use a class. If left blank only the name of
# the header file containing the class definition is used. Otherwise one should
# specify the list of include paths that are normally passed to the compiler
# using the -I flag.
STRIP_FROM_INC_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
# less readable) file names. This can be useful is your file systems doesn't
# support long names like on DOS, Mac, or CD-ROM.
# The default value is: NO.
SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
# first line (until the first dot) of a Javadoc-style comment as the brief
# description. If set to NO, the Javadoc-style will behave just like regular Qt-
# style comments (thus requiring an explicit @brief command for a brief
# description.)
# The default value is: NO.
JAVADOC_AUTOBRIEF = NO
# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
# such as
# /***************
# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
# Javadoc-style will behave just like regular comments and it will not be
# interpreted by doxygen.
# The default value is: NO.
JAVADOC_BANNER = NO
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
# line (until the first dot) of a Qt-style comment as the brief description. If
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
# requiring an explicit \brief command for a brief description.)
# The default value is: NO.
QT_AUTOBRIEF = NO
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
# a brief description. This used to be the default behavior. The new default is
# to treat a multi-line C++ comment block as a detailed description. Set this
# tag to YES if you prefer the old behavior instead.
#
# Note that setting this tag to YES also means that rational rose comments are
# not recognized any more.
# The default value is: NO.
MULTILINE_CPP_IS_BRIEF = NO
# By default Python docstrings are displayed as preformatted text and doxygen's
# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
# doxygen's special commands can be used and the contents of the docstring
# documentation blocks is shown as doxygen documentation.
# The default value is: YES.
PYTHON_DOCSTRING = YES
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.
# The default value is: YES.
INHERIT_DOCS = YES
# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
# page for each member. If set to NO, the documentation of a member will be part
# of the file/class/namespace that contains it.
# The default value is: NO.
SEPARATE_MEMBER_PAGES = NO
# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
# uses this value to replace tabs by spaces in code fragments.
# Minimum value: 1, maximum value: 16, default value: 4.
TAB_SIZE = 8
# This tag can be used to specify a number of aliases that act as commands in
# the documentation. An alias has the form:
# name=value
# For example adding
# "sideeffect=@par Side Effects:^^"
# will allow you to put the command \sideeffect (or @sideeffect) in the
# documentation, which will result in a user-defined paragraph with heading
# "Side Effects:". Note that you cannot put \n's in the value part of an alias
# to insert newlines (in the resulting output). You can put ^^ in the value part
# of an alias to insert a newline as if a physical newline was in the original
# file. When you need a literal { or } or , in the value part of an alias you
# have to escape them by means of a backslash (\), this can lead to conflicts
# with the commands \{ and \} for these it is advised to use the version @{ and
# @} or use a double escape (\\{ and \\})
ALIASES = "FIXME=\par Fix Me's:\n" \
"BlueBerry=\if BLUEBERRY" \
"endBlueBerry=\endif" \
"bundlemainpage{1}=\page \1" \
"embmainpage{1}=\page \1" \
"github{2}=<a href=\"https://github.com/MITK/MITK/blob/master/\1\">\2</a>" \
"deprecatedSince{1}=\xrefitem deprecatedSince\1 \" Deprecated as of \1\" \"Functions deprecated as of \1\"" \
"minimumCMakeVersion=@MITK_CMAKE_MINIMUM_REQUIRED_VERSION@" \
"minimumQt6Version=@MITK_QT6_MINIMUM_VERSION@" \
"imageMacro{3}=\image html \1 \2 \n \image latex \1 \2 width=\3cm" \
"developersguidemainpage{1}=\page \1" \
"usersguidemainpage{1}=\mainpage" \
"nondependentPluginLink{3}= \ref \1 \"\3\""
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
# members will be omitted, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_FOR_C = NO
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
# Python sources only. Doxygen will then generate output that is more tailored
# for that language. For instance, namespaces will be presented as packages,
# qualified scopes will look different, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
# sources. Doxygen will then generate output that is tailored for Fortran.
# The default value is: NO.
OPTIMIZE_FOR_FORTRAN = NO
# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
# sources. Doxygen will then generate output that is tailored for VHDL.
# The default value is: NO.
OPTIMIZE_OUTPUT_VHDL = NO
# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
# sources only. Doxygen will then generate output that is more tailored for that
# language. For instance, namespaces will be presented as modules, types will be
# separated into more groups, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_SLICE = NO
# Doxygen selects the parser to use depending on the extension of the files it
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
# default for Fortran type files). For instance to make doxygen treat .inc files
# as Fortran files (default is PHP), and .f files as C (default is Fortran),
# use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
# the files are not read by doxygen. When specifying no_extension you should add
# * to the FILE_PATTERNS.
#
# Note see also the list of default file extension mappings.
EXTENSION_MAPPING =
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
# documentation. See https://daringfireball.net/projects/markdown/ for details.
# The output of markdown processing is further processed by doxygen, so you can
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
# case of backward compatibilities issues.
# The default value is: YES.
MARKDOWN_SUPPORT = YES
# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
# Minimum value: 0, maximum value: 99, default value: 5.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 5
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or
# globally by setting AUTOLINK_SUPPORT to NO.
# The default value is: YES.
AUTOLINK_SUPPORT = YES
# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
# to include (a tag file for) the STL sources as input, then you should set this
# tag to YES in order to let doxygen match functions declarations and
# definitions whose arguments contain STL classes (e.g. func(std::string);
# versus func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
# The default value is: NO.
BUILTIN_STL_SUPPORT = YES
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
# The default value is: NO.
CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
# will parse them like normal C++ but will assume all classes use public instead
# of private inheritance when no explicit protection keyword is present.
# The default value is: NO.
SIP_SUPPORT = NO
# For Microsoft's IDL there are propget and propput attributes to indicate
# getter and setter methods for a property. Setting this option to YES will make
# doxygen to replace the get and set methods by a property in the documentation.
# This will only work if the methods are indeed getting or setting a simple
# type. If this is not the case, or you want to show the methods anyway, you
# should set this option to NO.
# The default value is: YES.
IDL_PROPERTY_SUPPORT = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
# The default value is: NO.
DISTRIBUTE_GROUP_DOC = YES
# If one adds a struct or class to a group and this option is enabled, then also
# any nested class or struct is added to the same group. By default this option
# is disabled and one has to add nested compounds explicitly via \ingroup.
# The default value is: NO.
GROUP_NESTED_COMPOUNDS = NO
# Set the SUBGROUPING tag to YES to allow class member groups of the same type
# (for instance a group of public functions) to be put as a subgroup of that
# type (e.g. under the Public Functions section). Set it to NO to prevent
# subgrouping. Alternatively, this can be done per class using the
# \nosubgrouping command.
# The default value is: YES.
SUBGROUPING = YES
# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
# are shown inside the group in which they are included (e.g. using \ingroup)
# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
# and RTF).
#
# Note that this feature does not work in combination with
# SEPARATE_MEMBER_PAGES.
# The default value is: NO.
INLINE_GROUPED_CLASSES = NO
# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
# with only public data fields or simple typedef fields will be shown inline in
# the documentation of the scope in which they are defined (i.e. file,
# namespace, or group documentation), provided this scope is documented. If set
# to NO, structs, classes, and unions are shown on a separate page (for HTML and
# Man pages) or section (for LaTeX and RTF).
# The default value is: NO.
INLINE_SIMPLE_STRUCTS = NO
# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
# enum is documented as struct, union, or enum with the name of the typedef. So
# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
# with name TypeT. When disabled the typedef will appear as a member of a file,
# namespace, or class. And the struct will be named TypeS. This can typically be
# useful for C code in case the coding convention dictates that all compound
# types are typedef'ed and only the typedef is referenced, never the tag name.
# The default value is: NO.
TYPEDEF_HIDES_STRUCT = NO
# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
# cache is used to resolve symbols given their name and scope. Since this can be
# an expensive process and often the same symbol appears multiple times in the
# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
# doxygen will become slower. If the cache is too large, memory is wasted. The
# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
# symbols. At the end of a run doxygen will report the cache usage and suggest
# the optimal cache size from a speed point of view.
# Minimum value: 0, maximum value: 9, default value: 0.
LOOKUP_CACHE_SIZE = 0
# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use
# during processing. When set to 0 doxygen will based this on the number of
# cores available in the system. You can set it explicitly to a value larger
# than 0 to get more control over the balance between CPU load and processing
# speed. At this moment only the input processing can be done using multiple
# threads. Since this is still an experimental feature the default is set to 1,
# which effectively disables parallel processing. Please report any issues you
# encounter. Generating dot graphs in parallel is controlled by the
# DOT_NUM_THREADS setting.
# Minimum value: 0, maximum value: 32, default value: 1.
NUM_PROC_THREADS = 1
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
# documentation are documented, even if no documentation was available. Private
# class members and static file members will be hidden unless the
# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
# Note: This will also disable the warnings about undocumented members that are
# normally produced when WARNINGS is set to YES.
# The default value is: NO.
EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
# be included in the documentation.
# The default value is: NO.
EXTRACT_PRIVATE = NO
# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
# methods of a class will be included in the documentation.
# The default value is: NO.
EXTRACT_PRIV_VIRTUAL = NO
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
# The default value is: NO.
EXTRACT_PACKAGE = NO
# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
# included in the documentation.
# The default value is: NO.
EXTRACT_STATIC = YES
# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
# locally in source files will be included in the documentation. If set to NO,
# only classes defined in header files are included. Does not have any effect
# for Java sources.
# The default value is: YES.
EXTRACT_LOCAL_CLASSES = @MITK_DOXYGEN_INTERNAL_DOCS@
# This flag is only useful for Objective-C code. If set to YES, local methods,
# which are defined in the implementation section but not in the interface are
# included in the documentation. If set to NO, only methods in the interface are
# included.
# The default value is: NO.
EXTRACT_LOCAL_METHODS = NO
# If this flag is set to YES, the members of anonymous namespaces will be
# extracted and appear in the documentation as a namespace called
# 'anonymous_namespace{file}', where file will be replaced with the base name of
# the file that contains the anonymous namespace. By default anonymous namespace
# are hidden.
# The default value is: NO.
EXTRACT_ANON_NSPACES = NO
# If this flag is set to YES, the name of an unnamed parameter in a declaration
# will be determined by the corresponding definition. By default unnamed
# parameters remain unnamed in the output.
# The default value is: YES.
RESOLVE_UNNAMED_PARAMS = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
# members will be included in the various overviews, but no documentation
# section is generated. This option has no effect if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set
# to NO, these classes will be included in the various overviews. This option
# will also hide undocumented C++ concepts if enabled. This option has no effect
# if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
# declarations. If set to NO, these declarations will be included in the
# documentation.
# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = @MITK_DOXYGEN_HIDE_FRIEND_COMPOUNDS@
# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
# documentation blocks found inside the body of a function. If set to NO, these
# blocks will be appended to the function's detailed documentation block.
# The default value is: NO.
HIDE_IN_BODY_DOCS = NO
# The INTERNAL_DOCS tag determines if documentation that is typed after a
# \internal command is included. If the tag is set to NO then the documentation
# will be excluded. Set it to YES to include the internal documentation.
# The default value is: NO.
INTERNAL_DOCS = @MITK_DOXYGEN_INTERNAL_DOCS@
# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
# able to match the capabilities of the underlying filesystem. In case the
# filesystem is case sensitive (i.e. it supports files in the same directory
# whose names only differ in casing), the option must be set to YES to properly
# deal with such files in case they appear in the input. For filesystems that
# are not case sensitive the option should be set to NO to properly deal with
# output files written for symbols that only differ in casing, such as for two
# classes, one named CLASS and the other named Class, and to also support
# references to files without having to specify the exact matching casing. On
# Windows (including Cygwin) and MacOS, users should typically set this option
# to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES.
# Possible values are: SYSTEM, NO and YES.
# The default value is: SYSTEM.
CASE_SENSE_NAMES = YES
# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
# their full class and namespace scopes in the documentation. If set to YES, the
# scope will be hidden.
# The default value is: NO.
HIDE_SCOPE_NAMES = NO
# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
# append additional text to a page's title, such as Class Reference. If set to
# YES the compound reference will be hidden.
# The default value is: NO.
HIDE_COMPOUND_REFERENCE= NO
# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
# will show which file needs to be included to use the class.
# The default value is: YES.
SHOW_HEADERFILE = YES
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
# the files that are included by a file in the documentation of that file.
# The default value is: YES.
SHOW_INCLUDE_FILES = YES
# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
# grouped member an include statement to the documentation, telling the reader
# which file to include in order to use the member.
# The default value is: NO.
SHOW_GROUPED_MEMB_INC = NO
# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
# files with double quotes in the documentation rather than with sharp brackets.
# The default value is: NO.
FORCE_LOCAL_INCLUDES = NO
# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
# documentation for inline members.
# The default value is: YES.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
# (detailed) documentation of file and class members alphabetically by member
# name. If set to NO, the members will appear in declaration order.
# The default value is: YES.
SORT_MEMBER_DOCS = YES
# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
# descriptions of file, namespace and class members alphabetically by member
# name. If set to NO, the members will appear in declaration order. Note that
# this will also influence the order of the classes in the class list.
# The default value is: NO.
SORT_BRIEF_DOCS = NO
# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
# (brief and detailed) documentation of class members so that constructors and
# destructors are listed first. If set to NO the constructors will appear in the
# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
# member documentation.
# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
# detailed member documentation.
# The default value is: NO.
SORT_MEMBERS_CTORS_1ST = NO
# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
# of group names into alphabetical order. If set to NO the group names will
# appear in their defined order.
# The default value is: NO.
SORT_GROUP_NAMES = NO
# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
# fully-qualified names, including namespaces. If set to NO, the class list will
# be sorted only by class name, not including the namespace part.
# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
# Note: This option applies only to the class list, not to the alphabetical
# list.
# The default value is: NO.
SORT_BY_SCOPE_NAME = YES
# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
# type resolution of all parameters of a function it will reject a match between
# the prototype and the implementation of a member function even if there is
# only one candidate or it is obvious which candidate to choose by doing a
# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
# accept a match between prototype and implementation in such cases.
# The default value is: NO.
STRICT_PROTO_MATCHING = NO
# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
# list. This list is created by putting \todo commands in the documentation.
# The default value is: YES.
GENERATE_TODOLIST = @MITK_DOXYGEN_GENERATE_TODOLIST@
# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
# list. This list is created by putting \test commands in the documentation.
# The default value is: YES.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
# list. This list is created by putting \bug commands in the documentation.
# The default value is: YES.
GENERATE_BUGLIST = @MITK_DOXYGEN_GENERATE_BUGLIST@
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
# the deprecated list. This list is created by putting \deprecated commands in
# the documentation.
# The default value is: YES.
GENERATE_DEPRECATEDLIST= @MITK_DOXYGEN_GENERATE_DEPRECATEDLIST@
# The ENABLED_SECTIONS tag can be used to enable conditional documentation
# sections, marked by \if <section_label> ... \endif and \cond <section_label>
# ... \endcond blocks.
ENABLED_SECTIONS = @MITK_DOXYGEN_ENABLED_SECTIONS@
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
# initial value of a variable or macro / define can have for it to appear in the
# documentation. If the initializer consists of more lines than specified here
# it will be hidden. Use a value of 0 to hide initializers completely. The
# appearance of the value of individual variables and macros / defines can be
# controlled using \showinitializer or \hideinitializer command in the
# documentation regardless of this setting.
# Minimum value: 0, maximum value: 10000, default value: 30.
MAX_INITIALIZER_LINES = 0
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
# the bottom of the documentation of classes and structs. If set to YES, the
# list will mention the files that were used to generate the documentation.
# The default value is: YES.
SHOW_USED_FILES = YES
# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
# will remove the Files entry from the Quick Index and from the Folder Tree View
# (if specified).
# The default value is: YES.
SHOW_FILES = YES
# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
# page. This will remove the Namespaces entry from the Quick Index and from the
# Folder Tree View (if specified).
# The default value is: YES.
SHOW_NAMESPACES = YES
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from
# the version control system). Doxygen will invoke the program by executing (via
# popen()) the command command input-file, where command is the value of the
# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
# by doxygen. Whatever the program writes to standard output is used as the file
# version. For an example see the documentation.
FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
# by doxygen. The layout file controls the global structure of the generated
# output files in an output format independent way. To create the layout file
# that represents doxygen's defaults, run doxygen with the -l option. You can
# optionally specify a file name after the option, if omitted DoxygenLayout.xml
# will be used as the name of the layout file. See also section "Changing the
# layout of pages" for information.
#
# Note that if you run doxygen from a directory containing a file called
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
# tag is left empty.
LAYOUT_FILE =
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib
# extension is automatically appended if omitted. This requires the bibtex tool
# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references.
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated to
# standard output by doxygen. If QUIET is set to YES this implies that the
# messages are off.
# The default value is: NO.
QUIET = YES
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
# this implies that the warnings are on.
#
# Tip: Turn warnings on while writing the documentation.
# The default value is: YES.
WARNINGS = YES
# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: YES.
WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as documenting some parameters in
# a documented function twice, or documenting parameters that don't exist or
# using markup commands wrongly.
# The default value is: YES.
WARN_IF_DOC_ERROR = YES
# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
# function parameter documentation. If set to NO, doxygen will accept that some
# parameters have no documentation without warning.
# The default value is: YES.
WARN_IF_INCOMPLETE_DOC = YES
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return
# value. If set to NO, doxygen will only warn about wrong parameter
# documentation, but not about the absence of documentation. If EXTRACT_ALL is
# set to YES then this flag will automatically be disabled. See also
# WARN_IF_INCOMPLETE_DOC
# The default value is: NO.
WARN_NO_PARAMDOC = NO
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
# undocumented enumeration values. If set to NO, doxygen will accept
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: NO.
WARN_IF_UNDOC_ENUM_VAL = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
# at the end of the doxygen process doxygen will return with a non-zero status.
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
# The default value is: NO.
WARN_AS_ERROR = NO
# The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which
# will be replaced by the file and line number from which the warning originated
# and the warning text. Optionally the format may contain $version, which will
# be replaced by the version of the file (if it could be obtained via
# FILE_VERSION_FILTER)
# See also: WARN_LINE_FORMAT
# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
# In the $text part of the WARN_FORMAT command it is possible that a reference
# to a more specific place is given. To make it easier to jump to this place
# (outside of doxygen) the user can define a custom "cut" / "paste" string.
# Example:
# WARN_LINE_FORMAT = "'vi $file +$line'"
# See also: WARN_FORMAT
# The default value is: at line $line of file $file.
WARN_LINE_FORMAT = "at line $line of file $file"
# The WARN_LOGFILE tag can be used to specify a file to which warning and error
# messages should be written. If left blank the output is written to standard
# error (stderr). In case the file specified cannot be opened for writing the
# warning and error messages are written to standard error. When as file - is
# specified the warning and error messages are written to standard output
# (stdout).
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag is used to specify the files and/or directories that contain
# documented source files. You may enter file names like myfile.cpp or
# directories like /usr/src/myproject. Separate the files or directories with
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = @USERS_GUIDE_INPUT@
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see:
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# See also: INPUT_FILE_ENCODING
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
# This tag can be used to specify the character encoding of the source files
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
# character encoding on a per file pattern basis. Doxygen will compare the file
# name with each pattern and apply the encoding instead of the default
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
# "INPUT_ENCODING" for further information on supported encodings.
INPUT_FILE_ENCODING =
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen.
#
# Note the list of default checked file patterns might differ from the list of
# default file extension mappings.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.dox \
*.md
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
# The default value is: NO.
RECURSIVE = YES
# The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
#
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
# The default value is: NO.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS = modules.dox \
*/Plugins/*/documentation/doxygen/*
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# ANamespace::AClass, ANamespace::*Test
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS =
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
# command).
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank all
# files are included.
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude commands
# irrespective of the value of the RECURSIVE tag.
# The default value is: NO.
EXAMPLE_RECURSIVE = YES
# The IMAGE_PATH tag can be used to specify one or more files or directories
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH = "@MITK_SOURCE_DIR@/Documentation/Doxygen/UserManual/" \
"@MITK_SOURCE_DIR@/Plugins/" \
"@MITK_SOURCE_DIR@/Examples/Plugins/"
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command:
#
# <filter> <input-file>
#
# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
# name of an input file. Doxygen will then use the output that the filter
# program writes to standard output. If FILTER_PATTERNS is specified, this tag
# will be ignored.
#
# Note that the filter must not add or remove lines; it is applied before the
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
#
# Note that doxygen will use the data processed and written to standard output
# for further processing, therefore nothing else, like debug statements or used
# commands (so in case of a Windows batch file always use @echo OFF), should be
# written to standard output.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the
# filter if there is a match. The filters are a list of the form: pattern=filter
# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
# patterns match the file name, INPUT_FILTER is applied.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
-FILTER_PATTERNS = *.cmake=@CMakeDoxygenFilter_EXECUTABLE@
+FILTER_PATTERNS =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will also be used to filter the input files that are used for
# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
# The default value is: NO.
FILTER_SOURCE_FILES = NO
# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
# it is also possible to disable source filtering for a specific pattern using
# *.ext= (so without naming a filter).
# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
FILTER_SOURCE_PATTERNS =
# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
# is part of the input, its contents will be placed on the main page
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE =
# The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common
# extension is to allow longer lines before the automatic comment starts. The
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
# be processed before the automatic comment starts.
# Minimum value: 7, maximum value: 10000, default value: 72.
FORTRAN_COMMENT_AFTER = 72
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
# generated. Documented entities will be cross-referenced with these sources.
#
# Note: To get rid of all source code in the generated output, make sure that
# also VERBATIM_HEADERS is set to NO.
# The default value is: NO.
SOURCE_BROWSER = YES
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
# classes and enums directly into the documentation.
# The default value is: NO.
INLINE_SOURCES = NO
# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
# special comment blocks from generated source code fragments. Normal C, C++ and
# Fortran comments will always remain visible.
# The default value is: YES.
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
# entity all documented functions referencing it will be listed.
# The default value is: NO.
REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES then for each documented function
# all documented entities called/used by that function will be listed.
# The default value is: NO.
REFERENCES_RELATION = YES
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
# to YES then the hyperlinks from functions in REFERENCES_RELATION and
# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
# link to the documentation.
# The default value is: YES.
REFERENCES_LINK_SOURCE = YES
# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
# source code will show a tooltip with additional information such as prototype,
# brief description and links to the definition and documentation. Since this
# will make the HTML file larger and loading of large files a bit slower, you
# can opt to disable this feature.
# The default value is: YES.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
SOURCE_TOOLTIPS = YES
# If the USE_HTAGS tag is set to YES then the references to source code will
# point to the HTML generated by the htags(1) tool instead of doxygen built-in
# source browser. The htags tool is part of GNU's global source tagging system
# (see https://www.gnu.org/software/global/global.html). You will need version
# 4.8.6 or higher.
#
# To use it do the following:
# - Install the latest version of global
# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
# - Make sure the INPUT points to the root of the source tree
# - Run doxygen as normal
#
# Doxygen will invoke htags (and that will in turn invoke gtags), so these
# tools must be available from the command line (i.e. in the search path).
#
# The result: instead of the source browser generated by doxygen, the links to
# source code will now point to the output of htags.
# The default value is: NO.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
USE_HTAGS = NO
# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
# verbatim copy of the header file for each class for which an include is
# specified. Set to NO to disable this.
# See also: Section \class.
# The default value is: YES.
VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
# clang parser (see:
# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
# performance. This can be particularly helpful with template rich C++ code for
# which doxygen's built-in parser lacks the necessary type information.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
# tag is set to YES then doxygen will add the directory of each input to the
# include path.
# The default value is: YES.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_ADD_INC_PATHS = YES
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
# If clang assisted parsing is enabled you can provide the clang parser with the
# path to the directory containing a file called compile_commands.json. This
# file is the compilation database (see:
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
# options used when the source files were built. This is equivalent to
# specifying the -p option to a clang tool, such as clang-check. These options
# will then be passed to the parser. Any options specified with CLANG_OPTIONS
# will be added as well.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
CLANG_DATABASE_PATH =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
# compounds will be generated. Enable this if the project contains a lot of
# classes, structs, unions or interfaces.
# The default value is: YES.
ALPHABETICAL_INDEX = YES
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
# that should be ignored while generating the index headers. The IGNORE_PREFIX
# tag works for classes, function and member names. The entity will be placed in
# the alphabetical list under the first letter of the entity name that remains
# after removing the prefix.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
# The default value is: YES.
GENERATE_HTML = NO
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = html
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).
# The default value is: .html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FILE_EXTENSION = .html
# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
# each generated HTML page. If the tag is left blank doxygen will generate a
# standard header.
#
# To get valid HTML the header file that includes any scripts and style sheets
# that doxygen needs, which is dependent on the configuration options used (e.g.
# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
# default header using
# doxygen -w html new_header.html new_footer.html new_stylesheet.css
# YourConfigFile
# and then modify the file new_header.html. See also section "Doxygen usage"
# for information on how to generate the default header that doxygen normally
# uses.
# Note: The header is subject to change so you typically have to regenerate the
# default header when upgrading to a newer version of doxygen. For a description
# of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
# generated HTML page. If the tag is left blank doxygen will generate a standard
# footer. See HTML_HEADER for more information on how to generate a default
# footer and what special commands can be used inside the footer. See also
# section "Doxygen usage" for information on how to generate the default footer
# that doxygen normally uses.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
# sheet that is used by each HTML page. It can be used to fine-tune the look of
# the HTML output. If left blank doxygen will generate a default style sheet.
# See also section "Doxygen usage" for information on how to generate the style
# sheet that doxygen normally uses.
# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
# it is more robust and this tag (HTML_STYLESHEET) will in the future become
# obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_STYLESHEET =
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
# created by doxygen. Using this option one can overrule certain style aspects.
# This is preferred over using HTML_STYLESHEET since it does not replace the
# standard style sheet and is therefore more robust against future updates.
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list).
# Note: Since the styling of scrollbars can currently not be overruled in
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
# one or more extra stylesheets have been specified. So if scrollbar
# customization is desired it has to be added explicitly. For an example see the
# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET = @MITK_DOXYGEN_STYLESHEET@
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
# that these files will be copied to the base HTML output directory. Use the
# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
# files will be copied as-is; there are no commands or markers available.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_FILES =
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
# should be rendered with a dark or light theme.
# Possible values are: LIGHT always generate light mode output, DARK always
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
# the user preference, use light mode if no preference is set (the default),
# AUTO_DARK automatically set the mode according to the user preference, use
# dark mode if no preference is set and TOGGLE allow to user to switch between
# light and dark mode via a button.
# The default value is: AUTO_LIGHT.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE = AUTO_LIGHT
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a color-wheel, see
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again.
# Minimum value: 0, maximum value: 359, default value: 220.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_HUE = 220
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
# in the HTML output. For a value of 0 the output will use gray-scales only. A
# value of 255 will produce the most vivid colors.
# Minimum value: 0, maximum value: 255, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_SAT = 100
# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
# luminance component of the colors in the HTML output. Values below 100
# gradually make the output lighter, whereas values above 100 make the output
# darker. The value divided by 100 is the actual gamma applied, so 80 represents
# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
# change the gamma.
# Minimum value: 40, maximum value: 240, default value: 80.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
# to YES can help to show when doxygen was last run and thus if the
# documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = YES
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
# are dynamically created via JavaScript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
# page. Disable this option to support browsers that do not have JavaScript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_MENUS = YES
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_SECTIONS = @MITK_DOXYGEN_HTML_DYNAMIC_SECTIONS@
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand
# and collapse entries dynamically later on. Doxygen will expand the tree to
# such a level that at most the specified number of entries are visible (unless
# a fully collapsed tree already exceeds this amount). So setting the number of
# entries 1 will produce a full collapsed tree by default. 0 is a special value
# representing an infinite number of entries and will result in a full expanded
# tree by default.
# Minimum value: 0, maximum value: 9999, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
# environment (see:
# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
# create a documentation set, doxygen will generate a Makefile in the HTML
# output directory. Running make will produce the docset in that directory and
# running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_DOCSET = NO
# This tag determines the name of the docset feed. A documentation feed provides
# an umbrella under which multiple documentation sets from a single provider
# (such as a company or product suite) can be grouped.
# The default value is: Doxygen generated docs.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDNAME = "Doxygen generated docs"
# This tag determines the URL of the docset feed. A documentation feed provides
# an umbrella under which multiple documentation sets from a single provider
# (such as a company or product suite) can be grouped.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDURL =
# This tag specifies a string that should uniquely identify the documentation
# set bundle. This should be a reverse domain-name style string, e.g.
# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_BUNDLE_ID = org.doxygen.Project
# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
# the documentation publisher. This should be a reverse domain-name style
# string, e.g. com.mycompany.MyDocSet.documentation.
# The default value is: org.doxygen.Publisher.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
# The default value is: Publisher.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
# on Windows. In the beginning of 2021 Microsoft took the original page, with
# a.o. the download links, offline the HTML help workshop was already many years
# in maintenance mode). You can download the HTML help workshop from the web
# archives at Installation executable (see:
# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
# files are now used as the Windows 98 help format, and will replace the old
# Windows help format (.hlp) on all Windows platforms in the future. Compressed
# HTML files also contain an index, a table of contents, and you can search for
# words in the documentation. The HTML workshop also contains a viewer for
# compressed HTML files.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_HTMLHELP = NO
# The CHM_FILE tag can be used to specify the file name of the resulting .chm
# file. You can add a path in front of the file if the result should not be
# written to the html output directory.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_FILE =
# The HHC_LOCATION tag can be used to specify the location (absolute path
# including file name) of the HTML help compiler (hhc.exe). If non-empty,
# doxygen will try to run the HTML help compiler on the generated index.hhp.
# The file has to be specified with full path.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated
# (YES) or that it should be included in the main .chm file (NO).
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
GENERATE_CHI = NO
# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
# and project file content.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_INDEX_ENCODING =
# The BINARY_TOC flag controls whether a binary table of contents is generated
# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
# enables the Previous and Next buttons.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members to
# the table of contents of the HTML help documentation and to the tree view.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
TOC_EXPAND = NO
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
# (.qch) of the generated HTML documentation.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_QHP = @MITK_DOXYGEN_GENERATE_QHP@
# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
# the file name of the resulting .qch file. The path specified is relative to
# the HTML output folder.
# This tag requires that the tag GENERATE_QHP is set to YES.
QCH_FILE = @MITK_DOXYGEN_QCH_FILE@
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
# (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_NAMESPACE = org.mitk
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_VIRTUAL_FOLDER = MITK
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
# Filters (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
# The QHG_LOCATION tag can be used to specify the location (absolute path
# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
# run qhelpgenerator on the generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION = @QT_HELPGENERATOR_EXECUTABLE@
# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
# generated, together with the HTML files, they form an Eclipse help plugin. To
# install this plugin and make it available under the help contents menu in
# Eclipse, the contents of the directory containing the HTML and XML files needs
# to be copied into the plugins directory of eclipse. The name of the directory
# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
# After copying Eclipse needs to be restarted before the help appears.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_ECLIPSEHELP = NO
# A unique identifier for the Eclipse help plugin. When installing the plugin
# the directory name containing the HTML and XML files should also have this
# name. Each documentation set should have its own identifier.
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
ECLIPSE_DOC_ID = org.doxygen.Project
# If you want full control over the layout of the generated HTML pages it might
# be necessary to disable the index and replace it with your own. The
# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
# of each HTML page. A value of NO enables the index and the value YES disables
# it. Since the tabs in the index contain the same information as the navigation
# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
DISABLE_INDEX = NO
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
# structure should be generated to display hierarchical information. If the tag
# value is set to YES, a side panel will be generated containing a tree-like
# index structure (just like the one that is generated for HTML Help). For this
# to work a browser that supports JavaScript, DHTML, CSS and frames is required
# (i.e. any modern browser). Windows users are probably better off using the
# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
# further fine tune the look of the index (see "Fine-tuning the output"). As an
# example, the default style sheet generated by doxygen has an example that
# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
# Since the tree basically has the same information as the tab index, you could
# consider setting DISABLE_INDEX to YES when enabling this option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = YES
# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
# area (value NO) or if it should extend to the full height of the window (value
# YES). Setting this to YES gives a layout similar to
# https://docs.readthedocs.io with more room for contents, but less room for the
# project logo, title, and description. If either GENERATE_TREEVIEW or
# DISABLE_INDEX is set to NO, this option has no effect.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
FULL_SIDEBAR = NO
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
# doxygen will group on one line in the generated HTML documentation.
#
# Note that a value of 0 will completely suppress the enum values from appearing
# in the overview section.
# Minimum value: 0, maximum value: 20, default value: 4.
# This tag requires that the tag GENERATE_HTML is set to YES.
ENUM_VALUES_PER_LINE = 4
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
# to set the initial width (in pixels) of the frame in which the tree is shown.
# Minimum value: 0, maximum value: 1500, default value: 250.
# This tag requires that the tag GENERATE_HTML is set to YES.
TREEVIEW_WIDTH = 300
# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
# external symbols imported via tag files in a separate window.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
EXT_LINKS_IN_WINDOW = NO
# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
# addresses.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
OBFUSCATE_EMAILS = YES
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
# the HTML output. These images will generally look nicer at scaled resolutions.
# Possible values are: png (the default) and svg (looks nicer but requires the
# pdf2svg or inkscape tool).
# The default value is: png.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FORMULA_FORMAT = png
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# doxygen run you need to manually remove any form_*.png images from the HTML
# output directory to force them to be regenerated.
# Minimum value: 8, maximum value: 50, default value: 10.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_FONTSIZE = 10
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details.
FORMULA_MACROFILE =
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# https://www.mathjax.org) which uses client side JavaScript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
# to it using the MATHJAX_RELPATH option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
USE_MATHJAX = NO
# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
# Note that the different versions of MathJax have different requirements with
# regards to the different settings, so it is possible that also other MathJax
# settings have to be changed when switching between the different MathJax
# versions.
# Possible values are: MathJax_2 and MathJax_3.
# The default value is: MathJax_2.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_VERSION = MathJax_2
# When MathJax is enabled you can set the default output format to be used for
# the MathJax output. For more details about the output format see MathJax
# version 2 (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
# (see:
# http://docs.mathjax.org/en/latest/web/components/output.html).
# Possible values are: HTML-CSS (which is slower, but has the best
# compatibility. This is the name for Mathjax version 2, for MathJax version 3
# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
# is the name for Mathjax version 3, for MathJax version 2 this will be
# translated into HTML-CSS) and SVG.
# The default value is: HTML-CSS.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_FORMAT = HTML-CSS
# When MathJax is enabled you need to specify the location relative to the HTML
# output directory using the MATHJAX_RELPATH option. The destination directory
# should contain the MathJax.js script. For instance, if the mathjax directory
# is located at the same level as the HTML output directory, then
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from https://www.mathjax.org before deployment. The default value is:
# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://www.mathjax.org/mathjax
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example
# for MathJax version 2 (see
# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# For example for MathJax version 3 (see
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
# MATHJAX_EXTENSIONS = ams
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
# (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_CODEFILE =
# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
# the HTML output. The underlying search engine uses javascript and DHTML and
# should work on any modern browser. Note that when using HTML help
# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
# there is already a search function so this one should typically be disabled.
# For large projects the javascript based search engine can be slow, then
# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
# search using the keyboard; to jump to the search box use <access key> + S
# (what the <access key> is depends on the OS and browser, but it is typically
# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
# key> to jump into the search results window, the results can be navigated
# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
# the search. The filter options can be selected when the cursor is inside the
# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
# to select a filter and <Enter> or <escape> to activate or cancel the filter
# option.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
# implemented using a web server instead of a web client using JavaScript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
# and searching needs to be provided by external tools. See the section
# "External Indexing and Searching" for details.
# The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES.
SERVER_BASED_SEARCH = NO
# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
# script for searching. Instead the search results are written to an XML file
# which needs to be processed by an external indexer. Doxygen will invoke an
# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
# search results.
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see:
# https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH = NO
# The SEARCHENGINE_URL should point to a search engine hosted by a web server
# which will return the search results when EXTERNAL_SEARCH is enabled.
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see:
# https://xapian.org/). See the section "External Indexing and Searching" for
# details.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
# search data is written to a file for indexing by an external tool. With the
# SEARCHDATA_FILE tag the name of this file can be specified.
# The default file is: searchdata.xml.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHDATA_FILE = searchdata.xml
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
# projects and redirect the results back to the right project.
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH_ID =
# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
# projects other than the one defined by this configuration file, but that are
# all added to the same external search index. Each project needs to have a
# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
# to a relative location where the documentation can be found. The format is:
# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
# The default value is: YES.
GENERATE_LATEX = YES
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: latex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked.
#
# Note that when not enabling USE_PDFLATEX the default is latex when enabling
# USE_PDFLATEX the default is pdflatex and when in the later case latex is
# chosen this is overwritten by pdflatex. For specific output languages the
# default can have been set differently, this depends on the implementation of
# the output language.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
# index for LaTeX.
# Note: This tag is used in the Makefile / make.bat.
# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
# (.tex).
# The default file is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
# generate index for LaTeX. In case there is no backslash (\) as first character
# it will be automatically added in the LaTeX code.
# Note: This tag is used in the generated output file (.tex).
# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
# The default value is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_MAKEINDEX_CMD = makeindex
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used by the
# printer.
# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
# 14 inches) and executive (7.25 x 10.5 inches).
# The default value is: a4.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. The package can be specified just
# by its name or with the correct syntax as to be used with the LaTeX
# \usepackage command. To get the times font for instance you can specify :
# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
# To use the option intlimits with the amsmath package you can specify:
# EXTRA_PACKAGES=[intlimits]{amsmath}
# If left blank no extra packages will be included.
# This tag requires that the tag GENERATE_LATEX is set to YES.
EXTRA_PACKAGES = amssymb
# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
# the generated LaTeX document. The header should contain everything until the
# first chapter. If it is left blank doxygen will generate a standard header. It
# is highly recommended to start with a default header using
# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
# and then modify the file new_header.tex. See also section "Doxygen usage" for
# information on how to generate the default header that doxygen normally uses.
#
# Note: Only use a user-defined header if you know what you are doing!
# Note: The header is subject to change so you typically have to regenerate the
# default header when upgrading to a newer version of doxygen. The following
# commands have a special meaning inside the header (and footer): For a
# description of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
# the generated LaTeX document. The footer should contain everything after the
# last chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer. See also section "Doxygen
# usage" for information on how to generate the default footer that doxygen
# normally uses. Note: Only use a user-defined footer if you know what you are
# doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# LaTeX style sheets that are included after the standard style sheets created
# by doxygen. Using this option one can overrule certain style aspects. Doxygen
# will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list).
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_STYLESHEET =
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the LATEX_OUTPUT output
# directory. Note that the files will be copied as-is; there are no commands or
# markers available.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_FILES =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
# contain links (just like the HTML output) instead of page references. This
# makes the output suitable for online browsing using a PDF viewer.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = YES
# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
# files. Set this option to YES, to get a higher quality PDF documentation.
#
# See also section LATEX_CMD_NAME for selecting the engine.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
# if errors occur, instead of asking the user for help.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BATCHMODE = YES
# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
# index chapters (such as File Index, Compound Index, etc.) in the output.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HIDE_INDICES = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# The default value is: plain.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered,
# it will be relative to the LATEX_OUTPUT directory. If left blank the
# LATEX_OUTPUT directory will be used.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EMOJI_DIRECTORY =
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
# RTF output is optimized for Word 97 and may not look too pretty with other RTF
# readers/editors.
# The default value is: NO.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: rtf.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
# contain hyperlink fields. The RTF file will contain links (just like the HTML
# output) instead of page references. This makes the output suitable for online
# browsing using Word or some other Word compatible readers that support those
# fields.
#
# Note: WordPad (write) and others do not support links.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# configuration file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
#
# See also section "Doxygen usage" for information on how to generate the
# default style sheet that doxygen normally uses.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
# similar to doxygen's configuration file. A template extensions file can be
# generated using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
# classes and files.
# The default value is: NO.
GENERATE_MAN = NO
# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it. A directory man3 will be created inside the directory specified by
# MAN_OUTPUT.
# The default directory is: man.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_OUTPUT = man
# The MAN_EXTENSION tag determines the extension that is added to the generated
# man pages. In case the manual section does not start with a number, the number
# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
# optional.
# The default value is: .3.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_EXTENSION = .3
# The MAN_SUBDIR tag determines the name of the directory created within
# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
# MAN_EXTENSION with the initial . removed.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_SUBDIR =
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
# will generate one additional man file for each entity documented in the real
# man page(s). These additional files only source the real man page, but without
# them the man command would be unable to find the correct page.
# The default value is: NO.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_LINKS = NO
#---------------------------------------------------------------------------
# Configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
# captures the structure of the code including all documentation.
# The default value is: NO.
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: xml.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_OUTPUT = xml
# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
# listings (including syntax highlighting and cross-referencing information) to
# the XML output. Note that enabling this will significantly increase the size
# of the XML output.
# The default value is: YES.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_PROGRAMLISTING = YES
# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
# namespace members in file scope as well, matching the HTML output.
# The default value is: NO.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_NS_MEMB_FILE_SCOPE = NO
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
# that can be used to generate PDF.
# The default value is: NO.
GENERATE_DOCBOOK = NO
# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
# front of it.
# The default directory is: docbook.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_OUTPUT = docbook
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
# the structure of the code including all documentation. Note that this feature
# is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
# file that captures the structure of the code including all documentation.
#
# Note that this feature is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_PERLMOD = NO
# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
# output from the Perl module output.
# The default value is: NO.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
# formatted so it can be parsed by a human reader. This is useful if you want to
# understand what is going on. On the other hand, if this tag is set to NO, the
# size of the Perl module output will be much smaller and Perl will parse it
# just the same.
# The default value is: YES.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_PRETTY = YES
# The names of the make variables in the generated doxyrules.make file are
# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
# so different doxyrules.make files included by the same Makefile don't
# overwrite each other's variables.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
# C-preprocessor directives found in the sources and include files.
# The default value is: YES.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
# in the source code. If set to NO, only conditional compilation will be
# performed. Macro expansion can be done in a controlled way by setting
# EXPAND_ONLY_PREDEF to YES.
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
# EXPAND_AS_DEFINED tags.
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = NO
# If the SEARCH_INCLUDES tag is set to YES, the include files in the
# INCLUDE_PATH will be searched if a #include is found.
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by the
# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
# RECURSIVE has no effect here.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will be
# used.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names that are
# defined before the preprocessor is started (similar to the -D option of e.g.
# gcc). The argument of the tag is a list of macros of the form: name or
# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
# is assumed. To prevent a macro definition from being undefined via #undef or
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
# macro definition that is found in the sources will be used. Use the PREDEFINED
# tag if you want to use a different macro definition that overrules the
# definition found in the source code.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all references to function-like macros that are alone on a line, have
# an all uppercase name, and do not end with a semicolon. Such function macros
# are typically used for boiler-plate code, and will confuse the parser if not
# removed.
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration options related to external references
#---------------------------------------------------------------------------
# The TAGFILES tag can be used to specify one or more tag files. For each tag
# file the location of the external documentation should be added. The format of
# a tag file without this location is as follows:
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
# TAGFILES = file1=loc1 "file2 = loc2" ...
# where loc1 and loc2 can be relative or absolute paths or URLs. See the
# section "Linking to external documentation" for more information about the use
# of tag files.
# Note: Each tag file must have a unique name (where the name does NOT include
# the path). If a tag file is not located in the directory in which doxygen is
# run, you must also specify the path to the tagfile here.
TAGFILES = @BLUEBERRY_DOXYGEN_TAGFILE@
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
# tag file that is based on the input files it reads. See section "Linking to
# external documentation" for more information about the usage of tag files.
GENERATE_TAGFILE = @MITK_DOXYGEN_TAGFILE_NAME@
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# the class index. If set to NO, only the inherited external classes will be
# listed.
# The default value is: NO.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will be
# listed.
# The default value is: YES.
EXTERNAL_GROUPS = NO
# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
# the related pages index. If set to NO, only the current project's pages will
# be listed.
# The default value is: YES.
EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
DIA_PATH =
# If set to YES the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class.
# The default value is: YES.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz (see:
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: NO.
HAVE_DOT = @HAVE_DOT@
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of
# processors available in the system. You can set it explicitly to a value
# larger than 0 to get control over the balance between CPU load and processing
# speed.
# Minimum value: 0, maximum value: 32, default value: 0.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NUM_THREADS = @MITK_DOXYGEN_DOT_NUM_THREADS@
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
# subgraphs. When you want a differently looking font in the dot files that
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
# Edge and Graph Attributes specification</a> You need to make sure dot is able
# to find the font, which can be done by putting it in a standard location or by
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
# directory containing the font. Default graphviz fontsize is 14.
# The default value is: fontname=Helvetica,fontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
# arrows shapes.</a>
# The default value is: labelfontname=Helvetica,labelfontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
# around nodes set 'shape=plain' or 'shape=plaintext' <a
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
# The default value is: shape=box,height=0.2,width=0.4.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
# You can set the path where dot can find font specified with fontname in
# DOT_COMMON_ATTR and others dot attributes.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
# graph for each documented class showing the direct and indirect inheritance
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
# to TEXT the direct and indirect inheritance relations will be shown as texts /
# links.
# Possible values are: NO, YES, TEXT and GRAPH.
# The default value is: YES.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
# graph for each documented class showing the direct and indirect implementation
# dependencies (inheritance, containment, and class references variables) of the
# class with other documented classes.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
# groups, showing the direct groups dependencies. See also the chapter Grouping
# in the manual.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GROUP_GRAPHS = YES
# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
UML_LOOK = @MITK_DOXYGEN_UML_LOOK@
# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
# class node. If there are many fields or methods and many nodes the graph may
# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
# number of items for each type to make the size more manageable. Set this to 0
# for no limit. Note that the threshold may be exceeded by 50% before the limit
# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
# but if the number exceeds 15, the total amount of fields shown is limited to
# 10.
# Minimum value: 0, maximum value: 100, default value: 10.
# This tag requires that the tag UML_LOOK is set to YES.
UML_LIMIT_NUM_FIELDS = 10
# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
# tag is set to YES, doxygen will add type and arguments for attributes and
# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
# will not generate fields with class member information in the UML graphs. The
# class diagrams will look similar to the default class diagrams but using UML
# notation for the relationships.
# Possible values are: NO, YES and NONE.
# The default value is: NO.
# This tag requires that the tag UML_LOOK is set to YES.
DOT_UML_DETAILS = NO
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold
# significantly it will wrapped across multiple lines. Some heuristics are apply
# to avoid ugly line breaks.
# Minimum value: 0, maximum value: 1000, default value: 17.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_WRAP_THRESHOLD = 17
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
# collaboration graphs will show the relations between templates and their
# instances.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
TEMPLATE_RELATIONS = YES
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
# YES then doxygen will generate a graph for each documented file showing the
# direct and indirect include dependencies of the file with other documented
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDE_GRAPH = NO
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
# set to YES then doxygen will generate a graph for each documented file showing
# the direct and indirect include dependencies of the file with other documented
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDED_BY_GRAPH = NO
# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
# dependency graph for every global function or class method.
#
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable call graphs for selected
# functions only using the \callgraph command. Disabling a call graph can be
# accomplished by means of the command \hidecallgraph.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
CALL_GRAPH = NO
# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
# dependency graph for every global function or class method.
#
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable caller graphs for selected
# functions only using the \callergraph command. Disabling a caller graph can be
# accomplished by means of the command \hidecallergraph.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
CALLER_GRAPH = NO
# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
# hierarchy of all classes instead of a textual one.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GRAPHICAL_HIERARCHY = NO
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
# dependencies a directory has on other directories in a graphical way. The
# dependency relations are determined by the #include relations between the
# files in the directories.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
DIRECTORY_GRAPH = YES
# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels
# of child directories generated in directory dependency graphs by dot.
# Minimum value: 1, maximum value: 25, default value: 1.
# This tag requires that the tag DIRECTORY_GRAPH is set to YES.
DIR_GRAPH_MAX_DEPTH = 1
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see:
# http://www.graphviz.org/)).
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus.
# The default value is: png.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_IMAGE_FORMAT = png
# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
# enable generation of interactive SVG images that allow zooming and panning.
#
# Note that this requires a modern browser other than Internet Explorer. Tested
# and working are Firefox, Chrome, Safari, and Opera.
# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
# the SVG files visible. Older versions of IE do not have SVG support.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
INTERACTIVE_SVG = NO
# The DOT_PATH tag can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found in the path.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_PATH = @DOXYGEN_DOT_PATH@
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the \dotfile
# command).
# This tag requires that the tag HAVE_DOT is set to YES.
DOTFILE_DIRS =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
MSCFILE_DIRS =
# The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile
# command).
DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
# path where java can find the plantuml.jar file or to the filename of jar file
# to be used. If left blank, it is assumed PlantUML is not used or called during
# a preprocessing step. Doxygen will generate a warning when it encounters a
# \startuml command in this case and will not generate output for the diagram.
PLANTUML_JAR_PATH =
# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
# configuration file for plantuml.
PLANTUML_CFG_FILE =
# When using plantuml, the specified paths are searched for files specified by
# the !include statement in a plantuml block.
PLANTUML_INCLUDE_PATH =
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
# that will be shown in the graph. If the number of nodes in a graph becomes
# larger than this value, doxygen will truncate the graph, which is visualized
# by representing a node as a red box. Note that doxygen if the number of direct
# children of the root node in a graph is already larger than
# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
# Minimum value: 0, maximum value: 10000, default value: 50.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_GRAPH_MAX_NODES = 50
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
# generated by dot. A depth value of 3 means that only nodes reachable from the
# root by following a path via at most 3 edges will be shown. Nodes that lay
# further from the root node will be omitted. Note that setting this option to 1
# or 2 may greatly reduce the computation time needed for large code bases. Also
# note that the size of a graph can be further restricted by
# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
# Minimum value: 0, maximum value: 1000, default value: 0.
# This tag requires that the tag HAVE_DOT is set to YES.
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
# this, this feature is disabled by default.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_MULTI_TARGETS = NO
# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
# explaining the meaning of the various boxes and arrows in the dot generated
# graphs.
# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal
# graphical representation for inheritance and collaboration diagrams is used.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs.
#
# Note: This setting is not only used for dot files but also for msc temporary
# files.
# The default value is: YES.
DOT_CLEANUP = YES
diff --git a/Examples/DocumentationExample.h b/Examples/DocumentationExample.h
index 1253cc69f0..af38be5fb6 100644
--- a/Examples/DocumentationExample.h
+++ b/Examples/DocumentationExample.h
@@ -1,67 +1,67 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
/**
* \brief This is a class for showing how to document your code using doxygen.
*
* The more detailed description is needed for some more elaborate description. Here you can describe
* anything anyone might ever want to know about your class. Of especial interest might be to mention
-* what it can be used for or what its main purpose is. If you want you can even use pictures (you migth want
+* what it can be used for or what its main purpose is. If you want you can even use pictures (you might want
* take a look at the doxygen documentation for that).
*/
class DocumentationExample
{
public:
/**
* \brief A constructor.
* A more elaborate description of the constructor.
*/
DocumentationExample();
/**
* \brief A destructor.
* A more elaborate description of the destructor.
*/
~DocumentationExample();
/**
* \brief Casts the char at position number to an int and returns it
*
* This function will take an int number and a char pointer name, then will try to access the int a position number
* and cast it to an int and return it. No verification is done within the function to ensure, that it is a valid
* position.
*
* @param number The position of the char to be cast. This should never be below 0 and not above the length of the char
* array.
* @param name A constant character pointer.
- * @return The char value of the charakter at position number casted to int
+ * @return The char value of the character at position number casted to int
*/
int ExampleCastCharToInt(int number, const char *name);
/**
* \brief Tests whether an integer is within the bounds of a string
*
* This will perform a check whether an int is bigger than an arbitrary zero and smaller than the length of the string.
* @param itsAString The string to be checked as reference
* @param theInt The position to be checked for
* @exception std::out_of_range parameter is out of range.
* @see zero()
* @return True if character is in range, should never return false but throw an exception instead.
*/
bool TestWithin(std::string itsAString, int theInt) throw(std::out_of_range);
/**
* \brief The public definition of zero
*
* This variable is used to determine what will be regarded as zero.
*/
int zero;
};
diff --git a/Examples/Dump/mitkdump.cpp b/Examples/Dump/mitkdump.cpp
index 33ef0ad2cf..8726c8cc98 100644
--- a/Examples/Dump/mitkdump.cpp
+++ b/Examples/Dump/mitkdump.cpp
@@ -1,283 +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.
============================================================================*/
/**
\file mitkdump.cpp
\brief Commandline application to see what DICOMFileReaderSelector would produce from a set of files.
Usage:
\verbatim
mitkdump [-v] file1 [... fileN]
-v output more details on commandline
fileN DICOM file
\endverbatim
The application will ask a DICOMFileReaderSelector to analyze the files given as file1 .. fileN.
Once the reader with the least number of files is selected, this result is printed to commandline.
If the "-v" flag is used (as a first parameter), the output will contain details about filenames,
which can make the output considerably harder to read.
Output is also written to a log file of name "%gt;datetime-stamp%lt;_dir_&gt;directory-name&lt;.mitkdump
*/
#include "mitkDICOMFileReaderSelector.h"
#include "mitkDICOMImageFrameInfo.h"
#include "mitkDICOMReaderConfigurator.h"
using mitk::DICOMTag;
std::string buildDateString()
{
std::time_t rawtime;
std::tm *timeinfo;
char buffer[80];
std::time(&rawtime);
timeinfo = std::localtime(&rawtime);
std::strftime(buffer, 80, "%Y%m%d-%H%M%S", timeinfo);
return std::string(buffer);
}
void gen_random(char *s, const int len)
{
static const char alphanum[] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (int i = 0; i < len; ++i)
{
s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
}
s[len] = 0;
}
std::string gen_random(const int len)
{
if (len > 0 && len < 100)
{
char retval[100];
gen_random(retval, len);
return std::string(retval);
}
else
{
return std::string("");
}
}
std::string removeUnsafeChars(const std::string &str)
{
std::string retval;
for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
{
const char &c = *it;
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '.') || (c == '-') ||
(c == '_'))
{
retval += c;
}
}
return retval;
}
std::string extractDirString(const std::string &dirString)
{
std::string wholeprefix = dirString.substr(0, dirString.find_last_of("/\\"));
std::string lastDirectoryPart = wholeprefix.substr(wholeprefix.find_last_of("/\\") + 1);
std::string cleanLastDirectoryPart = removeUnsafeChars(lastDirectoryPart);
if (!cleanLastDirectoryPart.empty())
{
return cleanLastDirectoryPart;
}
else
{
std::stringstream emptydirname;
emptydirname << "noname_" << gen_random(6);
return emptydirname.str();
}
}
int main(int argc, char *argv[])
{
bool fileDetails(false);
bool loadimage(false);
int firstFileIndex = 1;
// see if we got the '-v' flag to output file details
if (argc > 1 && std::string(argv[firstFileIndex]) == "-v")
{
fileDetails = true;
++firstFileIndex;
}
// see if we got the '-l' flag
if (argc > 1 && std::string(argv[firstFileIndex]) == "-l")
{
loadimage = true;
++firstFileIndex;
}
// analyze files from argv
mitk::StringList inputFiles;
for (int a = firstFileIndex; a < argc; ++a)
{
inputFiles.push_back(std::string(argv[a]));
}
if (inputFiles.empty())
{
MITK_INFO << "0 input files given, exiting...";
return EXIT_SUCCESS;
}
mitk::DICOMFileReaderSelector::Pointer configSelector = mitk::DICOMFileReaderSelector::New();
- configSelector->LoadBuiltIn3DConfigs(); // a set of compiled in ressources with standard configurations that work well
+ configSelector->LoadBuiltIn3DConfigs(); // a set of compiled in resources with standard configurations that work well
configSelector->SetInputFiles(inputFiles);
mitk::DICOMFileReader::Pointer reader = configSelector->GetFirstReaderWithMinimumNumberOfOutputImages();
if (reader.IsNull())
{
MITK_ERROR << "Could not configure any DICOM reader.. Exiting...";
return EXIT_FAILURE;
}
// output best reader result
MITK_INFO << "---- Best reader configuration '" << reader->GetConfigurationLabel() << "' with "
<< reader->GetNumberOfOutputs() << " outputs";
if (fileDetails)
{
reader->PrintOutputs(std::cout, fileDetails);
}
// construct the name of a log file
std::string datestring = buildDateString();
;
std::string dirString = extractDirString(argv[firstFileIndex]);
std::string logfilename = datestring + "_dir_" + dirString + ".mitkdump";
MITK_INFO << "Logfile " << logfilename;
// write output to file for later analysis
std::ofstream fs;
fs.open(logfilename.c_str());
fs << "---- " << dirString << ": Best reader configuration '" << reader->GetConfigurationLabel() << "' with "
<< reader->GetNumberOfOutputs() << " outputs" << std::endl;
reader->PrintOutputs(fs, true); // always verbose in log file
fs.close();
// serialize, deserialize, analyze again, verify result!
mitk::DICOMReaderConfigurator::Pointer serializer = mitk::DICOMReaderConfigurator::New();
std::string readerSerialization = serializer->CreateConfigStringFromReader(reader.GetPointer());
bool outputError(false);
for (unsigned int outputIndex = 0; outputIndex < reader->GetNumberOfOutputs(); ++outputIndex)
{
const mitk::DICOMImageBlockDescriptor &outputDescriptor = reader->GetOutput(outputIndex);
mitk::StringList filenamesOfThisGroup;
const mitk::DICOMImageFrameList &frames = outputDescriptor.GetImageFrameList();
for (auto fIter = frames.begin(); fIter != frames.end(); ++fIter)
{
filenamesOfThisGroup.push_back((*fIter)->Filename);
}
mitk::DICOMReaderConfigurator::Pointer readerConfigurator = mitk::DICOMReaderConfigurator::New();
mitk::DICOMFileReader::Pointer dicomReader = readerConfigurator->CreateFromUTF8ConfigString(readerSerialization);
dicomReader->SetInputFiles(filenamesOfThisGroup);
dicomReader->AnalyzeInputFiles();
if (dicomReader->GetNumberOfOutputs() != 1)
{
MITK_ERROR << "****** Re-analyzing files of output group " << outputIndex << " yields "
<< dicomReader->GetNumberOfOutputs() << " groups";
outputError = true;
for (auto fIter = frames.begin(); fIter != frames.end(); ++fIter)
{
MITK_INFO << "filename group " << outputIndex << ": " << (*fIter)->Filename;
}
}
else
{
MITK_INFO << "Re-analyzing files of output group " << outputIndex << " yields "
<< dicomReader->GetNumberOfOutputs() << " groups";
}
}
if (outputError)
{
std::stringstream es;
es << "Original reader configuration: " << std::endl;
reader->PrintConfiguration(es);
es << std::endl;
mitk::DICOMReaderConfigurator::Pointer readerConfigurator = mitk::DICOMReaderConfigurator::New();
mitk::DICOMFileReader::Pointer dicomReader = readerConfigurator->CreateFromUTF8ConfigString(readerSerialization);
es << "New reader configuration: " << std::endl;
dicomReader->PrintConfiguration(es);
es << std::endl;
es << "Original XML: \n" << readerSerialization << std::endl;
std::string newSerialization = serializer->CreateConfigStringFromReader(dicomReader.GetPointer());
es << "New XML: \n" << newSerialization << std::endl;
MITK_ERROR << es.str();
}
if (loadimage)
{
MITK_INFO << "Loading...";
reader->LoadImages();
mitk::Image::Pointer image = reader->GetOutput(0).GetMitkImage();
MITK_INFO << "---- Output image:";
mitk::BaseGeometry::Pointer geo3D = image->GetGeometry();
if (geo3D.IsNotNull())
{
mitk::SlicedGeometry3D::Pointer sg = dynamic_cast<mitk::SlicedGeometry3D *>(geo3D.GetPointer());
if (sg.IsNotNull())
{
unsigned int nos = sg->GetSlices();
mitk::PlaneGeometry::ConstPointer first = sg->GetPlaneGeometry(0);
mitk::PlaneGeometry::ConstPointer last = sg->GetPlaneGeometry(nos - 1);
mitk::Point3D firstOrigin = first->GetOrigin();
mitk::Point3D lastOrigin = last->GetOrigin();
MITK_INFO << "Geometry says: First slice at " << firstOrigin << ", last slice at " << lastOrigin;
mitk::StringLookupTableProperty::Pointer sliceLocations =
dynamic_cast<mitk::StringLookupTableProperty *>(image->GetProperty("dicom.image.0020.1041").GetPointer());
if (sliceLocations.IsNotNull())
{
std::string firstSliceLocation = sliceLocations->GetValue().GetTableValue(0);
std::string lastSliceLocation = sliceLocations->GetValue().GetTableValue(nos - 1);
MITK_INFO << "Image properties says: first slice location at " << firstSliceLocation
<< ", last slice location at " << lastSliceLocation;
}
mitk::StringLookupTableProperty::Pointer instanceNumbers =
dynamic_cast<mitk::StringLookupTableProperty *>(image->GetProperty("dicom.image.0020.0013").GetPointer());
if (instanceNumbers.IsNotNull())
{
std::string firstInstanceNumber = instanceNumbers->GetValue().GetTableValue(0);
std::string lastInstanceNumber = instanceNumbers->GetValue().GetTableValue(nos - 1);
MITK_INFO << "Image properties says: first instance number at " << firstInstanceNumber
<< ", last instance number at " << lastInstanceNumber;
}
}
}
MITK_INFO << "---- End of output";
}
// if we got so far, everything is fine
return EXIT_SUCCESS;
}
diff --git a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.h b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.h
index b73b3059e9..544b12e3e3 100644
--- a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.h
+++ b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.h
@@ -1,43 +1,43 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkExampleDataStructureWriterService_h
#define mitkExampleDataStructureWriterService_h
#include <mitkAbstractFileWriter.h>
#include <mitkExampleDataStructure.h>
namespace mitk
{
/**
- * Writes example data strucutres to a file
+ * Writes example data structures to a file
* @ingroup Process
*/
class ExampleDataStructureWriterService : public mitk::AbstractFileWriter
{
public:
typedef mitk::ExampleDataStructure InputType;
ExampleDataStructureWriterService();
~ExampleDataStructureWriterService() override;
using AbstractFileWriter::Write;
void Write() override;
protected:
ExampleDataStructureWriterService(const ExampleDataStructureWriterService &other);
mitk::ExampleDataStructureWriterService *Clone() const override;
};
} // end of namespace mitk
#endif
diff --git a/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureReaderWriterTest.cpp b/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureReaderWriterTest.cpp
index f765ccc53b..387adb5b39 100644
--- a/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureReaderWriterTest.cpp
+++ b/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureReaderWriterTest.cpp
@@ -1,71 +1,71 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkExampleDataStructure.h"
#include "mitkIOUtil.h"
// VTK includes
#include <vtkDebugLeaks.h>
class mitkExampleDataStructureReaderWriterTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkExampleDataStructureReaderWriterTestSuite);
// Test saving/loading
MITK_TEST(ReadWrite_ExampleData_SavedAndLoadedDataEqualToExample);
CPPUNIT_TEST_SUITE_END();
private:
mitk::ExampleDataStructure::Pointer m_Data;
std::string m_DefaultDataString;
public:
/**
- * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used
+ * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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_DefaultDataString = "This is the example data content\nAnd a second line\n";
m_Data = mitk::ExampleDataStructure::New();
m_Data->SetData(m_DefaultDataString);
}
void tearDown() override
{
m_DefaultDataString = "";
m_Data = nullptr;
}
void ReadWrite_ExampleData_SavedAndLoadedDataEqualToExample()
{
std::string path = mitk::IOUtil::GetTempPath() + "ExampleDataOutput.txt";
mitk::IOUtil::Save(m_Data, path);
mitk::ExampleDataStructure::Pointer loadedData =
mitk::IOUtil::Load<mitk::ExampleDataStructure >(path);
itksys::SystemTools::RemoveFile(path);
CPPUNIT_ASSERT_MESSAGE("Comparing created and loaded example data.",
mitk::Equal(m_Data, loadedData, mitk::eps, true));
}
};
MITK_TEST_SUITE_REGISTRATION(mitkExampleDataStructureReaderWriter)
diff --git a/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureTest.cpp b/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureTest.cpp
index 135771e8b4..b82767d23c 100644
--- a/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureTest.cpp
+++ b/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureTest.cpp
@@ -1,66 +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.
============================================================================*/
// Testing
#include "mitkTestFixture.h"
#include "mitkTestingMacros.h"
// std includes
#include <string>
// MITK includes
#include "mitkExampleDataStructure.h"
// VTK includes
#include <vtkDebugLeaks.h>
class mitkExampleDataStructureTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkExampleDataStructureTestSuite);
// Test the append method
MITK_TEST(Append_ExampleString_AddsExampleStringToData);
CPPUNIT_TEST_SUITE_END();
private:
mitk::ExampleDataStructure::Pointer m_Data;
std::string m_DefaultDataString;
public:
/**
- * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used
+ * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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_DefaultDataString = "This is the example data content\nAnd a second line\n";
m_Data = mitk::ExampleDataStructure::New();
m_Data->SetData(m_DefaultDataString);
}
void tearDown() override
{
m_DefaultDataString = "";
m_Data = nullptr;
}
void Append_ExampleString_AddsExampleStringToData()
{
std::string appendedString = "And a third line\n";
std::string wholeString = m_DefaultDataString + appendedString;
m_Data->AppendAString(appendedString);
CPPUNIT_ASSERT_MESSAGE("Checking whether string was correctly appended.", m_Data->GetData() == wholeString);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkExampleDataStructure)
diff --git a/Examples/Plugins/org.mitk.example.gui.customviewer.views/src/internal/DicomView.h b/Examples/Plugins/org.mitk.example.gui.customviewer.views/src/internal/DicomView.h
index 1a7a3d8326..733dce681e 100644
--- a/Examples/Plugins/org.mitk.example.gui.customviewer.views/src/internal/DicomView.h
+++ b/Examples/Plugins/org.mitk.example.gui.customviewer.views/src/internal/DicomView.h
@@ -1,74 +1,74 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 DICOMVIEW_H_
#define DICOMVIEW_H_
#include <QmitkAbstractView.h>
#include <berryQtViewPart.h>
#include "ui_QmitkDicomViewControls.h"
/**
* \brief A view class suited for the DicomPerspective within the custom viewer plug-in.
*
* This view class contributes dicom import functionality to the DicomPerspective.
* The view controls are provided within CreatePartControl() by the QmitkDicomExternalDataWidget
* class. A DicomView instance is part of the DicomPerspective for Dicom import functionality.
*/
// //! [DicomViewDecl]
class DicomView : public QmitkAbstractView
// //! [DicomViewDecl]
{
Q_OBJECT
public:
/**
* String based view identifier.
*/
static const std::string VIEW_ID;
/**
* Standard constructor.
*/
DicomView();
/**
* Standard destructor.
*/
~DicomView() override;
/**
* Creates the view control widgets provided by the QmitkDicomExternalDataWidget class.
* Widgets associated with unused functionality are being removed and DICOM import and data
- * storage transfer funcionality being connected to the appropriate slots.
+ * storage transfer functionality being connected to the appropriate slots.
*/
void CreateQtPartControl(QWidget *parent) override;
protected Q_SLOTS:
/**
* Loads the DICOM series specified by the given string parameter and adds the resulting data
* node to the data storage. Subsequently switches to the ViewerPerspective for further
* data examination.
*/
void AddDataNodeFromDICOM(QHash<QString, QVariant> eventProperties);
protected:
void SetFocus() override;
Ui::QmitkDicomViewControls m_Controls;
QWidget *m_Parent;
};
#endif /*DICOMVIEW_H_*/
diff --git a/Examples/Plugins/org.mitk.example.gui.customviewer/documentation/doxygen/CustomViewerExample.dox b/Examples/Plugins/org.mitk.example.gui.customviewer/documentation/doxygen/CustomViewerExample.dox
index fea77b2bb2..60dc547219 100644
--- a/Examples/Plugins/org.mitk.example.gui.customviewer/documentation/doxygen/CustomViewerExample.dox
+++ b/Examples/Plugins/org.mitk.example.gui.customviewer/documentation/doxygen/CustomViewerExample.dox
@@ -1,312 +1,312 @@
/**
\page BlueBerryExampleCustomViewer A highly customized viewer
This documentation is structured as follows:
-# \subpage Introduction
-# \subpage ViewerPluginCreation
-# \subpage MainWindowLayout
-# \subpage AddFunctionality
-# \subpage GUICustomization
\page Introduction Introduction
The Custom Viewer Example is a BlueBerry example plugin, developed to demonstrate customization capabilities provided by the BlueBerry application framework. The example plugin implements a GUI customized viewer application. The developed viewer incorporates simple viewer functionality embedded in a customized graphical user interface.
Spoken in BlueBerry terms, the following features are provided:
<UL>
<LI> Hidden Menu-, Tool- and Statusbars </LI>
<LI> Hidden Editor Area </LI>
<LI> Fixed perspectives </LI>
<LI> Customized main window contents </LI>
<LI> Customized perspectives bar based on QTabBar </LI>
<LI> GUI Customization using Qt-Stylesheets </LI>
</UL>
The custom viewer itself consists of two perspectives, i.e. a viewer perspective and a DICOM perspective. As part of the viewer perspective, an instance of QmitkDataManagerView allows for data selection. Visualization of the selected data is then provided by a simple render window view. According data can either be directly loaded from file or be imported as DICOM data. DICOM import functionality is accessible from the DICOM perspective incorporating the QmitkDicomExternalDataWidget. The GUI-appearance is customized using Qt-Stylesheets in order to give the application a non-native look and feel. This is further emphasized by a Tab-Widget-like presentation of the perspectives using a modified perspective bar based on a QTabBar. In addition to an absence of menu-, tool- and status-bars, simplicity is accentuated by a proper view and perspective design, i.e. the editor area being invisible and views being fixated. The following images depict the viewer- and DICOM-perspectives of the custom viewer.
\imageMacro{ViewerPerspective.png,"Viewer perspective of the Custom Viewer.",16.00}
\imageMacro{DicomPerspective.png,"Dicom perspective of the Custom Viewer.",16.00}
Go to the previous page \ref BlueBerryExampleCustomViewer. Or proceed to the next page \ref ViewerPluginCreation.
\page ViewerPluginCreation Creating the CustomViewer plugin
As we want to develop our Custom Viewer as part of BlueBerry to demonstrate the customization capabilities of the application framework, we have to integrate the components of our example application as BlueBerry plugins in order to make the application framework's functionalities available to our application. For example plugin startup with BlueBerry, a convenience application called BlueBerryExampleLauncher is already provided in the Examples/BlueBerryExampleLauncher directory of MITK. This application acts as a BlueBerry loading mechanism for any example application provided.
To make an example application startable by the BlueBerry example launcher, we have to provide a folder inside Examples/Plugins containing all files making up the application plugin itself. Additionally, we have to add entries to the PluginList.cmake in the Examples/Plugins directory for plugin registration and create a new file in the Examples/BlueBerryExampleLauncher/Configurations folder containing the plugin itself as well as any further required plugin.
The resulting plugin folder for our custom viewer application consists of several subfolders and files:
\imageMacro{CustomViewerDirectory.png,"The directory structure for the CustomViewer plugin.",16.00}
We can see a documentation and resources folder, a source folder containing all source files, some cmake-related files and a plugin.xml file for the BlueBerry related plugin extension and extension-point declarations.
Next, we add some source code to our plugin folder. First, we need a class for our CustomViewer application itself which we derive from the berry:IApplication application class:
\dontinclude CustomViewer.h
\skip class CustomViewer : public QObject
\until void Stop();
In short, this class acts as an entry point for the BlueBerry application runtime. It defines what the application does when it is started (Start()-Method) and before it is ended (Stop()-Method). Our Start()-Method creates a BlueBerry display for GUI-rendering and a WorkbenchAdvisor for workbench control. Then the BlueBerry workbench is created and run given the created display and WorkbenchAdvisor:
\snippet MinimalApplicationSnippet.cpp MinimalApplicationClass_StartMethod
The Stop()-method does not need to be further defined. In addition, a default perspective identifier is given to define an initial perspective to be shown by the WorkbenchWindow.
Later, we well need a proper WorkbenchAdvisor class derived from berry::WorkbenchAdvisor. In it, we will create a WorkbenchWindowAdvisor, which on his part is used to control the WorkbenchWindow's GUI creation. For now, we simply use the berry::WorkbenchWindowAdvisor, which creates the GUI in a default way. As the development of our custom viewer advances, we will want to take part in the GUI creation ourselves, so we will need to customize our own WorkbenchWindowAdvisor accordingly.
Now we create a class named ViewerPerspective which is derived from berry::IPerspectiveFactory:
\snippet ViewerPerspective.h ViewerPerspectiveClassDeclaration
It acts as an initial perspective to be displayed as part of our bulk application plugin. For now, the perspective will remain empty and can, alongside the definition of further perspectives, be provided with views later by overwriting the CreateInitialLayout()-Method.
Finally, we need a ctkPluginActivator derived class which is used to customize the starting and stopping of our plugin:
\snippet org_mitk_example_gui_customviewer_Activator.h PluginActivatorHeader
During plugin-start, we register our plugin classes given the ctkPluginContext by overwriting the start()-Method.
\dontinclude org_mitk_example_gui_customviewer_Activator.cpp
\skip ::start
\until ViewerPerspective
\skip PluginContext
\until }
In order to connect our application and its perspectives to the BlueBerry components we have to declare the according extension-point-contributions inside the plugin.xml file. As can be seen in the image below, our application and its perspectives contribute to the org.blueberry.osgi.applications and org.blueberry.ui.perspectives extension points respectively.
\imageMacro{plugin_xml_0.png,"Extension point contributions of our initial custom viewer application",16.00}
When we start the BlueBerryExampleLauncher (either directly or via the provided batch files), we can now choose our bulk application (among others present), and a window is presented showing our empty initial perspective.
\imageMacro{MinimalApplicationWindow.png,"Our first application window showing one single perspective",16.00}
Go to the previous page \ref Introduction. Or proceed to the next page \ref MainWindowLayout.
\page MainWindowLayout Main Window Layout: ViewerPerspective and DicomPerspective
Now that we have created a bulk plugin for our custom viewer, we intend to customize the way how the main window of our Blueberry application is laid out. We want:
<UL>
<LI> No visible menu-, tool- and status-bars </LI>
<LI> Two perspectives: a viewer perspective and a DICOM perspective </LI>
<LI> A tab-bar like perspective bar that allows for perspective switching </LI>
<LI> An open file button for perspective-independent file opening </LI>
</UL>
Customizing the main window contents requires creating a custom WorkbenchWindowAdvisor derived from berry::WorkbenchWindowAdvisor hence this class controls the WorkbenchWindow layout:
\snippet CustomViewerWorkbenchWindowAdvisor.h CustomViewerWorkbenchWindowAdvisorClassDeclaration
As we mentioned in \ref ViewerPluginCreation, it is the WorkbenchAdvisor class that creates the WorkbenchWindowAdvisor. Hence, we now create our own version of a WorkbenchAdvisor:
\snippet CustomViewerWorkbenchAdvisor.h WorkbenchAdvisorDecl
Here, we overwrite the CreateWorkbenchWindowAdvisor()-method:
\snippet CustomViewerWorkbenchAdvisor.cpp WorkbenchAdvisorCreateWindowAdvisor
First, to prevent the WorkbenchWindow from rendering any menu-, tool- and status-bars, we overwrite the PreWindowOpen()-Method of the WorkbenchWindowAdvisor and access the WorkbenchWindowConfigurer helper class instance. Additionally, we set an appropriate title for our application window:
\snippet CustomViewerWorkbenchWindowAdvisor.cpp CustomViewerWorkbenchWindowAdvisorPreWindowOpen
Then we forge bulk versions of our viewer and dicom perspectives. We already created a bulk version of the viewer perspective earlier (see \ref ViewerPluginCreation). Accordingly, we create our DicomPerspective by defining the perspective class, contributing to the perspectives-extension point, registering the perspective in the plugin activator and adding to the cmake files.
For the tab-bar like perspective bar we define a QtPerspectiveSwitcherTabBar derived from QTabBar:
\snippet QtPerspectiveSwitcherTabBar.h PerspectiveSwitcherDeclaration
The perspective switching functionality is implemented by a SwitchPerspective function, a signal-slot-connection that reacts on tab changes and a perspective listener that on perspective activation consistently switches to the according tab. Within the SwitchPerspective function, we show the perspective according to the current index indicating the currently active tab:
\snippet QtPerspectiveSwitcherTabBar.cpp PerspectiveSwitcherSwitchPerspective
Here, we have to ignore the first tab change event that can be fired during tab bar configuration. At that time, the perspective layout generally is not yet finished, which subsequently leads to an error. The SwitchPerspective slot is being connected to the tab-change-event during construction. The perspective listener is implemented as a helper friend struct derived from berry::IPerspectiveListener:
\snippet QtPerspectiveSwitcherTabBar.cpp SwitchPerspectiveListener
In the PerspectiveActivated-Method, we activate the tab according to the activated perspective's ID:
\snippet QtPerspectiveSwitcherTabBar.cpp SwitchPerspectiveListenerPerspectiveActivated
Now, our tab-bar like perspective bar is ready for use in the customized window layout.
The open file functionality will later be implemented as an OpenFile-slot to the WorkbenchWindowAdvisor. Refer to \ref AddFunctionality for details. As such, it can be connected to a perspective-independent push button that will be part of to the application's window contents, together with an instance of the QtPerspectiveSwitcherTabBar.
The customization of the window contents takes place within the CreateWindowContents method. That means, we can overwrite the superclass' CreateWindowContents method and lay out every widget of the main window individually. Given the method's berry::Shell parameter, we can extract the application's main window as QMainWindow using the berry::Shell::GetControl()-method:
\snippet CustomViewerWorkbenchWindowAdvisor.cpp WorkbenchWindowAdvisorCreateWindowContentsHead
Usually, as in the superclass' CreateWindowContents method, the shell ist given to the WindowConfigurer where the Page Composite, i.e. the part holding the perspective's view contents, is added as a single QControlWidget to an HBoxLayout.
For our purposes, we want to place the QtPerspectiveSwitcherTabBar and the View-Button alongside the Page Composite. We can achieve that by creating the Page Composite within the CreateWindowContents method, lay it out in the MainWindow together with the other widgets, and give it to the WorkbenchWindowConfigurer for the view control layout process, which will then take place wrapped within our own PageComposite widget:
\dontinclude CustomViewerWorkbenchWindowAdvisor.cpp
\skip mainWindow->setCentralWidget(CentralWidget);
\until PerspectivesLayer->addWidget(OpenFileButton);
\skip for correct initial layout
\until this->GetWindowConfigurer
-The OpenFile-Button and the QtPerspectiveSwitcherTabBar will be laid out together in a HBoxLayout called PerspectivesLayer. The PerspectivesLayer will be vertically arranged with the PageComposite widget in a VBoxLayout called CentralWidgetLayout. This CentralWidgetLayout will be assigned a QWidget being set the CentralWidget of our MainWindow. Caveat: we need to call the activate- and update-Methods of our CentralWidgetLayout; otherweise the widgets will not be laid out properly. See Bug-1654 for further details. See our bulk custom viewer application depicted below.
+The OpenFile-Button and the QtPerspectiveSwitcherTabBar will be laid out together in a HBoxLayout called PerspectivesLayer. The PerspectivesLayer will be vertically arranged with the PageComposite widget in a VBoxLayout called CentralWidgetLayout. This CentralWidgetLayout will be assigned a QWidget being set the CentralWidget of our MainWindow. Caveat: we need to call the activate- and update-Methods of our CentralWidgetLayout; otherwise the widgets will not be laid out properly. See Bug-1654 for further details. See our bulk custom viewer application depicted below.
\imageMacro{BulkApplicationWindow.png,"Our bulk application showing two empty perspectives managed with a tab-bar based perspectives bar",16.00}
Go to the previous page \ref ViewerPluginCreation. Or proceed to the next page \ref AddFunctionality.
\page AddFunctionality Adding functionality: Data Manager, Render Window, File Opening and DICOM Import
Up to now, we have developed a bulk custom viewer application, i.e. a runnable BlueBerry application showing an open file button and two perspectives switched by a custom tab-bar like perspectives bar. Now, we will add the desired functionality for our custom viewer. We want to integrate:
<UL>
<LI> A Data Manager, managing Data Nodes related to loaded or DICOM-imported images </LI>
<LI> A Render Window to visualize the Data Nodes </LI>
<LI> File Opening functionality connected to the Open-File-Button </LI>
<LI> DICOM Import functionality </LI>
</UL>
Except for the File Opening functionality, which is already GUI-represented, we need to integrate proper views to our perspectives in order to make the according functionality accessible. Concerning the design of our example application, two options appear straight-forward:
<OL>
<LI> Integrate the view-class source-code to the main-application-plugin (the custom viewer plugin) </LI>
<LI> Create a proper plugin for the views </LI>
</OL>
Taking into account the plugin dependencies, it can be revealed that the first solution is not an option. Without going into detail, that solution would result in a cyclic dependency scenario, so an adequate plugin activation order would not be achievable at runtime. So we will create a proper plugin for the views we intend to use. This is straightforward as shown in \ref ViewerPluginCreation.
For Data Manager functionality we will make use of the QmitkDataManagerView which - being a berry::IVewPart - can externally be integrated to our viewer perspective. The only thing we have to do is add the QMitkDataManagerView to the viewer perspective's CreateInitialLayout()-method:
\dontinclude ViewerPerspective.cpp
\skip ::CreateInitialLayout
\until org.mitk.views.datamanager
For the rendering functionality we have to create a proper view class. We derive that view class called SimpleRenderWindowView from QmitkAbstractView (for direct DataStorage access) and from mitk::IRenderWindowPart:
\snippet SimpleRenderWindowView.h SimpleRenderWindowViewDeclaration
Concrete implementations can for example be adapted from QmitkAbstractRenderEditor. The AbstractRenderWindowViewPrivate helper class is modified with regard to the views-Plugin-Activator:
\snippet SimpleRenderWindowView.cpp SimpleRenderWindowViewHelper
In CreateQtPartControl() we can now lay out the view controls. For that, we create a QmitkRenderWindow whose Renderer is subsequently be given the DataStorage:
\snippet SimpleRenderWindowView.cpp SimpleRenderWindowViewCreatePartControl
Finally we add the SimpleRenderWindowView in ViewerPerspective::CreateInitialLayout():
\snippet ViewerPerspective.cpp AddView1
For the DICOM import functionality we derive the DicomView class from QmitkAbstractView:
\snippet DicomView.h DicomViewDecl
In CreateQtPartControl(), we add a QmitkDicomExternalDataWidget to our view controls (this time e.g. via ui-File):
\snippet DicomView.cpp DicomViewCreatePartControl
The QmitkDicomExternalDataWidget yields a tree view for DICOM data, as well as a signal for Dicom transfer to the data manager and a slot for DICOM import to the tree view. With the Dicom transfer signal a string containing information about the DICOM series currently selected in the tree view is piggybacked. We use this information in an AddDataNodeFromDICOM slot defined in the DicomView class following the example of the DicomEventHandler class:
\snippet DicomView.cpp DicomViewCreateAddDataNodeInformation
The file path and seriesUID information are used to load the selected DICOM series into a mitk::DataNode:
\snippet DicomView.cpp DicomViewCreateAddDataNodeLoadSeries
which can then be added to the DataStorage:
\snippet DicomView.cpp DicomViewCreateAddDataNode
After that, we activate the viewer perspective to examine the data in the rendering window view.
\snippet DicomView.cpp DicomViewCreateAddDataNodeActivatePersp
Having a look back to the QmitkDicomExternalDataWidget, while there is already a view button present that triggers Dicom transfer signal emission, we still have to bind DICOM import functionality to a proper import button. We will do this once again in the CreateQtPartControl method (refer to the above snippet). After setting up the view controls containing the QmitkDicomExternalDataWidget and an import button, we render the unused widgets invisible. After connecting the Dicom transfer signal to the AddDataNodeFromDICOM slot and our import button to the DICOM import slot of the QmitkDicomExternalDataWidget, the DicomView is ready for use. Finally, the DicomView is added inside the DicomPerspective::CreateInitialLayout() method:
\snippet DicomPerspective.cpp DicomPerspCreateLayout
The following images show the Dicom import functionality.
\imageMacro{dicomImportFiles.png,"The DICOM file import dialog",16.00}
\imageMacro{dataTree.png,"Imported DICOM data shown in the tree view",16.00}
\imageMacro{DICOMimported.png,"Imported DICOM data presented in the render window view",16.00}
Now we implement the file open slot already defined earlier (see \ref MainWindowLayout). While it appears that we could simply assign a QmitkFileOpenAction to a QToolButton, this is not possible due to the fact, that by default, the WorkbenchUtil::LoadFiles() method invoked by the QmitkFileOpenAction awaits an editor to be present in the current application. To prevent the method from throwing an exception, we made a workaround by giving the LoadFiles() method an additional parameter that determines whether an editor is to be opened or not:
\snippet mitkWorkbenchUtil.cpp UtilLoadFiles
Hence, we have to invoke that method manually, e.g. inside an OpenFile-slot implemented inside the WorkbenchWindowAdvisor:
\snippet CustomViewerWorkbenchWindowAdvisor.cpp WorkbenchWindowAdvisorOpenFile
In it, a dialog is opened that asks for the user for a number of files to open. If any files are given, these are being loaded by the WorkbenchUtil::LoadFiles method. Finally, the viewer perspective is activated:
\snippet CustomViewerWorkbenchWindowAdvisor.cpp WorkbenchWindowAdvisorOpenFilePerspActive
-Before we can examine the loaded data, we have to manually invoke a reinit on it. The render window concept in mitk is actually undergoing some work, where this inconvenience will also be adressed. The images below show the resulting file opening functionality.
+Before we can examine the loaded data, we have to manually invoke a reinit on it. The render window concept in mitk is actually undergoing some work, where this inconvenience will also be addressed. The images below show the resulting file opening functionality.
\imageMacro{OpenFileDialog.png,"The open file dialog",16.00}
\imageMacro{FileOpened.png,"Opened file shown in the render window view",16.00}
Go to the previous page \ref MainWindowLayout. Or proceed to the next page \ref GUICustomization.
\page GUICustomization Customizing the Main Window using Qt-Stylesheets
In a final step, we want to further customize the appearance of our mainWindow to give it an distinct non-native look and feel. We want to achieve this by pursuing the following aims:
<UL>
<LI> Change the background and widget colors </LI>
<LI> Change the tab-widget and ToolButton style, also with respect to mouse-over-button (hovering) effects </LI>
<LI> Completing the non-native tab-widget like impression of the perspectives by gluing tab-bar and perspective's PageComposite together </LI>
<LI> DICOM Import functionality </LI>
</UL>
-For GUI customization, we will modify the Qt-Stylesheets files already used by blueberry applications. Within the Qt-Stylesheet-Files, all widgets can globally and locally be adressed inside the main window for style changes. We have to adress the berry::IQtStyleManager to tell the BlueBerry workbench to use a specific Qt-Stylesheet. This is done inside the WorkbenchAdvisor in the CustomViewerWorkbenchAdvisor::Initialize() method:
+For GUI customization, we will modify the Qt-Stylesheets files already used by blueberry applications. Within the Qt-Stylesheet-Files, all widgets can globally and locally be addressed inside the main window for style changes. We have to address the berry::IQtStyleManager to tell the BlueBerry workbench to use a specific Qt-Stylesheet. This is done inside the WorkbenchAdvisor in the CustomViewerWorkbenchAdvisor::Initialize() method:
\snippet CustomViewerWorkbenchAdvisor.cpp WorkbenchAdvisorInit
The style manager is taken from the application's plugin context via service reference. Invoking the berry::IQtStyleManager::AddStyle() and berry::IQtStyleManager::SetStyle() methods, the workbench will now use the announced qss-File to style our Workbench Window. In a production system, the stylesheets are usually compiled into the plug-in or application using the Qt resource system.
-However, during developement of the stylesheets it is often more convenient to reference them using a hard-coded path to the local file system (see live update functionality below).
+However, during development of the stylesheets it is often more convenient to reference them using a hard-coded path to the local file system (see live update functionality below).
Before we start customization we will first provide some customization convenience. We add an UpdateStyle()-slot to our CustomViewerWorkbenchWindowAdvisor where we explicitly reset the css-File to the style manager:
\snippet CustomViewerWorkbenchWindowAdvisor.cpp WorkbenchWindowAdvisorUpdateStyle
By integrating an update style button to the Application's main window and connecting this button with the previously defined slot, we can now button-push-update the style on runtime. This will of course
only work for stylesheets which are referenced from the local file system.
\imageMacro{StyledMainWindow0.png,"The unstyled Main Window",16.00}
First we might want to change the background color style by setting the background color of the QWidget#CentralWidget to a linear gradient from light to dark blue:
\snippet customstyleSnippet.qss CentralWidgetColor
Then, we give the page composite control widget a slight grey border (except for the upper border where no border should be visible) and the same background color as the activated tab widget:
\snippet customstyleSnippet.qss PageComposite
The image below depicts the style changes.
\imageMacro{StyledMainWindow2.png,"Background-color changed Central and Composite Control Widgets",16.00}
Concerning the tab-widget style, four states have to be customized: QtPerspectiveSwitcherTabBar::tab (the tab in general), QtPerspectiveSwitcherTabBar::tab:selected (when tab is selected), QtPerspectiveSwitcherTabBar::tab:selected:hover (when tab is selected and the mouse is hovering above), QtPerspectiveSwitcherTabBar::tab:!selected:hover (respectively).
All tabs are given round corners using border-top-left- and border-top-right-radius definitions. Additionally, all tabs is provided a gap to its neighbor defining a positive margin right. Selected tabs appear bigger by defining a negative upper margin, and they have no lower frame in the unselected state so a tab-widget appearance is provided. Finally, they have a brighter background color also used by the QWidget#ClientComposite. Hovering tabs are colored yellow and have a visible lower border:
\snippet customstyleSnippet.qss Tabs
Finally, we customize the Push- and Tool-Buttons in a similar way:
\snippet customstyleSnippet.qss Buttons
The resulting style-customized main window is shown below (the style update button removed).
\imageMacro{StyledMainWindow_final1.png,"The final version of our Custom Viewer (viewer perspective).",16.00}
\imageMacro{StyledMainWindow_final2.png,"The final version of our Custom Viewer (DICOM perspective).",16.00}
Proceed to the previous page \ref AddFunctionality.
*/
The custom viewer plugin implements simple viewer functionality presented in a customized look and feel. It was developed to demonstrate extensibility and customizability of the blueberry application framework.
As an example for the GUI customization capabilities provided by the BlueBerry application framework, the custom viewer plugin was developed. It features simple viewer functionality presented in a customized look and feel. The custom viewer consists of two perspectives, i.e. a viewer perspective and a DICOM perspective. As part of the viewer perspective, an instance of QmitkDataManagerView allows for data selection. Visualization of the selected data is then performed by a simple render window view. According data can either be directly loaded from file or be imported as DICOM data. DICOM import functionality is accessible from the DICOM perspective incorporating the QmitkDicomExternalDataWidget. The customization of Qt Stylesheets is used to give the application a non-native look and feel. This is further emphasized by a Tab-Widget-like unification of the perspectives with the according perspective bar. In addition to an absence of menu-, tool- and status-bars, simplicity is accentuated by a proper view and perspective design, i.e. the editor area being invisible and views being fixated.
diff --git a/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.cpp b/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.cpp
index a581c7a9f2..11fe749e66 100644
--- a/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.cpp
+++ b/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.cpp
@@ -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.
============================================================================*/
#include "QtPerspectiveSwitcherTabBar.h"
#include <berryIPerspectiveDescriptor.h>
#include <berryIWorkbench.h>
#include <berryIWorkbenchPage.h>
/**
- * \brief A Listener class for perspective changes. Neccessary for consistent tab activation
+ * \brief A Listener class for perspective changes. Necessary for consistent tab activation
* in QtPerspectiveSwitcherTabBar instances.
*/
// //! [SwitchPerspectiveListener]
struct QtPerspectiveSwitcherTabBarListener : public berry::IPerspectiveListener
// //! [SwitchPerspectiveListener]
{
/**
* Constructor.
*/
QtPerspectiveSwitcherTabBarListener(QtPerspectiveSwitcherTabBar *switcher) : switcher(switcher) {}
/**
* Only listens to perspective activation events.
*/
Events::Types GetPerspectiveEventTypes() const override { return Events::ACTIVATED; }
/**
* Sets the corresponding perspective index within the associated QtPerspectiveSwitcherTabBar instance.
*/
// //! [SwitchPerspectiveListenerPerspectiveActivated]
void PerspectiveActivated(const berry::IWorkbenchPage::Pointer & /*page*/,
const berry::IPerspectiveDescriptor::Pointer &perspective) override
{
int index = perspective->GetId() == "org.mitk.example.viewerperspective" ? 0 : 1;
switcher->setCurrentIndex(index);
}
// //! [SwitchPerspectiveListenerPerspectiveActivated]
private:
/**
* The associated QtPerspectiveSwitcherTabBar instance.
*/
QtPerspectiveSwitcherTabBar *switcher;
};
QtPerspectiveSwitcherTabBar::QtPerspectiveSwitcherTabBar(berry::IWorkbenchWindow::Pointer window)
: window(window), perspListener(new QtPerspectiveSwitcherTabBarListener(this))
{
this->tabChanged = false;
QWidget *parent = static_cast<QWidget *>(window->GetShell()->GetControl());
this->setParent(parent);
QObject::connect(this, SIGNAL(currentChanged(int)), this, SLOT(SwitchPerspective(void)));
window->AddPerspectiveListener(this->perspListener.data());
}
QtPerspectiveSwitcherTabBar::~QtPerspectiveSwitcherTabBar()
{
window->RemovePerspectiveListener(this->perspListener.data());
}
// //! [PerspectiveSwitcherSwitchPerspective]
void QtPerspectiveSwitcherTabBar::SwitchPerspective()
{
if (!this->tabChanged)
{
this->tabChanged = true;
return;
}
int index = this->currentIndex();
if (index == 0)
{
QString perspectiveId = "org.mitk.example.viewerperspective";
this->window->GetWorkbench()->ShowPerspective(perspectiveId, berry::IWorkbenchWindow::Pointer(window));
}
else if (index == 1)
{
QString perspectiveId = "org.mitk.example.dicomperspective";
this->window->GetWorkbench()->ShowPerspective(perspectiveId, berry::IWorkbenchWindow::Pointer(window));
}
}
// //! [PerspectiveSwitcherSwitchPerspective]
diff --git a/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.h b/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.h
index 1aaff429ea..4f2544f9c3 100644
--- a/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.h
+++ b/Examples/Plugins/org.mitk.example.gui.customviewer/src/internal/QtPerspectiveSwitcherTabBar.h
@@ -1,70 +1,70 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QTPERSPECTIVESWITCHERTABBAR_H_
#define QTPERSPECTIVESWITCHERTABBAR_H_
#include <QHash>
#include <QTabBar>
#include <berryIPerspectiveListener.h>
#include <berryIWorkbenchWindow.h>
/**
* \brief A QTabBar providing perspective bar functionality in BlueBerry applications.
*
* This subclass of QTabBar acts as a perspective bar in BlueBerry applications. Providing
* perspective switching functionality in a tab-bar like outfit, this class serves as an
* alternative to the ToolBar based berry::QtPerspectiveSwitcher class.
*/
// //! [PerspectiveSwitcherDeclaration]
class QtPerspectiveSwitcherTabBar : public QTabBar
// //! [PerspectiveSwitcherDeclaration]
{
Q_OBJECT
public:
/**
* Constructor.
*/
QtPerspectiveSwitcherTabBar(berry::IWorkbenchWindow::Pointer window);
/**
* Standard destructor.
*/
~QtPerspectiveSwitcherTabBar() override;
private Q_SLOTS:
/**
* Implements perspective switching.
*/
void SwitchPerspective();
private:
berry::IWorkbenchWindow::Pointer window;
QScopedPointer<berry::IPerspectiveListener> perspListener;
QHash<QString, QAction *> perspIdToActionMap;
/**
- * Neccessary to prevent initial tab switching.
+ * Necessary to prevent initial tab switching.
*/
bool tabChanged;
/**
- * Listener for perspective changes. Neccessary for consistent tab activation.
+ * Listener for perspective changes. Necessary for consistent tab activation.
*/
friend struct QtPerspectiveSwitcherTabBarListener;
};
#endif /* QTPERSPECTIVESWITCHERTABBAR_H_ */
diff --git a/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/ExampleExtensionPoint.dox b/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/ExampleExtensionPoint.dox
index f701c5ba4a..f767e72096 100644
--- a/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/ExampleExtensionPoint.dox
+++ b/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/ExampleExtensionPoint.dox
@@ -1,55 +1,55 @@
/**
\page ExtensionPointDefinition Extension Point Definition
This example plugin defines an extension point and collects extensions.
\image html ExtensionPointWithoutExtension.png
In this example the extension point concept is used to define an extension point and extend the plugin functionality with it. The GUI consists of a view with two group boxes. In the right box the user can insert text in an input text field. The left box is the area where extensions for this plugin are displayed.
In the plugin.xml file of this application the extension point is defined as follows:
\image html DefinitionXML.png
The according schema (\github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/schema/changetext.exsd,changetext.exsd}) defines attributes for extensions of this extension point like a description.
In the view of this plugin the registry is used to find all available extensions. For each found descriptor a push button is created that holds the functionality extension.
\snippet MinimalView.cpp Collect extensions through registry
These created push buttons are connected to the ChangeText method that alters the input text (in the way the extension defines) and outputs the changed text in another text field.
\snippet MinimalView.cpp Use extended functionality to alter input text
- The plugin can now be extened by extensions provided by other plugins.
+ The plugin can now be extended by extensions provided by other plugins.
View complete source files:
\li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ChangeTextDescriptor.cpp,ChangeTextDescriptor.cpp}
\li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ChangeTextDescriptor.h,ChangeTextDescriptor.h}
\li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ChangeTextRegistry.cpp,ChangeTextRegistry.cpp}
\li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ChangeTextRegistry.h,ChangeTextRegistry.h}
\li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ExtensionPointDefinition.cpp,ExtensionPointDefinition.cpp}
\li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ExtensionPointDefinition.h,ExtensionPointDefinition.h}
\li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ExtensionPointDefinitionConstants.cpp,ExtensionPointDefinitionConstants.cpp}
\li \github{Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/src/internal/ExtensionPointDefinitionConstants.h,ExtensionPointDefinitionConstants.h}
[\ref BlueBerryExampleExtensionPoint] [Next: \ref ExtensionContribution] [\ref BlueBerryExamples]
\page ExtensionContribution Extension Contribution
This example plugin holds no GUI. It just extends the Extension Point Definition plugin.
\image html ExtensionPointWithExtension.png
Through the extension of the Extension Point Definition plugin two functionalities are added by implementing the minimal IChangeText interface of the plugin.
In the plugin.xml file of the plugin the extensions are defined as follows:
\image html ContributionXML.png
The extension point ID in the plugin.xml matches the ID of the extension point and is therefore found by the registry as a valid extension. A description is given for each extension although the schema for the extension point allows an extension without a description (\c \<element ref="description" minOccurs="0" maxOccurs="1"/\>).
The GUI now holds two buttons that change the input text of the user to upper or lower case. This is done by two simple methods.
View complete source files:
\li \github{Examples/Plugins/org.mitk.example.gui.extensionpointcontribution/src/internal/ChangeTextToLowerCase.cpp,ChangeTextToLowerCase.cpp}
\li \github{Examples/Plugins/org.mitk.example.gui.extensionpointcontribution/src/internal/ChangeTextToLowerCase.h,ChangeTextToLowerCase.h}
\li \github{Examples/Plugins/org.mitk.example.gui.extensionpointcontribution/src/internal/ChangeTextToUpperCase.cpp,ChangeTextToUpperCase.cpp}
\li \github{Examples/Plugins/org.mitk.example.gui.extensionpointcontribution/src/internal/ChangeTextToUpperCase.h,ChangeTextToUpperCase.h}
[\ref BlueBerryExampleExtensionPoint] [Previous: \ref ExtensionPointDefinition] [\ref BlueBerryExamples]
*/
diff --git a/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/modules.dox b/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/modules.dox
index 5162e5ff62..348be1325f 100644
--- a/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/modules.dox
+++ b/Examples/Plugins/org.mitk.example.gui.extensionpointdefinition/documentation/doxygen/modules.dox
@@ -1,15 +1,15 @@
/**
\defgroup org_mitk_example_gui_extensionpointdefinition org.mitk.example.gui.extensionpointdefinition
\ingroup MITKExamplePlugins
- \brief A minimal applictaion that definines an extension point and collects extensions.
+ \brief A minimal applictaion that defines an extension point and collects extensions.
*/
/**
\defgroup org_mitk_example_gui_extensionpointdefinition_internal Internal
\ingroup org_mitk_example_gui_extensionpointdefinition
\brief This subcategory includes the internal classes of the org.mitk.example.gui.extensionpointdefinition plugin. Other
plugins must not rely on these classes. They contain implementation details and their interface
may change at any time. We mean it.
*/
diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkIsoSurfaceUserManual.dox b/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkIsoSurfaceUserManual.dox
index 5eeb7cc302..905c0e1936 100644
--- a/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkIsoSurfaceUserManual.dox
+++ b/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkIsoSurfaceUserManual.dox
@@ -1,37 +1,37 @@
/**
\page org_mitk_views_isosurface The Iso Surface Module
\imageMacro{IsoSurfaceIcon.png,"Icon of the Iso Surface Module",2.00}
Available sections:
- \ref QmitkIsoSurfaceUserManualOverview
- \ref QmitkIsoSurfaceUserManualFeatures
- \ref QmitkIsoSurfaceUserManualUsage
- \ref QmitkIsoSurfaceUserManualTroubleshooting
\section QmitkIsoSurfaceUserManualOverview Overview
-IsoSurface is a program module for creating surfaces (e.g. polygon structures) out of images. The user defines a threshold that seperates object and background inside the image. Pixels that belong to the object need grey values below the threshold. Pixles with grey values above the threshold will be ignored. The result is a polygon object
+IsoSurface is a program module for creating surfaces (e.g. polygon structures) out of images. The user defines a threshold that separates object and background inside the image. Pixels that belong to the object need grey values below the threshold. Pixles with grey values above the threshold will be ignored. The result is a polygon object
that can be saved as an *.stl-object.
\section QmitkIsoSurfaceUserManualFeatures Features
- Creates a surface by thresholding an image
\section QmitkIsoSurfaceUserManualUsage Usage
How to create a surface:
- Load an image into the program, for example by drag & drop
- Look for a meaningful threshold. All pixel grey values of the image that are lower than the threshold will be used to create the surface. All grey values that are higher than the surface will be ignored. You can find the best threshold by using the Volumetry-Functionality or by reading the grey value while clicking on a pixel (see picture 2).
- Insert the threshold into the GUI
- Press the Button "Create Surface"
\imageMacro{IsoSurfaceGUI.png,"Graphical User Interface of Iso Surface",16.00}
\section QmitkIsoSurfaceUserManualTroubleshooting Troubleshooting
*/
diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkSimpleExampleUserManual.dox b/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkSimpleExampleUserManual.dox
index e3493b2926..48e1d0f5d1 100644
--- a/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkSimpleExampleUserManual.dox
+++ b/Examples/Plugins/org.mitk.example.gui.imaging/documentation/UserManual/QmitkSimpleExampleUserManual.dox
@@ -1,20 +1,20 @@
/**
\page org_mitk_views_simpleexample The Simple Example module
\imageMacro{SimpleExample-dox.png,"Icon of the Simple Example Module",2.00}
\section QmitkSimpleExampleViewUserManualSummary Summary
This module is namely a simple example for simple image interaction.
It offers:
<ul>
<li>Sliders to navigate through the different slice stacks (Axial, Sagittal, Coronal) and the time steps
<li>A "player" for image time series. It automatically steps through all time series with a speed defined by the slider on the right.
<li>A button "Re-Initialize Navigators" to reinitialize those sliders to their initial position (Slice and time position 0)
<li>A button "Take Screenshot" to take a screenshot of the chosen window (Axial, Sagittal, Coronal, 3D)
<li>A button "Take HighDef 3D Screenshot" to take an high resolution screenshot of the 3D scene
-<li>A button "Generate Movie" whichs takes a movie of the chosen window by scrolling either through all slices (Axial, Sagittal, Coronal). Please use the MovieMaker view for recording a movie of a rotating 3D scene.
+<li>A button "Generate Movie" which takes a movie of the chosen window by scrolling either through all slices (Axial, Sagittal, Coronal). Please use the MovieMaker view for recording a movie of a rotating 3D scene.
<li>A selection box where the 3D view can be transformed into either a red-blue stereo or a D4D stereo view
</ul>
*/
diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/src/ExamplesDll.h b/Examples/Plugins/org.mitk.example.gui.imaging/src/ExamplesDll.h
index fc3ad053ed..e5d0c9bee3 100644
--- a/Examples/Plugins/org.mitk.example.gui.imaging/src/ExamplesDll.h
+++ b/Examples/Plugins/org.mitk.example.gui.imaging/src/ExamplesDll.h
@@ -1,36 +1,36 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 _EXAMPLES_EXPORT_DLL_H_
#define _EXAMPLES_EXPORT_DLL_H_
//
// The following block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the org_mitk_gui_qt_examples_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
-// org_mitk_gui_qt_examples_EXPORTS functions as being imported from a DLL, wheras this DLL sees symbols
+// org_mitk_gui_qt_examples_EXPORTS functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
//
#if defined(_WIN32) && !defined(MITK_STATIC)
#if defined(org_mitk_gui_qt_examples_EXPORTS)
#define EXAMPLES_EXPORT __declspec(dllexport)
#else
#define EXAMPLES_EXPORT __declspec(dllimport)
#endif
#endif
#if !defined(EXAMPLES_EXPORT)
#define EXAMPLES_EXPORT
#endif
#endif /*_EXAMPLES_EXPORT_DLL_H_*/
diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/surfaceutilities/mitkSurfaceToPointSetFilter.h b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/surfaceutilities/mitkSurfaceToPointSetFilter.h
index 487c733273..8ef1838cef 100644
--- a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/surfaceutilities/mitkSurfaceToPointSetFilter.h
+++ b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/surfaceutilities/mitkSurfaceToPointSetFilter.h
@@ -1,53 +1,53 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkSurfaceToPointSetFilter_h
#define mitkSurfaceToPointSetFilter_h
// mitk headers
#include "mitkSurface.h"
#include <mitkPointSetSource.h>
namespace mitk
{
/** Documentation
* @brief This filter converts the input surface into a point set. The output point set contains every point exactly
* one time
- * (no dublicated points like in the stl-format).
+ * (no duplicated points like in the stl-format).
*/
class SurfaceToPointSetFilter : public mitk::PointSetSource
{
public:
mitkClassMacro(SurfaceToPointSetFilter, mitk::PointSetSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
using Superclass::SetInput;
void SetInput(mitk::Surface::Pointer m_InputSurface);
std::string GetErrorMessage();
protected:
SurfaceToPointSetFilter();
~SurfaceToPointSetFilter() override;
/** @brief method generating the output of this filter. Called in the updated process of the pipeline. */
void GenerateData() override;
//############### members ########################
mitk::Surface::Pointer m_InputSurface;
std::string m_ErrorMessage;
};
} // namespace mitk
#endif
diff --git a/Examples/Plugins/org.mitk.example.gui.multipleperspectives/documentation/doxygen/MultiplePerspectives.dox b/Examples/Plugins/org.mitk.example.gui.multipleperspectives/documentation/doxygen/MultiplePerspectives.dox
index 0fd2354c43..eade0be3ea 100644
--- a/Examples/Plugins/org.mitk.example.gui.multipleperspectives/documentation/doxygen/MultiplePerspectives.dox
+++ b/Examples/Plugins/org.mitk.example.gui.multipleperspectives/documentation/doxygen/MultiplePerspectives.dox
@@ -1,36 +1,36 @@
/**
\page BlueBerryExampleMultiplePerspectives Multiple perspectives and views
\image html MultiplePerspectives.png
The second example creates two perspectives. The first perspective has a visible editor area and uses a folder to bring the first view to the left side. The second perspective sets the editor area to invisible. The two views are filled with an empty QListWidget. The perspective bar is enabled.
The visibility of the editor area is set in the corresponding perspective cpp file by:
\snippet MinimumPerspective.cpp Visibility of editor area
The visibility of the perspective bar is set in the applications MultiplePerspectives.cpp:
\snippet MultiplePerspectives.cpp Visibility of perspective bar
Here we can also set/change some other general preferences (e.g. the initial size):
\snippet MultiplePerspectives.cpp initial window size
The perspective bar:
\image html Perspectivebar.png
- The icons of the perspectives are definded by the extension declaration in the plugin.xml
+ The icons of the perspectives are defined by the extension declaration in the plugin.xml
of the example and lie in the resources directory of the plug-in:
\include org.mitk.example.gui.multipleperspectives/plugin.xml
View complete source files:
\li \github{Examples/Plugins/org.mitk.example.gui.multipleperspectives/src/internal/MultiplePerspectives.cpp,MultiplePerspectives.cpp}
\li \github{Examples/Plugins/org.mitk.example.gui.multipleperspectives/src/internal/MultiplePerspectives.h,MultiplePerspectives.h}
\li \github{Examples/Plugins/org.mitk.example.gui.multipleperspectives/src/internal/MinimumPerspective.cpp,MinimumPerspective.cpp}
\li \github{Examples/Plugins/org.mitk.example.gui.multipleperspectives/src/internal/MinimumPerspective.h,MinimumPerspective.h}
\li \github{Examples/Plugins/org.mitk.example.gui.multipleperspectives/src/internal/ExtendedPerspective.cpp,ExtendedPerspective.cpp}
\li \github{Examples/Plugins/org.mitk.example.gui.multipleperspectives/src/internal/ExtendedPerspective.h,ExtendedPerspective.h}
[\ref BlueBerryIntro] [Previous: \ref BlueBerryExampleMinimalApplication] [Next: \ref BlueBerrySelectionServiceIntro]
*/
diff --git a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/manifest_headers.cmake b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/manifest_headers.cmake
index b59bf17ec9..d89cacf219 100644
--- a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/manifest_headers.cmake
+++ b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/manifest_headers.cmake
@@ -1,7 +1,7 @@
-set(Plugin-Name "MITK Example: Seletion service MITK")
+set(Plugin-Name "MITK Example: Selection service MITK")
set(Plugin-Version "1.0.0")
set(Plugin-Vendor "German Cancer Research Center (DKFZ)")
set(Plugin-ContactAddress "https://www.mitk.org")
set(Require-Plugin
org.mitk.gui.qt.common
)
diff --git a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/ListenerViewMitk.h b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/ListenerViewMitk.h
index eaaa78049a..e3de314fef 100644
--- a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/ListenerViewMitk.h
+++ b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/ListenerViewMitk.h
@@ -1,79 +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 LISTENERVIEWMITK_H_
#define LISTENERVIEWMITK_H_
// QMitk includes
#include <QmitkAbstractView.h>
// berry includes
#include <berryIWorkbenchWindow.h>
// Qt includes
#include <QString>
// ui includes
#include "ui_ListenerViewMitkControls.h"
/**
* \ingroup org_mitk_example_gui_selectionservicemitk
*
* \brief This BlueBerry view is part of the BlueBerry example
* "Selection service MITK". It creates two radio buttons that listen
* for selection events of the Qlistwidget (SelectionProvider) and
* change the radio button state accordingly.
*
* @see SelectionViewMitk
*
*/
class ListenerViewMitk : public QmitkAbstractView
{
Q_OBJECT
public:
static const std::string VIEW_ID;
ListenerViewMitk();
protected:
void CreateQtPartControl(QWidget *parent) override;
void SetFocus() override;
//! [MITK Selection Listener method]
- /** @brief Reimplemention of method from QmitkAbstractView that implements the selection listener functionality.
+ /** @brief Reimplementation of method from QmitkAbstractView that implements the selection listener functionality.
* @param part The workbench part responsible for the selection change.
* @param nodes A list of selected nodes.
*
* @see QmitkAbstractView
*
*/
void OnSelectionChanged(berry::IWorkbenchPart::Pointer part,
const QList<mitk::DataNode::Pointer> &nodes) override;
//! [MITK Selection Listener method]
private Q_SLOTS:
/** @brief Simple slot function that changes the selection of the radio buttons according to the passed string.
- * @param selectStr QString that contains the name of the slected list element
+ * @param selectStr QString that contains the name of the selected list element
*
*/
void ToggleRadioMethod(QString selectStr);
private:
Ui::ListenerViewMitkControls m_Controls;
QWidget *m_Parent;
};
#endif /*LISTENERVIEWMITK_H_*/
diff --git a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/documentation/doxygen/SelectionServiceMitk.dox b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/documentation/doxygen/SelectionServiceMitk.dox
index 453fc73b90..e5cfe8ef6d 100644
--- a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/documentation/doxygen/SelectionServiceMitk.dox
+++ b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/documentation/doxygen/SelectionServiceMitk.dox
@@ -1,42 +1,42 @@
/**
\page BlueBerryExampleSelectionServiceMitk MITK DataNode Selections
\brief An example application plug-in with a minimal selection service based on mitk::DataNode objects.
\image html SelectionServiceMITK.png
This example is an alternative for the Qt selection service described in the previous example. The
selection service is based on MITK data nodes. Again the selection service is used to connect the
selection of QListWidgetItems of the SelectionView with the radio buttons from the ListenerView and the
- funtionality is the same.
+ functionality is the same.
This time the SelectionView does not inherit from berry::QtViewPart but from QmitkAbstractView. QmitkAbstractView provides
a virtual method called GetDataNodeSelectionModel() with which the
selection model of the QListWidget can be returned. No selection provider needs to be registered explicitly with the workbench.
In the SelectionViewMitk.h the method from QmitkAbstractView ist declared:
\snippet SelectionViewMitk.h MITK Selection Provider method
First we need to create two data nodes and set some creative names in the SelectionViewMitk.cpp:
\snippet SelectionViewMitk.cpp MITK Selection Provider data nodes
These data nodes are used to create two QListWidgetItems that are added to the QListWidget:
\snippet SelectionViewMitk.cpp MITK Selection Provider listwidgetitems
Now if one of the data nodes is selected in the QListWidget or the selection changes this is registered by the ListenerView.
In the ListenerViewMitk.h we reimplement the method from QmitkAbstractView that implements the selection listener functionality:
\snippet ListenerViewMitk.h MITK Selection Listener method
The simple implementation of this method looks as follows:
\snippet ListenerViewMitk.cpp MITK Selection Listener method implementation
View complete source files:
\li \github{Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/SelectionViewMitk.cpp,SelectionViewMitk.cpp}
\li \github{Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/SelectionViewMitk.h,SelectionViewMitk.h}
\li \github{Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/ListenerViewMitk.cpp,ListenerViewMitk.cpp}
\li \github{Examples/Plugins/org.mitk.example.gui.selectionservicemitk.views/src/internal/ListenerViewMitk.h,ListenerViewMitk.h}
[\ref BlueBerrySelectionServiceIntro] [Previous: \ref BlueBerryExampleSelectionServiceQt] [\ref BlueBerryExamples]
*/
diff --git a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/manifest_headers.cmake b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/manifest_headers.cmake
index ddeb405483..0433c5dd12 100644
--- a/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/manifest_headers.cmake
+++ b/Examples/Plugins/org.mitk.example.gui.selectionservicemitk/manifest_headers.cmake
@@ -1,7 +1,7 @@
-set(Plugin-Name "MITK Example: Seletion service MITK")
+set(Plugin-Name "MITK Example: Selection service MITK")
set(Plugin-Version "1.0.0")
set(Plugin-Vendor "German Cancer Research Center (DKFZ)")
set(Plugin-ContactAddress "https://www.mitk.org")
set(Require-Plugin
org.blueberry.ui.qt
)
diff --git a/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/documentation/doxygen/SelectionServiceQt.dox b/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/documentation/doxygen/SelectionServiceQt.dox
index 01c0933553..2dc994f491 100644
--- a/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/documentation/doxygen/SelectionServiceQt.dox
+++ b/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/documentation/doxygen/SelectionServiceQt.dox
@@ -1,47 +1,47 @@
/**
\page BlueBerryExampleSelectionServiceQt Qt Model/View selections
\brief An example application plug-in with a minimal selection service based on Qt selection models.
\image html SelectionServiceQT.png
In this example the selection service is used to connect the selection of the radio buttons from one
view with the selection of the list of the other view. The SelectionView holds a QListWidget that provides
the user selection (qt selection provider) for the selection listener (ListenerView). The radio buttons of
the listener view are changed according to the selection in the QListWidget. Vice versa the radio buttons
(the selection listener) does not provide any selection events. If the user changes the radio button state
the QListWidget is not altered.
- For additional informations on the selection service concept see https://www.mitk.org/wiki/Article_Using_the_Selection_Service
+ For additional information on the selection service concept see https://www.mitk.org/wiki/Article_Using_the_Selection_Service
The berry::QtSelectionProvider class implements the interface berry::ISelectionProvider. Due to the model/view concept in Qt, the workbench
provides the berry::QtSelectionProvider class for Qt viewers which must be provided with a QItemSelectionModel.
In the SelectionView.h we declare a pointer that holds the selection provider...
\snippet SelectionView.h Qt Selection Provider
...and in the SelectionView.cpp we set the selection model to the model of the QListWidget (m_SelectionList)
\snippet SelectionView.cpp Qt Selection Provider registration
Now that the QListWidget of the SelectionView sends out selection events we need a listener implementation. In the ListenerView.h
we need to include the ISelectionListener which is a simple class with just one method.
The method that implements the selection listener functionality of ISelectionListener and the pointer that holds the selection listener is declared...
\snippet ListenerView.h Qt Selection Listener method and pointer
...and implemented in the cpp-file:
\snippet ListenerView.cpp Qt Selection Listener method implementation
Now the name of the selected list element is passed to a method that toggles the radio buttons of the ListenerView accordingly.
View complete source files:
\li \github{Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/SelectionView.cpp,SelectionView.cpp}
\li \github{Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/SelectionView.h,SelectionView.h}
\li \github{Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/ListenerView.cpp,ListenerView.cpp}
\li \github{Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/ListenerView.h,ListenerView.h}
[\ref BlueBerrySelectionServiceIntro] [Next: \ref BlueBerryExampleSelectionServiceMitk] [\ref BlueBerryExamples]
*/
diff --git a/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/manifest_headers.cmake b/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/manifest_headers.cmake
index 7b788636a4..a6bfba48ff 100644
--- a/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/manifest_headers.cmake
+++ b/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/manifest_headers.cmake
@@ -1,7 +1,7 @@
-set(Plugin-Name "MITK Example: Seletion service QT")
+set(Plugin-Name "MITK Example: Selection service QT")
set(Plugin-Version "1.0.0")
set(Plugin-Vendor "German Cancer Research Center (DKFZ)")
set(Plugin-ContactAddress "https://www.mitk.org")
set(Require-Plugin
org.blueberry.ui.qt
)
diff --git a/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/ListenerView.h b/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/ListenerView.h
index a7fcf07224..d13916f867 100644
--- a/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/ListenerView.h
+++ b/Examples/Plugins/org.mitk.example.gui.selectionserviceqt/src/internal/ListenerView.h
@@ -1,81 +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 LISTENERVIEW_H_
#define LISTENERVIEW_H_
// berry includes
#include <berryISelectionListener.h>
#include <berryIStructuredSelection.h>
#include <berryQtViewPart.h>
// ui includes
#include "ui_ListenerViewControls.h"
/**
* \ingroup org_mitk_example_gui_selectionserviceqt
*
* \brief This BlueBerry view is part of the BlueBerry example
* "Selection service QT". It creates two radio buttons that listen
* for selection events of the Qlistwidget (SelectionProvider) and
* change the radio button state accordingly.
*
* @see SelectionView
*/
class ListenerView : public berry::QtViewPart
{
Q_OBJECT
public:
static const std::string VIEW_ID;
ListenerView();
~ListenerView() override;
protected:
void CreateQtPartControl(QWidget *parent) override;
void SetFocus() override;
private Q_SLOTS:
/**
* @brief Simple slot function that changes the selection of the radio buttons according to the passed string.
- * @param selectStr QString that contains the name of the slected list element
+ * @param selectStr QString that contains the name of the selected list element
*/
void ToggleRadioMethod(QString selectStr);
private:
//! [Qt Selection Listener method and pointer]
/**
* @brief Method of berry::ISelectionListener that implements the selection listener functionality.
* @param sourcepart The workbench part responsible for the selection change.
* @param selection This parameter holds the current selection.
*
* @see ISelectionListener
*/
void SelectionChanged(const berry::IWorkbenchPart::Pointer &sourcepart,
const berry::ISelection::ConstPointer &selection);
/** @brief this pointer holds the selection listener */
QScopedPointer<berry::ISelectionListener> m_SelectionListener;
//! [Qt Selection Listener method and pointer]
friend struct berry::SelectionChangedAdapter<ListenerView>;
Ui::ListenerViewControls m_Controls;
QWidget *m_Parent;
};
#endif /*LISTENERVIEW_H_*/
diff --git a/Examples/Tutorial/Step5/Step5.cpp b/Examples/Tutorial/Step5/Step5.cpp
index e4b149aef6..85d72b1e78 100644
--- a/Examples/Tutorial/Step5/Step5.cpp
+++ b/Examples/Tutorial/Step5/Step5.cpp
@@ -1,203 +1,203 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkRegisterClasses.h"
#include "QmitkRenderWindow.h"
#include "QmitkSliceWidget.h"
#include "mitkNodePredicateDataType.h"
#include "mitkProperties.h"
#include "mitkRenderingManager.h"
#include "mitkStandaloneDataStorage.h"
#include "mitkPointSet.h"
// NEW INCLUDE
#include "mitkPointSetDataInteractor.h"
#include <QApplication>
#include <QHBoxLayout>
#include <itksys/SystemTools.hxx>
#include <mitkIOUtil.h>
//##Documentation
//## @brief Interactively add points
//##
//## As in Step4, load one or more data sets (many image,
//## surface and other formats) and create 3 views on the data.
//## Additionally, we want to interactively add points. A node containing
//## a PointSet as data is added to the data tree and a PointSetDataInteractor
//## is associated with the node, which handles the interaction. The
//## @em interaction @em pattern is defined in a state-machine, stored in an
//## external XML file. Thus, we need to load a state-machine
//## The interaction patterns defines the @em events,
//## on which the interactor reacts (e.g., which mouse buttons are used to
//## set a point), the @em transition to the next state (e.g., the initial
//## may be "empty point set") and associated @a actions (e.g., add a point
-//## at the position where the mouse-click occured).
+//## at the position where the mouse-click occurred).
int main(int argc, char *argv[])
{
QApplication qtapplication(argc, argv);
if (argc < 2)
{
fprintf(
stderr, "Usage: %s [filename1] [filename2] ...\n\n", itksys::SystemTools::GetFilenameName(argv[0]).c_str());
return 1;
}
// Register Qmitk-dependent global instances
QmitkRegisterClasses();
//*************************************************************************
// Part I: Basic initialization
//*************************************************************************
// Create a DataStorage
mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New();
//*************************************************************************
// Part II: Create some data by reading files
//*************************************************************************
int i;
for (i = 1; i < argc; ++i)
{
// For testing
if (strcmp(argv[i], "-testing") == 0)
continue;
// Load datanode (eg. many image formats, surface formats, etc.)
mitk::StandaloneDataStorage::SetOfObjects::Pointer dataNodes = mitk::IOUtil::Load(argv[i], *ds);
//*********************************************************************
// Part III: Put the data into the datastorage
//*********************************************************************
// Add the node to the DataStorage
if (dataNodes->empty())
{
fprintf(stderr, "Could not open file %s \n\n", argv[i]);
exit(2);
}
}
//*************************************************************************
// Part V: Create windows and pass the tree to it
//*************************************************************************
// Create toplevel widget with horizontal layout
QWidget toplevelWidget;
QHBoxLayout layout;
layout.setSpacing(2);
layout.setContentsMargins({});
toplevelWidget.setLayout(&layout);
//*************************************************************************
// Part Va: 3D view
//*************************************************************************
// Create a renderwindow
QmitkRenderWindow renderWindow(&toplevelWidget);
layout.addWidget(&renderWindow);
// Tell the renderwindow which (part of) the tree to render
renderWindow.GetRenderer()->SetDataStorage(ds);
// Use it as a 3D view
renderWindow.GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard3D);
// Reposition the camera to include all visible actors
renderWindow.GetRenderer()->GetVtkRenderer()->ResetCamera();
//*************************************************************************
// Part Vb: 2D view for slicing axially
//*************************************************************************
// Create QmitkSliceWidget, which is based on the class
// QmitkRenderWindow, but additionally provides sliders
QmitkSliceWidget view2(&toplevelWidget);
layout.addWidget(&view2);
// Tell the QmitkSliceWidget which (part of) the tree to render.
// By default, it slices the data axially
view2.SetDataStorage(ds);
mitk::DataStorage::SetOfObjects::ConstPointer rs = ds->GetSubset(mitk::TNodePredicateDataType<mitk::Image>::New());
view2.SetData(rs->Begin(), mitk::AnatomicalPlane::Axial);
// We want to see the position of the slice in 2D and the
// slice itself in 3D: add it to the tree!
ds->Add(view2.GetRenderer()->GetCurrentWorldPlaneGeometryNode());
//*************************************************************************
// Part Vc: 2D view for slicing sagittally
//*************************************************************************
// Create QmitkSliceWidget, which is based on the class
// QmitkRenderWindow, but additionally provides sliders
QmitkSliceWidget view3(&toplevelWidget);
layout.addWidget(&view3);
// Tell the QmitkSliceWidget which (part of) the tree to render
// and to slice sagittally
view3.SetDataStorage(ds);
view3.SetData(rs->Begin(), mitk::AnatomicalPlane::Sagittal);
// We want to see the position of the slice in 2D and the
// slice itself in 3D: add it to the tree!
ds->Add(view3.GetRenderer()->GetCurrentWorldPlaneGeometryNode());
// *******************************************************
// ****************** START OF NEW PART ******************
// *******************************************************
//*************************************************************************
// Part VI: For allowing to interactively add points ...
//*************************************************************************
// ATTENTION: It is very important that the renderer already know their DataStorage,
// because registerig DataInteractors with the render windows is done automatically
// and only works if the BaseRenderer and the DataStorage know each other.
// Create PointSet and a node for it
mitk::PointSet::Pointer pointSet = mitk::PointSet::New();
mitk::DataNode::Pointer pointSetNode = mitk::DataNode::New();
// Store the point set in the DataNode
pointSetNode->SetData(pointSet);
// Add the node to the tree
ds->Add(pointSetNode);
// Create PointSetDataInteractor
mitk::PointSetDataInteractor::Pointer interactor = mitk::PointSetDataInteractor::New();
// Set the StateMachine pattern that describes the flow of the interactions
interactor->LoadStateMachine("PointSet.xml");
// Set the configuration file, which describes the user interactions that trigger actions
// in this file SHIFT + LeftClick triggers add Point, but by modifying this file,
// it could as well be changes to any other user interaction.
interactor->SetEventConfig("PointSetConfig.xml");
// Assign the pointSetNode to the interactor,
// alternatively one could also add the DataInteractor to the pointSetNode using the SetDataInteractor() method.
interactor->SetDataNode(pointSetNode);
// *******************************************************
// ******************* END OF NEW PART *******************
// *******************************************************
//*************************************************************************
// Part VII: Qt-specific initialization
//*************************************************************************
toplevelWidget.show();
return qtapplication.exec();
}
/**
\example Step5.cpp
*/
diff --git a/MITKCPackOptions.cmake.in b/MITKCPackOptions.cmake.in
index 5728c46f84..bd50c04e08 100644
--- a/MITKCPackOptions.cmake.in
+++ b/MITKCPackOptions.cmake.in
@@ -1,31 +1,31 @@
if(CPACK_GENERATOR MATCHES "NSIS")
# set the package header icon for MUI
set(CPACK_PACKAGE_ICON "@MITK_SOURCE_DIR@\\mitk.bmp")
- # set the install/unistall icon used for the installer itself
+ # set the install/uninstall icon used for the installer itself
# There is a bug in NSIS that does not handle full unix paths properly.
set(CPACK_NSIS_MUI_ICON "@MITK_SOURCE_DIR@\\mitk.ico")
set(CPACK_NSIS_MUI_UNIICON "@MITK_SOURCE_DIR@\\mitk.ico")
set(CPACK_NSIS_DISPLAY_NAME "MITK - Medical Imaging and Interaction Toolkit")
# tell cpack to create links to the doc files
set(CPACK_NSIS_MENU_LINKS
"https://www.mitk.org" "MITK Web Site"
)
# tell cpack the executables you want in the start menu as links
set(CPACK_PACKAGE_EXECUTABLES "@MITK_CPACK_PACKAGE_EXECUTABLES@")
# tell cpack to create a desktop link to MainApp
set(CPACK_CREATE_DESKTOP_LINKS "@CPACK_CREATE_DESKTOP_LINKS@")
set(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\mitk.ico")
set(CPACK_NSIS_HELP_LINK "http:\\\\www.mitk.org")
set(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\www.mitk.org")
set(CPACK_NSIS_CONTACT mitk@mitk.org)
set(CPACK_NSIS_MODIFY_PATH ON)
endif(CPACK_GENERATOR MATCHES "NSIS")
diff --git a/MITKConfig.cmake.in b/MITKConfig.cmake.in
index f7b840ceb2..b63d5b6dbe 100644
--- a/MITKConfig.cmake.in
+++ b/MITKConfig.cmake.in
@@ -1,252 +1,252 @@
if(CMAKE_VERSION VERSION_LESS @MITK_CMAKE_MINIMUM_REQUIRED_VERSION@)
message(FATAL_ERROR "MITK requires at least CMake Version @MITK_CMAKE_MINIMUM_REQUIRED_VERSION@")
endif()
# The MITK version number
set(MITK_VERSION_MAJOR "@MITK_VERSION_MAJOR@")
set(MITK_VERSION_MINOR "@MITK_VERSION_MINOR@")
set(MITK_VERSION_PATCH "@MITK_VERSION_PATCH@")
set(MITK_VERSION_STRING "@MITK_VERSION_STRING@")
#-----------------------------------------------------------------------------
# C++ language standard
#-----------------------------------------------------------------------------
set(MITK_CXX_STANDARD @MITK_CXX_STANDARD@)
#-----------------------------------------------------------------------------
# Include required CMake scripts
#-----------------------------------------------------------------------------
# Update the CMake module path
set(MITK_CMAKE_MODULE_PATH "@MITK_SOURCE_DIR@/CMake")
list(APPEND CMAKE_MODULE_PATH ${MITK_CMAKE_MODULE_PATH})
# Standard CMake macros
include(CMakeParseArguments)
include(FeatureSummary)
include(FindPackageHandleStandardArgs)
include(GenerateExportHeader)
# Include MITK macros
include(MacroParseArguments)
include(mitkFunctionAddManifest)
include(mitkFunctionAddCustomModuleTest)
include(mitkFunctionCheckMitkCompatibility)
include(mitkFunctionCheckModuleDependencies)
include(mitkFunctionConfigureVisualStudioUserProjectFile)
include(mitkFunctionCreateBlueBerryApplication)
include(mitkFunctionCreateCommandLineApp)
include(mitkFunctionCreateModule)
include(mitkFunctionCreatePlugin)
include(mitkFunctionCreateProvisioningFile)
include(mitkFunctionCreateWindowsBatchScript)
include(mitkFunctionGetLibrarySearchPaths)
include(mitkFunctionInstallAutoLoadModules)
include(mitkFunctionInstallCTKPlugin)
include(mitkFunctionInstallProvisioningFiles)
include(mitkFunctionInstallThirdPartyCTKPlugins)
include(mitkFunctionOrganizeSources)
include(mitkFunctionUseModules)
include(mitkMacroCreateExecutable)
include(mitkMacroCreateModuleTests)
include(mitkMacroFindDependency)
include(mitkMacroGenerateToolsLibrary)
include(mitkMacroGetPMDPlatformString)
include(mitkMacroInstall)
include(mitkMacroInstallHelperApp)
include(mitkMacroInstallTargets)
include(mitkMacroMultiplexPicType)
#-----------------------------------------------------------------------------
# MITK flags and directories
#-----------------------------------------------------------------------------
# MITK compiler flags
set(MITK_C_FLAGS "@MITK_C_FLAGS@")
set(MTTK_C_FLAGS_DEBUG "@MITK_C_FLAGS_DEBUG@")
set(MITK_C_FLAGS_RELEASE "@MITK_C_FLAGS_RELEASE@")
set(MITK_CXX_FLAGS "@MITK_CXX_FLAGS@")
set(MTTK_CXX_FLAGS_DEBUG "@MITK_CXX_FLAGS_DEBUG@")
set(MITK_CXX_FLAGS_RELEASE "@MITK_CXX_FLAGS_RELEASE@")
# MITK linker flags
set(MITK_EXE_LINKER_FLAGS "@MITK_EXE_LINKER_FLAGS@")
set(MITK_SHARED_LINKER_FLAGS "@MITK_SHARED_LINKER_FLAGS@")
set(MITK_MODULE_LINKER_FLAGS "@MITK_MODULE_LINKER_FLAGS@")
# MITK specific directories
set(MITK_SOURCE_DIR "@MITK_SOURCE_DIR@")
set(MITK_BINARY_DIR "@MITK_BINARY_DIR@")
set(MITK_CMAKE_DIR "@MITK_CMAKE_DIR@")
# MITK output directories
set(MITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY "@MITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY@")
set(MITK_CMAKE_ARCHIVE_OUTPUT_DIRECTORY "@MITK_CMAKE_ARCHIVE_OUTPUT_DIRECTORY@")
set(MITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY "@MITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY@")
#-----------------------------------------------------------------------------
# Miscellaneous variables
#-----------------------------------------------------------------------------
# Internal version numbers, used for approximate compatibility checks
# of a MITK development version (non-release).
set(MITK_VERSION_PLUGIN_SYSTEM 2) # dropped legacy BlueBerry plug-in CMake support
set(MITK_DATA_DIR "@MITK_DATA_DIR@")
set(UTILITIES_DIR "@UTILITIES_DIR@")
set(REGISTER_QFUNCTIONALITY_CPP_IN "@REGISTER_QFUNCTIONALITY_CPP_IN@")
set(MITK_DOXYGEN_TAGFILE_NAME "@MITK_DOXYGEN_TAGFILE_NAME@")
set(MITK_LEGACY_EXPORT_MACRO_NAME 1)
set(DCMTK_CMAKE_DEBUG_POSTFIX @DCMTK_CMAKE_DEBUG_POSTFIX@)
# Get the canonical name of the directory to avoid potential case mismatch,
# e.g. in the drive letter on Windows.
get_filename_component(CMAKE_CURRENT_LIST_DIR_REALPATH ${CMAKE_CURRENT_LIST_DIR} REALPATH)
if(CMAKE_CURRENT_LIST_DIR_REALPATH STREQUAL MITK_BINARY_DIR)
set(MITK_EXTERNAL_PROJECT_PREFIX @MITK_EXTERNAL_PROJECT_PREFIX@)
endif()
set(MITK_MODULES_PACKAGE_DEPENDS_DIR "@MITK_MODULES_PACKAGE_DEPENDS_DIR@")
if(MODULES_PACKAGE_DEPENDS_DIRS)
list(APPEND MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR})
list(REMOVE_DUPLICATES MODULES_PACKAGE_DEPENDS_DIRS)
else()
set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR})
endif()
#-----------------------------------------------------------------------------
# External dependencies
#-----------------------------------------------------------------------------
list(APPEND CMAKE_PREFIX_PATH "@MITK_EXTERNAL_PROJECT_PREFIX@")
# -----------------------------------------
# Qt variables and dependencies
set(MITK_USE_Qt6 @MITK_USE_Qt6@)
if(MITK_USE_Qt6)
set(MITK_QT6_MINIMUM_VERSION @MITK_QT6_MINIMUM_VERSION@)
set(MITK_QT6_COMPONENTS @MITK_QT6_COMPONENTS@)
mitkMacroFindDependency(Qt6 ${MITK_QT6_MINIMUM_VERSION} COMPONENTS ${MITK_QT6_COMPONENTS})
endif()
# -----------------------------------------
# Special Python variables
set(MITK_USE_Python3 @MITK_USE_Python3@)
if(MITK_USE_Python3)
set(PYTHON_EXECUTABLE "@PYTHON_EXECUTABLE@" CACHE FILEPATH "")
set(PYTHON_INCLUDE_DIR "@PYTHON_INCLUDE_DIR@" CACHE PATH "")
set(PYTHON_LIBRARY "@PYTHON_LIBRARY@" CACHE FILEPATH "")
set(PYTHON_INCLUDE_DIR2 "@PYTHON_INCLUDE_DIR2@" CACHE PATH "")
mitkMacroFindDependency(Python3 COMPONENTS Interpreter Development NumPy)
endif()
# -----------------------------------------
# Special Boost variables
set(MITK_USE_Boost_LIBRARIES @MITK_USE_Boost_LIBRARIES@)
set(MITK_USE_SYSTEM_Boost @MITK_USE_SYSTEM_Boost@)
set(Boost_ROOT "@Boost_ROOT@" CACHE PATH "")
set(BOOST_LIBRARYDIR "@BOOST_LIBRARYDIR@" CACHE PATH "")
set(Boost_ADDITIONAL_VERSIONS 1.74 1.74.0)
# We need this later for a DCMTK workaround
set(_dcmtk_dir_orig "@DCMTK_DIR@")
# -----------------------------------------
# External dependencies from the superbuild
# or injected from somewhere else via
# <project>_DIR variables.
@MITK_CONFIG_EXTERNAL_PROJECTS@
# Ensure the MITK module path comes first
set(CMAKE_MODULE_PATH ${MITK_CMAKE_MODULE_PATH} ${CMAKE_MODULE_PATH})
# -----------------------------------------
# Special handling for DCMTK
if(MITK_USE_DCMTK)
# Due to the preferred CONFIG mode in find_package calls above,
# the DCMTKConfig.cmake file is read, which does not provide useful
- # package information. We explictly need MODULE mode to find DCMTK.
+ # package information. We explicitly need MODULE mode to find 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()
find_package(DCMTK REQUIRED MODULE)
endif()
# -----------------------------------------
# Special handling for DCMQI
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.
+ # package information. We explicitly 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()
# -----------------------------------------
# Special handling for Boost
if(MITK_USE_Boost)
link_directories(${Boost_LIBRARY_DIRS})
endif()
# -----------------------------------------
# Internal project dependencies
set(CppMicroServices_DIR "@CppMicroServices_DIR@")
mitkMacroFindDependency(CppMicroServices)
set(MITK_USE_BLUEBERRY @MITK_USE_BLUEBERRY@)
if(MITK_USE_BLUEBERRY)
set(MITK_PLUGIN_USE_FILE "@MITK_PLUGIN_USE_FILE@")
if(MITK_PLUGIN_USE_FILE)
if(EXISTS "${MITK_PLUGIN_USE_FILE}")
include("${MITK_PLUGIN_USE_FILE}")
endif()
endif()
set(MITK_PLUGIN_PROVISIONING_FILE "@MITK_EXTAPP_PROVISIONING_FILE@")
set(MITK_PROVISIONING_FILES
"${BLUEBERRY_PLUGIN_PROVISIONING_FILE}"
"${MITK_PLUGIN_PROVISIONING_FILE}")
endif()
set(BLUEBERRY_USE_QT_HELP @BLUEBERRY_USE_QT_HELP@)
if(BLUEBERRY_USE_QT_HELP AND DOXYGEN_VERSION VERSION_LESS "1.8.7")
message("Setting BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.")
set(BLUEBERRY_USE_QT_HELP OFF)
endif()
set(BLUEBERRY_QTPLUGIN_PATH "@BLUEBERRY_QTPLUGIN_PATH@")
set(QT_HELPGENERATOR_EXECUTABLE "@QT_HELPGENERATOR_EXECUTABLE@")
set(QT_COLLECTIONGENERATOR_EXECUTABLE "@QT_COLLECTIONGENERATOR_EXECUTABLE@")
#-----------------------------------------------------------------------------
# Import MITK targets and set custom properties
#-----------------------------------------------------------------------------
if(NOT MITK_EXPORTS_FILE_INCLUDED)
if(EXISTS "@MITK_EXPORTS_FILE@")
set(MITK_EXPORTS_FILE_INCLUDED 1)
include("@MITK_EXPORTS_FILE@")
endif()
endif()
# Set properties on exported targets
@MITK_EXPORTED_TARGET_PROPERTIES@
diff --git a/Modules/AlgorithmsExt/include/mitkAnisotropicIterativeClosestPointRegistration.h b/Modules/AlgorithmsExt/include/mitkAnisotropicIterativeClosestPointRegistration.h
index ddbcd05183..a602085b42 100644
--- a/Modules/AlgorithmsExt/include/mitkAnisotropicIterativeClosestPointRegistration.h
+++ b/Modules/AlgorithmsExt/include/mitkAnisotropicIterativeClosestPointRegistration.h
@@ -1,309 +1,309 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkAnisotropicIterativeClosestPointRegistration_h
#define mitkAnisotropicIterativeClosestPointRegistration_h
// MITK
#include <mitkCommon.h>
#include <mitkVector.h>
// EXPORTS
#include "MitkAlgorithmsExtExports.h"
// STL
#include <vector>
// ITK
#include <itkMatrix.h>
// forward declarations
class vtkPoints;
class vtkKdTreePointLocator;
namespace mitk
{
class Surface;
class WeightedPointTransform;
/**
* \ingroup AnisotropicRegistration
*
* @brief Implementation of the anisotropic iterative closest point (A-ICP)
- * algoritm.
+ * algorithm.
*
- * This class implements the anisotropic interative closest point (A-ICP)
+ * This class implements the anisotropic iterative closest point (A-ICP)
* algorithm presented in L. Maier-Hein et al. in "Convergent Iterative
- * Closest-Point Algorithm to Accomodate Anisotropic and Inhomogenous
+ * Closest-Point Algorithm to Accommodate Anisotropic and Inhomogenous
* Localization Error.", IEEE T Pattern Anal 34 (8), 1520-1532, 2012.
* The algorithm computes the optimal transformation to align two surfaces.
* In addition to the surfaces a list of covariance matrices is used as
* input for every surface. Each covariance matrix represents the error of
* a specific vertex in the Surface. The covariance matrices
* for each surface can be defined by the user, or calculated
* by the CovarianceMatrixCalculator. In addition a trimmed algorithm version
* is provided to compute the registration of partial overlapping surfaces.
* The algorithm needs a clean surface non manifold edges and without duplicated
* vertices. In addition vtkCleanPolyData can be used to ensure a correct
* Surface representation.
*
* \note The correspondence search is accelerated when OpenMP is enabled.
*
* \b Example:
*
*
* \code
* typedef itk::Matrix < double, 3, 3 > Matrix3x3;
* typedef itk::Vector < double, 3 > Vector3;
* typedef std::vector < Matrix3x3 > CovarianceMatrixList;
*
* // compute the covariance matrices
* mitk::CovarianceMatrixCalculator::Pointer matrixCalculator =
* mitk::CovarianceMatrixCalculator::New();
*
* // compute the covariance matrices for the moving surface (X)
* matrixCalculator->SetInputSurface(movingSurface);
* matrixCalculator->ComputeCovarianceMatrices();
* CovarianceMatrixList sigmas_X = matrixCalculator->GetCovarianceMatrices();
* double meanVarX = matrixCalculator->GetMeanVariance();
*
* // compute the covariance matrices for the fixed surface (Y)
* matrixCalculator->SetInputSurface(fixedSurface);
* matrixCalculator->ComputeCovarianceMatrices();
* CovarianceMatrixList sigmas_Y = matrixCalculator->GetCovarianceMatrices();
* double meanVarY = matrixCalculator->GetMeanVariance();
*
* // the FRE normalization factor
* double normalizationFactor = sqrt( meanVarX + meanVarY);
*
* // A-ICP algorithm
* mitk::AnisotropicIterativeClosestPointRegistration::Pointer aICP =
* mitk::AnisotropicIterativeClosestPointRegistration::New();
*
* // set up parameters
* aICP->SetMovingSurface(movingSurface);
* aICP->SetFixedSurface(fixedSurface);
* aICP->SetCovarianceMatricesMovingSurface(sigmas_X);
* aICP->SetCovarianceMatricesFixedSurface(sigmas_Y);
* aICP->SetFRENormalizationFactor(normalizationFactor);
*
* // Trimming is enabled if a fator > 0.0 is set.
* // 40 percent of the moving point set
* // will be used for registration in this example.
* // To disable trimming set the trim factor back to 0.0
* aICP->SetTrimmFactor(0.4);
*
* // run the algorithm
* aICP->Update();
*
* // retrieve the computed transformation
* Matrix3x3 rotation = aICP->GetRotation();
* Vector3 translation = aICP->GetTranslation();
* \endcode
*
*/
class MITKALGORITHMSEXT_EXPORT AnisotropicIterativeClosestPointRegistration : public itk::Object
{
protected:
/** Definition of a 3x3 covariance matrix.*/
typedef itk::Matrix<double, 3, 3> CovarianceMatrix;
/** Definition of a list of covariance matrices.*/
typedef std::vector<CovarianceMatrix> CovarianceMatrixList;
/** Definition of a translation vector.*/
typedef mitk::Vector3D Translation;
/** Definition of a 3x3 rotation matrix.*/
typedef CovarianceMatrix Rotation;
/** Definition of a correspondeces, index and distance.*/
typedef std::pair<unsigned int, double> Correspondence;
/** Definition of a list of correspondences.*/
typedef std::vector<Correspondence> CorrespondenceList;
AnisotropicIterativeClosestPointRegistration();
~AnisotropicIterativeClosestPointRegistration() override;
/** Max amount of iterations. Default is 1000.*/
unsigned int m_MaxIterations;
/** Threshold used for termination. Default is 1.0e-6.*/
double m_Threshold;
/** Normalization factor for the feducial registration error. default is 0.0.*/
double m_FRENormalizationFactor;
/** Search radius for the correspondence search. Default is 30.*/
double m_SearchRadius;
/** The maximum number of iterations in the weighted point based
* registration. Default is 1000.
*/
double m_MaxIterationsInWeightedPointTransform;
/** The fiducial registration error (FRE).*/
double m_FRE;
/** Trimmfactor for partial overlapping registration. Default is 0.*/
double m_TrimmFactor;
/** Amount of iterations used by the algorithm.*/
unsigned int m_NumberOfIterations;
/** Moving surface that is transformed on the fixed surface.*/
itk::SmartPointer<Surface> m_MovingSurface;
/** The fixed / target surface.*/
itk::SmartPointer<Surface> m_FixedSurface;
/** The weighted point based registration algorithm.*/
itk::SmartPointer<WeightedPointTransform> m_WeightedPointTransform;
/** The covariance matrices belonging to the moving surface (X).*/
CovarianceMatrixList m_CovarianceMatricesMovingSurface;
/** The covariance matrices belonging to the moving surface (Y).*/
CovarianceMatrixList m_CovarianceMatricesFixedSurface;
/** The computed 3x1 translation vector.*/
Translation m_Translation;
/** The computed 3x3 rotation matrix.*/
Rotation m_Rotation;
/**
* Method that computes the correspondences between the moving point set X
* and the fixed point set Y. The distances between the points
* are weighted with weight matrices that are computed from the covariances
* along the surfaces axes. This method implements the runtime optimization
* presented by L. Maier-Hein et al.. The correspondences are computed with
* the help of a kd tree. The correspondences are searched in a given radius
* in the euklidian space. Every correspondence found in this radius is
* weighted based on the covariance matrices and the best weighting will be
* used as a correspondence.
*
* @param X The moving point set.
* @param Z The returned correspondences from the fixed point set.
* @param Y The fixed point set saved in a kd tree.
* @param sigma_X Covariance matrices belonging to the moving point set.
* @param sigma_Y Covariance matrices belonging to the fixed point set.
* @param sigma_Z Covariance matrices belonging to the correspondences found.
* @param correspondences Saved correspondences, in a pair containing the
* their index in Y and distance.
* @param radius The search radius used in in kd tree.
*
*/
void ComputeCorrespondences(vtkPoints *X,
vtkPoints *Z,
vtkKdTreePointLocator *Y,
const CovarianceMatrixList &sigma_X,
const CovarianceMatrixList &sigma_Y,
CovarianceMatrixList &sigma_Z,
CorrespondenceList &correspondences,
const double radius);
public:
mitkClassMacroItkParent(AnisotropicIterativeClosestPointRegistration, itk::Object);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Set the maximum amount of iterations used by the algorithm. */
itkSetMacro(MaxIterations, unsigned int);
/** Set the threshold used to terminate the algorithm.*/
itkSetMacro(Threshold, double);
/** Set the normalization factor for the fiducial registration error (FRE).
* The normalization factor is computed with the help of the mean variance
* of both CovarianceMatrixList that can be obtained when the covariance
* matrices are calculated with the CovarianceMatrixCalculator:
*
* \code{.cpp}
* double FRENormalizationFactor = sqrt ( MeanVarianceX + MeanVarianceY );
* \endcode
*
* if no FRE normalization is used the normalization factor is set to 1.0
* as default value.
*/
itkSetMacro(FRENormalizationFactor, double);
/** Set search radius for the correspondence search.*/
itkSetMacro(SearchRadius, double);
- /** Set the maximim number of iterations used by the point based registration
+ /** Set the maximum number of iterations used by the point based registration
* algorithm.
*/
itkSetMacro(MaxIterationsInWeightedPointTransform, double);
/** Get the fiducial registration error (FRE).*/
itkGetMacro(FRE, double);
/** Get the number of iterations used by the algorithm.*/
itkGetMacro(NumberOfIterations, unsigned int);
/**
* Factor that trimms the point set in percent for
- * partial overlapping surfaces. E.g. 0.4 will use 40 precent
+ * partial overlapping surfaces. E.g. 0.4 will use 40 percent
* of the point set. To enable the trimmed version a
* factor > 0 and < 1 must be set. The default value is 0.0.
*/
itkSetMacro(TrimmFactor, double);
/**
* Set moving surface that includes the point set (X).
*/
itkSetMacro(MovingSurface, itk::SmartPointer<Surface>);
/**
* Set fixed surface that includes the point set (Y).
*/
itkSetMacro(FixedSurface, itk::SmartPointer<Surface>);
/**
* Returns the 3x1 translation vector computed by the algorithm.
*/
itkGetConstReferenceMacro(Translation, Translation);
/**
* Returns the 3x3 rotation matrix computed by the algorithm.
*/
itkGetConstReferenceMacro(Rotation, Rotation);
/**
* Set the covariance matrices of the moving surface. The algorithm
* need the same amount of covariance and points available in the surface.
* The covariance matrix for every vertex in a Surface can be calculated by
* the CovarianceMatrixCalculator. It is also possible to define
* arbitrary matrices by hand.
*/
void SetCovarianceMatricesMovingSurface(CovarianceMatrixList &list)
{
m_CovarianceMatricesMovingSurface = list;
}
/**
* Set the covariance matrices of the fixed surface. The algorithm
* need the same amount of covariance and points available in the surface.
* The covariance matrix for every vertex in a Surface can be calculated by
* the CovarianceMatrixCalculator. It is also possible to define
* arbitrary matrices by hand.
*/
void SetCovarianceMatricesFixedSurface(CovarianceMatrixList &list) { m_CovarianceMatricesFixedSurface = list; }
/**
* This method executes the algorithm.
*
* @warning The algorithm is only a simple calculation filter and can not be
- * used in a mitk filter pipline.
+ * used in a mitk filter pipeline.
*
* @throws Exception if the search radius was doubled more than 20 times to
* prevent endless loops. Re-run the with a different search radius that
* will find the correspondences.
*/
void Update();
};
}
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkAnisotropicRegistrationCommon.h b/Modules/AlgorithmsExt/include/mitkAnisotropicRegistrationCommon.h
index 098d4774dd..147158dd40 100644
--- a/Modules/AlgorithmsExt/include/mitkAnisotropicRegistrationCommon.h
+++ b/Modules/AlgorithmsExt/include/mitkAnisotropicRegistrationCommon.h
@@ -1,126 +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 mitkAnisotropicRegistrationCommon_h
#define mitkAnisotropicRegistrationCommon_h
#include <itkMatrix.h>
#include <mitkCommon.h>
#include <mitkVector.h>
#include "MitkAlgorithmsExtExports.h"
// forward declarations
class vtkPoints;
namespace mitk
{
class PointSet;
/**
* \ingroup AnisotropicRegistration
* \brief A Class that provides common static functions used by all classes
* and tests in the anisotropic iterative closest point algorithm
* (AnisotropicIterativeClosestPointRegistration).
*
* The class provides common functionality used by the A-ICP algorithm like:
* compute weightmatrices (@ref CalculateWeightMatrix()),
* transform points (@ref TransformPoints()), propagate 3 x 3 matrices
* (@ref PropagateMatrices()) and compute the target registration error (TRE)
* (@ref ComputeTargetRegistrationError()).
*/
class MITKALGORITHMSEXT_EXPORT AnisotropicRegistrationCommon
{
protected:
// local typedefs
/** Definition of the 3 x 3 weight matrix.*/
typedef itk::Matrix<double, 3, 3> WeightMatrix;
/** Definition of a rotation matrix.*/
typedef WeightMatrix Rotation;
/** Definition of the covariance matrix.*/
typedef WeightMatrix CovarianceMatrix;
/** Definition of the translation vector.*/
typedef mitk::Vector3D Translation;
/** Definition of the weight matrix list.*/
typedef std::vector<WeightMatrix> MatrixList;
AnisotropicRegistrationCommon() {}
~AnisotropicRegistrationCommon() {}
public:
/** @brief Method that computes a WeightMatrix with two CovarianceMatrices.
* @param sigma_X CovarianceMatrix from the moving point set.
* @param sigma_Y CovarianceMatrix from the fixed point set.
* @return The computed WeighMatrix.
*/
static WeightMatrix CalculateWeightMatrix(const CovarianceMatrix &sigma_X, const CovarianceMatrix &sigma_Y);
/**
* @brief Transforms a point cloud with a Rotation and Translation.
*
* The method uses two point sets as input. It transforms every point from the
- * source point set and saves the result in the destination. The soure is not
+ * source point set and saves the result in the destination. The source is not
* modified. If the same point set is used as source and destination. The method
* will modify the source and the transformation is done in place.
*
* @warning No bound check is done. Ensure that source and destination are
* allocated and have the same size.
*
* @param src The source point set.
* @param dst The destination point set.
* @param rotation The rotation matrix.
* @param translation The translation vector.
*
*/
static void TransformPoints(vtkPoints *src,
vtkPoints *dst,
const Rotation &rotation,
const Translation &translation);
/**
* @brief Propagate a list of matrices with a rotation matrix.
*
* Method that propagate the source list and saves the result in the destination.
* If the source and destination lists are the same the matrices will be computed
* in place.
*
* @warning No bound check is done. Make sure that both lists are allocated and
* have the same size.
*
* @param src Reference to the source matrices list.
* @param dst Reference to the destination list
* @param rotation Reference to a rotation matrix.
*/
static void PropagateMatrices(const MatrixList &src, MatrixList &dst, const Rotation &rotation);
/**
* @brief Compute the target registration error between two point sets.
*
* Method that is used for testing and evaluation. It computes the target
* registration error (TRE) between two point sets with a rotation matrix and
* a translation vector.
*
* @param movingTargets The target points of the moving point set.
* @param fixedTargets The target points of the fixed point set.
* @param rotation A 3x3 rotation matrix.
* @param translation A 3x1 translation vector.
*
* @return The Target Registration Error (TRE).
*/
static double ComputeTargetRegistrationError(const mitk::PointSet *movingTargets,
const mitk::PointSet *fixedTargets,
const Rotation &rotation,
const Translation &translation);
};
}
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.h b/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.h
index 9b88f6c334..3193336193 100644
--- a/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.h
+++ b/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.h
@@ -1,141 +1,141 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkBoundingObjectCutter_h
#define mitkBoundingObjectCutter_h
#include "MitkAlgorithmsExtExports.h"
#include "itkImage.h"
#include "mitkBoundingObject.h"
#include "mitkCommon.h"
#include "mitkImageTimeSelector.h"
#include "mitkImageToImageFilter.h"
namespace mitk
{
//##Documentation
//## @brief Cuts an Boundingobject out of an mitk Image
//##
//## Input Parameters are a mitk::BoundingObject and optionally an mitk::Image
//## if no mitk::Image is provided, the resulting image will have m_InsideValue as pixelvalue on inside pixel,
//## otherwise it will have the pixelvalue of the input image.
//## Pixel on the outside of the BoundingObject will have a pixelvalue of m_OutsideValue
//## \todo What Image resolution/spacing should be used, if no input image is given?
//## @ingroup Process
class MITKALGORITHMSEXT_EXPORT BoundingObjectCutter : public ImageToImageFilter
{
public:
mitkClassMacro(BoundingObjectCutter, ImageToImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
void SetBoundingObject(const mitk::BoundingObject *boundingObject);
const mitk::BoundingObject *GetBoundingObject() const;
//##Description
//## @brief Set for inside pixels, used when m_UseInsideValue is @a true
itkSetMacro(InsideValue, ScalarType);
itkGetMacro(InsideValue, ScalarType);
//##Description
//## @brief Set value for outside pixels, used when m_AutoOutsideValue is \a false
itkSetMacro(OutsideValue, ScalarType);
itkGetMacro(OutsideValue, ScalarType);
itkSetMacro(UseInsideValue, bool);
itkGetMacro(UseInsideValue, bool);
itkBooleanMacro(UseInsideValue);
//##Description
- //## @brief If set to \a true the minimum of the ouput pixel type is
+ //## @brief If set to \a true the minimum of the output pixel type is
//## used as outside value.
itkSetMacro(AutoOutsideValue, bool);
itkGetMacro(AutoOutsideValue, bool);
itkBooleanMacro(AutoOutsideValue);
itkGetMacro(InsidePixelCount, unsigned int);
itkGetMacro(OutsidePixelCount, unsigned int);
itkSetMacro(UseWholeInputRegion, bool);
itkGetMacro(UseWholeInputRegion, bool);
protected:
BoundingObjectCutter();
~BoundingObjectCutter() override;
virtual const PixelType GetOutputPixelType();
void GenerateInputRequestedRegion() override;
void GenerateOutputInformation() override;
void GenerateData() override;
template <typename TPixel, unsigned int VImageDimension, typename TOutputPixel>
friend void CutImageWithOutputTypeSelect(itk::Image<TPixel, VImageDimension> *inputItkImage,
mitk::BoundingObjectCutter *cutter,
int boTimeStep,
TOutputPixel *dummy);
template <typename TPixel, unsigned int VImageDimension, typename TOutputPixel>
friend void CutImageWithOutputTypeSelect(itk::VectorImage<TPixel, VImageDimension> *inputItkImage,
mitk::BoundingObjectCutter *cutter,
int boTimeStep,
TOutputPixel *dummy);
template <typename TPixel, unsigned int VImageDimension>
friend void CutImage(itk::Image<TPixel, VImageDimension> *itkImage,
mitk::BoundingObjectCutter *cutter,
int boTimeStep);
template <typename TPixel, unsigned int VImageDimension>
friend void CutImage(itk::VectorImage<TPixel, VImageDimension> *itkImage,
mitk::BoundingObjectCutter *cutter,
int boTimeStep);
virtual void ComputeData(mitk::Image *input3D, int boTimeStep);
//##Description
//## @brief BoundingObject that will be cut
mitk::BoundingObject::Pointer m_BoundingObject;
//##Description
//## @brief Value for inside pixels, used when m_UseInsideValue is @a true
//##
//## \sa m_UseInsideValue
ScalarType m_InsideValue;
//##Description
//## @brief Value for outside pixels (default: 0)
//##
//## Used only if m_AutoOutsideValue is \a false.
ScalarType m_OutsideValue;
//##Description
- //## @brief If \a true the minimum of the ouput pixel type is
+ //## @brief If \a true the minimum of the output pixel type is
//## used as outside value (default: \a false)
bool m_AutoOutsideValue;
//##Description
//## @brief Use m_InsideValue for inside pixels (default: \a false)
//##
//## If @a true, pixels that are inside m_BoundingObject
//## will get m_InsideValue in the cutting process
//## If @a false, they keep their original value.
//## \sa m_InsideValue
bool m_UseInsideValue;
unsigned int m_OutsidePixelCount;
unsigned int m_InsidePixelCount;
//##Description
//## @brief Region of input needed for cutting
mitk::SlicedData::RegionType m_InputRequestedRegion;
//##Description
//## @brief Time when Header was last initialized
itk::TimeStamp m_TimeOfHeaderInitialization;
mitk::ImageTimeSelector::Pointer m_InputTimeSelector;
mitk::ImageTimeSelector::Pointer m_OutputTimeSelector;
bool m_UseWholeInputRegion;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.txx b/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.txx
index ab5e26ebbe..dbedb115a6 100644
--- a/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.txx
+++ b/Modules/AlgorithmsExt/include/mitkBoundingObjectCutter.txx
@@ -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.
============================================================================*/
#ifndef MITKBOUNDINGOBJECTCUTTER_TXX
#define MITKBOUNDINGOBJECTCUTTER_TXX
#include "itkImageRegionIteratorWithIndex.h"
#include "mitkImageToItk.h"
#include "mitkStatusBar.h"
namespace mitk
{
template <typename TPixel, unsigned int VImageDimension, typename TOutputPixel>
void CutImageWithOutputTypeSelect(itk::Image<TPixel, VImageDimension> *inputItkImage,
mitk::BoundingObjectCutter *cutter,
int /* boTimeStep */,
TOutputPixel * /* dummy */)
{
typedef itk::Image<TPixel, VImageDimension> ItkInputImageType;
typedef itk::Image<TOutputPixel, VImageDimension> ItkOutputImageType;
typedef typename itk::ImageBase<VImageDimension>::RegionType ItkRegionType;
typedef itk::ImageRegionIteratorWithIndex<ItkInputImageType> ItkInputImageIteratorType;
typedef itk::ImageRegionIteratorWithIndex<ItkOutputImageType> ItkOutputImageIteratorType;
if (cutter->m_BoundingObject.IsNull())
return;
if (inputItkImage == nullptr)
{
mitk::StatusBar::GetInstance()->DisplayErrorText(
"An internal error occurred. Can't convert Image. Please report to bugs@mitk.org");
std::cout << " image is NULL...returning" << std::endl;
return;
}
// PART 1: convert m_InputRequestedReg ion (type mitk::SlicedData::RegionType)
// into ITK-image-region (ItkImageType::RegionType)
// unfortunately, we cannot use input->GetRequestedRegion(), because it
// has been destroyed by the mitk::CastToItkImage call of PART 1
// (which sets the m_RequestedRegion to the LargestPossibleRegion).
- // Thus, use our own member m_InputRequestedRegion insead.
+ // Thus, use our own member m_InputRequestedRegion instead.
// first convert the index
typename ItkRegionType::IndexType::IndexValueType tmpIndex[3];
itk2vtk(cutter->m_InputRequestedRegion.GetIndex(), tmpIndex);
typename ItkRegionType::IndexType index;
index.SetIndex(tmpIndex);
// then convert the size
typename ItkRegionType::SizeType::SizeValueType tmpSize[3];
itk2vtk(cutter->m_InputRequestedRegion.GetSize(), tmpSize);
typename ItkRegionType::SizeType size;
size.SetSize(tmpSize);
// create the ITK-image-region out of index and size
ItkRegionType inputRegionOfInterest(index, size);
// PART 2: get access to the MITK output image via an ITK image
typename mitk::ImageToItk<ItkOutputImageType>::Pointer outputimagetoitk =
mitk::ImageToItk<ItkOutputImageType>::New();
outputimagetoitk->SetInput(cutter->m_OutputTimeSelector->GetOutput());
outputimagetoitk->Update();
typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
// PART 3: iterate over input and output using ITK iterators
// create the iterators
ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
ItkOutputImageIteratorType outputIt(outputItkImage, outputItkImage->GetLargestPossibleRegion());
// Cut the boundingbox out of the image by iterating through
// all pixels and checking if they are inside using IsInside()
cutter->m_OutsidePixelCount = 0;
cutter->m_InsidePixelCount = 0;
mitk::Point3D p;
mitk::BaseGeometry *inputGeometry = cutter->GetInput()->GetGeometry();
TOutputPixel outsideValue;
if (cutter->m_AutoOutsideValue)
{
outsideValue = itk::NumericTraits<TOutputPixel>::min();
}
else
{
outsideValue = (TOutputPixel)cutter->m_OutsideValue;
}
// shall we use a fixed value for each inside pixel?
if (cutter->GetUseInsideValue())
{
TOutputPixel insideValue = (TOutputPixel)cutter->m_InsideValue;
// yes, use a fixed value for each inside pixel (create a binary mask of the bounding object)
for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt)
{
vtk2itk(inputIt.GetIndex(), p);
inputGeometry->IndexToWorld(p, p);
if (cutter->m_BoundingObject->IsInside(p))
{
outputIt.Set(insideValue);
++cutter->m_InsidePixelCount;
}
else
{
outputIt.Set(outsideValue);
++cutter->m_OutsidePixelCount;
}
}
}
else
{
// no, use the pixel value of the original image (normal cutting)
for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt)
{
vtk2itk(inputIt.GetIndex(), p);
inputGeometry->IndexToWorld(p, p);
if (cutter->m_BoundingObject->IsInside(p))
{
outputIt.Set((TOutputPixel)inputIt.Value());
++cutter->m_InsidePixelCount;
}
else
{
outputIt.Set(outsideValue);
++cutter->m_OutsidePixelCount;
}
}
}
}
template <typename TPixel, unsigned int VImageDimension, typename TOutputPixel>
void CutImageWithOutputTypeSelect(itk::VectorImage<TPixel, VImageDimension> *inputItkImage,
mitk::BoundingObjectCutter *cutter,
int /* boTimeStep */,
TOutputPixel * /* dummy */)
{
typedef itk::VectorImage<TPixel, VImageDimension> ItkInputImageType;
typedef itk::VectorImage<TOutputPixel, VImageDimension> ItkOutputImageType;
typedef typename itk::ImageBase<VImageDimension>::RegionType ItkRegionType;
typedef itk::ImageRegionIteratorWithIndex<ItkInputImageType> ItkInputImageIteratorType;
typedef itk::ImageRegionIteratorWithIndex<ItkOutputImageType> ItkOutputImageIteratorType;
if (cutter->m_BoundingObject.IsNull())
return;
if (inputItkImage == nullptr)
{
mitk::StatusBar::GetInstance()->DisplayErrorText(
"An internal error occurred. Can't convert Image. Please report to bugs@mitk.org");
std::cout << " image is NULL...returning" << std::endl;
return;
}
// PART 1: convert m_InputRequestedReg ion (type mitk::SlicedData::RegionType)
// into ITK-image-region (ItkImageType::RegionType)
// unfortunately, we cannot use input->GetRequestedRegion(), because it
// has been destroyed by the mitk::CastToItkImage call of PART 1
// (which sets the m_RequestedRegion to the LargestPossibleRegion).
- // Thus, use our own member m_InputRequestedRegion insead.
+ // Thus, use our own member m_InputRequestedRegion instead.
// first convert the index
typename ItkRegionType::IndexType::IndexValueType tmpIndex[3];
itk2vtk(cutter->m_InputRequestedRegion.GetIndex(), tmpIndex);
typename ItkRegionType::IndexType index;
index.SetIndex(tmpIndex);
// then convert the size
typename ItkRegionType::SizeType::SizeValueType tmpSize[3];
itk2vtk(cutter->m_InputRequestedRegion.GetSize(), tmpSize);
typename ItkRegionType::SizeType size;
size.SetSize(tmpSize);
// create the ITK-image-region out of index and size
ItkRegionType inputRegionOfInterest(index, size);
// PART 2: get access to the MITK output image via an ITK image
typename mitk::ImageToItk<ItkOutputImageType>::Pointer outputimagetoitk =
mitk::ImageToItk<ItkOutputImageType>::New();
outputimagetoitk->SetInput(cutter->m_OutputTimeSelector->GetOutput());
outputimagetoitk->Update();
typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
// PART 3: iterate over input and output using ITK iterators
// create the iterators
ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
ItkOutputImageIteratorType outputIt(outputItkImage, outputItkImage->GetLargestPossibleRegion());
// Cut the boundingbox out of the image by iterating through
// all pixels and checking if they are inside using IsInside()
cutter->m_OutsidePixelCount = 0;
cutter->m_InsidePixelCount = 0;
mitk::Point3D p;
mitk::BaseGeometry *inputGeometry = cutter->GetInput()->GetGeometry();
typename ItkOutputImageType::PixelType outsideValue;
outsideValue.SetSize(outputItkImage->GetVectorLength());
if (cutter->m_AutoOutsideValue)
{
outsideValue.Fill(itk::NumericTraits<TOutputPixel>::min());
}
else
{
outsideValue.Fill(cutter->m_OutsideValue);
}
// shall we use a fixed value for each inside pixel?
if (cutter->GetUseInsideValue())
{
typename ItkOutputImageType::PixelType insideValue;
insideValue.Fill(cutter->m_InsideValue);
// yes, use a fixed value for each inside pixel (create a binary mask of the bounding object)
for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt)
{
vtk2itk(inputIt.GetIndex(), p);
inputGeometry->IndexToWorld(p, p);
if (cutter->m_BoundingObject->IsInside(p))
{
outputIt.Set(insideValue);
++cutter->m_InsidePixelCount;
}
else
{
outputIt.Set(outsideValue);
++cutter->m_OutsidePixelCount;
}
}
}
else
{
// no, use the pixel value of the original image (normal cutting)
for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt)
{
vtk2itk(inputIt.GetIndex(), p);
inputGeometry->IndexToWorld(p, p);
if (cutter->m_BoundingObject->IsInside(p))
{
outputIt.Set(inputIt.Get());
++cutter->m_InsidePixelCount;
}
else
{
outputIt.Set(outsideValue);
++cutter->m_OutsidePixelCount;
}
}
}
}
template <typename TPixel, unsigned int VImageDimension>
void CutImage(itk::Image<TPixel, VImageDimension> *inputItkImage, mitk::BoundingObjectCutter *cutter, int boTimeStep)
{
TPixel *dummy = nullptr;
CutImageWithOutputTypeSelect<TPixel, VImageDimension, TPixel>(inputItkImage, cutter, boTimeStep, dummy);
}
template <typename TPixel, unsigned int VImageDimension>
void CutImage(itk::VectorImage<TPixel, VImageDimension> *inputItkImage,
mitk::BoundingObjectCutter *cutter,
int boTimeStep)
{
TPixel *dummy = nullptr;
CutImageWithOutputTypeSelect<TPixel, VImageDimension, TPixel>(inputItkImage, cutter, boTimeStep, dummy);
}
} // of namespace mitk
#include "mitkImageCast.h"
#endif // of MITKBOUNDINGOBJECTCUTTER_TXX
diff --git a/Modules/AlgorithmsExt/include/mitkCovarianceMatrixCalculator.h b/Modules/AlgorithmsExt/include/mitkCovarianceMatrixCalculator.h
index 290bc93c66..d7c22530b9 100644
--- a/Modules/AlgorithmsExt/include/mitkCovarianceMatrixCalculator.h
+++ b/Modules/AlgorithmsExt/include/mitkCovarianceMatrixCalculator.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 mitkCovarianceMatrixCalculator_h
#define mitkCovarianceMatrixCalculator_h
// exports
#include "MitkAlgorithmsExtExports.h"
#include <mitkCommon.h>
#include <itkMatrix.h>
#include <itkObjectFactory.h>
#include <vector>
namespace mitk
{
// forward declarations
class Surface;
struct CovarianceMatrixCalculatorData;
/**
* \ingroup AnisotropicRegistration
*
* @brief Class that computes the covariance matrices for every point in a
* {@link Surface} used in the A-ICP algorithm.
*
* Computes a covariance matrix for every vertex in a given {@link Surface}
* based on it's direct neighbours and saves them into a CovarianceMatrixList.
* The Class implements the CM_PCA method presented by
* L. Maier-Hein et al. in "Convergent Iterative Closest-Point Algorithm
- * to Accomodate Anisotropic and Inhomogenous Localization Error.",
+ * to Accommodate Anisotropic and Inhomogenous Localization Error.",
* IEEE T Pattern Anal 34 (8), 1520-1532, 2012. The algorithm needs
* a clean Surface with non manifold edges and no duplicated vertices. To
* ensure a clean Surface representation use vtkCleanPolyData.
*/
class MITKALGORITHMSEXT_EXPORT CovarianceMatrixCalculator : public itk::Object
{
private:
/** Pimpl to hold private data.*/
CovarianceMatrixCalculatorData *d;
protected:
// local typedefs
/** Definition of the covariance matrix.*/
typedef itk::Matrix<double, 3, 3> CovarianceMatrix;
/** Definition of a list of covariance matrices */
typedef std::vector<CovarianceMatrix> CovarianceMatrixList;
typedef double Vertex[3];
/** List that stores the computed covariance matrices. */
CovarianceMatrixList m_CovarianceMatrixList;
/** This method projects all surrounding vertices of given vertex in a Surface
* in the normal direction onto a plane and computes a primary component
* analysis on the projected vertices. In the next step a orthonormal
* system is created.
*
* @param index The index of the input Vertex in the Surface.
* @param normal The normal of the input Vertex.
* @param principalComponents CovarianceMatrix of the principal component analysis.
* @param variances Variances along the axes of the createt Orthonormal system.
* @param curVertex The current Vertex in the surface
*
*/
void ComputeOrthonormalCoordinateSystem(
const int index, Vertex normal, CovarianceMatrix &principalComponents, Vertex variances, Vertex curVertex);
CovarianceMatrixCalculator();
~CovarianceMatrixCalculator() override;
public:
mitkClassMacroItkParent(CovarianceMatrixCalculator, itk::Object);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Sets the scaling factor for the voronoi area.
* @param factor The scaling factor.
*/
void SetVoronoiScalingFator(const double factor);
/** Enables/disables the covariance matrix normalization.
* @param state Enables the covariance matrix normalization.
*/
void EnableNormalization(bool state);
/** Returns the mean of variance of all computed covariance matrices.
* @return The mean variance.
*/
double GetMeanVariance() const;
/** Returns a reference to the CovarianceMatrixList with the computed covariance matrices.
* @return A CovarianceMatrixList.
*/
const CovarianceMatrixList &GetCovarianceMatrices() const;
/** Sets the input {@link Surface} for which the covariance matrices will be calculated.
* @param input A {@link Surface}.
*/
void SetInputSurface(Surface *input);
/** Method that computes the covariance matrices for the input surface.
* @throws std::exception If the input surface is not set.
*/
void ComputeCovarianceMatrices();
};
}
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkGeometryClipImageFilter.h b/Modules/AlgorithmsExt/include/mitkGeometryClipImageFilter.h
index 647327c440..6ddc54e2fe 100644
--- a/Modules/AlgorithmsExt/include/mitkGeometryClipImageFilter.h
+++ b/Modules/AlgorithmsExt/include/mitkGeometryClipImageFilter.h
@@ -1,182 +1,182 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkGeometryClipImageFilter_h
#define mitkGeometryClipImageFilter_h
#include "MitkAlgorithmsExtExports.h"
#include "mitkCommon.h"
#include "mitkGeometryData.h"
#include "mitkImageTimeSelector.h"
#include "mitkImageToImageFilter.h"
namespace itk
{
template <class TPixel, unsigned int VImageDimension>
class ITK_EXPORT Image;
}
namespace mitk
{
//##Documentation
//## @brief Filter for clipping an image with a PlaneGeometry
//##
//## The given geometry for clipping can be either a PlaneGeometry
//## or a TimeGeometry containing multiple instances
//## of PlaneGeometry
//##
//## \todo add AutoOrientLabels, which makes the "left" side (minimum X value) side of the image get one defined
//label.
//## left-most because vtkPolyDataNormals uses the same definition and this filter is used for visualization of
//## front/back side of curved planes
//##
//## @ingroup Process
class MITKALGORITHMSEXT_EXPORT GeometryClipImageFilter : public ImageToImageFilter
{
public:
mitkClassMacro(GeometryClipImageFilter, ImageToImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* Set the geometry to be used for clipping
*
* The given geometry for clipping must be a PlaneGeometry.
*/
void SetClippingGeometry(const mitk::BaseGeometry *aClippingGeometry);
/**
* Set the geometry to be used for clipping
*
* The given geometry for clipping must a
* TimeGeometry containing multiple instances
* of PlaneGeometry
*/
void SetClippingGeometry(const mitk::TimeGeometry *aClippingGeometry);
const mitk::BaseGeometry *GetClippingGeometry() const;
const mitk::TimeGeometry *GetClippingTimeGeometry() const;
//##Description
//## @brief Get whether the part above or below the geometry
//## shall be clipped (default: @a true)
itkGetConstMacro(ClipPartAboveGeometry, bool);
//## @brief Set whether the part above or below the geometry
//## shall be clipped (default: @a true)
itkSetMacro(ClipPartAboveGeometry, bool);
//## @brief Set whether the part above or below the geometry
//## shall be clipped (default: @a true)
itkBooleanMacro(ClipPartAboveGeometry);
//##Description
//## @brief Set value for outside pixels (default: 0),
//## used when m_AutoOutsideValue is \a false
itkSetMacro(OutsideValue, ScalarType);
itkGetConstMacro(OutsideValue, ScalarType);
//##Description
- //## @brief If set to \a true the minimum of the ouput pixel type is
+ //## @brief If set to \a true the minimum of the output pixel type is
//## used as outside value (default: \a false)
itkSetMacro(AutoOutsideValue, bool);
itkGetConstMacro(AutoOutsideValue, bool);
itkBooleanMacro(AutoOutsideValue);
itkSetMacro(AutoOrientLabels, bool);
itkGetConstMacro(AutoOrientLabels, bool);
//##Description
//## @brief If set to \a true both sides of the clipping
- //## geometry will be labeld using m_AboveGeometryLabel and
+ //## geometry will be labeled using m_AboveGeometryLabel and
//## m_BelowGeometryLabel
itkSetMacro(LabelBothSides, bool);
itkGetConstMacro(LabelBothSides, bool);
itkBooleanMacro(LabelBothSides);
//##Description
//## @brief Set for voxels above the clipping geometry.
//## This value is only used, if m_LabelBothSides is set to true.
itkSetMacro(AboveGeometryLabel, ScalarType);
itkGetConstMacro(AboveGeometryLabel, ScalarType);
//##Description
//## @brief Set for voxels below the clipping geometry.
//## This value is only used, if m_LabelBothSides is set to true.
itkSetMacro(BelowGeometryLabel, ScalarType);
itkGetConstMacro(BelowGeometryLabel, ScalarType);
protected:
GeometryClipImageFilter();
~GeometryClipImageFilter() override;
void GenerateInputRequestedRegion() override;
void GenerateOutputInformation() override;
void GenerateData() override;
template <typename TPixel, unsigned int VImageDimension>
void _InternalComputeClippedImage(itk::Image<TPixel, VImageDimension> *itkImage,
mitk::GeometryClipImageFilter *geometryClipper,
const mitk::PlaneGeometry *clippingPlaneGeometry);
mitk::BaseGeometry::ConstPointer m_ClippingGeometry;
mitk::GeometryData::Pointer m_ClippingGeometryData;
mitk::TimeGeometry::ConstPointer m_TimeClippingGeometry;
mitk::ImageTimeSelector::Pointer m_InputTimeSelector;
mitk::ImageTimeSelector::Pointer m_OutputTimeSelector;
//##Description
//## @brief Defines whether the part above or below the geometry
//## shall be clipped (default: @a true)
bool m_ClipPartAboveGeometry;
//##Description
//## @brief Value for outside pixels (default: 0)
//##
//## Used only if m_AutoOutsideValue is \a false.
ScalarType m_OutsideValue;
//##Description
- //## @brief If \a true the minimum of the ouput pixel type is
+ //## @brief If \a true the minimum of the output pixel type is
//## used as outside value (default: \a false)
bool m_AutoOutsideValue;
//##Description
//## @brief If \a true all pixels above and below the geometry
//## are labeled with m_AboveGeometryLabel and m_BelowGeometryLabel
bool m_LabelBothSides;
/**
* \brief Orient above like vtkPolyDataNormals does with AutoOrientNormals
*/
bool m_AutoOrientLabels;
//##Description
//## @brief Is used for labeling all pixels above the geometry
//## when m_LabelBothSides is on
ScalarType m_AboveGeometryLabel;
//##Description
//## @brief Is used for labeling all pixels below the geometry
//## when m_LabelBothSides is on
ScalarType m_BelowGeometryLabel;
//##Description
//## @brief Time when Header was last initialized
itk::TimeStamp m_TimeOfHeaderInitialization;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkGeometryDataSource.h b/Modules/AlgorithmsExt/include/mitkGeometryDataSource.h
index 5a04d3436a..0a60edf166 100644
--- a/Modules/AlgorithmsExt/include/mitkGeometryDataSource.h
+++ b/Modules/AlgorithmsExt/include/mitkGeometryDataSource.h
@@ -1,68 +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 mitkGeometryDataSource_h
#define mitkGeometryDataSource_h
#include "MitkAlgorithmsExtExports.h"
#include "mitkBaseDataSource.h"
namespace mitk
{
class GeometryData;
/**
* @brief Superclass of all classes generating GeometryData (instances of class
* GeometryData) 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::GeometryDataSource.
* @ingroup Process
*/
class MITKALGORITHMSEXT_EXPORT GeometryDataSource : public BaseDataSource
{
public:
mitkClassMacro(GeometryDataSource, BaseDataSource);
itkNewMacro(Self);
typedef mitk::GeometryData OutputType;
mitkBaseDataSourceGetOutputDeclarations
/**
* 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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
protected:
GeometryDataSource();
~GeometryDataSource() override;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkImageToUnstructuredGridFilter.h b/Modules/AlgorithmsExt/include/mitkImageToUnstructuredGridFilter.h
index ad8d0db096..b75e97dd45 100644
--- a/Modules/AlgorithmsExt/include/mitkImageToUnstructuredGridFilter.h
+++ b/Modules/AlgorithmsExt/include/mitkImageToUnstructuredGridFilter.h
@@ -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.
============================================================================*/
#ifndef mitkImageToUnstructuredGridFilter_h
#define mitkImageToUnstructuredGridFilter_h
#include <MitkAlgorithmsExtExports.h>
#include <mitkCommon.h>
#include <mitkImage.h>
#include <mitkUnstructuredGrid.h>
#include <mitkUnstructuredGridSource.h>
namespace mitk
{
/**
* @brief Converts an Image into an UnstructuredGrid represented by Points.
* The filter uses a Threshold to extract every pixel, with value higher than
* the threshold, as point.
* If no threshold is set, every pixel is extracted as a point.
*/
class MITKALGORITHMSEXT_EXPORT ImageToUnstructuredGridFilter : public UnstructuredGridSource
{
public:
mitkClassMacro(ImageToUnstructuredGridFilter, UnstructuredGridSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** This method is called by Update(). */
void GenerateData() override;
/** Initializes the output information */
void GenerateOutputInformation() override;
/** Returns a const reference to the input image */
const mitk::Image *GetInput(void) const;
mitk::Image *GetInput(void);
/** Set the source image. As input every mitk 3D image can be used. */
using itk::ProcessObject::SetInput;
virtual void SetInput(const mitk::Image *image);
/**
* Set the threshold for extracting points. Every pixel, which value
* is higher than this value, will be a point.
*/
void SetThreshold(double threshold);
/** Returns the threshold */
double GetThreshold();
/** Returns the number of extracted points after edge detection */
itkGetMacro(NumberOfExtractedPoints, int);
protected :
/** Constructor */
ImageToUnstructuredGridFilter();
/** Destructor */
~ImageToUnstructuredGridFilter() override;
/**
* Access method for extracting the points from the input image
*/
template <typename TPixel, unsigned int VImageDimension>
void ExtractPoints(const itk::Image<TPixel, VImageDimension> *image);
/** The number of points extracted by the filter */
int m_NumberOfExtractedPoints;
private:
/**
- * Geometry of the input image, needed to tranform the image points
+ * Geometry of the input image, needed to transform the image points
* into world points
*/
mitk::BaseGeometry *m_Geometry;
/** Threshold for extracting the points */
double m_Threshold;
/** The output of the filter, which contains the extracted points */
mitk::UnstructuredGrid::Pointer m_UnstructGrid;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkPlaneFit.h b/Modules/AlgorithmsExt/include/mitkPlaneFit.h
index ea215b16b6..7601dd7c0d 100644
--- a/Modules/AlgorithmsExt/include/mitkPlaneFit.h
+++ b/Modules/AlgorithmsExt/include/mitkPlaneFit.h
@@ -1,138 +1,138 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkPlaneFit_h
#define mitkPlaneFit_h
#include "MitkAlgorithmsExtExports.h"
#include "mitkGeometryDataSource.h"
#include "mitkPlaneGeometry.h"
#include "mitkPointSet.h"
#include "mitkTimeGeometry.h"
namespace mitk
{
//!
// kind regards to dr. math!
// function [x0, a, d, normd] = lsplane(X)
// ---------------------------------------------------------------------
// LSPLANE.M Least-squares plane (orthogonal distance
// regression).
//
// Version 1.0
// Last amended I M Smith 27 May 2002.
// Created I M Smith 08 Mar 2002
// ---------------------------------------------------------------------
// Input
// X Array [x y z] where x = vector of x-coordinates,
// y = vector of y-coordinates and z = vector of
// z-coordinates.
// Dimension: m x 3.
//
// Output
// x0 Centroid of the data = point on the best-fit plane.
// Dimension: 3 x 1.
//
// a Direction cosines of the normal to the best-fit
// plane.
// Dimension: 3 x 1.
//
// <Optional...
// d Residuals.
// Dimension: m x 1.
//
// normd Norm of residual errors.
// Dimension: 1 x 1.
// ...>
//
// [x0, a <, d, normd >] = lsplane(X)
// ---------------------------------------------------------------------
class MITKALGORITHMSEXT_EXPORT PlaneFit : public GeometryDataSource
{
public:
mitkClassMacro(PlaneFit, GeometryDataSource);
itkNewMacro(Self);
typedef mitk::PointSet::PointDataType PointDataType;
typedef mitk::PointSet::PointDataIterator PointDataIterator;
void GenerateOutputInformation() override;
void GenerateData() override;
/*!Getter for point set.
*
*/
const mitk::PointSet *GetInput();
/*! filter initialisation.
*
*/
using mitk::GeometryDataSource::SetInput;
virtual void SetInput(const mitk::PointSet *ps);
/*! returns the center of gravity of the point set.
*
*/
virtual const mitk::Point3D &GetCentroid(int t = 0) const;
/*! returns the plane geometry which represents the point set.
*
*/
virtual mitk::PlaneGeometry::Pointer GetPlaneGeometry(int t = 0);
/*! returns the normal of the plane which represents the point set.
*
*/
virtual const mitk::Vector3D &GetPlaneNormal(int t = 0) const;
protected:
PlaneFit();
~PlaneFit() override;
/*! Calculates the centroid of the point set.
* the center of gravity is calculated through the mean value of the whole point set
*/
void CalculateCentroid(int t = 0);
/*! working with an SVD algorithm form matrix dataM.
- * ITK suplies the vnl_svd to solve an plan fit eigentvector problem
+ * ITK supplies the vnl_svd to solve an plan fit eigentvector problem
* points are processed in the SVD matrix. The normal vector is the
* singular vector of dataM corresponding to its smalest singular value.
- * The mehtod uses VNL library from ITK and at least the mehtod nullvector()
+ * The method uses VNL library from ITK and at least the method nullvector()
* to extract the normalvector.
*/
void ProcessPointSet(int t = 0);
/*! Initialize Plane and configuration.
*
*/
void InitializePlane(int t = 0);
private:
/*!keeps a copy of the pointset.*/
const mitk::PointSet *m_PointSet;
/* output object - a time sliced geometry.*/
mitk::TimeGeometry::Pointer m_TimeGeometry;
std::vector<mitk::PlaneGeometry::Pointer> m_Planes;
/*! the calculatet center point of all points in the point set.*/
std::vector<mitk::Point3D> m_Centroids;
- /* the normal vector to descrie a plane gemoetry.*/
+ /* the normal vector to describe a plane geometry.*/
std::vector<mitk::Vector3D> m_PlaneVectors;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkSurfaceToPointSetFilter.h b/Modules/AlgorithmsExt/include/mitkSurfaceToPointSetFilter.h
index 9101aeb147..27a7116957 100644
--- a/Modules/AlgorithmsExt/include/mitkSurfaceToPointSetFilter.h
+++ b/Modules/AlgorithmsExt/include/mitkSurfaceToPointSetFilter.h
@@ -1,55 +1,55 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkSurfaceToPointSetFilter_h
#define mitkSurfaceToPointSetFilter_h
// exports
#include "MitkAlgorithmsExtExports.h"
// mitk headers
#include "mitkSurface.h"
#include <mitkPointSetSource.h>
//#include <itkExtendedDoublyLinkedFaceList.h>
namespace mitk
{
/** Documentation
* @brief This filter converts the input surface into a point set. The output point set contains every point exactly
* one time
- * (no dublicated points like in the stl-format).
+ * (no duplicated points like in the stl-format).
*/
class MITKALGORITHMSEXT_EXPORT SurfaceToPointSetFilter : public mitk::PointSetSource
{
public:
mitkClassMacro(SurfaceToPointSetFilter, mitk::PointSetSource);
itkNewMacro(Self);
using itk::ProcessObject::SetInput;
void SetInput(mitk::Surface::Pointer m_InputSurface);
std::string GetErrorMessage();
protected:
SurfaceToPointSetFilter();
~SurfaceToPointSetFilter() override;
/** @brief method generating the output of this filter. Called in the updated process of the pipeline. */
void GenerateData() override;
//############### members ########################
mitk::Surface::Pointer m_InputSurface;
std::string m_ErrorMessage;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkUnstructuredGridClusteringFilter.h b/Modules/AlgorithmsExt/include/mitkUnstructuredGridClusteringFilter.h
index d22c020acd..a8bf1def62 100644
--- a/Modules/AlgorithmsExt/include/mitkUnstructuredGridClusteringFilter.h
+++ b/Modules/AlgorithmsExt/include/mitkUnstructuredGridClusteringFilter.h
@@ -1,128 +1,128 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkUnstructuredGridClusteringFilter_h
#define mitkUnstructuredGridClusteringFilter_h
#include <MitkAlgorithmsExtExports.h>
#include <mitkCommon.h>
#include <mitkUnstructuredGrid.h>
#include <mitkUnstructuredGridToUnstructuredGridFilter.h>
#include <vtkIdList.h>
#include <vtkPoints.h>
#include <vtkSmartPointer.h>
namespace mitk
{
/**
* @brief This filter uses the DBSCAN algorithm for clustering an
* mitk::UnstructuredGrid. "MinPts" defines the number of neighbours which are
* required to be a kernel point if a point is in range of a kernel point
- * but hasnt enough neighbours this point is added to the cluster but is a
+ * but hasn't enough neighbours this point is added to the cluster but is a
* density reachable point and the cluster ends at this point. "eps" is the
* range in which the neighbours are searched. If "Meshing" is set the
* clusteres UnstructuredGrid is meshed and visible in 2D renderwindows.
*
* DBSCAN algorithm:
*
* DBSCAN(D, eps, MinPts)
* C = 0
* for each unvisited point P in dataset D
* mark P as visited
* N = D.regionQuery(P, eps)
* if sizeof(N) < MinPts
* mark P as NOISE
* else
* C = next cluster
* expandCluster(P, N, C, eps, MinPts)
*
* expandCluster(P, N, C, eps, MinPts)
* add P to cluster C
* for each point P' in N
* if P' is not visited
* mark P' as visited
* N' = D.regionQuery(P', eps)
* if sizeof(N') >= MinPts
* N = N joined with N'
* if P' is not yet member of any cluster
* add P' to cluster C
*/
class MITKALGORITHMSEXT_EXPORT UnstructuredGridClusteringFilter : public UnstructuredGridToUnstructuredGridFilter
{
public:
mitkClassMacro(UnstructuredGridClusteringFilter, UnstructuredGridToUnstructuredGridFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Sets the distance for the neighbour search */
itkSetMacro(eps, double);
itkGetMacro(eps, double);
/** Sets the number of required neighbours */
itkSetMacro(MinPts, int);
itkGetMacro(MinPts, int);
/** If activated the clusteres UnstructuredGrid is meshed */
itkSetMacro(Meshing, bool);
/** Returns all clusters as UnstructuredGrids which were found */
virtual std::vector<mitk::UnstructuredGrid::Pointer> GetAllClusters();
/** Returns the number of the clusters which were found */
virtual int GetNumberOfFoundClusters();
protected:
/** Constructor */
UnstructuredGridClusteringFilter();
/** Destructor */
~UnstructuredGridClusteringFilter() override;
/** Defines the output of the filter */
void GenerateOutputInformation() override;
/** Is called by the Update() method */
void GenerateData() override;
private:
/** Used for the DBSCAN algorithm to expand a cluster and add more points to it */
void ExpandCluster(int id, vtkIdList *pointIDs, vtkPoints *cluster, vtkPoints *inpPoints);
/** The result main Cluster */
mitk::UnstructuredGrid::Pointer m_UnstructGrid;
/** All clusters which were found */
std::vector<vtkSmartPointer<vtkPoints>> m_Clusters;
/** The distances of the points from the input UnstructuredGrid*/
std::vector<vtkSmartPointer<vtkDoubleArray>> m_DistanceArrays;
- /** The range for the neighbout search */
+ /** The range for the neighbour search */
double m_eps;
/** The number of the required neighbours */
int m_MinPts;
/** Activates the meshing for the UnstructuredGrid clusters*/
bool m_Meshing;
/** If its activated the distance of the clusters is used instead of the
* size */
bool m_DistCalc;
};
} // namespace mitk
#endif
diff --git a/Modules/AlgorithmsExt/include/mitkWeightedPointTransform.h b/Modules/AlgorithmsExt/include/mitkWeightedPointTransform.h
index 670d873266..dc1b53189c 100644
--- a/Modules/AlgorithmsExt/include/mitkWeightedPointTransform.h
+++ b/Modules/AlgorithmsExt/include/mitkWeightedPointTransform.h
@@ -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.
============================================================================*/
#ifndef mitkWeightedPointTransform_h
#define mitkWeightedPointTransform_h
// EXPORTS
#include "MitkAlgorithmsExtExports.h"
// ITK
#include <itkMatrix.h>
#include <itkVariableSizeMatrix.h>
#include <mitkCommon.h>
#include <mitkPointSet.h>
#include <vector>
#include <vtkSmartPointer.h>
// forward declarations
class vtkPoints;
class vtkLandmarkTransform;
namespace mitk
{
/**
* \ingroup AnisotropicRegistration
*
* @brief This class implements an extension of the
* weighted point based registration algorithm
* from A. Danilchenko, R. Balachandran and J. M. Fitzpatrick.
*
* The class implements an extension of the weighted point based registration
* from A. Danilchenko et al.
* presented by L. Maier-Hein et al. in "Convergent Iterative Closest-Point Algorithm
- * to Accomodate Anisotropic and Inhomogenous Localization Error.",
+ * to Accommodate Anisotropic and Inhomogenous Localization Error.",
* IEEE T Pattern Anal 34 (8), 1520-1532, 2012. The extension computes, in order
* to ensure the convergence of the algorithm, an isotropic estimation
* by an unweighted point based registration algorithm as an initial estimate.
* The implemantion was originally ported to C/C++ by A. Franz.
*
* \note Some methods are accelerated when OpenMP is enabled.
*
*/
class MITKALGORITHMSEXT_EXPORT WeightedPointTransform : public itk::Object
{
/** Definition of a 3x3 matrix.*/
typedef itk::Matrix<double, 3, 3> Matrix3x3;
/** Definition of a 3x3 Weighting matrix.*/
typedef Matrix3x3 WeightMatrix;
/** Definition of a Rotation matrix.*/
typedef Matrix3x3 Rotation;
/** Definition of a translation vector.*/
typedef itk::Vector<double, 3> Translation;
/** Definition of a weight matrix list.*/
typedef std::vector<WeightMatrix> WeightMatrixList;
/** Definition of a covariance matrix list.*/
typedef std::vector<Matrix3x3> CovarianceMatrixList;
public:
mitkClassMacroItkParent(WeightedPointTransform, itk::Object);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** @brief Method which registers both point sets. */
void ComputeTransformation();
/** @brief Sets the threshold of the registration. Default value is 0.0001.*/
itkSetMacro(Threshold, double);
/** @brief Sets the maximum number of iterations of the registration.
* Default value is 1000.
*/
itkSetMacro(MaxIterations, double);
/** @return Returns the number of iterations of the last run
* of the registration algorithm. Returns -1 if there was no
* run of the registration yet.
*/
itkGetMacro(Iterations, int);
/** @return Returns the FRE of the last run of the registration algorithm.
* Returns -1 if there was no run of the registration yet.
*/
itkGetMacro(FRE, double);
/** @brief Sets the FRE normalization factor. Default value is 1.0. */
itkSetMacro(FRENormalizationFactor, double);
/** @return Returns the current FRE normalization factor.*/
itkGetMacro(FRENormalizationFactor, double);
/** Sets the moving point set used for the registration.
* @param p The input point set.
*/
void SetMovingPointSet(vtkSmartPointer<vtkPoints> p);
/**
* Set the list of 3x3 covariance matrices belonging to the moving point set.
* @param matrices List of covariance matrices.
*/
void SetCovarianceMatricesMoving(const CovarianceMatrixList &matrices);
/** Sets the fixed point set used for the registration.
* @param p The input point set.
*/
void SetFixedPointSet(vtkSmartPointer<vtkPoints> p);
/**
* Set the list of 3x3 covariance matrices belonging to the fixed point set.
* @param matrices List of covariance matrices.
*/
void SetCovarianceMatricesFixed(const CovarianceMatrixList &matrices);
/**
* The translation vector computed by the algorithm.
* @return 3x1 translation vector.
*/
const Translation &GetTransformT() const { return m_Translation; }
/**
* The rotation matrix computed by the algorithm.
*/
const Rotation &GetTransformR() const { return m_Rotation; }
protected:
WeightedPointTransform();
~WeightedPointTransform() override;
/** Threshold used to terminate the algorithm.*/
double m_Threshold;
/** Max allowed iterations used by the algorithm.*/
int m_MaxIterations;
/** The amount of iterations needed by the algorithm.*/
int m_Iterations;
/** The fiducial registration error (FRE) used in the algorithm.*/
double m_FRE;
/** Normalization factor for the FRE.*/
double m_FRENormalizationFactor;
/** Isotropic point based registration used for initial estimate.*/
vtkSmartPointer<vtkLandmarkTransform> m_LandmarkTransform;
/** The fixed point set (Y).*/
vtkSmartPointer<vtkPoints> m_FixedPointSet;
/** Moving point set (X).*/
vtkSmartPointer<vtkPoints> m_MovingPointSet;
/** Covariance matrices of the moving point set (Sigma_X).*/
CovarianceMatrixList m_CovarianceMatricesMoving;
/** Covariance matrices of the moving point set (Sigma_Y).*/
CovarianceMatrixList m_CovarianceMatricesFixed;
/** 3x1 translation vector.*/
Translation m_Translation;
/** 3x3 rotation matrix.*/
Rotation m_Rotation;
/**
* original matlab-function:
*
* Constructs the C matrix of the linear version of the registration
* problem, Cq = e, where q = [delta_angle(1:3),delta_translation(1:3)] and
* e is produced by e_maker(X,Y,W)
*
* Authors: JM Fitzpatrick and R Balachandran
* Creation: February 2009
*
* --------------------------------------------
*
* converted to C++ by Alfred Franz in March/April 2010
*/
void C_maker(vtkPoints *X, const WeightMatrixList &W, itk::VariableSizeMatrix<double> &returnValue);
/**
* original matlab-function:
*
* Constructs the e vector of the linear version of the registration
* problem, Cq = e, where q = [delta_angle(1:3),delta_translation(1:3)] and
* C is produced by C_maker(X,W)
*
* Authors: JM Fitzpatrick and R Balachandran
* Creation: February 2009
*
* --------------------------------------------
*
* converted to C++ by Alfred Franz in March/April 2010
*/
void E_maker(vtkPoints *X, vtkPoints *Y, const WeightMatrixList &W, vnl_vector<double> &returnValue);
/**
* This method computes the change in a root mean squared
* sense between the previous and the actual iteration.
* The computed value is used as a termination constraint of the algorithm and
* compared against the threshold.
*
* @param X The moving point set in the previous iteration step.
* @param X_new The moving point set in the actual step.
*
* @return The computed change between the two point sets.
*/
double CalculateConfigChange(vtkPoints *X, vtkPoints *X_new);
/**
* @brief This method performs a variant of the weighted point register algorithm presented by
* A. Danilchenko, R. Balachandran and J. M. Fitzpatrick in January 2010. (Modified in January 2011)
* converted to C++ by Alfred Franz in March/April 2010
*
* @param X (input) the moving point set
* @param Y (input) the fixed (static) point set
* @param Sigma_X (input) a 3-by-3-by-N array, each page containing the weighting matrix for the Nth pair
* of points in X
* @param Sigma_Y (input) a 3-by-3-by-N array, each page containing the weighting matrix for the Nth pair
* of points in Y
* @param Threshold (input) the relative size of the change to the moving set above which the iteration
* continues
* @param MaxIterations (input) the maximum number of iterations allowed
* @param TransformationR (output) this variable will hold the computed rotation matrix
* @param TransformationT (output) this variable will hold the computed translation vector
* @param FRE (output) this variable will hold the computed rotation FRE of the transformation
* @param n (output) this variable will hold the number of iterations used by the algorithm
*/
void 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);
};
}
#endif
diff --git a/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp b/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp
index e34ff3e2c3..729732e83d 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
+// Get a list with the id's of all surrounding connected 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
+// Computes a primary component analysis of the surrounding 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
+ // we got a symmetric 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().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 a19cad722b..0f8079384c 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!
+ // Workaround T27883. See https://phabricator.mitk.org/T27883#219473 for more details.
+ // This workaround should be removed as soon as T28262 is solved!
if (timestep + 1 == sourceGeometry->CountTimeSteps() && minTP == maxTP)
{
maxTP = minTP + 1.;
}
- // End of workarround for T27883
+ // End of workaround 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() const
{
Superclass::VerifyInputInformation();
VerifyInputImage(this->GetInput());
}
diff --git a/Modules/AlgorithmsExt/src/mitkHeightFieldSurfaceClipImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkHeightFieldSurfaceClipImageFilter.cpp
index 447c254832..8aca5d0ff0 100644
--- a/Modules/AlgorithmsExt/src/mitkHeightFieldSurfaceClipImageFilter.cpp
+++ b/Modules/AlgorithmsExt/src/mitkHeightFieldSurfaceClipImageFilter.cpp
@@ -1,414 +1,414 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkHeightFieldSurfaceClipImageFilter.h"
#include "mitkImageTimeSelector.h"
#include "mitkProperties.h"
#include "mitkTimeHelper.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageToItk.h"
#include <itkImageRegionConstIterator.h>
#include <itkImageRegionIteratorWithIndex.h>
#include <itkImageSliceConstIteratorWithIndex.h>
#include <vtkCellLocator.h>
#include <vtkPolyData.h>
#include <limits>
namespace mitk
{
HeightFieldSurfaceClipImageFilter::HeightFieldSurfaceClipImageFilter()
: m_ClippingMode(CLIPPING_MODE_CONSTANT),
m_ClippingConstant(0.0),
m_MultiplicationFactor(2.0),
m_MultiPlaneValue(2),
m_HeightFieldResolutionX(256),
m_HeightFieldResolutionY(256),
m_MaxHeight(1024.0)
{
this->SetNumberOfIndexedInputs(8);
this->SetNumberOfRequiredInputs(2);
m_InputTimeSelector = ImageTimeSelector::New();
m_OutputTimeSelector = ImageTimeSelector::New();
}
HeightFieldSurfaceClipImageFilter::~HeightFieldSurfaceClipImageFilter() {}
void HeightFieldSurfaceClipImageFilter::SetClippingSurface(Surface *clippingSurface)
{
this->SetNthInput(1, clippingSurface);
}
void HeightFieldSurfaceClipImageFilter::SetClippingSurfaces(ClippingPlaneList planeList)
{
if (planeList.size() > 7)
{
MITK_WARN << "Only 7 clipping planes are allowed!";
}
for (unsigned int i = 0; i < planeList.size(); ++i)
{
this->SetNthInput(i + 1, planeList.at(i));
}
}
const Surface *HeightFieldSurfaceClipImageFilter::GetClippingSurface() const
{
return dynamic_cast<const Surface *>(itk::ProcessObject::GetInput(1));
}
void HeightFieldSurfaceClipImageFilter::SetClippingMode(int mode) { m_ClippingMode = mode; }
int HeightFieldSurfaceClipImageFilter::GetClippingMode() { return m_ClippingMode; }
void HeightFieldSurfaceClipImageFilter::SetClippingModeToConstant() { m_ClippingMode = CLIPPING_MODE_CONSTANT; }
void HeightFieldSurfaceClipImageFilter::SetClippingModeToMultiplyByFactor()
{
m_ClippingMode = CLIPPING_MODE_MULTIPLYBYFACTOR;
}
void HeightFieldSurfaceClipImageFilter::SetClippingModeToMultiPlaneValue()
{
m_ClippingMode = CLIPPING_MODE_MULTIPLANE;
}
void HeightFieldSurfaceClipImageFilter::GenerateInputRequestedRegion()
{
Image *outputImage = this->GetOutput();
Image *inputImage = this->GetInput(0);
const Surface *inputSurface = dynamic_cast<const Surface *>(this->GetInput(1));
if (!outputImage->IsInitialized() || inputSurface == nullptr)
{
return;
}
inputImage->SetRequestedRegionToLargestPossibleRegion();
GenerateTimeInInputRegion(outputImage, inputImage);
}
void HeightFieldSurfaceClipImageFilter::GenerateOutputInformation()
{
const Image *inputImage = this->GetInput(0);
Image *outputImage = this->GetOutput();
if (outputImage->IsInitialized() && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime()))
{
return;
}
itkDebugMacro(<< "GenerateOutputInformation()");
unsigned int i;
auto tmpDimensions = new unsigned int[inputImage->GetDimension()];
for (i = 0; i < inputImage->GetDimension(); ++i)
{
tmpDimensions[i] = inputImage->GetDimension(i);
}
outputImage->Initialize(
inputImage->GetPixelType(), inputImage->GetDimension(), tmpDimensions, inputImage->GetNumberOfChannels());
delete[] tmpDimensions;
outputImage->SetGeometry(static_cast<Geometry3D *>(inputImage->GetGeometry()->Clone().GetPointer()));
outputImage->SetPropertyList(inputImage->GetPropertyList()->Clone());
m_TimeOfHeaderInitialization.Modified();
}
template <typename TPixel, unsigned int VImageDimension>
void HeightFieldSurfaceClipImageFilter::_InternalComputeClippedImage(
itk::Image<TPixel, VImageDimension> *inputItkImage,
HeightFieldSurfaceClipImageFilter *clipImageFilter,
vtkPolyData *clippingPolyData,
AffineTransform3D *imageToPlaneTransform)
{
typedef itk::Image<TPixel, VImageDimension> ItkInputImageType;
typedef itk::Image<TPixel, VImageDimension> ItkOutputImageType;
typedef itk::ImageSliceConstIteratorWithIndex<ItkInputImageType> ItkInputImageIteratorType;
typedef itk::ImageRegionIteratorWithIndex<ItkOutputImageType> ItkOutputImageIteratorType;
typename ImageToItk<ItkOutputImageType>::Pointer outputimagetoitk = ImageToItk<ItkOutputImageType>::New();
outputimagetoitk->SetInput(clipImageFilter->m_OutputTimeSelector->GetOutput());
outputimagetoitk->Update();
typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
std::vector<double> test;
// create the iterators
typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion();
ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest);
// Get bounds of clipping data
clippingPolyData->ComputeBounds();
double *bounds = clippingPolyData->GetBounds();
double xWidth = bounds[1] - bounds[0];
double yWidth = bounds[3] - bounds[2];
// Create vtkCellLocator for clipping poly data
vtkCellLocator *cellLocator = vtkCellLocator::New();
cellLocator->SetDataSet(clippingPolyData);
cellLocator->CacheCellBoundsOn();
cellLocator->AutomaticOn();
cellLocator->BuildLocator();
// Allocate memory for 2D image to hold the height field generated by
// projecting the clipping data onto the plane
auto heightField = new double[m_HeightFieldResolutionX * m_HeightFieldResolutionY];
// Walk through height field and for each entry calculate height of the
// clipping poly data at this point by means of vtkCellLocator. The
// clipping data x/y bounds are used for converting from poly data space to
// image (height-field) space.
MITK_INFO << "Calculating Height Field..." << std::endl;
for (unsigned int y = 0; y < m_HeightFieldResolutionY; ++y)
{
for (unsigned int x = 0; x < m_HeightFieldResolutionX; ++x)
{
double p0[3], p1[3], surfacePoint[3], pcoords[3];
p0[0] = bounds[0] + xWidth * x / (double)m_HeightFieldResolutionX;
p0[1] = bounds[2] + yWidth * y / (double)m_HeightFieldResolutionY;
p0[2] = -m_MaxHeight;
p1[0] = p0[0];
p1[1] = p0[1];
p1[2] = m_MaxHeight;
double t, distance;
int subId;
if (cellLocator->IntersectWithLine(p0, p1, 0.1, t, surfacePoint, pcoords, subId))
{
distance = (2.0 * t - 1.0) * m_MaxHeight;
}
else
{
distance = -65536.0;
}
heightField[y * m_HeightFieldResolutionX + x] = distance;
itk::Image<double, 2>::IndexType index;
index[0] = x;
index[1] = y;
}
}
// Walk through entire input image and for each point determine its distance
// from the x/y plane.
MITK_INFO << "Performing clipping..." << std::endl;
auto factor = static_cast<TPixel>(clipImageFilter->m_MultiplicationFactor);
TPixel clippingConstant = clipImageFilter->m_ClippingConstant;
inputIt.SetFirstDirection(0);
inputIt.SetSecondDirection(1);
// through all slices
for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); inputIt.NextSlice())
{
// through all lines of a slice
for (; !inputIt.IsAtEndOfSlice(); inputIt.NextLine())
{
// Transform the start(line) point from the image to the plane
Point3D imageP0, planeP0;
imageP0[0] = inputIt.GetIndex()[0];
imageP0[1] = inputIt.GetIndex()[1];
imageP0[2] = inputIt.GetIndex()[2];
planeP0 = imageToPlaneTransform->TransformPoint(imageP0);
// Transform the end point (line) from the image to the plane
Point3D imageP1, planeP1;
imageP1[0] = imageP0[0] + inputRegionOfInterest.GetSize(0);
imageP1[1] = imageP0[1];
imageP1[2] = imageP0[2];
planeP1 = imageToPlaneTransform->TransformPoint(imageP1);
// calculate the step size (if the plane is rotate, you go "crossway" through the image)
Vector3D step = (planeP1 - planeP0) / (double)inputRegionOfInterest.GetSize(0);
// over all pixel
for (; !inputIt.IsAtEndOfLine(); ++inputIt, ++outputIt, planeP0 += step)
{
// Only ConstantMode: if image pixel value == constant mode value-->set output pixel value directly
if ((clipImageFilter->m_ClippingMode == CLIPPING_MODE_CONSTANT) &&
((TPixel)inputIt.Get() == clippingConstant))
{
outputIt.Set(clippingConstant);
}
else
{
auto x0 = (int)((double)(m_HeightFieldResolutionX) * (planeP0[0] - bounds[0]) / xWidth);
auto y0 = (int)((double)(m_HeightFieldResolutionY) * (planeP0[1] - bounds[2]) / yWidth);
bool clip;
- // if the current point is outside of the plane region (RegionOfInterest)-->clip the pixel allways
+ // if the current point is outside of the plane region (RegionOfInterest)-->clip the pixel always
if ((x0 < 0) || (x0 >= (int)m_HeightFieldResolutionX) || (y0 < 0) || (y0 >= (int)m_HeightFieldResolutionY))
{
clip = true;
}
else
{
// Calculate bilinearly interpolated height field value at plane point
int x1 = x0 + 1;
int y1 = y0 + 1;
if (x1 >= (int)m_HeightFieldResolutionX)
{
x1 = x0;
}
if (y1 >= (int)m_HeightFieldResolutionY)
{
y1 = y0;
}
// Get the neighbour points for the interpolation
ScalarType q00, q01, q10, q11;
q00 = heightField[y0 * m_HeightFieldResolutionX + x0];
q01 = heightField[y0 * m_HeightFieldResolutionX + x1];
q10 = heightField[y1 * m_HeightFieldResolutionX + x0];
q11 = heightField[y1 * m_HeightFieldResolutionX + x1];
double p00 = ((double)(m_HeightFieldResolutionX) * (planeP0[0] - bounds[0]) / xWidth);
double p01 = ((double)(m_HeightFieldResolutionY) * (planeP0[1] - bounds[2]) / yWidth);
ScalarType q =
q00 * ((double)x1 - p00) * ((double)y1 - p01) + q01 * (p00 - (double)x0) * ((double)y1 - p01) +
q10 * ((double)x1 - p00) * (p01 - (double)y0) + q11 * (p00 - (double)x0) * (p01 - (double)y0);
if (q - planeP0[2] < 0)
{
clip = true;
}
else
{
clip = false;
}
}
- // different modes: differnt values for the clipped pixel
+ // different modes: different values for the clipped pixel
if (clip)
{
if (clipImageFilter->m_ClippingMode == CLIPPING_MODE_CONSTANT)
{
outputIt.Set(clipImageFilter->m_ClippingConstant);
}
else if (clipImageFilter->m_ClippingMode == CLIPPING_MODE_MULTIPLYBYFACTOR)
{
outputIt.Set(inputIt.Get() * factor);
}
else if (clipImageFilter->m_ClippingMode == CLIPPING_MODE_MULTIPLANE)
{
if (inputIt.Get() != 0)
outputIt.Set(inputIt.Get() + m_MultiPlaneValue);
else
outputIt.Set(inputIt.Get());
}
}
// the non-clipped pixel keeps his value
else
{
outputIt.Set(inputIt.Get());
}
}
}
}
}
MITK_INFO << "DONE!" << std::endl;
// Clean-up
cellLocator->Delete();
}
void HeightFieldSurfaceClipImageFilter::GenerateData()
{
const Image *inputImage = this->GetInput(0);
const Image *outputImage = this->GetOutput();
m_InputTimeSelector->SetInput(inputImage);
m_OutputTimeSelector->SetInput(outputImage);
Image::RegionType outputRegion = outputImage->GetRequestedRegion();
const TimeGeometry *outputTimeGeometry = outputImage->GetTimeGeometry();
const TimeGeometry *inputTimeGeometry = inputImage->GetTimeGeometry();
ScalarType timeInMS;
int timestep = 0;
int tstart = outputRegion.GetIndex(3);
int tmax = tstart + outputRegion.GetSize(3);
for (unsigned int i = 1; i < this->GetNumberOfInputs(); ++i)
{
Surface *inputSurface = dynamic_cast<Surface *>(itk::ProcessObject::GetInput(i));
if (!outputImage->IsInitialized() || inputSurface == nullptr)
return;
MITK_INFO << "Plane: " << i;
MITK_INFO << "Clipping: Start\n";
// const PlaneGeometry *clippingGeometryOfCurrentTimeStep = nullptr;
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();
// Compose IndexToWorld transform of image with WorldToIndexTransform of
// clipping data for conversion from image index space to plane index space
AffineTransform3D::Pointer planeWorldToIndexTransform = AffineTransform3D::New();
inputSurface->GetGeometry(t)->GetIndexToWorldTransform()->GetInverse(planeWorldToIndexTransform);
AffineTransform3D::Pointer imageToPlaneTransform = AffineTransform3D::New();
imageToPlaneTransform->SetIdentity();
imageToPlaneTransform->Compose(inputTimeGeometry->GetGeometryForTimeStep(t)->GetIndexToWorldTransform());
imageToPlaneTransform->Compose(planeWorldToIndexTransform);
MITK_INFO << "Accessing ITK function...\n";
if (i == 1)
{
AccessByItk_3(m_InputTimeSelector->GetOutput(),
_InternalComputeClippedImage,
this,
inputSurface->GetVtkPolyData(t),
imageToPlaneTransform);
}
else
{
mitk::Image::Pointer extensionImage = m_OutputTimeSelector->GetOutput()->Clone();
AccessByItk_3(
extensionImage, _InternalComputeClippedImage, this, inputSurface->GetVtkPolyData(t), imageToPlaneTransform);
}
if (m_ClippingMode == CLIPPING_MODE_MULTIPLANE)
m_MultiPlaneValue = m_MultiPlaneValue * 2;
}
}
m_TimeOfHeaderInitialization.Modified();
}
} // namespace
diff --git a/Modules/AlgorithmsExt/src/mitkMovieGeneratorWin32.cpp b/Modules/AlgorithmsExt/src/mitkMovieGeneratorWin32.cpp
index cdd6aba532..4ca31812c6 100755
--- a/Modules/AlgorithmsExt/src/mitkMovieGeneratorWin32.cpp
+++ b/Modules/AlgorithmsExt/src/mitkMovieGeneratorWin32.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 "mitkMovieGeneratorWin32.h"
#include <GL/gl.h>
mitk::MovieGeneratorWin32::MovieGeneratorWin32()
{
}
void mitk::MovieGeneratorWin32::SetFileName(const char *fileName)
{
m_sFile = _T(fileName);
if (_tcsstr((char *)m_sFile, _T("avi")) == nullptr)
m_sFile += _T( ".avi" );
}
void mitk::MovieGeneratorWin32::InitBitmapHeader()
{
m_width = m_renderer->GetRenderWindow()->GetSize()[0]; // changed from glGetIntegerv( GL_VIEWPORT, viewport );
m_height = m_renderer->GetRenderWindow()->GetSize()[1]; // due to sometimes strange dimensions
m_width -= 10; // remove colored boarders around renderwindows
m_height -= 10;
m_width -= m_width % 4; // some video codecs have prerequisites to the image dimensions
m_height -= m_height % 4;
BITMAPINFOHEADER bih;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = m_width;
bih.biHeight = m_height;
bih.biPlanes = 1;
int imgSize = 3 /* BRG*/ * bih.biWidth * bih.biHeight;
bih.biBitCount = 24;
bih.biCompression = BI_RGB;
bih.biSizeImage = imgSize;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
// ASSERT(bih.biWidth%4==0);
// ASSERT(bih.biHeight%4==0);
// copying bitmap info structure.
memcpy(&m_bih, &bih, sizeof(BITMAPINFOHEADER));
}
bool mitk::MovieGeneratorWin32::InitGenerator()
{
InitBitmapHeader();
AVISTREAMINFO strHdr; // information for a single stream
AVICOMPRESSOPTIONS opts;
AVICOMPRESSOPTIONS FAR *aopts[1] = {&opts};
TCHAR szBuffer[1024];
HRESULT hr;
m_sError = _T("Ok");
// Step 0 : Let's make sure we are running on 1.1
DWORD wVer = HIWORD(VideoForWindowsVersion());
if (wVer < 0x010a)
{
// oops, we are too old, blow out of here
m_sError = _T("Version of Video for Windows too old. Come on, join the 21th century!");
return false;
}
// Step 1 : initialize AVI engine
AVIFileInit();
// Step 2 : Open the movie file for writing....
hr = AVIFileOpen(&m_pAVIFile, // Address to contain the new file interface pointer
(LPCTSTR)m_sFile, // Null-terminated string containing the name of the file to open
OF_WRITE | OF_CREATE, // Access mode to use when opening the file.
nullptr); // use handler determined from file extension.
// Name your file .avi -> very important
if (hr != AVIERR_OK)
{
_tprintf(szBuffer, _T("AVI Engine failed to initialize. Check filename %s."), m_sFile);
m_sError = szBuffer;
- // Check it succeded.
+ // Check it succeeded.
switch (hr)
{
case AVIERR_BADFORMAT:
m_sError += _T("The file couldn't be read, indicating a corrupt file or an unrecognized format.");
break;
case AVIERR_MEMORY:
m_sError += _T("The file could not be opened because of insufficient memory.");
break;
case AVIERR_FILEREAD:
m_sError += _T("A disk error occurred while reading the file.");
break;
case AVIERR_FILEOPEN:
m_sError += _T("A disk error occurred while opening the file.");
break;
case REGDB_E_CLASSNOTREG:
m_sError += _T("According to the registry, the type of file specified in AVIFileOpen does not have a handler ")
_T("to process it");
break;
}
return false;
}
// Fill in the header for the video stream....
memset(&strHdr, 0, sizeof(strHdr));
strHdr.fccType = streamtypeVIDEO; // video stream type
strHdr.fccHandler = 0;
strHdr.dwScale = 1; // should be one for video
strHdr.dwRate = static_cast<DWORD>(m_FrameRate); // fps
strHdr.dwSuggestedBufferSize = m_bih.biSizeImage; // Recommended buffer size, in bytes, for the stream.
SetRect(&strHdr.rcFrame,
0,
0, // rectangle for stream
(int)m_bih.biWidth,
(int)m_bih.biHeight);
// Step 3 : Create the stream;
hr = AVIFileCreateStream(m_pAVIFile, // file pointer
&m_pStream, // returned stream pointer
&strHdr); // stream header
- // Check it succeded.
+ // Check it succeeded.
if (hr != AVIERR_OK)
{
m_sError = _T("AVI Stream creation failed. Check Bitmap info.");
if (hr == AVIERR_READONLY)
{
m_sError += _T(" Read only file.");
}
return false;
}
// Step 4: Get codec and infos about codec
memset(&opts, 0, sizeof(opts));
// predefine MS-CRAM as standard codec
opts.fccType = streamtypeVIDEO;
// creates a video with minor quality! Use different codec (must be installed on local machine) to generate movies
// with higher quality
opts.fccHandler = mmioFOURCC('M', 'S', 'V', 'C');
opts.dwQuality = 90000; // means 90% quality; dwQuality goes from [0...10000]
-// Poping codec dialog
+// Popping codec dialog
// GUI Codec selection does not work in a vs 2005 compiled mitk, since we do not pass a hwnd as first parameter
// of AVISaveOptions
#if !(_MSC_VER >= 1400)
if (!AVISaveOptions(nullptr, 0, 1, &m_pStream, (LPAVICOMPRESSOPTIONS FAR *)&aopts))
{
AVISaveOptionsFree(1, (LPAVICOMPRESSOPTIONS FAR *)&aopts);
// return false;
}
#endif
// Step 5: Create a compressed stream using codec options.
hr = AVIMakeCompressedStream(&m_pStreamCompressed, m_pStream, &opts, nullptr);
if (hr != AVIERR_OK)
{
m_sError = _T("AVI Compressed Stream creation failed.");
switch (hr)
{
case AVIERR_NOCOMPRESSOR:
m_sError += _T(" A suitable compressor cannot be found.");
break;
case AVIERR_MEMORY:
m_sError += _T(" There is not enough memory to complete the operation.");
break;
case AVIERR_UNSUPPORTED:
m_sError += _T("Compression is not supported for this type of data. This error might be returned if you try ")
_T("to compress data that is not audio or video.");
break;
}
return false;
}
// releasing memory allocated by AVISaveOptionFree
hr = AVISaveOptionsFree(1, (LPAVICOMPRESSOPTIONS FAR *)&aopts);
if (hr != AVIERR_OK)
{
m_sError = _T("Error releasing memory");
return false;
}
// Step 6 : sets the format of a stream at the specified position
hr = AVIStreamSetFormat(m_pStreamCompressed,
0, // position
&m_bih, // stream format
m_bih.biSize + // format size
m_bih.biClrUsed * sizeof(RGBQUAD));
if (hr != AVIERR_OK)
{
m_sError = _T("AVI Compressed Stream format setting failed.");
return false;
}
// Step 6 : Initialize step counter
m_lFrame = 0;
return true;
}
bool mitk::MovieGeneratorWin32::AddFrame(void *data)
{
HRESULT hr = AVIStreamWrite(m_pStreamCompressed, // stream pointer
m_lFrame, // time of this frame
1, // number to write
(BYTE *)data, // image buffer
m_bih.biSizeImage, // size of this frame
AVIIF_KEYFRAME, // flags....
nullptr,
nullptr);
// updating frame counter
m_lFrame++;
if (hr == AVIERR_OK)
return true;
else
return false;
}
bool mitk::MovieGeneratorWin32::TerminateGenerator()
{
if (m_pStream)
{
AVIStreamRelease(m_pStream);
m_pStream = nullptr;
}
if (m_pStreamCompressed)
{
AVIStreamRelease(m_pStreamCompressed);
m_pStreamCompressed = nullptr;
}
if (m_pAVIFile)
{
AVIFileRelease(m_pAVIFile);
m_pAVIFile = nullptr;
}
// Close engine
AVIFileExit();
return true;
}
diff --git a/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp b/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp
index dd617de6f5..2b0c9c2c02 100644
--- a/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp
+++ b/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp
@@ -1,192 +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 <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
+ // the vector will be ordered 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/mitkUnstructuredGridClusteringFilter.cpp b/Modules/AlgorithmsExt/src/mitkUnstructuredGridClusteringFilter.cpp
index 26a7ed4dca..d7cc326ae0 100644
--- a/Modules/AlgorithmsExt/src/mitkUnstructuredGridClusteringFilter.cpp
+++ b/Modules/AlgorithmsExt/src/mitkUnstructuredGridClusteringFilter.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.
============================================================================*/
#include <mitkUnstructuredGridClusteringFilter.h>
#include <vector>
#include <vtkDataArray.h>
#include <vtkDelaunay3D.h>
#include <vtkDoubleArray.h>
#include <vtkPointData.h>
#include <vtkPointLocator.h>
#include <vtkPoints.h>
#include <vtkPolyVertex.h>
#include <vtkSmartPointer.h>
#include <vtkUnstructuredGrid.h>
#include <vtkVariant.h>
mitk::UnstructuredGridClusteringFilter::UnstructuredGridClusteringFilter()
: m_eps(5.0), m_MinPts(4), m_Meshing(false), m_DistCalc(false)
{
this->m_UnstructGrid = mitk::UnstructuredGrid::New();
}
mitk::UnstructuredGridClusteringFilter::~UnstructuredGridClusteringFilter()
{
}
std::map<int, bool> visited;
std::map<int, bool> isNoise;
std::map<int, bool> clusterMember;
vtkSmartPointer<vtkPointLocator> pLocator;
std::vector<vtkSmartPointer<vtkPoints>> clusterVector;
std::vector<std::vector<int>> clustersPointsIDs;
void mitk::UnstructuredGridClusteringFilter::GenerateOutputInformation()
{
m_UnstructGrid = this->GetOutput();
}
void mitk::UnstructuredGridClusteringFilter::GenerateData()
{
mitk::UnstructuredGrid::Pointer inputGrid = const_cast<mitk::UnstructuredGrid *>(this->GetInput());
if (inputGrid.IsNull())
return;
vtkSmartPointer<vtkUnstructuredGrid> vtkInpGrid = inputGrid->GetVtkUnstructuredGrid();
vtkSmartPointer<vtkPoints> inpPoints = vtkInpGrid->GetPoints();
pLocator = vtkSmartPointer<vtkPointLocator>::New();
vtkSmartPointer<vtkDoubleArray> distances = vtkSmartPointer<vtkDoubleArray>::New();
if (inputGrid->GetVtkUnstructuredGrid()->GetPointData()->GetNumberOfArrays() > 0)
{
m_DistCalc = true;
distances = dynamic_cast<vtkDoubleArray *>(vtkInpGrid->GetPointData()->GetArray(0));
}
pLocator->SetDataSet(vtkInpGrid);
pLocator->AutomaticOn();
pLocator->SetNumberOfPointsPerBucket(2);
pLocator->BuildLocator();
// fill the visited map with false for checking
for (int i = 0; i < inpPoints->GetNumberOfPoints(); i++)
{
visited[i] = false;
isNoise[i] = false;
clusterMember[i] = false;
}
for (int i = 0; i < inpPoints->GetNumberOfPoints(); i++)
{
if (!visited[i])
{
visited[i] = true; // mark P as visited
vtkSmartPointer<vtkIdList> idList = vtkSmartPointer<vtkIdList>::New(); // represent N
pLocator->FindPointsWithinRadius(m_eps, inpPoints->GetPoint(i), idList); // N = D.regionQuery(P, eps)
if (idList->GetNumberOfIds() < m_MinPts) // if sizeof(N) < MinPts
{
isNoise[i] = true; // mark P as NOISE
}
else
{
vtkSmartPointer<vtkPoints> cluster = vtkSmartPointer<vtkPoints>::New(); // represent a cluster
clusterVector.push_back(cluster); // C = next cluster
this->ExpandCluster(
i, idList, cluster, inpPoints); // expandCluster(P, N, C, eps, MinPts) mod. the parameter list
}
}
}
// OUTPUT LOGIC
m_Clusters = clusterVector;
int numberOfClusterPoints = 0;
int IdOfBiggestCluster = 0;
for (unsigned int i = 0; i < m_Clusters.size(); i++)
{
vtkSmartPointer<vtkDoubleArray> array = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkPoints> points = m_Clusters.at(i);
if (m_DistCalc)
{
array->SetNumberOfComponents(1);
array->SetNumberOfTuples(points->GetNumberOfPoints());
for (int j = 0; j < points->GetNumberOfPoints(); j++)
{
double point[3];
points->GetPoint(j, point);
if (clustersPointsIDs.at(i).at(j) < inpPoints->GetNumberOfPoints())
{
if (distances->GetValue(clustersPointsIDs.at(i).at(j)) > 0.001)
{
double dist[1] = {distances->GetValue(clustersPointsIDs.at(i).at(j))};
array->SetTuple(j, dist);
}
else
{
double dist[1] = {0.0};
array->SetTuple(j, dist);
}
}
}
m_DistanceArrays.push_back(array);
}
if (points->GetNumberOfPoints() > numberOfClusterPoints)
{
numberOfClusterPoints = points->GetNumberOfPoints();
IdOfBiggestCluster = i;
}
}
vtkSmartPointer<vtkUnstructuredGrid> biggestCluster = vtkSmartPointer<vtkUnstructuredGrid>::New();
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
points = m_Clusters.at(IdOfBiggestCluster);
vtkSmartPointer<vtkPolyVertex> verts = vtkSmartPointer<vtkPolyVertex>::New();
verts->GetPointIds()->SetNumberOfIds(m_Clusters.at(IdOfBiggestCluster)->GetNumberOfPoints());
for (int i = 0; i < m_Clusters.at(IdOfBiggestCluster)->GetNumberOfPoints(); i++)
{
verts->GetPointIds()->SetId(i, i);
}
biggestCluster->Allocate(1);
biggestCluster->InsertNextCell(verts->GetCellType(), verts->GetPointIds());
biggestCluster->SetPoints(m_Clusters.at(IdOfBiggestCluster));
if (m_Meshing)
{
vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
mesher->SetInputData(biggestCluster);
mesher->SetAlpha(0.9);
mesher->Update();
vtkSmartPointer<vtkUnstructuredGrid> output = mesher->GetOutput();
m_UnstructGrid->SetVtkUnstructuredGrid(output);
}
else
{
m_UnstructGrid->SetVtkUnstructuredGrid(biggestCluster);
}
clusterVector.clear();
clustersPointsIDs.clear();
}
void mitk::UnstructuredGridClusteringFilter::ExpandCluster(int id,
vtkIdList *pointIDs,
vtkPoints *cluster,
vtkPoints *inpPoints)
{
std::vector<int> x;
x.push_back(id);
cluster->InsertNextPoint(inpPoints->GetPoint(id)); // add P to cluster C
clusterMember[id] = true;
vtkSmartPointer<vtkPoints> neighbours = vtkSmartPointer<vtkPoints>::New(); // same N as in other function
inpPoints->GetPoints(pointIDs, neighbours);
for (int i = 0; i < pointIDs->GetNumberOfIds(); i++) // for each point P' in N
{
if (!visited[pointIDs->GetId(i)]) // if P' is not visited
{
visited[pointIDs->GetId(i)] = true; // mark P' as visited
vtkSmartPointer<vtkIdList> idList = vtkSmartPointer<vtkIdList>::New(); // represent N'
pLocator->FindPointsWithinRadius(
m_eps, inpPoints->GetPoint(pointIDs->GetId(i)), idList); // N' = D.regionQuery(P', eps)
if (idList->GetNumberOfIds() >= m_MinPts) // if sizeof(N') >= MinPts
{
for (int j = 0; j < idList->GetNumberOfIds(); j++) // N = N joined with N'
{
- if (idList->GetId(j) < inpPoints->GetNumberOfPoints()) // a litte bit hacked ?!
+ if (idList->GetId(j) < inpPoints->GetNumberOfPoints()) // a little bit hacked ?!
{
pointIDs->InsertNextId(idList->GetId(j));
}
}
}
}
if (!clusterMember[pointIDs->GetId(i)]) // if P' is not yet member of any cluster
{
if (pointIDs->GetId(i) < inpPoints->GetNumberOfPoints())
{
clusterMember[pointIDs->GetId(i)] = true;
x.push_back(pointIDs->GetId(i));
cluster->InsertNextPoint(inpPoints->GetPoint(pointIDs->GetId(i))); // add P' to cluster C
}
}
}
clustersPointsIDs.push_back(x);
}
std::vector<mitk::UnstructuredGrid::Pointer> mitk::UnstructuredGridClusteringFilter::GetAllClusters()
{
std::vector<mitk::UnstructuredGrid::Pointer> mitkUGridVector;
for (unsigned int i = 0; i < m_Clusters.size(); i++)
{
vtkSmartPointer<vtkUnstructuredGrid> cluster = vtkSmartPointer<vtkUnstructuredGrid>::New();
vtkSmartPointer<vtkPoints> points = m_Clusters.at(i);
vtkSmartPointer<vtkPolyVertex> verts = vtkSmartPointer<vtkPolyVertex>::New();
verts->GetPointIds()->SetNumberOfIds(points->GetNumberOfPoints());
for (int j = 0; j < points->GetNumberOfPoints(); j++)
{
verts->GetPointIds()->SetId(j, j);
}
cluster->Allocate(1);
cluster->InsertNextCell(verts->GetCellType(), verts->GetPointIds());
cluster->SetPoints(points);
if (m_DistCalc)
{
cluster->GetPointData()->AddArray(m_DistanceArrays.at(i));
}
mitk::UnstructuredGrid::Pointer mitkGrid = mitk::UnstructuredGrid::New();
if (m_Meshing)
{
vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
mesher->SetInputData(cluster);
mesher->SetAlpha(0.9);
mesher->Update();
vtkSmartPointer<vtkUnstructuredGrid> output = mesher->GetOutput();
mitkGrid->SetVtkUnstructuredGrid(output);
}
else
{
mitkGrid->SetVtkUnstructuredGrid(cluster);
}
mitkUGridVector.push_back(mitkGrid);
}
return mitkUGridVector;
}
int mitk::UnstructuredGridClusteringFilter::GetNumberOfFoundClusters()
{
return m_Clusters.size();
}
diff --git a/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp b/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp
index d1ceab8092..696096c8ff 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)
+ // convert to itk data types (necessary 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):
+ // good descriptions 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().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/AlgorithmsExt/test/mitkAnisotropicIterativeClosestPointRegistrationTest.cpp b/Modules/AlgorithmsExt/test/mitkAnisotropicIterativeClosestPointRegistrationTest.cpp
index c916881b8e..69383d9709 100644
--- a/Modules/AlgorithmsExt/test/mitkAnisotropicIterativeClosestPointRegistrationTest.cpp
+++ b/Modules/AlgorithmsExt/test/mitkAnisotropicIterativeClosestPointRegistrationTest.cpp
@@ -1,168 +1,168 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkSurface.h>
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <vtkCleanPolyData.h>
#include <vtkTransformPolyDataFilter.h>
#include <vtkTransform.h>
#include "mitkAnisotropicIterativeClosestPointRegistration.h"
#include "mitkAnisotropicRegistrationCommon.h"
#include "mitkCovarianceMatrixCalculator.h"
/**
* Test to verify the results of the A-ICP registration.
* The test runs the standard A-ICP and the trimmed variant.
*/
class mitkAnisotropicIterativeClosestPointRegistrationTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkAnisotropicIterativeClosestPointRegistrationTestSuite);
MITK_TEST(testAicpRegistration);
MITK_TEST(testTrimmedAicpregistration);
CPPUNIT_TEST_SUITE_END();
private:
typedef itk::Matrix<double, 3, 3> Matrix3x3;
typedef itk::Vector<double, 3> Vector3;
typedef std::vector<Matrix3x3> CovarianceMatrixList;
mitk::Surface::Pointer m_MovingSurface;
mitk::Surface::Pointer m_FixedSurface;
mitk::PointSet::Pointer m_TargetsMovingSurface;
mitk::PointSet::Pointer m_TargetsFixedSurface;
CovarianceMatrixList m_SigmasMovingSurface;
CovarianceMatrixList m_SigmasFixedSurface;
double m_FRENormalizationFactor;
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.
+ * correct and new initialization 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
{
mitk::CovarianceMatrixCalculator::Pointer matrixCalculator = mitk::CovarianceMatrixCalculator::New();
m_MovingSurface = mitk::IOUtil::Load<mitk::Surface>(GetTestDataFilePath("AICPRegistration/head_green.vtp"));
m_FixedSurface = mitk::IOUtil::Load<mitk::Surface>(GetTestDataFilePath("AICPRegistration/head_red.vtp"));
m_TargetsMovingSurface = mitk::IOUtil::Load<mitk::PointSet>(GetTestDataFilePath("AICPRegistration/targets_head_green.mps"));
m_TargetsFixedSurface = mitk::IOUtil::Load<mitk::PointSet>(GetTestDataFilePath("AICPRegistration/targets_head_red.mps"));
// compute covariance matrices
matrixCalculator->SetInputSurface(m_MovingSurface);
matrixCalculator->ComputeCovarianceMatrices();
m_SigmasMovingSurface = matrixCalculator->GetCovarianceMatrices();
const double meanVarX = matrixCalculator->GetMeanVariance();
matrixCalculator->SetInputSurface(m_FixedSurface);
matrixCalculator->ComputeCovarianceMatrices();
m_SigmasFixedSurface = matrixCalculator->GetCovarianceMatrices();
const double meanVarY = matrixCalculator->GetMeanVariance();
m_FRENormalizationFactor = sqrt(meanVarX + meanVarY);
}
void tearDown() override
{
m_MovingSurface = nullptr;
m_FixedSurface = nullptr;
m_TargetsMovingSurface = nullptr;
m_TargetsFixedSurface = nullptr;
m_SigmasMovingSurface.clear();
m_SigmasFixedSurface.clear();
}
void testAicpRegistration()
{
const double expFRE = 26.3453;
const double expTRE = 3.8707;
mitk::AnisotropicIterativeClosestPointRegistration::Pointer aICP =
mitk::AnisotropicIterativeClosestPointRegistration::New();
// set up parameters
aICP->SetMovingSurface(m_MovingSurface);
aICP->SetFixedSurface(m_FixedSurface);
aICP->SetCovarianceMatricesMovingSurface(m_SigmasMovingSurface);
aICP->SetCovarianceMatricesFixedSurface(m_SigmasFixedSurface);
aICP->SetFRENormalizationFactor(m_FRENormalizationFactor);
aICP->SetThreshold(0.000001);
// run the algorithm
aICP->Update();
MITK_INFO << "FRE: Expected: " << expFRE << ", computed: " << aICP->GetFRE();
CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test FRE",
mitk::Equal(aICP->GetFRE(), expFRE, 0.0001));
// compute the target registration Error
const double tre =
mitk::AnisotropicRegistrationCommon::ComputeTargetRegistrationError(m_TargetsMovingSurface.GetPointer(),
m_TargetsFixedSurface.GetPointer(),
aICP->GetRotation(),
aICP->GetTranslation());
// MITK_INFO << "R:\n" << aICP->GetRotation() << "T: "<< aICP->GetTranslation();
MITK_INFO << "TRE: Expected: " << expTRE << ", computed: " << tre;
CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test TRE",
mitk::Equal(tre, expTRE, 0.00001));
}
void testTrimmedAicpregistration()
{
const double expFRE = 18.5469;
const double expTRE = 5.5871;
mitk::AnisotropicIterativeClosestPointRegistration::Pointer aICP =
mitk::AnisotropicIterativeClosestPointRegistration::New();
// Swap X and Y for partial overlapping registration
aICP->SetMovingSurface(m_MovingSurface);
aICP->SetFixedSurface(m_FixedSurface);
aICP->SetCovarianceMatricesMovingSurface(m_SigmasMovingSurface);
aICP->SetCovarianceMatricesFixedSurface(m_SigmasFixedSurface);
aICP->SetFRENormalizationFactor(m_FRENormalizationFactor);
aICP->SetThreshold(0.000001);
aICP->SetTrimmFactor(0.80);
// run the algorithm
aICP->Update();
MITK_INFO << "FRE: Expected: " << expFRE << ", computed: " << aICP->GetFRE();
CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test FRE",
mitk::Equal(aICP->GetFRE(), expFRE, 0.01));
// compute the target registration Error
const double tre =
mitk::AnisotropicRegistrationCommon::ComputeTargetRegistrationError(m_TargetsMovingSurface.GetPointer(),
m_TargetsFixedSurface.GetPointer(),
aICP->GetRotation(),
aICP->GetTranslation());
MITK_INFO << "TRE: Expected: " << expTRE << ", computed: " << tre;
CPPUNIT_ASSERT_MESSAGE("mitkAnisotropicIterativeClosestPointRegistrationTest:AicpRegistration Test TRE",
mitk::Equal(tre, expTRE, 0.01));
}
};
MITK_TEST_SUITE_REGISTRATION(mitkAnisotropicIterativeClosestPointRegistration)
diff --git a/Modules/AlgorithmsExt/test/mitkBoundingObjectCutterTest.cpp b/Modules/AlgorithmsExt/test/mitkBoundingObjectCutterTest.cpp
index 0651db2802..cb3c5493d3 100644
--- a/Modules/AlgorithmsExt/test/mitkBoundingObjectCutterTest.cpp
+++ b/Modules/AlgorithmsExt/test/mitkBoundingObjectCutterTest.cpp
@@ -1,120 +1,120 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkImage.h>
#include <mitkImageCast.h>
#include <mitkImageDataItem.h>
#include <mitkBoundingObject.h>
#include <mitkBoundingObjectCutter.h>
#include <mitkCuboid.h>
#include <itkImage.h>
#include <fstream>
#include <vtkImageData.h>
#include <mitkTestingMacros.h>
int mitkBoundingObjectCutterTest(int /*argc*/, char * /*argv*/ [])
{
MITK_TEST_BEGIN(mitkBoundingObjectCutterTest);
////Create Image out of nowhere
// mitk::Image::Pointer image;
// mitk::PixelType pt(mitk::MakeScalarPixelType<int>() );
// unsigned int dim[]={100,100,20};
- // MITK_TEST_OUTPUT(<< "Creating Image as imput for cutting: ");
+ // MITK_TEST_OUTPUT(<< "Creating Image as input for cutting: ");
// image=mitk::Image::New();
// image->Initialize(mitk::MakeScalarPixelType<int>(), 3, dim);
// mitk::ImageReadAccessor imgAcc(image);
// int *p = (int*)imgAcc.GetData();
// unsigned int i;
// unsigned int size = dim[0]*dim[1]*dim[2];
// for(i=0; i<size; ++i, ++p)
// *p= (signed int)i;
// std::cout<<"[PASSED]"<<std::endl;
// MITK_TEST_OUTPUT(<< "Testing mitk::BoundingObject::FitGeometry(image->GetGeometry()) with an mitk::Cuboid
// (sub-class of mitk::BoundingObject): ");
// mitk::Cuboid::Pointer cuboid = mitk::Cuboid::New();
// cuboid->FitGeometry(image->GetGeometry());
// std::cout<<"[PASSED]"<<std::endl;
// MITK_TEST_OUTPUT(<< "Testing whether corners of the cuboid are identical to corners of the image: ");
// int c;
// for(c=0; c<6; ++c)
// {
// MITK_TEST_OUTPUT(<< " Testing GetCornerPoint(" << c << "): ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetCornerPoint(c),cuboid->GetGeometry()->GetCornerPoint(c)-1), "");
// }
// MITK_TEST_OUTPUT(<< "Testing whether diagonal^2 of fitted mitk::Cuboid is identical to diagonal^2 of image: ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetDiagonalLength2(),cuboid->GetGeometry()->GetDiagonalLength2()), "");
// MITK_TEST_OUTPUT(<< "Testing mitk::BoundingObjectCutter: ");
// mitk::BoundingObjectCutter::Pointer boCutter = mitk::BoundingObjectCutter::New();
// boCutter->SetInput(image);
// boCutter->SetBoundingObject(cuboid);
// MITK_TEST_OUTPUT(<< " Testing mitk::BoundingObjectCutter::UpdateLargestPossibleRegion():: ");
// boCutter->UpdateLargestPossibleRegion();
// std::cout<<"[PASSED]"<<std::endl;
// mitk::Image::Pointer cuttedImage = boCutter->GetOutput();
// MITK_TEST_OUTPUT(<< " Testing whether origin of cutted image is identical to origin of original image: ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetOrigin(),cuttedImage->GetGeometry()->GetOrigin()), "");
// MITK_TEST_OUTPUT(<< " Testing whether spacing of cutted image is identical to spacing of original image: ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetSpacing(),cuttedImage->GetGeometry()->GetSpacing()), "");
// MITK_TEST_OUTPUT(<< " Testing whether center of cutted image is identical to center of original image: ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetCenter(),cuttedImage->GetGeometry()->GetCenter()), "");
// MITK_TEST_OUTPUT(<< " Testing whether diagonal^2 of cutted image is identical to diagonal^2 of original image: ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetDiagonalLength2(),cuttedImage->GetGeometry()->GetDiagonalLength2()), "");
// MITK_TEST_OUTPUT(<< " Testing whether corners of cutted image are identical to corners of original image: ");
// for(c=0; c<6; ++c)
//{
// MITK_TEST_OUTPUT(<< " Testing GetCornerPoint(" << c << "): ");
// MITK_TEST_CONDITION_REQUIRED(
// mitk::Equal(image->GetGeometry()->GetCornerPoint(c),cuttedImage->GetGeometry()->GetCornerPoint(c)), "");
//}
// MITK_TEST_OUTPUT(<< " Testing whether pixel data of cutted image are identical to pixel data of original image: ");
// mitk::ImageReadAccessor imgAcc(image);
// p = (int*)imgAcc.GetData();
// mitk::ImageReadAccessor cuttedImage(cuttedImage);
// int *pCutted = (int*)cuttedImage.GetData();
// for(i=0; i<size; ++i, ++p, ++pCutted)
//{
// if(*p!=*pCutted)
// break;
//}
// MITK_TEST_CONDITION_REQUIRED(i==size, "");
// MITK_TEST_OUTPUT(<< " Testing whether geometry of cutted image has ImageGeometry==true: ");
// MITK_TEST_CONDITION_REQUIRED(cuttedImage->GetGeometry()->GetImageGeometry(), "");
MITK_TEST_END();
return EXIT_SUCCESS;
}
diff --git a/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp b/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp
index 84373e97d8..80644df37c 100644
--- a/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp
+++ b/Modules/AlgorithmsExt/test/mitkImageToUnstructuredGridFilterTest.cpp
@@ -1,79 +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.
============================================================================*/
#include "mitkTestingMacros.h"
#include <mitkImageToUnstructuredGridFilter.h>
#include <mitkTestFixture.h>
#include <mitkIOUtil.h>
class mitkImageToUnstructuredGridFilterTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkImageToUnstructuredGridFilterTestSuite);
MITK_TEST(testImageToUnstructuredGridFilterInitialization);
MITK_TEST(testInput);
MITK_TEST(testUnstructuredGridGeneration);
MITK_TEST(testThreshold);
CPPUNIT_TEST_SUITE_END();
private:
/** Members used inside the different test methods. All members are initialized via setUp().*/
mitk::Image::Pointer m_BallImage;
public:
/**
- * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used
+ * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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_BallImage = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("BallBinary30x30x30.nrrd")); }
void testImageToUnstructuredGridFilterInitialization()
{
mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New();
CPPUNIT_ASSERT_MESSAGE("Testing instantiation of test object", testFilter.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing initialization of threshold member variable", testFilter->GetThreshold() == -0.1);
}
void testInput()
{
mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New();
testFilter->SetInput(m_BallImage);
CPPUNIT_ASSERT_MESSAGE("Testing set / get input!", testFilter->GetInput() == m_BallImage);
}
void testUnstructuredGridGeneration()
{
mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New();
testFilter->SetInput(m_BallImage);
testFilter->Update();
CPPUNIT_ASSERT_MESSAGE("Testing UnstructuredGrid generation!", testFilter->GetOutput() != nullptr);
}
void testThreshold()
{
mitk::ImageToUnstructuredGridFilter::Pointer testFilter1 = mitk::ImageToUnstructuredGridFilter::New();
testFilter1->SetInput(m_BallImage);
testFilter1->Update();
int numberOfPoints1 = testFilter1->GetNumberOfExtractedPoints();
mitk::ImageToUnstructuredGridFilter::Pointer testFilter2 = mitk::ImageToUnstructuredGridFilter::New();
testFilter2->SetInput(m_BallImage);
testFilter2->SetThreshold(1.0);
testFilter2->Update();
int numberOfPoints2 = testFilter2->GetNumberOfExtractedPoints();
CPPUNIT_ASSERT_MESSAGE("Testing Threshold", numberOfPoints1 > numberOfPoints2);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkImageToUnstructuredGridFilter)
diff --git a/Modules/AlgorithmsExt/test/mitkPlaneFitTest.cpp b/Modules/AlgorithmsExt/test/mitkPlaneFitTest.cpp
index bbbb62cb99..34d9ff5133 100644
--- a/Modules/AlgorithmsExt/test/mitkPlaneFitTest.cpp
+++ b/Modules/AlgorithmsExt/test/mitkPlaneFitTest.cpp
@@ -1,88 +1,88 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <fstream>
#include <mitkGeometry3D.h>
#include <mitkGeometryData.h>
#include <mitkNumericTypes.h>
#include <mitkPlaneFit.h>
#include <mitkPlaneGeometry.h>
#include <mitkPointSet.h>
int mitkPlaneFitTest(int, char *[])
{
// float bounds[]={0.0f,10.0f,0.0f,10.0f,0.0f,5.0f};
mitk::PlaneFit::Pointer PlaneFit = mitk::PlaneFit::New();
mitk::PointSet::Pointer PointSet = mitk::PointSet::New();
mitk::Point3D Point;
- // first without any point, then incrementally add points within thre points there will be a plane geometry
+ // first without any point, then incrementally add points within the points there will be a plane geometry
std::cout << "Start PlaneFitTest " << std::endl;
for (int position = 0; position < 6; position++)
{
// add a point directly
mitk::FillVector3D(Point, (float)position, (float)position * 1.5, 2.5);
PointSet->GetPointSet()->GetPoints()->InsertElement(position, Point);
}
// Set Input
PlaneFit->SetInput(PointSet);
const mitk::PointSet *testPointSet = PlaneFit->GetInput();
std::cout << " Size test of Input Method: ";
if (testPointSet->GetSize() == PointSet->GetSize())
{
std::cout << "[PASSED]" << std::endl;
}
else
{
std::cout << "[FAILED]" << std::endl;
return EXIT_FAILURE;
}
// Test Centroid
std::cout << " Testing centroid calculaation: ";
PlaneFit->Update();
const mitk::Point3D &centroid = PlaneFit->GetCentroid();
mitk::Point3D expectedCentroid;
expectedCentroid[0] = 2.5;
expectedCentroid[1] = 3.75;
expectedCentroid[2] = 2.5;
if (centroid == expectedCentroid)
{
std::cout << "[PASSED]" << std::endl;
}
else
{
std::cout << "[FAILED]" << std::endl;
return EXIT_FAILURE;
}
// Test PlaneGeometry
std::cout << " Test PlaneGeometry: ";
auto *PlaneGeometry = dynamic_cast<mitk::PlaneGeometry *>(PlaneFit->GetOutput()->GetGeometry());
if (PlaneGeometry)
{
std::cout << "[PASSED]" << std::endl;
}
else
{
std::cout << "[FAILED]" << std::endl;
return EXIT_FAILURE;
}
std::cout << "[TEST DONE]" << std::endl;
return EXIT_SUCCESS;
}
diff --git a/Modules/AlgorithmsExt/test/mitkSimpleHistogramTest.cpp b/Modules/AlgorithmsExt/test/mitkSimpleHistogramTest.cpp
index 4e0959183e..d4207c45fe 100644
--- a/Modules/AlgorithmsExt/test/mitkSimpleHistogramTest.cpp
+++ b/Modules/AlgorithmsExt/test/mitkSimpleHistogramTest.cpp
@@ -1,43 +1,43 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkSurface.h>
#include <mitkTestingMacros.h>
int mitkSimpleHistogramTest(int /*argc*/, char * /*argv*/ [])
{
MITK_TEST_BEGIN("mitkSimpleHistogram");
auto myTestSimpleImageHistogram = new mitk::SimpleImageHistogram();
- MITK_TEST_CONDITION_REQUIRED(myTestSimpleImageHistogram != nullptr, "Testing instanciation.");
+ MITK_TEST_CONDITION_REQUIRED(myTestSimpleImageHistogram != nullptr, "Testing instantiation.");
MITK_TEST_CONDITION_REQUIRED(myTestSimpleImageHistogram->GetMax() == 1, "Testing GetMax().");
MITK_TEST_CONDITION_REQUIRED(myTestSimpleImageHistogram->GetMin() == 0, "Testing GetMin().");
MITK_TEST_CONDITION_REQUIRED(myTestSimpleImageHistogram->GetRelativeBin(1.0, 5.0) == 0, "Testing GetRelativeBin().");
bool success = true;
try
{
myTestSimpleImageHistogram->ComputeFromBaseData(nullptr);
myTestSimpleImageHistogram->ComputeFromBaseData(mitk::Image::New()); // an empty image
myTestSimpleImageHistogram->ComputeFromBaseData(mitk::Surface::New()); // an invalid value
}
catch (...)
{
success = false;
}
MITK_TEST_CONDITION_REQUIRED(success, "Testing ComputeFromBaseData() with invalid input values.");
MITK_TEST_CONDITION_REQUIRED(!myTestSimpleImageHistogram->GetValid(),
"Testing if histogram is invalid after invalid input.");
MITK_TEST_END();
}
diff --git a/Modules/Annotation/include/mitkLabelAnnotation3D.h b/Modules/Annotation/include/mitkLabelAnnotation3D.h
index 7b28a20386..ad25755325 100644
--- a/Modules/Annotation/include/mitkLabelAnnotation3D.h
+++ b/Modules/Annotation/include/mitkLabelAnnotation3D.h
@@ -1,109 +1,109 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkLabelAnnotation3D_h
#define mitkLabelAnnotation3D_h
#include "MitkAnnotationExports.h"
#include <mitkLocalStorageHandler.h>
#include <mitkVtkAnnotation3D.h>
#include <vtkSmartPointer.h>
class vtkStringArray;
class vtkPolyDataMapper;
class vtkPolyData;
class vtkActor2D;
class vtkProperty2D;
class vtkPointSetToLabelHierarchy;
class vtkLabelPlacementMapper;
class vtkIntArray;
namespace mitk
{
class PointSet;
/** \brief Can display a high amount of 3D labels to a PointSet */
class MITKANNOTATION_EXPORT LabelAnnotation3D : public mitk::VtkAnnotation3D
{
public:
/** \brief Internal class holding the vtkActor, etc. for each of the render windows */
class LocalStorage : public mitk::Annotation::BaseLocalStorage
{
public:
vtkSmartPointer<vtkPolyData> m_Points;
vtkSmartPointer<vtkActor2D> m_LabelsActor;
vtkSmartPointer<vtkIntArray> m_Sizes;
vtkSmartPointer<vtkStringArray> m_Labels;
vtkSmartPointer<vtkLabelPlacementMapper> m_LabelMapper;
vtkSmartPointer<vtkPointSetToLabelHierarchy> m_PointSetToLabelHierarchyFilter;
/** \brief Timestamp of last update of stored data. */
itk::TimeStamp m_LastUpdateTime;
/** \brief Default constructor of the local storage. */
LocalStorage();
/** \brief Default deconstructor of the local storage. */
~LocalStorage();
};
mitkClassMacro(LabelAnnotation3D, mitk::VtkAnnotation3D);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** \brief Set the vector of labels that are shown to each corresponding point3D. The size has to be equal to the
provided LabelCoordinates. */
void SetLabelVector(const std::vector<std::string> &LabelVector);
/** \brief Optional: Provide a vector of priorities. The labels with higher priorities will be visible in lower LOD
*/
void SetPriorityVector(const std::vector<int> &PriorityVector);
/** \brief Coordinates of the labels */
void SetLabelCoordinates(itk::SmartPointer<PointSet> LabelCoordinates);
void PointSetModified(const itk::Object *, const itk::EventObject &);
protected:
/** \brief The LocalStorageHandler holds all LocalStorages for the render windows. */
mutable mitk::LocalStorageHandler<LocalStorage> m_LSH;
vtkProp *GetVtkProp(BaseRenderer *renderer) const override;
void UpdateVtkAnnotation(mitk::BaseRenderer *renderer) override;
/** \brief explicit constructor which disallows implicit conversions */
explicit LabelAnnotation3D();
/** \brief virtual destructor in order to derive from this class */
~LabelAnnotation3D() override;
private:
/** \brief The char arrays in this vector are displayed at the corresponding coordinates.*/
std::vector<std::string> m_LabelVector;
- /** \brief values in this array set a priority to each label. Higher priority labels are not covert by labels with
+ /** \brief values in this array set a priority to each label. Higher priority labels are not convert by labels with
* lower priority.*/
std::vector<int> m_PriorityVector;
/** \brief The coordinates of the labels. Indices must match the labelVector and the priorityVector.*/
itk::SmartPointer<PointSet> m_LabelCoordinates;
unsigned long m_PointSetModifiedObserverTag;
/** \brief copy constructor */
LabelAnnotation3D(const LabelAnnotation3D &);
/** \brief assignment operator */
LabelAnnotation3D &operator=(const LabelAnnotation3D &);
};
} // namespace mitk
#endif
diff --git a/Modules/Annotation/test/mitkAnnotationTest.cpp b/Modules/Annotation/test/mitkAnnotationTest.cpp
index b2e19cc085..7de6db0b26 100644
--- a/Modules/Annotation/test/mitkAnnotationTest.cpp
+++ b/Modules/Annotation/test/mitkAnnotationTest.cpp
@@ -1,55 +1,55 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkIOUtil.h>
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <usGetModuleContext.h>
#include <usModuleContext.h>
#include <usServiceReference.h>
#include "mitkManualPlacementAnnotationRenderer.h"
#include "mitkLayoutAnnotationRenderer.h"
class mitkAnnotationTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkAnnotationTestSuite);
MITK_TEST(AnnotationUtilsTest);
CPPUNIT_TEST_SUITE_END();
private:
public:
void setUp() override {}
void AnnotationUtilsTest()
{
mitk::ManualPlacementAnnotationRenderer *ap1_test1 = mitk::ManualPlacementAnnotationRenderer::GetAnnotationRenderer("test1");
CPPUNIT_ASSERT_MESSAGE("Testing availability of ManualPlacementAnnotationRenderer service", ap1_test1);
mitk::ManualPlacementAnnotationRenderer *ap2_test1 = mitk::ManualPlacementAnnotationRenderer::GetAnnotationRenderer("test1");
- CPPUNIT_ASSERT_MESSAGE("Testing if ManualPlacementAnnotationRenderer of same kind stays avaliable", ap1_test1 == ap2_test1);
+ CPPUNIT_ASSERT_MESSAGE("Testing if ManualPlacementAnnotationRenderer of same kind stays available", ap1_test1 == ap2_test1);
mitk::ManualPlacementAnnotationRenderer *ap1_test2 = mitk::ManualPlacementAnnotationRenderer::GetAnnotationRenderer("test2");
CPPUNIT_ASSERT_MESSAGE("Testing if new instance can be created by using different ID", ap1_test2 != ap1_test1);
mitk::LayoutAnnotationRenderer *ol1_test1 = mitk::LayoutAnnotationRenderer::GetAnnotationRenderer("test1");
CPPUNIT_ASSERT_MESSAGE("Testing availability of LayoutAnnotationRenderer service", ol1_test1);
mitk::LayoutAnnotationRenderer *ol2_test1 = mitk::LayoutAnnotationRenderer::GetAnnotationRenderer("test1");
- CPPUNIT_ASSERT_MESSAGE("Testing if LayoutAnnotationRenderer of same kind stays avaliable", ol2_test1 == ol1_test1);
+ CPPUNIT_ASSERT_MESSAGE("Testing if LayoutAnnotationRenderer of same kind stays available", ol2_test1 == ol1_test1);
mitk::LayoutAnnotationRenderer *ol1_test2 = mitk::LayoutAnnotationRenderer::GetAnnotationRenderer("test2");
CPPUNIT_ASSERT_MESSAGE("Testing if new instance can be created by using different ID", ol1_test2 != ol1_test1);
CPPUNIT_ASSERT_MESSAGE(
"Testing if LayoutAnnotationRenderer and ManualPlacementAnnotationRenderer services are different",
(mitk::AbstractAnnotationRenderer *)ol1_test1 != (mitk::AbstractAnnotationRenderer *)ap1_test1);
}
void AnnotationTest() {}
};
MITK_TEST_SUITE_REGISTRATION(mitkAnnotation)
diff --git a/Modules/AppUtil/include/mitkBaseApplication.h b/Modules/AppUtil/include/mitkBaseApplication.h
index c4fe0e9116..8e51fe1316 100644
--- a/Modules/AppUtil/include/mitkBaseApplication.h
+++ b/Modules/AppUtil/include/mitkBaseApplication.h
@@ -1,320 +1,320 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkBaseApplication_h
#define mitkBaseApplication_h
#include <MitkAppUtilExports.h>
#include <Poco/Util/Application.h>
#include <QString>
#include <QVariant>
class ctkPluginContext;
class ctkPluginFramework;
class QCoreApplication;
class QTranslator;
namespace mitk
{
/**
* A utility class for starting BlueBerry applications.
*
* In the simplest case, create an instance of this class and call run().
* This will launch a CTK plugin framework instance and execute the
* default application registered by a plug-in via the
* org.blueberry.osgi.applications extension point.
*
* This class contains many convenience methods to:
* - Put the application in <em>safe mode</em> which catches unhandled
* exceptions thrown in the Qt event loop and displays an error
* message.
* - Put the application in <em>single mode</em> which by default
* sends the command line arguments to an already running instance
* of the same application instead of creating a second instance.
* - Add a list of library names which should be pre-loaded at
* application start-up, e.g. to speed up the initial launch during
* the caching process of the plug-in meta-data.
* - Set a custom provisioning file to start a specific set of CTK
* plug-ins during application start-up.
* - Set and get CTK plugin framework properties
*
* The behavior can further be customized by deriving from BaseApplication
* and overriding specific methods, such as:
* - initializeLibraryPaths() to add specific library / plugin search paths
* - defineOptions(Poco::Util::OptionSet&) to define a custom set of
* command line options
* - getQApplication() to provide a custom QCoreApplication instance
*
* A simple but complete example:
* \code
* #include <mitkBaseApplication.h>
*
* int main(int argc, char* argv[])
* {
* mitk::BaseApplication app(argc, argv);
* app.setApplicationName("MyApp");
* app.setOrganizationName("MyOrganization");
*
* // Run the workbench
* return app.run();
* }
* \endcode
*/
class MITKAPPUTIL_EXPORT BaseApplication : public Poco::Util::Application
{
public:
// Command line arguments
static const QString ARG_APPLICATION;
static const QString ARG_CLEAN;
static const QString ARG_CONSOLELOG;
static const QString ARG_DEBUG;
static const QString ARG_FORCE_PLUGIN_INSTALL;
static const QString ARG_HOME;
static const QString ARG_NEWINSTANCE;
static const QString ARG_NO_LAZY_REGISTRY_CACHE_LOADING;
static const QString ARG_NO_REGISTRY_CACHE;
static const QString ARG_PLUGIN_CACHE;
static const QString ARG_PLUGIN_DIRS;
static const QString ARG_PRELOAD_LIBRARY;
static const QString ARG_PRODUCT;
static const QString ARG_PROVISIONING;
static const QString ARG_REGISTRY_MULTI_LANGUAGE;
static const QString ARG_SPLASH_IMAGE;
static const QString ARG_STORAGE_DIR;
static const QString ARG_XARGS;
static const QString ARG_LOG_QT_MESSAGES;
static const QString ARG_SEGMENTATION_LABELSET_PRESET;
static const QString ARG_SEGMENTATION_LABEL_SUGGESTIONS;
// BlueBerry specific plugin framework properties
static const QString PROP_APPLICATION;
static const QString PROP_FORCE_PLUGIN_INSTALL;
static const QString PROP_NEWINSTANCE;
static const QString PROP_NO_LAZY_REGISTRY_CACHE_LOADING;
static const QString PROP_NO_REGISTRY_CACHE;
static const QString PROP_PRODUCT;
static const QString PROP_REGISTRY_MULTI_LANGUAGE;
BaseApplication(int argc, char **argv);
~BaseApplication() override;
/**
* Initialize the Qt library such that a QCoreApplication
* instance is available and e.g. Qt widgets can be created.
*
* This is usually not called directly by the user.
*/
void initializeQt();
/**
* Launches the BlueBerry framework and runs the default application
* or the one specified in the PROP_APPLICATION framework property.
*
* @return The return code of the application after it was shut down.
*/
int run() override;
void printHelp(const std::string &name, const std::string &value);
/**
* Set the application name. Same as QCoreApplication::setApplicationName.
* @param name The application name.
*/
void setApplicationName(const QString &name);
QString getApplicationName() const;
/**
* Set the organization name. Same as QCoreApplication::setOrganizationName.
* @param name The organization name.
*/
void setOrganizationName(const QString &name);
QString getOrganizationName() const;
/**
* Set the organization domain. Same as QCoreApplication::setOrganizationDomain.
* @param name The organization domain.
*/
void setOrganizationDomain(const QString &name);
QString getOrganizationDomain() const;
/**
* Put the application in single mode, which by default only allows
* a single instance of the application to be created.
*
* Calling this method after run() has been called has no effect.
*
* @param singleMode
*/
void setSingleMode(bool singleMode);
bool getSingleMode() const;
/**
* Put the application in safe mode, catching exceptions from the
* Qt event loop.
*
* @param safeMode
*/
void setSafeMode(bool safeMode);
bool getSafeMode() const;
/**
- * Set a list of library names or absoulte file paths
+ * Set a list of library names or absolute file paths
* which should be loaded at application start-up. The name
* and file path may contain a library version appended at the
* end and separated by a '$' charactger.
*
* For example <code>liborg_mitk_gui_qt_common$1.0</code>.
* Platform specific suffixes are appended automatically.
*
* @param libraryBaseNames A list of library base names.
*/
void setPreloadLibraries(const QStringList &libraryBaseNames);
/**
* Get the list of library base names which should be pre-loaded.
*
* @return A list of pre-loaded libraries.
*/
QStringList getPreloadLibraries() const;
/**
* Set the path to the provisioning file.
*
* By default a provisioning file located in the same directory
* as the executable and named \<executable\>.provisioning
* is loaded if it exists. To disable parsing of provisioning
* files, use an empty string as the argument. Use a
* null QString (\c QString::null ) to reset to the
* default behaviour.
*
* @param filePath An absolute file path to the provisioning file.
*/
void setProvisioningFilePath(const QString &filePath);
/**
* Get the file path to the provisioning file.
* @return The provisioning file path.
*/
QString getProvisioningFilePath() const;
void setProperty(const QString &property, const QVariant &value);
QVariant getProperty(const QString &property) const;
void installTranslator(QTranslator*);
bool isRunning();
void sendMessage(const QByteArray);
protected:
void initialize(Poco::Util::Application &self) override;
void uninitialize() override;
int getArgc() const;
char **getArgv() const;
/**
* Get the framework storage directory for the CTK plugin
* framework. This method is called in the initialize(Poco::Util::Application&)
* method. It must not be called without a QCoreApplications instance.
*
* @return The CTK Plugin Framework storage directory.
*/
virtual QString getCTKFrameworkStorageDir() const;
/**
* Initialize the CppMicroServices library.
*
* The default implementation set the CppMicroServices storage
* path to the current ctkPluginConstants::FRAMEWORK_STORAGE property
* value.
*
* This method is called in the initialize(Poco::Util::Application&)
* after the CTK Plugin Framework storage directory property
* was set.
*/
virtual void initializeCppMicroServices();
/**
* Get the QCoreApplication object.
*
* This method is called in the initialize(Poco::Util::Application&)
* method and must create a QCoreApplication instance if the
* global qApp variable is not initialized yet.
*
* @return The current QCoreApplication instance. This method
* never returns null.
*/
virtual QCoreApplication *getQApplication() const;
/**
* Add plugin library search paths to the CTK Plugin Framework.
*
* This method is called in the nitialize(Poco::Util::Application&)
* method after getQApplication() was called.
*/
virtual void initializeLibraryPaths();
/**
* Runs the application for which the platform was started. The platform
* must be running.
* <p>
* The given argument is passed to the application being run. If it is an invalid QVariant
* then the command line arguments used in starting the platform, and not consumed
* by the platform code, are passed to the application as a <code>QStringList</code>.
* </p>
* @param args the argument passed to the application. May be <code>invalid</code>
* @return the result of running the application
* @throws std::exception if anything goes wrong
*/
int main(const std::vector<std::string> &args) override;
/**
* Define command line arguments
* @param options
*/
void defineOptions(Poco::Util::OptionSet &options) override;
QSharedPointer<ctkPluginFramework> getFramework() const;
ctkPluginContext *getFrameworkContext() const;
/**
* Get the initial properties for the CTK plugin framework.
*
* The returned map contains the initial framework properties for
* initializing the CTK plugin framework. The value of specific
* properties may change at runtime and differ from the initial
* value.
*
* @return The initial CTK Plugin Framework properties.
*/
QHash<QString, QVariant> getFrameworkProperties() const;
/*
* Initialize and display the splash screen if an image filename is given
*
*/
void initializeSplashScreen(QCoreApplication * application) const;
private:
struct Impl;
Impl* d;
};
}
#endif
diff --git a/Modules/BasicImageProcessing/documentation/UserManual/mitkBasicImageProcessingMiniAppsPortalPage.dox b/Modules/BasicImageProcessing/documentation/UserManual/mitkBasicImageProcessingMiniAppsPortalPage.dox
index c78a2e14d3..681be03b72 100644
--- a/Modules/BasicImageProcessing/documentation/UserManual/mitkBasicImageProcessingMiniAppsPortalPage.dox
+++ b/Modules/BasicImageProcessing/documentation/UserManual/mitkBasicImageProcessingMiniAppsPortalPage.dox
@@ -1,45 +1,45 @@
/**
\page mitkBasicImageProcessingMiniAppsPortalPage MITK Basic Image Processing Mini Apps
\tableofcontents
-The Basic Image Processing Mini Apps bundle the functionality that is commonly neeeded for the processing of medical images. As all other MiniApps, they follow the <a href="https://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation">Slicer Execution Model</a> in describing themselves via xml. You can simply obtain a description by calling the MiniApp without any parameter. If the MiniApp is calles with the option "--xml" a XML description of all possible parameter is added.
+The Basic Image Processing Mini Apps bundle the functionality that is commonly needed for the processing of medical images. As all other MiniApps, they follow the <a href="https://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation">Slicer Execution Model</a> in describing themselves via xml. You can simply obtain a description by calling the MiniApp without any parameter. If the MiniApp is calles with the option "--xml" a XML description of all possible parameter is added.
\section bipmasec1 Description of Mini Apps
\subsection bipmasub1 mitkFileConverter
Allows to convert a file from one type to another file type, for example to convert an image, saved in the nifti format to an image saved in the .nrrd format.
\subsection bipmasub2 mitkForwardWavelet
Calculates the forward wavelet transformation of an image. The output will consist of multiple images, which will be saved in the format \<output\>%id%\<output-extension\>, where \<output\> and \<output-extension\> are specified by the user and %id% is a consecutive number.
\subsection bipmasub3 mitkImageAndValueArithmetic
Mathematical operations with two operants, the individual voxels of the image and a specified floating point value. By default, the floating point value is the right operand.
\subsection bipmasub4 mitkImageTypeCovnerter
-Convert the data fromat that is used to save a voxel of an image.
+Convert the data format that is used to save a voxel of an image.
\subsection bipmasub5 mitkLaplacianOfGaussian
Calculate the Laplacian of Gaussian of an image with the specified sigma value.
\subsection bipmasub6 mitkMaskOutlierFiltering
Can be used to clean an segmentation. The mean and standard deviation of the intensities which are masked is calculated and then all mask voxels are removed that cover image voxels which are not within a 3 sigma range.
\subsection bipmasub7 mitkMaskRangeBasedFiltering
Removing all voxels from a mask that cover image voxels which are outside of a given range. The range can be either specified by a lower limit, a upper limit, or both at the same time.
\subsection bipmasub8 mitkMultiResolutionPyramid
Calculate a Multi-Resolution Pyramid of the given image. The resolution is reduced by factor 2 for each step.
\subsection bipmasub9 mitkResampleImage
Resample a mask to a new spacing
\subsection bipmasub10 mitkResampleMask
Similar to mitkResampleImage, but specificly tailored to resampling a mask.
\subsection bipmasub11 mitkSingleImageArithmetic
Applies single operand mathematical operations to each voxel of an image
\subsection bipmasub12 mitkTwoImageArithmetic
Applied two operand mathematical operations to each voxels of two images.
*/
diff --git a/Modules/BoundingShape/include/mitkBoundingShapeCropper.h b/Modules/BoundingShape/include/mitkBoundingShapeCropper.h
index 695b52ab03..c99a4c4843 100644
--- a/Modules/BoundingShape/include/mitkBoundingShapeCropper.h
+++ b/Modules/BoundingShape/include/mitkBoundingShapeCropper.h
@@ -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.
============================================================================*/
#ifndef mitkBoundingShapeCropper_h
#define mitkBoundingShapeCropper_h
#include "MitkBoundingShapeExports.h"
#include "mitkBoundingShapeCropper.h"
#include "mitkCommon.h"
#include "mitkGeometryData.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageTimeSelector.h"
#include "mitkImageToImageFilter.h"
#include "itkImage.h"
namespace mitk
{
/** Documentation
* @brief Crops or masks an Boundingbox defined by GeometryData out of an mitk Image
*
* Input Parameters are a mitk::GeometryData and an mitk::Image
* Masking: Pixel on the outside of the bounding box will have a pixelvalue of m_OutsideValue
* Cropping: Output image has the same size as the bounding box
*/
//## @ingroup Process
class MITKBOUNDINGSHAPE_EXPORT BoundingShapeCropper : public ImageToImageFilter
{
public:
mitkClassMacro(BoundingShapeCropper, ImageToImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* @brief Set geometry of the bounding object
*/
void SetGeometry(const mitk::GeometryData *geometry);
/**
* @brief Get geometry of the bounding object
*/
// const mitk::GeometryData* GetGeometryData() const;
/**
* @brief Sets and Gets the outside value for masking
*/
itkSetMacro(OutsideValue, ScalarType);
itkGetMacro(OutsideValue, ScalarType);
/**
* @brief Sets and Gets whether a masking or cropping needs to be performed
*/
itkSetMacro(UseWholeInputRegion, bool);
itkGetMacro(UseWholeInputRegion, bool);
/**
- * @brief Sets and Gets the current timestep for images with 4 dimensons
+ * @brief Sets and Gets the current timestep for images with 4 dimensions
*/
itkSetMacro(CurrentTimeStep, ScalarType);
itkGetMacro(CurrentTimeStep, ScalarType);
/**
*@brief Sets and Gets whether only one timestep is cropped / masked
*/
itkSetMacro(UseCropTimeStepOnly, bool);
itkGetMacro(UseCropTimeStepOnly, bool);
protected:
BoundingShapeCropper();
~BoundingShapeCropper() override;
virtual const PixelType GetOutputPixelType();
/**
* @brief Reimplemented from ImageToImageFilter
*/
void GenerateInputRequestedRegion() override;
void GenerateOutputInformation() override;
void GenerateData() override;
/**
* @brief Template Function for cropping and masking images with scalar pixel type
*/
template <typename TPixel, unsigned int VImageDimension>
void CutImage(itk::Image<TPixel, VImageDimension> *inputItkImage, int timeStep);
/**
*@brief Process the image and create the output
**/
virtual void ComputeData(mitk::Image *input3D, int boTimeStep);
// virtual void ComputeData(mitk::LabelSetImage* image, int boTimeStep);
private:
/**
*@brief GeometryData Type to capsulate all necessary components of the bounding object
**/
mitk::GeometryData::Pointer m_Geometry;
/**
* @brief scalar value for outside pixels (default: 0)
*/
ScalarType m_OutsideValue;
/**
* @brief Use m_UseCropTimeStepOnly for only cropping a single time step(default: \a false)
*/
bool m_UseCropTimeStepOnly;
/**
* @brief Current time step displayed
*/
int m_CurrentTimeStep;
/**
* @brief Use m_UseWholeInputRegion for deciding whether a cropping or masking will be performed
*/
bool m_UseWholeInputRegion;
/**
* @brief Select single input image in a timeseries
*/
mitk::ImageTimeSelector::Pointer m_InputTimeSelector;
/**
* @brief Select single output image in a timeseries
*/
mitk::ImageTimeSelector::Pointer m_OutputTimeSelector;
/**
* @brief Region of input needed for cutting
*/
typedef itk::ImageRegion<5> RegionType;
mitk::SlicedData::RegionType m_InputRequestedRegion;
/**
* @brief Time when Header was last initialized
**/
itk::TimeStamp m_TimeOfHeaderInitialization;
};
} // namespace mitk
#endif
diff --git a/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp
index d092bb6d5d..9ccb154549 100644
--- a/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp
+++ b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp
@@ -1,346 +1,346 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkBoundingShapeCropper.h"
#include "mitkGeometry3D.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageCast.h"
#include "mitkImageToItk.h"
#include "mitkStatusBar.h"
#include "mitkTimeHelper.h"
#include <cmath>
#include "vtkMatrix4x4.h"
#include "vtkSmartPointer.h"
#include "vtkTransform.h"
#include "itkImageRegionIteratorWithIndex.h"
#include <itkImageIOBase.h>
#include <itkImageRegionConstIterator.h>
#include <itkRGBAPixel.h>
#include <itkRGBPixel.h>
namespace mitk
{
BoundingShapeCropper::BoundingShapeCropper()
: m_Geometry(nullptr),
m_OutsideValue(0),
m_UseCropTimeStepOnly(false),
m_CurrentTimeStep(0),
m_UseWholeInputRegion(false),
m_InputTimeSelector(mitk::ImageTimeSelector::New()),
m_OutputTimeSelector(mitk::ImageTimeSelector::New())
{
this->SetNumberOfIndexedInputs(2);
this->SetNumberOfRequiredInputs(2);
}
BoundingShapeCropper::~BoundingShapeCropper() {}
template <typename TPixel, unsigned int VImageDimension>
void BoundingShapeCropper::CutImage(itk::Image<TPixel, VImageDimension> *inputItkImage, int timeStep)
{
MITK_INFO << "Scalar Pixeltype" << std::endl;
typedef TPixel TOutputPixel;
typedef itk::Image<TPixel, VImageDimension> ItkInputImageType;
typedef itk::Image<TOutputPixel, VImageDimension> ItkOutputImageType;
typedef typename itk::ImageBase<VImageDimension>::RegionType ItkRegionType;
typedef itk::ImageRegionIteratorWithIndex<ItkInputImageType> ItkInputImageIteratorType;
typedef itk::ImageRegionIteratorWithIndex<ItkOutputImageType> ItkOutputImageIteratorType;
TOutputPixel outsideValue = this->GetOutsideValue();
// currently 0 if not set in advance
// TODO: change default value to itk::NumericTraits<TOutputPixel>::min();
if (this->m_Geometry.IsNull())
return;
if (inputItkImage == nullptr)
{
mitk::StatusBar::GetInstance()->DisplayErrorText(
"An internal error occurred. Can't convert Image. Please report to bugs@mitk.org");
std::cout << " image is nullptr...returning" << std::endl;
return;
}
// first convert the index
typename ItkRegionType::IndexType::IndexValueType tmpIndex[3];
itk2vtk(this->m_InputRequestedRegion.GetIndex(), tmpIndex);
typename ItkRegionType::IndexType index;
index.SetIndex(tmpIndex);
// then convert the size
typename ItkRegionType::SizeType::SizeValueType tmpSize[3];
itk2vtk(this->m_InputRequestedRegion.GetSize(), tmpSize);
typename ItkRegionType::SizeType size;
size.SetSize(tmpSize);
// create the ITK-image-region out of index and size
ItkRegionType inputRegionOfInterest(index, size);
// Get access to the MITK output image via an ITK image
typename mitk::ImageToItk<ItkOutputImageType>::Pointer outputimagetoitk =
mitk::ImageToItk<ItkOutputImageType>::New();
outputimagetoitk->SetInput(this->m_OutputTimeSelector->GetOutput());
outputimagetoitk->Update();
typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput();
// create the iterators
ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest);
ItkOutputImageIteratorType outputIt(outputItkImage, outputItkImage->GetLargestPossibleRegion());
// Cut the boundingbox out of the image by iterating through all images
// TODO: use more efficient method by using the contour instead off all single pixels
mitk::Point3D p;
mitk::BaseGeometry *inputGeometry = this->GetInput()->GetGeometry(timeStep);
// calculates translation based on offset+extent not on the transformation matrix
// NOTE: center of the box is
vtkSmartPointer<vtkMatrix4x4> imageTransform = this->m_Geometry->GetGeometry()->GetVtkTransform()->GetMatrix();
Point3D center = this->m_Geometry->GetGeometry()->GetCenter();
auto translation = vtkSmartPointer<vtkTransform>::New();
translation->Translate(center[0] - imageTransform->GetElement(0, 3),
center[1] - imageTransform->GetElement(1, 3),
center[2] - imageTransform->GetElement(2, 3));
auto transform = vtkSmartPointer<vtkTransform>::New();
transform->SetMatrix(imageTransform);
transform->PostMultiply();
transform->Concatenate(translation);
transform->Update();
mitk::Vector3D extent;
for (unsigned int i = 0; i < 3; ++i)
extent[i] = (this->m_Geometry->GetGeometry()->GetExtent(i));
for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt)
{
vtk2itk(inputIt.GetIndex(), p);
inputGeometry->IndexToWorld(p, p);
ScalarType p2[4];
p2[0] = p[0];
p2[1] = p[1];
p2[2] = p[2];
p2[3] = 1;
// transform point from world to object coordinates
transform->GetInverse()->TransformPoint(p2, p2);
// check if the world point is within bounds
bool isInside = (p2[0] >= (-extent[0] / 2.0)) && (p2[0] <= (extent[0] / 2.0)) && (p2[1] >= (-extent[1] / 2.0)) &&
(p2[1] <= (extent[1] / 2.0)) && (p2[2] >= (-extent[2] / 2.0)) && (p2[2] <= (extent[2] / 2.0));
if ((!this->m_UseCropTimeStepOnly && isInside) ||
(this->m_UseCropTimeStepOnly && timeStep == this->m_CurrentTimeStep && isInside))
{
outputIt.Set((TOutputPixel)inputIt.Value());
}
else
{
outputIt.Set(outsideValue);
}
}
}
void BoundingShapeCropper::SetGeometry(const mitk::GeometryData *geometry)
{
m_Geometry = const_cast<mitk::GeometryData *>(geometry);
// Process object is not const-correct so the const_cast is required here
this->ProcessObject::SetNthInput(1, const_cast<mitk::GeometryData *>(geometry));
}
// const mitk::GeometryData* BoundingShapeCropper::GetGeometryData() const
//{
// return m_Geometry.GetPointer();
//}
const mitk::PixelType BoundingShapeCropper::GetOutputPixelType() { return this->GetInput()->GetPixelType(); }
void BoundingShapeCropper::GenerateInputRequestedRegion()
{
mitk::Image *output = this->GetOutput();
if ((output->IsInitialized() == false) || (m_Geometry.IsNull()) ||
(m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0))
return;
GenerateTimeInInputRegion(output, this->GetInput());
}
void BoundingShapeCropper::GenerateOutputInformation()
{
// Set Cropping region
mitk::Image::Pointer output = this->GetOutput();
if ((output->IsInitialized()) && (output->GetPipelineMTime() <= m_TimeOfHeaderInitialization.GetMTime()))
return;
mitk::Image::Pointer input = this->GetInput();
if (input.IsNull())
{
mitkThrow() << "Input is not a mitk::Image";
}
itkDebugMacro(<< "GenerateOutputInformation()");
unsigned int dimension = input->GetDimension();
if (dimension < 3)
{
mitkThrow() << "ImageCropper cannot handle 1D or 2D Objects.";
}
if ((m_Geometry.IsNull()) || (m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0))
return;
mitk::BaseGeometry *bsGeometry = m_Geometry->GetGeometry();
mitk::BaseGeometry *inputImageGeometry = input->GetSlicedGeometry();
// calculate bounding box
mitk::BoundingBox::Pointer bsBoxRelativeToImage =
bsGeometry->CalculateBoundingBoxRelativeToTransform(inputImageGeometry->GetIndexToWorldTransform());
// pre-initialize input-requested-region to largest-possible-region
m_InputRequestedRegion = input->GetLargestPossibleRegion();
// build region out of bounding-box of index and size of the bounding box
mitk::SlicedData::IndexType index = m_InputRequestedRegion.GetIndex(); // init times and channels
mitk::BoundingBox::PointType min = bsBoxRelativeToImage->GetMinimum();
mitk::SlicedData::SizeType size = m_InputRequestedRegion.GetSize(); // init times and channels
mitk::BoundingBox::PointType max = bsBoxRelativeToImage->GetMaximum();
mitk::Point<BoundingBox::PointType::CoordRepType, 5> maxCorrected;
mitk::Point<BoundingBox::PointType::CoordRepType, 5> minCorrected;
for (unsigned int i = 0; i < 3; i++)
{
maxCorrected[i] = max[i];
minCorrected[i] = min[i];
}
maxCorrected[3] = input->GetDimensions()[3];
maxCorrected[4] = 0;
minCorrected[3] = 0;
minCorrected[4] = 0;
for (unsigned int i = 0; i < dimension; i++)
{
index[i] = (mitk::SlicedData::IndexType::IndexValueType)(std::ceil(minCorrected[i]));
size[i] = (mitk::SlicedData::SizeType::SizeValueType)(std::ceil(maxCorrected[i]) - index[i]);
}
mitk::SlicedData::RegionType bsRegion(index, size);
if (m_UseWholeInputRegion == false)
{
// crop input-requested-region with region of bounding-object
if (m_InputRequestedRegion.Crop(bsRegion) == false)
{
// crop not possible => do nothing: set time size to 0.
size.Fill(0);
m_InputRequestedRegion.SetSize(size);
bsRegion.SetSize(size);
mitkThrow() << "No overlap of the image and the cropping object.";
}
}
// initialize output image
auto dimensions = new unsigned int[dimension];
if (dimension > 3 && !this->GetUseCropTimeStepOnly())
memcpy(dimensions + 3, input->GetDimensions() + 3, (dimension - 3) * sizeof(unsigned int));
else
dimension = 3; // set timeStep to zero if GetUseCropTimeStepOnly is true
itk2vtk(m_InputRequestedRegion.GetSize(), dimensions);
output->Initialize(mitk::PixelType(GetOutputPixelType()), dimension, dimensions);
delete[] dimensions;
// Apply transform of the input image to the new generated output image
mitk::BoundingShapeCropper::RegionType outputRegion = output->GetRequestedRegion();
m_TimeOfHeaderInitialization.Modified();
}
void BoundingShapeCropper::ComputeData(mitk::Image *image, int boTimeStep)
{
// examine dimension and pixeltype
if ((image == nullptr) || (image->GetDimension() > 4) || (image->GetDimension() <= 2))
{
MITK_ERROR << "Filter cannot handle dimensions less than 2 and greater than 4" << std::endl;
itkExceptionMacro("Filter cannot handle dimensions less than 2 and greater than 4");
return;
}
AccessByItk_1(image, CutImage, boTimeStep);
}
void BoundingShapeCropper::GenerateData()
{
MITK_INFO << "Generate Data" << std::endl;
mitk::Image::ConstPointer input = this->GetInput();
mitk::Image::Pointer output = this->GetOutput();
if (input.IsNull())
return;
if ((output->IsInitialized() == false) || (m_Geometry.IsNull()) ||
(m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0))
return;
m_InputTimeSelector->SetInput(input);
m_OutputTimeSelector->SetInput(this->GetOutput());
mitk::BoundingShapeCropper::RegionType outputRegion = output->GetRequestedRegion();
mitk::BaseGeometry *inputImageGeometry = input->GetSlicedGeometry();
- // iterate over all time steps and perform cropping or masking on all or a specific timestep (perviously specified
+ // iterate over all time steps and perform cropping or masking on all or a specific timestep (previously specified
// by UseCurrentTimeStepOnly)
int tstart = outputRegion.GetIndex(3);
int tmax = tstart + outputRegion.GetSize(3);
if (this->m_UseCropTimeStepOnly)
{
mitk::SlicedGeometry3D *slicedGeometry = output->GetSlicedGeometry(tstart);
auto indexToWorldTransform = AffineTransform3D::New();
indexToWorldTransform->SetParameters(input->GetSlicedGeometry(tstart)->GetIndexToWorldTransform()->GetParameters());
slicedGeometry->SetIndexToWorldTransform(indexToWorldTransform);
const mitk::SlicedData::IndexType &start = m_InputRequestedRegion.GetIndex();
mitk::Point3D origin;
vtk2itk(start, origin);
inputImageGeometry->IndexToWorld(origin, origin);
slicedGeometry->SetOrigin(origin);
m_InputTimeSelector->SetTimeNr(m_CurrentTimeStep);
m_InputTimeSelector->UpdateLargestPossibleRegion();
m_OutputTimeSelector->SetTimeNr(tstart);
m_OutputTimeSelector->UpdateLargestPossibleRegion();
ComputeData(m_InputTimeSelector->GetOutput(), m_CurrentTimeStep);
}
else
{
int t;
for (t = tstart; t < tmax; ++t)
{
mitk::SlicedGeometry3D *slicedGeometry = output->GetSlicedGeometry(t);
auto indexToWorldTransform = AffineTransform3D::New();
indexToWorldTransform->SetParameters(input->GetSlicedGeometry(t)->GetIndexToWorldTransform()->GetParameters());
slicedGeometry->SetIndexToWorldTransform(indexToWorldTransform);
const mitk::SlicedData::IndexType &start = m_InputRequestedRegion.GetIndex();
mitk::Point3D origin;
vtk2itk(start, origin);
inputImageGeometry->IndexToWorld(origin, origin);
slicedGeometry->SetOrigin(origin);
m_InputTimeSelector->SetTimeNr(t);
m_InputTimeSelector->UpdateLargestPossibleRegion();
m_OutputTimeSelector->SetTimeNr(t);
m_OutputTimeSelector->UpdateLargestPossibleRegion();
ComputeData(m_InputTimeSelector->GetOutput(), t);
}
}
m_InputTimeSelector->SetInput(nullptr);
m_OutputTimeSelector->SetInput(nullptr);
m_TimeOfHeaderInitialization.Modified();
}
} // of namespace mitk
diff --git a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp
index 27a7210bb2..5ce2869177 100644
--- a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp
+++ b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp
@@ -1,605 +1,605 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <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() : OriginalInteractionEnabled(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 OriginalInteractionEnabled;
Point3D InitialPickedWorldPoint;
Point3D LastPickedWorldPoint;
Point2D InitialPickedDisplayPoint;
std::vector<Handle> Handles;
Handle ActiveHandle;
Geometry3D::Pointer OriginalGeometry;
bool RotationEnabled;
std::map<us::ServiceReferenceU, mitk::EventConfig> DisplayInteractionConfigs;
};
}
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, 0.0, 0.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->DisableOriginalInteraction();
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->DisableOriginalInteraction();
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->DisableOriginalInteraction(); // disable crosshair interaction and scolling if user is hovering over the object
+ this->DisableOriginalInteraction(); // disable crosshair interaction and scrolling 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->EnableOriginalInteraction(); // enable crosshair interaction and scolling if user is hovering over the object
+ this->EnableOriginalInteraction(); // enable crosshair interaction and scrolling 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
+ 0.0) // only update current position if a movement occurred
{
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 and 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);
EnableOriginalInteraction();
// update rendering
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::BoundingShapeInteractor::EnableOriginalInteraction()
{
// Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools
// in new interaction framework
for (const auto& displayInteractionConfig : m_Impl->DisplayInteractionConfigs)
{
if (displayInteractionConfig.first)
{
auto displayActionEventBroadcast = static_cast<mitk::DisplayActionEventBroadcast *>(
us::GetModuleContext()->GetService<mitk::InteractionEventObserver>(displayInteractionConfig.first));
if (nullptr != displayActionEventBroadcast)
{
// here the regular configuration is loaded again
displayActionEventBroadcast->SetEventConfig(displayInteractionConfig.second);
}
}
}
m_Impl->DisplayInteractionConfigs.clear();
m_Impl->OriginalInteractionEnabled = true;
}
void mitk::BoundingShapeInteractor::DisableOriginalInteraction()
{
// dont deactivate twice, else we will clutter the config list ...
if (false == m_Impl->OriginalInteractionEnabled)
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->DisplayInteractionConfigs.clear();
auto eventObservers = us::GetModuleContext()->GetServiceReferences<mitk::InteractionEventObserver>();
for (const auto& eventObserver : eventObservers)
{
auto *displayActionEventBroadcast = dynamic_cast<mitk::DisplayActionEventBroadcast *>(
us::GetModuleContext()->GetService<mitk::InteractionEventObserver>(eventObserver));
if (nullptr != displayActionEventBroadcast)
{
// remember the original configuration
m_Impl->DisplayInteractionConfigs.insert(std::make_pair(eventObserver, displayActionEventBroadcast->GetEventConfig()));
// here the alternative configuration is loaded
displayActionEventBroadcast->AddEventConfig("DisplayConfigBlockLMB.xml");
}
}
m_Impl->OriginalInteractionEnabled = false;
}
diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp
index 5114a48201..c57fb18949 100644
--- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp
+++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp
@@ -1,462 +1,462 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkBaseProperty.h>
#include <mitkBoundingShapeVtkMapper2D.h>
#include <vtkActor2D.h>
#include <vtkAppendPolyData.h>
#include <vtkCoordinate.h>
#include <vtkMath.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper2D.h>
#include <vtkProperty2D.h>
#include <vtkStripper.h>
#include <vtkTransformFilter.h>
#include <vtkTransformPolyDataFilter.h>
namespace mitk
{
class BoundingShapeVtkMapper2D::Impl
{
public:
Impl()
{
Point3D initialPoint;
initialPoint.Fill(0);
for (int i = 0; i < 6; ++i)
HandlePropertyList.push_back(Handle(initialPoint, i, GetHandleIndices(i)));
}
std::vector<Handle> HandlePropertyList;
mitk::LocalStorageHandler<LocalStorage> LocalStorageHandler;
};
}
mitk::BoundingShapeVtkMapper2D::LocalStorage::LocalStorage()
: m_Actor(vtkSmartPointer<vtkActor>::New()),
m_HandleActor(vtkSmartPointer<vtkActor2D>::New()),
m_SelectedHandleActor(vtkSmartPointer<vtkActor2D>::New()),
m_Mapper(vtkSmartPointer<vtkPolyDataMapper>::New()),
m_HandleMapper(vtkSmartPointer<vtkPolyDataMapper2D>::New()),
m_SelectedHandleMapper(vtkSmartPointer<vtkPolyDataMapper2D>::New()),
m_Cutter(vtkSmartPointer<vtkCutter>::New()),
m_CuttingPlane(vtkSmartPointer<vtkPlane>::New()),
m_LastSliceNumber(0),
m_PropAssembly(vtkSmartPointer<vtkPropAssembly>::New()),
m_ZoomFactor(1.0)
{
m_Actor->SetMapper(m_Mapper);
m_Actor->VisibilityOn();
m_HandleActor->SetMapper(m_HandleMapper);
m_HandleActor->VisibilityOn();
m_SelectedHandleActor->VisibilityOn();
m_SelectedHandleActor->GetProperty()->SetColor(0, 1.0, 0);
m_SelectedHandleActor->SetMapper(m_SelectedHandleMapper);
vtkCoordinate *tcoord = vtkCoordinate::New();
tcoord->SetCoordinateSystemToWorld();
m_SelectedHandleMapper->SetTransformCoordinate(tcoord);
tcoord->Delete();
m_Cutter->SetCutFunction(m_CuttingPlane);
for (int i = 0; i < 6; ++i)
m_Handles.push_back(vtkSmartPointer<vtkCubeSource>::New());
m_PropAssembly->AddPart(m_Actor);
m_PropAssembly->AddPart(m_HandleActor);
m_PropAssembly->VisibilityOn();
}
bool mitk::BoundingShapeVtkMapper2D::LocalStorage::IsUpdateRequired(mitk::BaseRenderer *renderer,
mitk::Mapper *mapper,
mitk::DataNode *dataNode)
{
const mitk::PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
if (m_LastGenerateDataTime < worldGeometry->GetMTime())
return true;
unsigned int sliceNumber = renderer->GetSlice();
if (m_LastSliceNumber != sliceNumber)
return true;
if (mapper && m_LastGenerateDataTime < mapper->GetMTime())
return true;
if (dataNode)
{
if (m_LastGenerateDataTime < dataNode->GetMTime())
return true;
mitk::BaseData *data = dataNode->GetData();
if (data && m_LastGenerateDataTime < data->GetMTime())
return true;
}
return false;
}
mitk::BoundingShapeVtkMapper2D::LocalStorage::~LocalStorage()
{
}
void mitk::BoundingShapeVtkMapper2D::Update(mitk::BaseRenderer *renderer)
{
this->GenerateDataForRenderer(renderer);
}
void mitk::BoundingShapeVtkMapper2D::SetDefaultProperties(DataNode *node, BaseRenderer *renderer, bool overwrite)
{
Superclass::SetDefaultProperties(node, renderer, overwrite);
node->AddProperty("opacity", FloatProperty::New(0.2f), renderer, overwrite);
}
mitk::BoundingShapeVtkMapper2D::BoundingShapeVtkMapper2D() : m_Impl(new Impl)
{
}
mitk::BoundingShapeVtkMapper2D::~BoundingShapeVtkMapper2D()
{
delete m_Impl;
}
void mitk::BoundingShapeVtkMapper2D::GenerateDataForRenderer(BaseRenderer *renderer)
{
const DataNode::Pointer node = GetDataNode();
if (node == nullptr)
return;
LocalStorage *localStorage = m_Impl->LocalStorageHandler.GetLocalStorage(renderer);
// either update if GeometryData was modified or if the zooming was performed
bool needGenerateData = localStorage->IsUpdateRequired(
renderer, this, GetDataNode()); // true; // localStorage->GetLastGenerateDataTime() < node->GetMTime() ||
// localStorage->GetLastGenerateDataTime() < node->GetData()->GetMTime();
// //localStorage->IsGenerateDataRequired(renderer, this, GetDataNode());
double scale = renderer->GetScaleFactorMMPerDisplayUnit();
if (std::abs(scale - localStorage->m_ZoomFactor) > 0.001)
{
localStorage->m_ZoomFactor = scale;
needGenerateData = true;
}
if (needGenerateData)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if (!visible)
{
localStorage->m_Actor->VisibilityOff();
return;
}
GeometryData::Pointer shape = static_cast<GeometryData *>(node->GetData());
if (shape == nullptr)
return;
mitk::BaseGeometry::Pointer geometry = shape->GetGeometry();
mitk::Vector3D spacing = geometry->GetSpacing();
// calculate cornerpoints and extent from geometry with visualization offset
std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true);
Point3D p0 = cornerPoints[0];
Point3D p1 = cornerPoints[1];
Point3D p2 = cornerPoints[2];
Point3D p4 = cornerPoints[4];
Point3D extent;
extent[0] =
sqrt((p0[0] - p4[0]) * (p0[0] - p4[0]) + (p0[1] - p4[1]) * (p0[1] - p4[1]) + (p0[2] - p4[2]) * (p0[2] - p4[2]));
extent[1] =
sqrt((p0[0] - p2[0]) * (p0[0] - p2[0]) + (p0[1] - p2[1]) * (p0[1] - p2[1]) + (p0[2] - p2[2]) * (p0[2] - p2[2]));
extent[2] =
sqrt((p0[0] - p1[0]) * (p0[0] - p1[0]) + (p0[1] - p1[1]) * (p0[1] - p1[1]) + (p0[2] - p1[2]) * (p0[2] - p1[2]));
// calculate center based on half way of the distance between two opposing cornerpoints
mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]);
if (m_Impl->HandlePropertyList.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->HandlePropertyList[0].SetPosition(pointLeft);
m_Impl->HandlePropertyList[1].SetPosition(pointRight);
m_Impl->HandlePropertyList[2].SetPosition(pointTop);
m_Impl->HandlePropertyList[3].SetPosition(pointBottom);
m_Impl->HandlePropertyList[4].SetPosition(pointFront);
m_Impl->HandlePropertyList[5].SetPosition(pointBack);
}
- // caculate face normals
+ // calculate face normals
double cubeFaceNormal0[3], cubeFaceNormal1[3], cubeFaceNormal2[3];
double a[3], b[3];
a[0] = (cornerPoints[5][0] - cornerPoints[6][0]);
a[1] = (cornerPoints[5][1] - cornerPoints[6][1]);
a[2] = (cornerPoints[5][2] - cornerPoints[6][2]);
b[0] = (cornerPoints[5][0] - cornerPoints[4][0]);
b[1] = (cornerPoints[5][1] - cornerPoints[4][1]);
b[2] = (cornerPoints[5][2] - cornerPoints[4][2]);
vtkMath::Cross(a, b, cubeFaceNormal0);
a[0] = (cornerPoints[0][0] - cornerPoints[6][0]);
a[1] = (cornerPoints[0][1] - cornerPoints[6][1]);
a[2] = (cornerPoints[0][2] - cornerPoints[6][2]);
b[0] = (cornerPoints[0][0] - cornerPoints[2][0]);
b[1] = (cornerPoints[0][1] - cornerPoints[2][1]);
b[2] = (cornerPoints[0][2] - cornerPoints[2][2]);
vtkMath::Cross(a, b, cubeFaceNormal1);
a[0] = (cornerPoints[2][0] - cornerPoints[7][0]);
a[1] = (cornerPoints[2][1] - cornerPoints[7][1]);
a[2] = (cornerPoints[2][2] - cornerPoints[7][2]);
b[0] = (cornerPoints[2][0] - cornerPoints[6][0]);
b[1] = (cornerPoints[2][1] - cornerPoints[6][1]);
b[2] = (cornerPoints[2][2] - cornerPoints[6][2]);
vtkMath::Cross(a, b, cubeFaceNormal2);
vtkMath::Normalize(cubeFaceNormal0);
vtkMath::Normalize(cubeFaceNormal1);
vtkMath::Normalize(cubeFaceNormal2);
// create cube for rendering bounding box
auto cube = vtkCubeSource::New();
cube->SetXLength(extent[0] / spacing[0]);
cube->SetYLength(extent[1] / spacing[1]);
cube->SetZLength(extent[2] / spacing[2]);
// calculates translation based on offset+extent not on the transformation matrix
vtkSmartPointer<vtkMatrix4x4> imageTransform = geometry->GetVtkTransform()->GetMatrix();
auto translation = vtkSmartPointer<vtkTransform>::New();
translation->Translate(center[0] - imageTransform->GetElement(0, 3),
center[1] - imageTransform->GetElement(1, 3),
center[2] - imageTransform->GetElement(2, 3));
auto transform = vtkSmartPointer<vtkTransform>::New();
transform->SetMatrix(imageTransform);
transform->PostMultiply();
transform->Concatenate(translation);
transform->Update();
cube->Update();
auto transformFilter = vtkSmartPointer<vtkTransformFilter>::New();
transformFilter->SetInputData(cube->GetOutput());
transformFilter->SetTransform(transform);
transformFilter->Update();
cube->Delete();
vtkSmartPointer<vtkPolyData> polydata = transformFilter->GetPolyDataOutput();
if (polydata == nullptr || (polydata->GetNumberOfPoints() < 1))
{
localStorage->m_Actor->VisibilityOff();
localStorage->m_HandleActor->VisibilityOff();
localStorage->m_SelectedHandleActor->VisibilityOff();
return;
}
// estimate current image plane to decide whether the cube is visible or not
const PlaneGeometry *planeGeometry = renderer->GetCurrentWorldPlaneGeometry();
if ((planeGeometry == nullptr) || (!planeGeometry->IsValid()) || (!planeGeometry->HasReferenceGeometry()))
return;
double origin[3];
origin[0] = planeGeometry->GetOrigin()[0];
origin[1] = planeGeometry->GetOrigin()[1];
origin[2] = planeGeometry->GetOrigin()[2];
double displayPlaneNormal[3];
displayPlaneNormal[0] = planeGeometry->GetNormal()[0];
displayPlaneNormal[1] = planeGeometry->GetNormal()[1];
displayPlaneNormal[2] = planeGeometry->GetNormal()[2];
vtkMath::Normalize(displayPlaneNormal);
localStorage->m_CuttingPlane->SetOrigin(origin);
localStorage->m_CuttingPlane->SetNormal(displayPlaneNormal);
// add cube polydata to local storage
localStorage->m_Cutter->SetInputData(polydata);
localStorage->m_Cutter->SetGenerateCutScalars(1);
localStorage->m_Cutter->Update();
if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_HandleActor))
localStorage->m_PropAssembly->RemovePart(localStorage->m_HandleActor);
if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_Actor))
localStorage->m_PropAssembly->RemovePart(localStorage->m_Actor);
vtkCoordinate *tcoord = vtkCoordinate::New();
tcoord->SetCoordinateSystemToWorld();
localStorage->m_HandleMapper->SetTransformCoordinate(tcoord);
tcoord->Delete();
if (localStorage->m_Cutter->GetOutput()->GetNumberOfPoints() > 0) // if plane is visible in the renderwindow
{
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 = 0.02;
mitk::Point2D displaySize = renderer->GetDisplaySizeInMM();
double handleSize = ((displaySize[0] + displaySize[1]) / 2.0) * initialHandleSize;
auto appendPoly = vtkSmartPointer<vtkAppendPolyData>::New();
unsigned int handleIdx = 0;
// add handles and their assigned properties to the local storage
mitk::IntProperty::Pointer activeHandleId =
dynamic_cast<mitk::IntProperty *>(node->GetProperty("Bounding Shape.Active Handle ID"));
double angle0 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal0)));
if (angle0 > 179.0) angle0 -= 180.0;
double angle1 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal1)));
if (angle1 > 179.0) angle1 -= 180.0;
double angle2 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal2)));
if (angle2 > 179.0) angle2 -= 180.0;
bool visible = false;
bool selected = false;
for (auto& handle : localStorage->m_Handles)
{
Point3D handleCenter = m_Impl->HandlePropertyList[handleIdx].GetPosition();
handle->SetXLength(handleSize);
handle->SetYLength(handleSize);
handle->SetZLength(handleSize);
handle->SetCenter(handleCenter[0], handleCenter[1], handleCenter[2]);
// show handles only if the corresponding face is aligned to the render window
if ( (handleIdx != 0 && handleIdx != 1 && std::abs(angle0) < 0.1) || // handles 0 and 1
(handleIdx != 2 && handleIdx != 3 && std::abs(angle1) < 0.1) || // handles 2 and 3
(handleIdx != 4 && handleIdx != 5 && std::abs(angle2) < 0.1) ) // handles 4 and 5
{
if (activeHandleId == nullptr)
{
appendPoly->AddInputConnection(handle->GetOutputPort());
}
else
{
if ((activeHandleId->GetValue() != m_Impl->HandlePropertyList[handleIdx].GetIndex()))
{
appendPoly->AddInputConnection(handle->GetOutputPort());
}
else
{
handle->Update();
localStorage->m_SelectedHandleMapper->SetInputData(handle->GetOutput());
localStorage->m_SelectedHandleActor->VisibilityOn();
selected = true;
}
}
visible = true;
}
++handleIdx;
}
if (visible)
{
appendPoly->Update();
}
else
{
localStorage->m_HandleActor->VisibilityOff();
localStorage->m_SelectedHandleActor->VisibilityOff();
}
auto stripper = vtkSmartPointer<vtkStripper>::New();
stripper->SetInputData(localStorage->m_Cutter->GetOutput());
stripper->Update();
auto cutPolyData = vtkSmartPointer<vtkPolyData>::New();
cutPolyData->SetPoints(stripper->GetOutput()->GetPoints());
cutPolyData->SetPolys(stripper->GetOutput()->GetLines());
localStorage->m_Actor->GetMapper()->SetInputDataObject(cutPolyData);
this->ApplyColorAndOpacityProperties(renderer, localStorage->m_Actor);
if (activeHandleId != nullptr)
{
localStorage->m_HandleActor->GetProperty()->SetColor(1, 0, 0);
}
else
{
localStorage->m_HandleActor->GetProperty()->SetColor(1, 1, 1);
}
localStorage->m_HandleActor->GetMapper()->SetInputDataObject(appendPoly->GetOutput());
// add parts to the overall storage
localStorage->m_PropAssembly->AddPart(localStorage->m_Actor);
localStorage->m_PropAssembly->AddPart(localStorage->m_HandleActor);
if (selected)
{
localStorage->m_PropAssembly->AddPart(localStorage->m_SelectedHandleActor);
}
localStorage->m_PropAssembly->VisibilityOn();
localStorage->m_Actor->VisibilityOn();
localStorage->m_HandleActor->VisibilityOn();
}
else
{
localStorage->m_PropAssembly->VisibilityOff();
localStorage->m_Actor->VisibilityOff();
localStorage->m_HandleActor->VisibilityOff();
localStorage->m_SelectedHandleActor->VisibilityOff();
localStorage->UpdateGenerateDataTime();
}
localStorage->UpdateGenerateDataTime();
}
}
vtkProp *mitk::BoundingShapeVtkMapper2D::GetVtkProp(BaseRenderer *renderer)
{
return m_Impl->LocalStorageHandler.GetLocalStorage(renderer)->m_PropAssembly;
}
void mitk::BoundingShapeVtkMapper2D::ApplyColorAndOpacityProperties(BaseRenderer *renderer, vtkActor *actor)
{
auto* property = actor->GetProperty();
std::array<float, 3> color = { 1.0, 0.0, 0.0 };
this->GetDataNode()->GetColor(color.data(), renderer);
property->SetColor(color[0], color[1], color[2]);
float opacity = 0.2f;
this->GetDataNode()->GetOpacity(opacity, renderer);
property->SetOpacity(opacity);
}
diff --git a/Modules/CEST/include/mitkCESTImageNormalizationFilter.h b/Modules/CEST/include/mitkCESTImageNormalizationFilter.h
index 8a0dde411f..5ff20f9440 100644
--- a/Modules/CEST/include/mitkCESTImageNormalizationFilter.h
+++ b/Modules/CEST/include/mitkCESTImageNormalizationFilter.h
@@ -1,85 +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.
============================================================================*/
#ifndef mitkCESTImageNormalizationFilter_h
#define mitkCESTImageNormalizationFilter_h
#include <MitkCESTExports.h>
// MITK
#include "mitkImageToImageFilter.h"
namespace mitk
{
/** \brief Normalization filter for CEST images.
*
* This filter can be used to normalize CEST images, it only works with 4D images and assumes that the input
- * mitk::Image has a property called mitk::CustomTagParser::m_OffsetsPropertyName, whith offsets separated by
+ * mitk::Image has a property called mitk::CustomTagParser::m_OffsetsPropertyName, with offsets separated by
* spaces. The number of offsets has to match the number of timesteps.
*
* Each timestep with a corresponding offset greater than 299 or less than -299 will be interpreted as normalization (M0) image.
* If only one M0 image is present normalization will be done by dividing the voxel value by the corresponding
* M0 voxel value. If multiple M0 images are present normalization between any two M0 images will be done by
* dividing by a linear interpolation between the two.
* The M0 images themselves will be removed from the result.
* The output image will have the same 3D geometry as the input image, a time geometry only consisting of non M0 images and a double pixel type.
*/
class MITKCEST_EXPORT CESTImageNormalizationFilter : public ImageToImageFilter
{
public:
mitkClassMacro(CESTImageNormalizationFilter, ImageToImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
protected:
/*!
\brief standard constructor
*/
CESTImageNormalizationFilter();
/*!
\brief standard destructor
*/
~CESTImageNormalizationFilter() override;
/*!
\brief Method generating the output information of this filter (e.g. image dimension, image type, etc.).
The interface ImageToImageFilter requires this implementation. Everything is taken from the input image.
*/
void GenerateOutputInformation() override;
/*!
\brief Method generating the output of this filter. Called in the updated process of the pipeline.
This method generates the normalized output image.
*/
void GenerateData() override;
/** Internal templated method that normalizes across timesteps
*/
template <typename TPixel, unsigned int VImageDimension>
void NormalizeTimeSteps(const itk::Image<TPixel, VImageDimension>* image);
/// Offsets without M0s
std::string m_RealOffsets;
/// non M0 indices
std::vector< unsigned int > m_NonM0Indices;
};
/** This helper function can be used to check if an image was already normalized.
* The function assumes that an image that is not normalized is indicated by the following properties:
* - mitk::Image has a property called mitk::CEST_PROPERTY_NAME_OFFSETS, with offsets separated by spaces.
* - The number of offsets has to match the number of timesteps.
* - At least one of the offsets is a normalization (M0) image. M0 are indicated by offsets greater than 299 or less than -299.
*/
MITKCEST_EXPORT bool IsNotNormalizedCESTImage(const Image* cestImage);
} // END mitk namespace
#endif
diff --git a/Modules/CEST/include/mitkCESTPropertyHelper.h b/Modules/CEST/include/mitkCESTPropertyHelper.h
index 73d34c2e79..e65ff055ec 100644
--- a/Modules/CEST/include/mitkCESTPropertyHelper.h
+++ b/Modules/CEST/include/mitkCESTPropertyHelper.h
@@ -1,60 +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 mitkCESTPropertyHelper_h
#define mitkCESTPropertyHelper_h
#include "mitkIPropertyProvider.h"
#include "mitkIPropertyOwner.h"
#include "MitkCESTExports.h"
namespace mitk
{
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_PREPERATIONTYPE();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_RECOVERYMODE();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_SPOILINGTYPE();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_OFFSETS();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_TREC();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_FREQ();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_PULSEDURATION();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_B1Amplitude();
const std::string MITKCEST_EXPORT CEST_PROPERTY_NAME_DutyCycle();
/**Helper function that gets the CEST B1 amplitude property ("CEST.B1Amplitude") from the passed property provider.
If it is not possible to generate/get the value an mitk::Exception will be thrown.*/
double MITKCEST_EXPORT GetCESTB1Amplitude(const IPropertyProvider* provider);
/**Helper function that gets the CEST frequency property ("CEST.FREQ") from the input image.
If it is not possible to generate/get the value an mitk::Exception will be thrown.
The value is returned in [MHz]. Normally in the property it is stored in [Hz].*/
double MITKCEST_EXPORT GetCESTFrequency(const IPropertyProvider* provider);
/**Helper function that sets the CEST frequency property ("CEST.FREQ") in the passed owner.
If it owner is nullptr nothing will be done.
The value is passed in [MHz] and set in the property in [Hz].*/
void MITKCEST_EXPORT SetCESTFrequencyMHz(IPropertyOwner* owner, double freqInMHz);
/**Helper function that gets the CEST pulse duration property ("CEST.PulseDuration") from the input image.
If it is not possible to generate/get the value an mitk::Exception will be thrown.
The value is returned in [s]. Normally in the property it is stored in micro secs.*/
double MITKCEST_EXPORT GetCESTPulseDuration(const IPropertyProvider* provider);
/**Helper function that gets the CEST duty cycle property ("CEST.DutyCycle") from the input image.
If it is not possible to generate/get the value an mitk::Exception will be thrown.
- The value is returned as scaling factor (1 == 100%), in contrast to the porperty where it is stored as
+ The value is returned as scaling factor (1 == 100%), in contrast to the property where it is stored as
a percentage value (e.g. 56 %, so the function return will be 0.56).*/
double MITKCEST_EXPORT GetCESTDutyCycle(const IPropertyProvider* provider);
}
#endif
diff --git a/Modules/CEST/include/mitkCustomTagParser.h b/Modules/CEST/include/mitkCustomTagParser.h
index 2ba6b107b5..8b7511031e 100644
--- a/Modules/CEST/include/mitkCustomTagParser.h
+++ b/Modules/CEST/include/mitkCustomTagParser.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 mitkCustomTagParser_h
#define mitkCustomTagParser_h
#include<mitkPropertyList.h>
#include<mitkTemporoSpatialStringProperty.h>
#include <MitkCESTExports.h>
namespace mitk
{
/**
The custom tag parser can be used to parse the custom dicom tag of the siemens private tag
(0x0029, 0x1020) to extract relevant CEST data.
An initial parsing determines whether the provided string belongs to CEST data at all.
- To make the check and extract the revision number the following rules are aplied: \n
+ To make the check and extract the revision number the following rules are applied: \n
<ol>
<li>Sequence name (tSequenceFileName) must either
<ol>
<li>start with the substring "CEST" (case insensitiv), or</li>
<li>contain the substring "_CEST" (case insensitiv).</li>
</ol>
</li>
<li>Sequence name (tSequenceFileName) must contain the substring "_Rev" (case insensitiv).</li>
<li>All numbers after "_Rev" represent the revision number; until either
<ol>
<li>the next _, or</li>
<li>end of sequence name.</li>
</ol>
</li>
</ol>
Which custom parameters to save and to which property name can be controlled by a json file.
This file can be either provided as a resource for the MitkCEST module during compilation or
placed next to the MitkCEST library in your binary folder.
The expected format for the file "REVISIONNUMBER.json": <br>
{ <br>
"REVISIONNUMBER" : "revision_json", <br>
"sWiPMemBlock.alFree[1]" : "AdvancedMode", <br>
"sWiPMemBlock.alFree[2]" : "RetreatMode" <br>
} <br>
where :
<ul>
<li> REVISIONNUMBER is the revision number of this json parameter mapping (files with non digit characters in their
name will be ignored)
<li> sWiPMemBlock.alFree[1] is the name of one parameter in the private dicom tag
<li> AdvancedMode is the name of the property the content of sWiPMemBlock.alFree[1] should be saved to
</ul>
\note It is assumed that the entire content of tag (0x0029, 0x1020) is provided and that it es hex encoded
(12\23\04...).
If the sampling type is list it will try to access LIST.txt at the location provided in the constructor to
read the offsets.
*/
class MITKCEST_EXPORT CustomTagParser
{
public:
/// the constructor expects a path to one of the files to be loaded or the directory of the dicom files
CustomTagParser(std::string relevantFile);
/// parse the provided dicom property and return a property list based on the closest revision parameter mapping
mitk::PropertyList::Pointer ParseDicomProperty(mitk::TemporoSpatialStringProperty *dicomProperty);
/// parse the provided string and return a property list based on the closest revision parameter mapping
mitk::PropertyList::Pointer ParseDicomPropertyString(std::string dicomPropertyString);
static std::string ReadListFromFile(const std::string& filePath);
/** Extract the revision out of the passed sequenceFileName. If the file name is not a valid CEST file name
(see rules in the class documentation) exceptions will be thrown. If the file name is valid but contains no
revision number an empty string will be returned.
*/
static std::string ExtractRevision(std::string sequenceFileName);
void SetParseStrategy(std::string parseStrategy);
void SetRevisionMappingStrategy(std::string revisionMappingStrategy);
/// name of the property for the data acquisition revision
static const std::string m_RevisionPropertyName;
/// name of the property for the json parameter mapping revision
static const std::string m_JSONRevisionPropertyName;
/// prefix for all CEST related property names
static const std::string m_CESTPropertyPrefix;
protected:
std::string GetRevisionAppropriateJSONString(std::string revisionString);
void GetClosestLowerRevision(std::string revisionString);
std::string GetClosestLowerRevision(std::string revisionString, std::vector<int> availableRevisionsVector);
/// Decides whether or not the image is likely to be a T1Map, if not it is assumed to be a CEST sequence
bool IsT1Sequence(std::string preparationType, std::string recoveryMode, std::string spoilingType, std::string revisionString);
- /// Get a string filled with the properly formated offsets based on the sampling type and offset
+ /// Get a string filled with the properly formatted offsets based on the sampling type and offset
std::string GetOffsetString(std::string samplingType, std::string offset, std::string measurements);
/// returns a vector revision numbers of all REVISIONNUMBER.json found beside the MitkCEST library
std::vector<int> GetExternalRevisions();
/// returns a vector revision numbers of all REVISIONNUMBER.json provided as resources during the compile
std::vector<int> GetInternalRevisions();
/// returns the path where external jsons are expected to be located
std::string GetExternalJSONDirectory();
/// the closest lower revision provided as resource, empty if none found
std::string m_ClosestInternalRevision;
/// the closest lower revision provided as a json beside the library, empty if none found
std::string m_ClosestExternalRevision;
/// revision independent mapping to inject into the revision dependent json string
static const std::string m_RevisionIndependentMapping;
/// default revision dependent json string if none is found
static const std::string m_DefaultJsonString;
/// path to the dicom data
std::string m_DicomDataPath;
/// Should the kind of data be automatically determined or should it be parsed as a specific one
std::string m_ParseStrategy;
/// How to handle parameter mapping based on absent revision jsons
std::string m_RevisionMappingStrategy;
};
}
#endif
diff --git a/Modules/CEST/include/mitkExtractCESTOffset.h b/Modules/CEST/include/mitkExtractCESTOffset.h
index 031d55fe1a..75dcde577c 100644
--- a/Modules/CEST/include/mitkExtractCESTOffset.h
+++ b/Modules/CEST/include/mitkExtractCESTOffset.h
@@ -1,39 +1,39 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkExtractCESTOffset_h
#define mitkExtractCESTOffset_h
#include <mitkBaseData.h>
#include "MitkCESTExports.h"
namespace mitk
{
/**Helper function that gets the CEST offset property ("CEST.Offsets") from the input
image as vector of ScalarType.
- If it is not possible to generate/get the offset an mitk::Excpetion will be thrown.
+ If it is not possible to generate/get the offset an mitk::Exception will be thrown.
The values of the vector are in [ppm].
@post Number of extracted offsets equal the number of timesteps of the image.
*/
MITKCEST_EXPORT std::vector<ScalarType> ExtractCESTOffset(const BaseData* image);
/**Helper function that gets the CEST offset property ("CEST.TREC") from the input image as vector of ScalarType.
- If it is not possible to generate/get the T1 times an mitk::Excpetion will be thrown.
+ If it is not possible to generate/get the T1 times an mitk::Exception will be thrown.
The values of the vector are in [sec]. In the property they are stored in [ms] and scaled appropriately
before returning.
@post Number of extracted T1 times equal the number of timesteps of the image.
*/
MITKCEST_EXPORT std::vector<ScalarType> ExtractCESTT1Time(const BaseData* image);
}
#endif
diff --git a/Modules/CEST/src/mitkCustomTagParser.cpp b/Modules/CEST/src/mitkCustomTagParser.cpp
index ae68040759..ae5333dc96 100644
--- a/Modules/CEST/src/mitkCustomTagParser.cpp
+++ b/Modules/CEST/src/mitkCustomTagParser.cpp
@@ -1,855 +1,855 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkCustomTagParser.h"
#include <mitkProperties.h>
#include <mitkStringProperty.h>
#include "mitkCESTPropertyHelper.h"
#include "mitkIPropertyPersistence.h"
#include "usGetModuleContext.h"
#include "usModule.h"
#include "usModuleContext.h"
#include "usModuleResource.h"
#include "usModuleResourceStream.h"
#include <itksys/SystemTools.hxx>
#include <Poco/Glob.h>
#include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>
#include <algorithm>
#include <cstdint>
#include <map>
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
using namespace nlohmann;
namespace
{
mitk::IPropertyPersistence *GetPersistenceService()
{
mitk::IPropertyPersistence *result = nullptr;
std::vector<us::ServiceReference<mitk::IPropertyPersistence>> persRegisters =
us::GetModuleContext()->GetServiceReferences<mitk::IPropertyPersistence>();
if (!persRegisters.empty())
{
if (persRegisters.size() > 1)
{
MITK_WARN << "Multiple property description services found. Using just one.";
}
result = us::GetModuleContext()->GetService<mitk::IPropertyPersistence>(persRegisters.front());
}
return result;
};
}
const std::string mitk::CustomTagParser::m_CESTPropertyPrefix = "CEST.";
const std::string mitk::CustomTagParser::m_RevisionPropertyName = m_CESTPropertyPrefix + "Revision";
const std::string mitk::CustomTagParser::m_JSONRevisionPropertyName = m_CESTPropertyPrefix + "revision_json";
const std::string mitk::CustomTagParser::m_RevisionIndependentMapping = R"(
"sProtConsistencyInfo.tSystemType" : "SysType",
"sProtConsistencyInfo.flNominalB0" : "NominalB0",
"sTXSPEC.asNucleusInfo[0].lFrequency" : "FREQ",
"sTXSPEC.asNucleusInfo[0].flReferenceAmplitude" : "RefAmp",
"alTR[0]" : "TR",
"alTE[0]" : "TE",
"lAverages" : "averages",
"lRepetitions" : "repetitions",
"adFlipAngleDegree[0]" : "ImageFlipAngle",
"lTotalScanTimeSec" : "TotalScanTime",
)";
const std::string mitk::CustomTagParser::m_DefaultJsonString = R"({
"default mapping, corresponds to revision 1416" : "revision_json",
"sWiPMemBlock.alFree[1]" : "AdvancedMode",
"sWiPMemBlock.alFree[2]" : "RecoveryMode",
"sWiPMemBlock.alFree[3]" : "DoubleIrrMode",
"sWiPMemBlock.alFree[4]" : "BinomMode",
"sWiPMemBlock.alFree[5]" : "MtMode",
"sWiPMemBlock.alFree[6]" : "PreparationType",
"sWiPMemBlock.alFree[7]" : "PulseType",
"sWiPMemBlock.alFree[8]" : "SamplingType",
"sWiPMemBlock.alFree[9]" : "SpoilingType",
"sWiPMemBlock.alFree[10]" : "measurements",
"sWiPMemBlock.alFree[11]" : "NumberOfPulses",
"sWiPMemBlock.alFree[12]" : "NumberOfLockingPulses",
"sWiPMemBlock.alFree[13]" : "PulseDuration",
"sWiPMemBlock.alFree[14]" : "DutyCycle",
"sWiPMemBlock.alFree[15]" : "RecoveryTime",
"sWiPMemBlock.alFree[16]" : "RecoveryTimeM0",
"sWiPMemBlock.alFree[17]" : "ReadoutDelay",
"sWiPMemBlock.alFree[18]" : "BinomDuration",
"sWiPMemBlock.alFree[19]" : "BinomDistance",
"sWiPMemBlock.alFree[20]" : "BinomNumberofPulses",
"sWiPMemBlock.alFree[21]" : "BinomPreRepetions",
"sWiPMemBlock.alFree[22]" : "BinomType",
"sWiPMemBlock.adFree[1]" : "Offset",
"sWiPMemBlock.adFree[2]" : "B1Amplitude",
"sWiPMemBlock.adFree[3]" : "AdiabaticPulseMu",
"sWiPMemBlock.adFree[4]" : "AdiabaticPulseBW",
"sWiPMemBlock.adFree[5]" : "AdiabaticPulseLength",
"sWiPMemBlock.adFree[6]" : "AdiabaticPulseAmp",
"sWiPMemBlock.adFree[7]" : "FermiSlope",
"sWiPMemBlock.adFree[8]" : "FermiFWHM",
"sWiPMemBlock.adFree[9]" : "DoubleIrrDuration",
"sWiPMemBlock.adFree[10]" : "DoubleIrrAmplitude",
"sWiPMemBlock.adFree[11]" : "DoubleIrrRepetitions",
"sWiPMemBlock.adFree[12]" : "DoubleIrrPreRepetitions"
})";
mitk::CustomTagParser::CustomTagParser(std::string relevantFile) : m_ClosestInternalRevision(""), m_ClosestExternalRevision("")
{
std::string pathToDirectory;
std::string fileName;
itksys::SystemTools::SplitProgramPath(relevantFile, pathToDirectory, fileName);
m_DicomDataPath = pathToDirectory;
m_ParseStrategy = "Automatic";
m_RevisionMappingStrategy = "Fuzzy";
}
std::string mitk::CustomTagParser::ExtractRevision(std::string sequenceFileName)
{
- //all rules are case insesitive. Thus we convert everything to lower case
+ //all rules are case insensitive. Thus we convert everything to lower case
//in order to check everything only once.
std::string cestPrefix = "cest";
std::string cestPrefix2 = "_cest";
std::string cestPrefix3 = "\\cest"; //this version covers the fact that the strings extracted
- //from the SIEMENS tag has an additional prefix that is seperated by backslash.
+ //from the SIEMENS tag has an additional prefix that is separated by backslash.
std::string revisionPrefix = "_rev";
std::transform(sequenceFileName.begin(), sequenceFileName.end(), sequenceFileName.begin(), ::tolower);
bool isCEST = sequenceFileName.compare(0, cestPrefix.length(), cestPrefix) == 0;
std::size_t foundPosition = 0;
if (!isCEST)
{
foundPosition = sequenceFileName.find(cestPrefix2);
isCEST = foundPosition != std::string::npos;
}
if (!isCEST)
{
foundPosition = sequenceFileName.find(cestPrefix3);
isCEST = foundPosition != std::string::npos;
}
if (!isCEST)
{
mitkThrow() << "Invalid CEST sequence file name. No CEST prefix found. Could not extract revision.";
}
foundPosition = sequenceFileName.find(revisionPrefix, foundPosition);
if (foundPosition == std::string::npos)
{
mitkThrow() << "Invalid CEST sequence file name. No revision prefix was found in CEST sequence file name. Could not extract revision.";
}
std::string revisionString = sequenceFileName.substr(foundPosition + revisionPrefix.length(), std::string::npos);
std::size_t firstNoneNumber = revisionString.find_first_not_of("0123456789");
if (firstNoneNumber != std::string::npos)
{
revisionString.erase(firstNoneNumber, std::string::npos);
}
return revisionString;
}
bool mitk::CustomTagParser::IsT1Sequence(std::string preparationType,
std::string recoveryMode,
std::string spoilingType,
std::string revisionString)
{
bool isT1 = false;
// if a forced parse strategy is set, use that one
if ("T1" == m_ParseStrategy)
{
return true;
}
if ("CEST/WASABI" == m_ParseStrategy)
{
return false;
}
if (("T1Recovery" == preparationType) || ("T1Inversion" == preparationType))
{
isT1 = true;
}
// How to interpret the recoveryMode depends on the age of the sequence
// older sequences use 0 = false and 1 = true, newer ones 1 = false and 2 = true.
// A rough rule of thumb is to assume that if the SpoilingType is 0, then the first
// convention is chosen, if it is 1, then the second applies. Otherwise
// we assume revision 1485 and newer to follow the new convention.
// This unfortunate heuristic is due to somewhat arbitrary CEST sequence implementations.
if (!isT1)
{
std::string thisIsTrue = "1";
std::string thisIsFalse = "0";
if ("0" == spoilingType)
{
thisIsFalse = "0";
thisIsTrue = "1";
}
else if ("1" == spoilingType)
{
thisIsFalse = "1";
thisIsTrue = "2";
}
else
{
int revisionNrWeAssumeToBeDifferenciating = 1485;
if (std::stoi(revisionString) - revisionNrWeAssumeToBeDifferenciating < 0)
{
thisIsFalse = "0";
thisIsTrue = "1";
}
else
{
thisIsFalse = "1";
thisIsTrue = "2";
}
}
if (thisIsFalse == recoveryMode)
{
isT1 = false;
}
else if (thisIsTrue == recoveryMode)
{
isT1 = true;
}
}
return isT1;
}
mitk::PropertyList::Pointer mitk::CustomTagParser::ParseDicomPropertyString(std::string dicomPropertyString)
{
auto results = mitk::PropertyList::New();
if ("" == dicomPropertyString)
{
//MITK_ERROR << "Could not parse empty custom dicom string";
return results;
}
auto comp = [](const std::string& s1, const std::string& s2)
{
return boost::algorithm::lexicographical_compare(s1, s2, boost::algorithm::is_iless());
};
std::map<std::string, std::string, decltype(comp)> privateParameters(comp);
// The Siemens private tag contains information like "43\52\23\34".
// We jump over each "\" and convert the number;
std::string bytes;
{
const std::size_t SUBSTR_LENGTH = 2;
const std::size_t INPUT_LENGTH = dicomPropertyString.length();
if (INPUT_LENGTH < SUBSTR_LENGTH)
return results;
const std::size_t MAX_INPUT_OFFSET = INPUT_LENGTH - SUBSTR_LENGTH;
bytes.reserve(INPUT_LENGTH / 3 + 1);
try
{
for (std::size_t i = 0; i <= MAX_INPUT_OFFSET; i += 3)
{
std::string byte_string = dicomPropertyString.substr(i, SUBSTR_LENGTH);
int byte = static_cast<std::string::value_type>(std::stoi(byte_string.c_str(), nullptr, 16));
bytes.push_back(byte);
}
}
catch (const std::invalid_argument&) // std::stoi() could not perform conversion
{
return results;
}
}
// extract parameter list
std::string parameterListString;
{
const std::string ASCCONV_MARKER = "###";
const std::string ASCCONV_BEGIN = "### ASCCONV BEGIN";
const std::string ASCCONV_END = "### ASCCONV END";
auto ascconvBeginPos = bytes.find(ASCCONV_BEGIN);
if (std::string::npos == ascconvBeginPos)
return results;
ascconvBeginPos += ASCCONV_BEGIN.length();
ascconvBeginPos = bytes.find(ASCCONV_MARKER, ascconvBeginPos);
if (std::string::npos == ascconvBeginPos)
return results;
ascconvBeginPos += ASCCONV_MARKER.length(); // closing "###"
auto ascconvEndPos = bytes.find(ASCCONV_END, ascconvBeginPos);
if (std::string::npos == ascconvEndPos)
return results;
auto count = ascconvEndPos - ascconvBeginPos;
parameterListString = bytes.substr(ascconvBeginPos, count);
}
boost::replace_all(parameterListString, "\r\n", "\n");
boost::replace_all(parameterListString, "\t", "");
boost::char_separator<char> newlineSeparator("\n");
boost::tokenizer<boost::char_separator<char>> parameters(parameterListString, newlineSeparator);
for (const auto &parameter : parameters)
{
std::vector<std::string> parts;
boost::split(parts, parameter, boost::is_any_of("="));
if (parts.size() == 2)
{
parts[0].erase(std::remove(parts[0].begin(), parts[0].end(), ' '), parts[0].end());
parts[1].erase(parts[1].begin(), parts[1].begin() + 1); // first character is a space
privateParameters[parts[0]] = parts[1];
}
}
std::string revisionString = "";
try
{
revisionString = ExtractRevision(privateParameters["tSequenceFileName"]);
}
catch (const std::exception &e)
{
MITK_ERROR << "Cannot deduce revision information. Reason: "<< e.what();
return results;
}
results->SetProperty(m_RevisionPropertyName, mitk::StringProperty::New(revisionString));
std::string jsonString = GetRevisionAppropriateJSONString(revisionString);
json root;
try
{
root = json::parse(jsonString);
}
catch (const json::exception &e)
{
mitkThrow() << "Could not parse json file. Error was:\n" << e.what();
}
for (const auto &it : root.items())
{
if (it.value().is_string())
{
auto propertyName = m_CESTPropertyPrefix + it.value().get<std::string>();
if (m_JSONRevisionPropertyName == propertyName)
{
results->SetProperty(propertyName, mitk::StringProperty::New(it.key()));
}
else
{
results->SetProperty(propertyName, mitk::StringProperty::New(privateParameters[it.key()]));
}
}
else
{
MITK_ERROR << "Currently no support for nested dicom tag descriptors in json file.";
}
}
std::string offset = "";
std::string measurements = "";
results->GetStringProperty("CEST.Offset", offset);
results->GetStringProperty("CEST.measurements", measurements);
if (measurements.empty())
{
std::string stringRepetitions = "";
results->GetStringProperty("CEST.repetitions", stringRepetitions);
std::string stringAverages = "";
results->GetStringProperty("CEST.averages", stringAverages);
const auto ERROR_STRING = "Could not find measurements, fallback assumption of repetitions + averages could not be determined either.";
if (!stringRepetitions.empty() && !stringAverages.empty())
{
std::stringstream measurementStream;
try
{
measurementStream << std::stoi(stringRepetitions) + std::stoi(stringAverages);
measurements = measurementStream.str();
MITK_INFO << "Could not find measurements, assuming repetitions + averages. That is: " << measurements;
}
catch (const std::invalid_argument&)
{
MITK_ERROR << ERROR_STRING;
}
}
else
{
MITK_WARN << ERROR_STRING;
}
}
std::string preparationType = "";
std::string recoveryMode = "";
std::string spoilingType = "";
results->GetStringProperty(CEST_PROPERTY_NAME_PREPERATIONTYPE().c_str(), preparationType);
results->GetStringProperty(CEST_PROPERTY_NAME_RECOVERYMODE().c_str(), recoveryMode);
results->GetStringProperty(CEST_PROPERTY_NAME_SPOILINGTYPE().c_str(), spoilingType);
if (this->IsT1Sequence(preparationType, recoveryMode, spoilingType, revisionString))
{
MITK_INFO << "Parsed as T1 image";
std::stringstream trecStream;
std::string trecPath = m_DicomDataPath + "/TREC.txt";
auto trec = ReadListFromFile(trecPath);
if(trec.empty())
{
MITK_WARN << "Assumed T1, but could not load TREC at " << trecPath;
}
results->SetStringProperty(CEST_PROPERTY_NAME_TREC().c_str(), trec.c_str());
}
else
{
MITK_INFO << "Parsed as CEST or WASABI image";
std::string sampling = "";
bool hasSamplingInformation = results->GetStringProperty("CEST.SamplingType", sampling);
if (hasSamplingInformation)
{
std::string offsets = GetOffsetString(sampling, offset, measurements);
results->SetStringProperty(CEST_PROPERTY_NAME_OFFSETS().c_str(), offsets.c_str());
}
else
{
MITK_WARN << "Could not determine sampling type.";
}
}
//persist all properties
mitk::IPropertyPersistence *persSrv = GetPersistenceService();
if (persSrv)
{
auto propertyMap = results->GetMap();
for (auto const &prop : *propertyMap)
{
PropertyPersistenceInfo::Pointer info = PropertyPersistenceInfo::New();
std::string key = prop.first;
std::replace(key.begin(), key.end(), '.', '_');
info->SetNameAndKey(prop.first, key);
persSrv->AddInfo(info);
}
}
return results;
}
std::string mitk::CustomTagParser::ReadListFromFile(const std::string& filePath)
{
std::stringstream listStream;
std::ifstream list(filePath.c_str());
list.imbue(std::locale("C"));
if (list.good())
{
std::string currentValue;
while (std::getline(list, currentValue))
{
listStream << currentValue << " ";
}
}
return listStream.str();
}
mitk::PropertyList::Pointer mitk::CustomTagParser::ParseDicomProperty(mitk::TemporoSpatialStringProperty *dicomProperty)
{
if (!dicomProperty)
{
MITK_ERROR << "DICOM property empty";
}
auto results = mitk::PropertyList::New();
if (dicomProperty)
{
results = ParseDicomPropertyString(dicomProperty->GetValue());
}
return results;
}
std::vector<int> mitk::CustomTagParser::GetInternalRevisions()
{
const std::vector<us::ModuleResource> configs =
us::GetModuleContext()->GetModule()->FindResources("/", "*.json", false);
std::vector<int> availableRevisionsVector;
for (const auto& resource : configs)
{
availableRevisionsVector.push_back(std::stoi(resource.GetBaseName()));
}
return availableRevisionsVector;
}
std::vector<int> mitk::CustomTagParser::GetExternalRevisions()
{
std::string stringToJSONDirectory = GetExternalJSONDirectory();
std::string prospectiveJsonsPath = stringToJSONDirectory + "/*.json";
std::set<std::string> JsonFiles;
Poco::Glob::glob(prospectiveJsonsPath, JsonFiles, Poco::Glob::GLOB_CASELESS);
std::vector<int> availableRevisionsVector;
for (const auto& jsonpath : JsonFiles)
{
std::string jsonDir;
std::string jsonName;
itksys::SystemTools::SplitProgramPath(jsonpath, jsonDir, jsonName);
std::string revision = itksys::SystemTools::GetFilenameWithoutExtension(jsonName);
// disregard jsons which contain letters in their name
bool onlyNumbers = (revision.find_first_not_of("0123456789") == std::string::npos);
if(onlyNumbers)
{
availableRevisionsVector.push_back(std::stoi(revision));
}
}
return availableRevisionsVector;
}
std::string mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString, std::vector<int> availableRevisionsVector)
{
// descending order
std::sort(availableRevisionsVector.begin(), availableRevisionsVector.end(), std::greater<>());
int revision = std::stoi(revisionString);
int index = 0;
int numberOfRevisions = availableRevisionsVector.size();
while (index < numberOfRevisions)
{
// current mapping still has a higher revision number
if ((availableRevisionsVector[index] - revision) > 0)
{
++index;
}
else
{
break;
}
}
if (index < numberOfRevisions)
{
std::stringstream foundRevisionStream;
foundRevisionStream << availableRevisionsVector[index];
return foundRevisionStream.str();
}
return "";
}
void mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString)
{
m_ClosestInternalRevision = GetClosestLowerRevision(revisionString, GetInternalRevisions());
m_ClosestExternalRevision = GetClosestLowerRevision(revisionString, GetExternalRevisions());
if ("Strict" == m_RevisionMappingStrategy && !((0 == m_ClosestInternalRevision.compare(revisionString)) ||
(0 == m_ClosestExternalRevision.compare(revisionString))))
{ // strict revision mapping and neither revision does match the dicom meta data
std::stringstream errorMessageStream;
errorMessageStream << "\nCould not parse dicom data in strict mode, data revision " << revisionString
<< " has no known matching parameter mapping. To use the closest known older parameter mapping select the "
<< "\"Fuzzy\" revision mapping option when loading the data.\n"
<< "\nCurrently known revision mappings are:\n Precompiled:";
for (const auto revision : GetInternalRevisions())
{
errorMessageStream << " " << revision;
}
errorMessageStream << "\n External:";
for (const auto revision : GetExternalRevisions())
{
errorMessageStream << " " << revision;
}
errorMessageStream << "\n\nExternal revision mapping descriptions should be located at\n\n";
std::string stringToJSONDirectory = GetExternalJSONDirectory();
errorMessageStream << stringToJSONDirectory;
errorMessageStream << "\n\nTo provide an external mapping for this revision create a " << revisionString
<< ".json there. You might need to create the directory first.";
mitkThrow() << errorMessageStream.str();
}
}
std::string mitk::CustomTagParser::GetRevisionAppropriateJSONString(std::string revisionString)
{
std::string returnValue = "";
if ("" == revisionString)
{
MITK_WARN << "Could not extract revision";
}
else
{
GetClosestLowerRevision(revisionString);
bool useExternal = false;
bool useInternal = false;
if ("" != m_ClosestExternalRevision)
{
useExternal = true;
}
if ("" != m_ClosestInternalRevision)
{
useInternal = true;
}
if (useExternal && useInternal)
{
if (std::stoi(m_ClosestInternalRevision) > std::stoi(m_ClosestExternalRevision))
{
useExternal = false;
}
}
if (useExternal)
{
std::string stringToJSONDirectory = GetExternalJSONDirectory();
std::string prospectiveJsonPath = stringToJSONDirectory + "/" + m_ClosestExternalRevision + ".json";
std::ifstream externalJSON(prospectiveJsonPath.c_str());
if (externalJSON.good())
{
MITK_INFO << "Found external json for CEST parameters at " << prospectiveJsonPath;
std::stringstream buffer;
buffer << externalJSON.rdbuf();
returnValue = buffer.str();
useInternal = false;
}
}
if (useInternal)
{
std::string filename = m_ClosestInternalRevision + ".json";
us::ModuleResource jsonResource = us::GetModuleContext()->GetModule()->GetResource(filename);
if (jsonResource.IsValid() && jsonResource.IsFile())
{
MITK_INFO << "Found no external json for CEST parameters. Closest internal mapping is for revision "
<< m_ClosestInternalRevision;
us::ModuleResourceStream jsonStream(jsonResource);
std::stringstream buffer;
buffer << jsonStream.rdbuf();
returnValue = buffer.str();
}
}
}
if ("" == returnValue)
{
MITK_WARN << "Could not identify parameter mapping for the given revision " << revisionString
<< ", using default mapping.";
returnValue = m_DefaultJsonString;
}
// inject the revision independent mapping before the first newline
{
returnValue.insert(returnValue.find("\n"), m_RevisionIndependentMapping);
}
return returnValue;
}
std::string mitk::CustomTagParser::GetOffsetString(std::string samplingType, std::string offset, std::string measurements)
{
std::stringstream results;
results.imbue(std::locale("C"));
std::string normalizationIndicatingOffset = "-300";
double offsetDouble = 0.0;
int measurementsInt = 0;
bool validOffset = false;
bool validMeasurements = false;
if ("" != offset)
{
validOffset = true;
offsetDouble = std::stod(offset);
}
if ("" != measurements)
{
validMeasurements = true;
measurementsInt = std::stoi(measurements);
}
std::vector<double> offsetVector;
if (validOffset && validMeasurements)
{
for (int step = 0; step < measurementsInt -1; ++step)
{
double currentOffset = -offsetDouble + 2 * step * offsetDouble / (measurementsInt - 2.0);
offsetVector.push_back(currentOffset);
}
}
else
{
MITK_WARN << "Invalid offset or measurements, offset calculation will only work for list sampling type.";
}
if (samplingType == "1" || samplingType == "Regular")
{
if (validOffset && validMeasurements)
{
results << normalizationIndicatingOffset << " ";
for (const auto& entry : offsetVector)
{
results << entry << " ";
}
}
}
else if (samplingType == "2" || samplingType == "Alternating")
{
if (validOffset && validMeasurements)
{
results << normalizationIndicatingOffset << " ";
for (auto& entry : offsetVector)
{
entry = std::abs(entry);
}
std::sort(offsetVector.begin(), offsetVector.end(), std::greater<>());
for (unsigned int index = 0; index < offsetVector.size(); ++index)
{
offsetVector[index] = std::pow(-1, index) * offsetVector[index];
}
for (auto& entry : offsetVector)
{
results << entry << " ";
}
}
}
else if (samplingType == "3" || samplingType == "List")
{
std::string listPath = m_DicomDataPath + "/LIST.txt";
auto values = ReadListFromFile(listPath);
if (!values.empty())
{
results << values;
}
else
{
MITK_ERROR << "Could not load list at " << listPath;
}
}
else if (samplingType == "4" || samplingType == "SingleOffset")
{
if (validOffset && validMeasurements)
{
results << normalizationIndicatingOffset << " ";
for (int step = 0; step < measurementsInt - 1; ++step)
{
results << offsetDouble << " ";
}
}
}
else
{
MITK_WARN << "Encountered unknown sampling type.";
}
std::string resultString = results.str();
// replace multiple spaces by a single space
std::string::iterator newEnditerator =
std::unique(resultString.begin(), resultString.end(),
[=](char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); }
);
resultString.erase(newEnditerator, resultString.end());
if ((resultString.length() > 0) && (resultString.at(resultString.length() - 1) == ' '))
{
resultString.erase(resultString.end() - 1, resultString.end());
}
if ((resultString.length() > 0) && (resultString.at(0) == ' '))
{
resultString.erase(resultString.begin(), ++(resultString.begin()));
}
return resultString;
}
void mitk::CustomTagParser::SetParseStrategy(std::string parseStrategy)
{
m_ParseStrategy = parseStrategy;
}
void mitk::CustomTagParser::SetRevisionMappingStrategy(std::string revisionMappingStrategy)
{
m_RevisionMappingStrategy = revisionMappingStrategy;
}
std::string mitk::CustomTagParser::GetExternalJSONDirectory()
{
std::string moduleLocation = us::GetModuleContext()->GetModule()->GetLocation();
std::string stringToModule;
std::string libraryName;
itksys::SystemTools::SplitProgramPath(moduleLocation, stringToModule, libraryName);
std::stringstream jsonDirectory;
jsonDirectory << stringToModule << "/CESTRevisionMapping";
return jsonDirectory.str();
}
diff --git a/Modules/Chart/Test/mitkChartExampleTest.cpp b/Modules/Chart/Test/mitkChartExampleTest.cpp
index 689362b095..e411d9f95f 100644
--- a/Modules/Chart/Test/mitkChartExampleTest.cpp
+++ b/Modules/Chart/Test/mitkChartExampleTest.cpp
@@ -1,196 +1,196 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// qt
#include <QApplication>
// mitk chart
#include <mitkChartExampleTestHelper.h>
// mitk core
#include <mitkStandaloneDataStorage.h>
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkPropertyNameHelper.h>
// std includes
#include <string>
class mitkChartExampleTestSuite : public mitk::TestFixture
{
QApplication a();
CPPUNIT_TEST_SUITE(mitkChartExampleTestSuite);
MITK_TEST(AddingDataTest);
MITK_TEST(CheckingDataTest);
MITK_TEST(ClearingDataTest);
CPPUNIT_TEST_SUITE_END();
private:
mitk::ChartExampleTestHelper* helper;
public:
void setUp() override
{
helper = new mitk::ChartExampleTestHelper;
}
void tearDown() override
{
delete helper;
}
void AddingDataTest()
{
MITK_INFO << "=== AddingDataTest start ===";
AddNewData();
CheckData();
ClearData();
MITK_INFO << "=== AddingDataTest end ===";
}
void CheckingDataTest()
{
MITK_INFO << "=== CheckingDataTest start ===";
CheckData();
MITK_INFO << "=== CheckingDataTest end ===";
}
void ClearingDataTest()
{
MITK_INFO << "=== ClearingDataTest start ===";
ClearData();
MITK_INFO << "=== ClearingDataTest end ===";
}
void AddNewData()
{
MITK_INFO << "=== AddNewData";
// Adding data
//size_t size;
for (size_t i = 1; i < 6; i++)
{
//helper.Add(i);
//size = helper.qmitkChartWidget.ReturnSizeOfMemory();
//CPPUNIT_ASSERT_MESSAGE("Data storage does not contain the right amount of items!", size == i);
}
}
void CheckData()
{
auto myDataOne = helper->GetDataOne();
auto xDataOne = myDataOne->GetXData();
auto yDataOne = myDataOne->GetYData();
auto labelOne = myDataOne->GetLabel();
auto typeOne = myDataOne->GetChartType();
auto colorOne = myDataOne->GetColor();
auto styleOne = myDataOne->GetLineStyle();
auto dataToCheckOne = helper->qmitkChartWidget.GetDataElementByLabel(labelOne.toString().toStdString());
CPPUNIT_ASSERT_MESSAGE("Dataset one was not saved correctly", dataToCheckOne->GetXData() == xDataOne &&
dataToCheckOne->GetYData() == yDataOne &&
dataToCheckOne->GetLabel() == labelOne &&
dataToCheckOne->GetChartType() == typeOne &&
dataToCheckOne->GetColor() == colorOne &&
dataToCheckOne->GetLineStyle() == styleOne);
auto myDataTwo = helper->GetDataTwo();
auto xDataTwo = myDataTwo->GetXData();
auto yDataTwo = myDataTwo->GetYData();
auto labelTwo = myDataTwo->GetLabel();
auto typeTwo = myDataTwo->GetChartType();
auto colorTwo = myDataTwo->GetColor();
auto styleTwo = myDataTwo->GetLineStyle();
auto dataToCheckTwo = helper->qmitkChartWidget.GetDataElementByLabel(labelTwo.toString().toStdString());
CPPUNIT_ASSERT_MESSAGE("Dataset two was not saved correctly", dataToCheckTwo->GetXData() == xDataTwo &&
dataToCheckTwo->GetYData() == yDataTwo &&
dataToCheckTwo->GetLabel() == labelTwo &&
dataToCheckTwo->GetChartType() == typeTwo &&
dataToCheckTwo->GetColor() == colorTwo &&
dataToCheckTwo->GetLineStyle() == styleTwo);
auto myDataThree = helper->GetDataThree();
auto xDataThree = myDataThree->GetXData();
auto yDataThree = myDataThree->GetYData();
auto labelThree = myDataThree->GetLabel();
auto typeThree = myDataThree->GetChartType();
auto colorThree = myDataThree->GetColor();
auto styleThree = myDataThree->GetLineStyle();
auto dataToCheckThree = helper->qmitkChartWidget.GetDataElementByLabel(labelThree.toString().toStdString());
CPPUNIT_ASSERT_MESSAGE("Dataset three was not saved correctly", dataToCheckThree->GetXData() == xDataThree &&
dataToCheckThree->GetYData() == yDataThree &&
dataToCheckThree->GetLabel() == labelThree &&
dataToCheckThree->GetChartType() == typeThree &&
dataToCheckThree->GetColor() == colorThree &&
dataToCheckThree->GetLineStyle() == styleThree);
auto myDataFour = helper->GetDataFour();
auto xDataFour = myDataFour->GetXData();
auto yDataFour = myDataFour->GetYData();
auto labelFour = myDataFour->GetLabel();
auto typeFour = myDataFour->GetChartType();
auto colorFour = myDataFour->GetColor();
auto styleFour = myDataFour->GetLineStyle();
auto dataToCheckFour = helper->qmitkChartWidget.GetDataElementByLabel(labelFour.toString().toStdString());
CPPUNIT_ASSERT_MESSAGE("Dataset one was not saved correctly", dataToCheckFour->GetXData() == xDataFour &&
dataToCheckFour->GetYData() == yDataFour &&
dataToCheckFour->GetLabel() == labelFour &&
dataToCheckFour->GetChartType() == typeFour &&
dataToCheckFour->GetColor() == colorFour &&
dataToCheckFour->GetLineStyle() == styleFour);
auto myDataFive = helper->GetDataFive();
auto xDataFive = myDataFive->GetXData();
auto yDataFive = myDataFive->GetYData();
auto labelFive = myDataFive->GetLabel();
auto typeFive = myDataFive->GetChartType();
auto colorFive = myDataFive->GetColor();
auto styleFive = myDataFive->GetLineStyle();
auto dataToCheckFive = helper->qmitkChartWidget.GetDataElementByLabel(labelFive.toString().toStdString());
CPPUNIT_ASSERT_MESSAGE("Dataset one was not saved correctly", dataToCheckFive->GetXData() == xDataFive &&
dataToCheckFive->GetYData() == yDataFive &&
dataToCheckFive->GetLabel() == labelFive &&
dataToCheckFive->GetChartType() == typeFive &&
dataToCheckFive->GetColor() == colorFive &&
dataToCheckFive->GetLineStyle() == styleFive);
}
void ClearData()
{
MITK_INFO << "=== ClearData";
// Claering data
mitk::ChartExampleTestHelper helper;
helper.qmitkChartWidget.Clear();
//int size = helper.qmitkChartWidget.ReturnSizeOfMemory();
- //CPPUNIT_ASSERT_MESSAGE("Data storage was not cleared completly!", size == 0);
+ //CPPUNIT_ASSERT_MESSAGE("Data storage was not cleared completely!", size == 0);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkChartExample)
diff --git a/Modules/Chart/src/QmitkChartWidget.cpp b/Modules/Chart/src/QmitkChartWidget.cpp
index 543dd4dfce..8110163093 100644
--- a/Modules/Chart/src/QmitkChartWidget.cpp
+++ b/Modules/Chart/src/QmitkChartWidget.cpp
@@ -1,940 +1,940 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <regex>
#include <QGridLayout>
#include <QWebChannel>
#include <QWebEngineSettings>
#include <QWebEngineView>
#include <QmitkChartWidget.h>
#include "mitkExceptionMacro.h"
#include <QmitkChartData.h>
#include <QmitkChartxyData.h>
class CustomPage : public QWebEnginePage
{
public:
CustomPage(QObject *parent = nullptr) : QWebEnginePage(parent) {}
virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel /*level*/,
const QString &message,
int lineNumber,
const QString & /*sourceID*/) override
{
MITK_INFO << "JS > " << lineNumber << ": " << message.toStdString();
}
};
class QmitkChartWidget::Impl final
{
public:
explicit Impl(QWidget *parent);
~Impl();
Impl(const Impl &) = delete;
Impl &operator=(const Impl &) = delete;
void AddData1D(const std::vector<double> &data1D, const std::string &label, QmitkChartWidget::ChartType chartType);
void AddData2D(const std::vector< std::pair<double, double> > &data2D,
const std::string &label,
QmitkChartWidget::ChartType chartType);
void AddChartExampleData(const std::vector< std::pair<double, double> >& data2D,
const std::string& label,
const std::string& type,
const std::string& color,
const std::string& style,
const std::string& pieLabelsData = 0);
void UpdateData1D(const std::vector<double> &data1D, const std::string &label);
void UpdateData2D(const std::vector< std::pair<double, double> > &data2D, const std::string &label);
void UpdateChartExampleData(const std::vector< std::pair<double, double> >& data2D,
const std::string& label,
const std::string& type,
const std::string& color,
const std::string& lineStyle,
const std::string& pieLabelsData = 0);
void RemoveData(const std::string &label);
void UpdateLabel(const std::string &existingLabel, const std::string &newLabel);
QmitkChartxyData* GetDataElementByLabel(const std::string& label) const;
void ClearData();
void SetColor(const std::string &label, const std::string &colorName);
void SetLineStyle(const std::string &label, LineStyle style);
void SetMarkerSymbol(const std::string &label, MarkerSymbol symbol);
void SetYAxisScale(AxisScale scale);
void SetXAxisLabel(const std::string &label);
void SetYAxisLabel(const std::string &label);
void SetPieLabels(const std::vector<std::string> &pieLabels, const std::string &label);
void SetTitle(const std::string &title);
void SetXErrorBars(const std::string &label,
const std::vector<double> &errorPlus,
const std::vector<double> &errorMinus = std::vector<double>());
void SetYErrorBars(const std::string &label,
const std::vector<double> &errorPlus,
const std::vector<double> &errorMinus = std::vector<double>());
std::string GetThemeName() const;
void SetThemeName(ColorTheme style);
void SetLegendPosition(LegendPosition position);
void Show(bool showSubChart);
void SetShowLegend(bool show);
void SetShowErrorBars(bool show);
void SetStackedData(bool stacked);
void SetShowDataPoints(bool showDataPoints = false);
void SetShowSubchart(bool showSubChart);
void SetChartType(const std::string &label, QmitkChartWidget::ChartType chartType);
void SetMinMaxValueXView(double minValueX, double maxValueX);
void SetMinMaxValueYView(double minValueY, double maxValueY);
QList<QVariant> ConvertErrorVectorToQList(const std::vector<double> &error);
QList<QVariant> ConvertVectorToQList(const std::vector<std::string> &vec);
std::string ConvertChartTypeToString(QmitkChartWidget::ChartType chartType) const;
void ClearJavaScriptChart();
void InitializeJavaScriptChart();
void CallJavaScriptFuntion(const QString &command);
QSize sizeHint() const;
void GetImageUrl();
private:
using ChartxyDataVector = std::vector<std::unique_ptr<QmitkChartxyData>>;
std::string GetUniqueLabelName(const QList<QVariant> &labelList, const std::string &label) const;
QList<QVariant> GetDataLabels(const ChartxyDataVector &c3xyData) const;
QWebChannel *m_WebChannel;
QWebEngineView *m_WebEngineView;
QmitkChartData m_C3Data;
ChartxyDataVector m_C3xyData;
std::map<QmitkChartWidget::ChartType, std::string> m_ChartTypeToName;
std::map<QmitkChartWidget::ChartColor, std::string> m_ChartColorToName;
std::map<QmitkChartWidget::ColorTheme, std::string> m_ColorThemeToName;
std::map<QmitkChartWidget::LegendPosition, std::string> m_LegendPositionToName;
std::map<QmitkChartWidget::LineStyle, std::string> m_LineStyleToName;
std::map<QmitkChartWidget::MarkerSymbol, std::string> m_MarkerSymbolToName;
std::map<QmitkChartWidget::AxisScale, std::string> m_AxisScaleToName;
};
QmitkChartWidget::Impl::Impl(QWidget *parent)
: m_WebChannel(new QWebChannel(parent)), m_WebEngineView(new QWebEngineView(parent))
{
// disable context menu for QWebEngineView
m_WebEngineView->setContextMenuPolicy(Qt::NoContextMenu);
m_WebEngineView->setPage(new CustomPage());
// Set the webengineview to an initial empty page. The actual chart will be loaded once the data is calculated.
m_WebEngineView->load(QUrl(QStringLiteral("qrc:///Chart/empty.html")));
m_WebEngineView->page()->setWebChannel(m_WebChannel);
m_WebEngineView->settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false);
- //This is added as a workarround for T28252 (https://phabricator.mitk.org/T28252)
+ //This is added as a workaround for T28252 (https://phabricator.mitk.org/T28252)
//can be removed if task is properly fixed.
m_WebEngineView->settings()->setAttribute(QWebEngineSettings::ShowScrollBars, false);
connect(m_WebEngineView, SIGNAL(loadFinished(bool)), parent, SLOT(OnLoadFinished(bool)));
auto layout = new QGridLayout(parent);
layout->setContentsMargins({});
layout->addWidget(m_WebEngineView);
m_ChartTypeToName.emplace(ChartType::bar, "bar");
m_ChartTypeToName.emplace(ChartType::line, "line");
m_ChartTypeToName.emplace(ChartType::spline, "spline");
m_ChartTypeToName.emplace(ChartType::pie, "pie");
m_ChartTypeToName.emplace(ChartType::area, "area");
m_ChartTypeToName.emplace(ChartType::area_spline, "area-spline");
m_ChartTypeToName.emplace(ChartType::scatter, "scatter");
m_ChartColorToName.emplace(ChartColor::red, "red");
m_ChartColorToName.emplace(ChartColor::orange, "orange");
m_ChartColorToName.emplace(ChartColor::yellow, "yellow");
m_ChartColorToName.emplace(ChartColor::green, "green");
m_ChartColorToName.emplace(ChartColor::blue, "blue");
m_ChartColorToName.emplace(ChartColor::purple, "purple");
m_ChartColorToName.emplace(ChartColor::brown, "brown");
m_ChartColorToName.emplace(ChartColor::magenta, "magenta");
m_ChartColorToName.emplace(ChartColor::tan, "tan");
m_ChartColorToName.emplace(ChartColor::cyan, "cyan");
m_ChartColorToName.emplace(ChartColor::olive, "olive");
m_ChartColorToName.emplace(ChartColor::maroon, "maroon");
m_ChartColorToName.emplace(ChartColor::navy, "navy");
m_ChartColorToName.emplace(ChartColor::aquamarine, "aquamarine");
m_ChartColorToName.emplace(ChartColor::turqouise, "turqouise");
m_ChartColorToName.emplace(ChartColor::silver, "silver");
m_ChartColorToName.emplace(ChartColor::lime, "lime");
m_ChartColorToName.emplace(ChartColor::teal, "teal");
m_ChartColorToName.emplace(ChartColor::indigo, "indigo");
m_ChartColorToName.emplace(ChartColor::violet, "violet");
m_ChartColorToName.emplace(ChartColor::pink, "pink");
m_ChartColorToName.emplace(ChartColor::black, "black");
m_ChartColorToName.emplace(ChartColor::white, "white");
m_ChartColorToName.emplace(ChartColor::grey, "grey");
m_LegendPositionToName.emplace(LegendPosition::bottomMiddle, "bottomMiddle");
m_LegendPositionToName.emplace(LegendPosition::bottomRight, "bottomRight");
m_LegendPositionToName.emplace(LegendPosition::topRight, "topRight");
m_LegendPositionToName.emplace(LegendPosition::topLeft, "topLeft");
m_LegendPositionToName.emplace(LegendPosition::middleRight, "middleRight");
m_LineStyleToName.emplace(LineStyle::solid, "solid");
m_LineStyleToName.emplace(LineStyle::dashed, "dashed");
m_MarkerSymbolToName.emplace(MarkerSymbol::circle, "circle");
m_MarkerSymbolToName.emplace(MarkerSymbol::cross, "cross");
m_MarkerSymbolToName.emplace(MarkerSymbol::diamond, "diamond");
m_MarkerSymbolToName.emplace(MarkerSymbol::pentagon, "pentagon");
m_MarkerSymbolToName.emplace(MarkerSymbol::square, "square");
m_MarkerSymbolToName.emplace(MarkerSymbol::star, "star");
m_MarkerSymbolToName.emplace(MarkerSymbol::x, "x");
m_MarkerSymbolToName.emplace(MarkerSymbol::diamond_tall, "diamond-tall");
m_MarkerSymbolToName.emplace(MarkerSymbol::star_diamond, "star-diamond");
m_MarkerSymbolToName.emplace(MarkerSymbol::star_triangle_up, "star-triangle-up");
m_MarkerSymbolToName.emplace(MarkerSymbol::star_triangle_down, "star-triangle-down");
m_MarkerSymbolToName.emplace(MarkerSymbol::asterisk, "asterisk");
m_MarkerSymbolToName.emplace(MarkerSymbol::cross_thin, "cross-thin");
m_MarkerSymbolToName.emplace(MarkerSymbol::x_thin, "x-thin");
m_AxisScaleToName.emplace(AxisScale::linear, "");
m_AxisScaleToName.emplace(AxisScale::log, "log");
m_ColorThemeToName.emplace(ColorTheme::lightstyle, "light");
m_ColorThemeToName.emplace(ColorTheme::darkstyle, "dark");
}
QmitkChartWidget::Impl::~Impl() {}
std::string QmitkChartWidget::Impl::GetThemeName() const
{
return m_C3Data.GetThemeName().toString().toStdString();
}
std::string CheckForCorrectHex(const std::string &colorName)
{
std::regex rgx("([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})");
std::smatch match;
if (!colorName.empty() && colorName.at(0) != '#' && std::regex_search(colorName.begin(), colorName.end(), match, rgx))
{
return "#" + colorName;
}
else
{
return colorName;
}
}
void QmitkChartWidget::Impl::GetImageUrl()
{
m_C3Data.EmitSignalImageUrl();
}
void QmitkChartWidget::Impl::AddData1D(const std::vector<double> &data1D,
const std::string &label,
QmitkChartWidget::ChartType type)
{
std::vector< std::pair<double, double> > transformedData2D;
unsigned int count = 0;
// transform the 1D data to 2D data
for (const auto &ele : data1D)
{
transformedData2D.emplace_back(count, ele);
count++;
}
AddData2D(transformedData2D, label, type);
}
void QmitkChartWidget::Impl::AddData2D(const std::vector< std::pair<double, double> > &data2D,
const std::string &label,
QmitkChartWidget::ChartType type)
{
const std::string chartTypeName(m_ChartTypeToName.at(type));
auto definedLabels = GetDataLabels(m_C3xyData);
auto uniqueLabel = GetUniqueLabelName(definedLabels, label);
unsigned int sizeOfC3xyData = static_cast<unsigned int>(m_C3xyData.size());
m_C3xyData.push_back(std::make_unique<QmitkChartxyData>(data2D,
QVariant(QString::fromStdString(uniqueLabel)),
QVariant(QString::fromStdString(chartTypeName)),
QVariant(sizeOfC3xyData)));
}
void QmitkChartWidget::Impl::AddChartExampleData(const std::vector< std::pair<double, double> >& data2D,
const std::string& label,
const std::string& type,
const std::string& color,
const std::string& lineStyle,
const std::string& pieLabelsData)
{
auto definedLabels = GetDataLabels(m_C3xyData);
auto uniqueLabel = GetUniqueLabelName(definedLabels, label);
if (type == "scatter")
{
SetShowDataPoints(true);
MITK_INFO << "Enabling data points for all because of scatter plot";
}
unsigned int sizeOfC3xyData = static_cast<unsigned int>(m_C3xyData.size());
std::unique_ptr<QmitkChartxyData> chartData =
std::make_unique<QmitkChartxyData>(
data2D,
QVariant(QString::fromStdString(uniqueLabel)),
QVariant(QString::fromStdString(type)),
QVariant(sizeOfC3xyData));
chartData->SetColor(QVariant(QString::fromStdString(color)));
chartData->SetLineStyle(QVariant(QString::fromStdString(lineStyle)));
if (pieLabelsData != "")
{
std::string pieLabelsDataWorkingString = pieLabelsData;
QList<QVariant> pieLabelsDataList;
while (pieLabelsDataWorkingString.size() != 0)
{
QVariant oneElement = QString::fromStdString(pieLabelsDataWorkingString.substr(0, pieLabelsDataWorkingString.find(";")));
pieLabelsDataList.push_back(oneElement);
if (pieLabelsDataWorkingString.find(";") != std::string::npos)
{
pieLabelsDataWorkingString.erase(0, pieLabelsDataWorkingString.find(";") + 1);
}
else
{
pieLabelsDataWorkingString.erase(pieLabelsDataWorkingString.begin(), pieLabelsDataWorkingString.end());
}
}
chartData->SetPieLabels(pieLabelsDataList);
}
m_C3xyData.push_back(std::move(chartData));
}
void QmitkChartWidget::Impl::UpdateData1D(const std::vector<double> &data1D, const std::string &label)
{
std::vector< std::pair<double, double> > transformedData2D;
unsigned int count = 0;
// transform the 1D data to 2D data
for (const auto &ele : data1D)
{
transformedData2D.emplace_back( count, ele );
count++;
}
UpdateData2D(transformedData2D, label);
}
void QmitkChartWidget::Impl::UpdateData2D(const std::vector< std::pair<double, double> > &data2D, const std::string &label)
{
auto element = GetDataElementByLabel(label);
if (element)
element->SetData(data2D);
}
void QmitkChartWidget::Impl::UpdateChartExampleData(const std::vector< std::pair<double, double> >& data2D,
const std::string& label,
const std::string& type,
const std::string& color,
const std::string& lineStyle,
const std::string& pieLabelsData)
{
UpdateData2D(data2D, label);
auto element = GetDataElementByLabel(label);
if (element)
{
element->SetChartType(QString::fromStdString(type));
element->SetColor(QString::fromStdString(color));
element->SetLineStyle(QString::fromStdString(lineStyle));
if (pieLabelsData != "")
{
std::string pieLabelsDataWorkingString = pieLabelsData;
QList<QVariant> pieLabelsDataList;
while (pieLabelsDataWorkingString.size() != 0)
{
QVariant oneElement = QString::fromStdString(pieLabelsDataWorkingString.substr(0, pieLabelsDataWorkingString.find(";")));
pieLabelsDataList.push_back(oneElement);
if (pieLabelsDataWorkingString.find(";") != std::string::npos)
{
pieLabelsDataWorkingString.erase(0, pieLabelsDataWorkingString.find(";") + 1);
}
else
{
pieLabelsDataWorkingString.erase(pieLabelsDataWorkingString.begin(), pieLabelsDataWorkingString.end());
}
}
element->SetPieLabels(pieLabelsDataList);
}
}
}
void QmitkChartWidget::Impl::RemoveData(const std::string &label)
{
for (ChartxyDataVector::iterator iter = m_C3xyData.begin(); iter != m_C3xyData.end(); ++iter)
{
if ((*iter)->GetLabel().toString().toStdString() == label)
{
m_C3xyData.erase(iter);
return;
}
}
throw std::invalid_argument("Cannot Remove Data because the label does not exist.");
}
void QmitkChartWidget::Impl::ClearData()
{
for (auto &xyData : m_C3xyData)
{
m_WebChannel->deregisterObject(xyData.get());
}
m_C3xyData.clear();
}
void QmitkChartWidget::Impl::UpdateLabel(const std::string &existingLabel, const std::string &newLabel) {
auto element = GetDataElementByLabel(existingLabel);
if (element)
{
auto definedLabels = GetDataLabels(m_C3xyData);
auto uniqueLabel = GetUniqueLabelName(definedLabels, newLabel);
element->SetLabel(QString::fromStdString(uniqueLabel));
}
}
void QmitkChartWidget::Impl::SetColor(const std::string &label, const std::string &colorName)
{
auto element = GetDataElementByLabel(label);
if (element)
{
auto colorChecked = CheckForCorrectHex(colorName);
element->SetColor(QVariant(QString::fromStdString(colorChecked)));
}
}
void QmitkChartWidget::Impl::SetLineStyle(const std::string &label, LineStyle style)
{
auto element = GetDataElementByLabel(label);
const std::string lineStyleName(m_LineStyleToName.at(style));
element->SetLineStyle(QVariant(QString::fromStdString(lineStyleName)));
}
void QmitkChartWidget::Impl::SetMarkerSymbol(const std::string &label, MarkerSymbol symbol)
{
auto element = GetDataElementByLabel(label);
const std::string markerSymbolName(m_MarkerSymbolToName.at(symbol));
element->SetMarkerSymbol(QVariant(QString::fromStdString(markerSymbolName)));
}
void QmitkChartWidget::Impl::SetYAxisScale(AxisScale scale)
{
const std::string axisScaleName(m_AxisScaleToName.at(scale));
m_C3Data.SetYAxisScale(QString::fromStdString(axisScaleName));
}
QmitkChartxyData *QmitkChartWidget::Impl::GetDataElementByLabel(const std::string &label) const
{
for (const auto &qmitkChartxyData : m_C3xyData)
{
if (qmitkChartxyData->GetLabel().toString() == label.c_str())
{
return qmitkChartxyData.get();
}
}
return nullptr;
}
QList<QVariant> QmitkChartWidget::Impl::GetDataLabels(const ChartxyDataVector &c3xyData) const
{
QList<QVariant> dataLabels;
for (auto element = c3xyData.begin(); element != c3xyData.end(); ++element)
{
dataLabels.push_back((*element)->GetLabel());
}
return dataLabels;
}
void QmitkChartWidget::Impl::SetXAxisLabel(const std::string &label)
{
m_C3Data.SetXAxisLabel(QString::fromStdString(label));
}
void QmitkChartWidget::Impl::SetYAxisLabel(const std::string &label)
{
m_C3Data.SetYAxisLabel(QString::fromStdString(label));
}
void QmitkChartWidget::Impl::SetPieLabels(const std::vector<std::string> &pieLabels, const std::string &label)
{
auto element = GetDataElementByLabel(label);
if (element)
{
if (element->GetChartType() == QVariant("pie"))
{
auto dataY = element->GetYData();
element->SetPieLabels(ConvertVectorToQList(pieLabels));
if (static_cast<unsigned>(dataY.size()) != pieLabels.size())
{
MITK_INFO << "data has " << dataY.size() << " entries whereas pie labels have " << pieLabels.size()
<< " entries. Unnamed pie labels automatically get a numerical label.";
}
}
else
{
MITK_INFO << "label" << label << "has chart type " << element->GetChartType().toString().toStdString() << ", but pie is required";
}
}
}
void QmitkChartWidget::Impl::SetTitle(const std::string &title)
{
m_C3Data.SetTitle(QString::fromStdString(title));
}
void QmitkChartWidget::Impl::SetThemeName(QmitkChartWidget::ColorTheme style)
{
const std::string themeName(m_ColorThemeToName.at(style));
m_C3Data.SetThemeName(QString::fromStdString(themeName));
}
void QmitkChartWidget::Impl::SetLegendPosition(QmitkChartWidget::LegendPosition legendPosition)
{
const std::string legendPositionName(m_LegendPositionToName.at(legendPosition));
m_C3Data.SetLegendPosition(QString::fromStdString(legendPositionName));
}
void QmitkChartWidget::Impl::Show(bool showSubChart)
{
if (m_C3xyData.empty())
{
MITK_WARN << "no data available for display in chart";
}
else
{
m_C3Data.SetAppearance(showSubChart, m_C3xyData.front()->GetChartType() == QVariant("pie"));
}
InitializeJavaScriptChart();
}
void QmitkChartWidget::Impl::SetShowLegend(bool show)
{
m_C3Data.SetShowLegend(show);
}
void QmitkChartWidget::Impl::SetStackedData(bool stacked)
{
m_C3Data.SetStackedData(stacked);
}
void QmitkChartWidget::Impl::SetShowErrorBars(bool show)
{
m_C3Data.SetShowErrorBars(show);
}
void QmitkChartWidget::Impl::SetShowDataPoints(bool showDataPoints)
{
if (showDataPoints == true)
{
m_C3Data.SetDataPointSize(6.5);
}
else
{
m_C3Data.SetDataPointSize(0);
}
}
void QmitkChartWidget::Impl::SetShowSubchart(bool showSubChart) {
m_C3Data.SetShowSubchart(showSubChart);
}
void QmitkChartWidget::Impl::SetChartType(const std::string &label, QmitkChartWidget::ChartType chartType)
{
auto element = GetDataElementByLabel(label);
if (element)
{
if (chartType == ChartType::scatter)
{
SetShowDataPoints(true);
MITK_INFO << "Enabling data points for all because of scatter plot";
}
const std::string chartTypeName(m_ChartTypeToName.at(chartType));
element->SetChartType(QVariant(QString::fromStdString(chartTypeName)));
}
}
void QmitkChartWidget::Impl::SetMinMaxValueXView(double minValueX, double maxValueX) {
m_C3Data.SetMinValueXView(minValueX);
m_C3Data.SetMaxValueXView(maxValueX);
}
void QmitkChartWidget::Impl::SetMinMaxValueYView(double minValueY, double maxValueY) {
m_C3Data.SetMinValueYView(minValueY);
m_C3Data.SetMaxValueYView(maxValueY);
}
QList<QVariant> QmitkChartWidget::Impl::ConvertErrorVectorToQList(const std::vector<double> &error)
{
QList<QVariant> errorConverted;
for (const auto &aValue : error)
{
errorConverted.append(aValue);
}
return errorConverted;
}
QList<QVariant> QmitkChartWidget::Impl::ConvertVectorToQList(const std::vector<std::string> &vec)
{
QList<QVariant> vecConverted;
for (const auto &aValue : vec)
{
vecConverted.append(QString::fromStdString(aValue));
}
return vecConverted;
}
void QmitkChartWidget::Impl::SetXErrorBars(const std::string &label,
const std::vector<double> &errorPlus,
const std::vector<double> &errorMinus)
{
auto element = GetDataElementByLabel(label);
if (element)
{
auto errorConvertedPlus = ConvertErrorVectorToQList(errorPlus);
auto errorConvertedMinus = ConvertErrorVectorToQList(errorMinus);
element->SetXErrorDataPlus(errorConvertedPlus);
element->SetXErrorDataMinus(errorConvertedMinus);
}
}
void QmitkChartWidget::Impl::SetYErrorBars(const std::string &label,
const std::vector<double> &errorPlus,
const std::vector<double> &errorMinus)
{
auto element = GetDataElementByLabel(label);
if (element)
{
auto errorConvertedPlus = ConvertErrorVectorToQList(errorPlus);
auto errorConvertedMinus = ConvertErrorVectorToQList(errorMinus);
element->SetYErrorDataPlus(errorConvertedPlus);
element->SetYErrorDataMinus(errorConvertedMinus);
}
}
std::string QmitkChartWidget::Impl::ConvertChartTypeToString(QmitkChartWidget::ChartType chartType) const
{
return m_ChartTypeToName.at(chartType);
}
QSize QmitkChartWidget::Impl::sizeHint() const
{
return QSize(400, 300);
}
void QmitkChartWidget::Impl::CallJavaScriptFuntion(const QString &command)
{
m_WebEngineView->page()->runJavaScript(command);
}
void QmitkChartWidget::Impl::ClearJavaScriptChart()
{
m_WebEngineView->load(QUrl(QStringLiteral("qrc:///Chart/empty.html")));
}
void QmitkChartWidget::Impl::InitializeJavaScriptChart()
{
auto alreadyRegisteredObjects = m_WebChannel->registeredObjects();
auto alreadyRegisteredObjectsValues = alreadyRegisteredObjects.values();
// only register objects that have not been registered yet
if (alreadyRegisteredObjectsValues.indexOf(&m_C3Data) == -1)
{
m_WebChannel->registerObject(QStringLiteral("chartData"), &m_C3Data);
}
unsigned count = 0;
for (auto &xyData : m_C3xyData)
{
// only register objects that have not been registered yet
if (alreadyRegisteredObjectsValues.indexOf(xyData.get()) == -1)
{
QString variableName = "xyData" + QString::number(count);
m_WebChannel->registerObject(variableName, xyData.get());
}
count++;
}
m_WebEngineView->load(QUrl(QStringLiteral("qrc:///Chart/QmitkChartWidget.html")));
}
std::string QmitkChartWidget::Impl::GetUniqueLabelName(const QList<QVariant> &labelList, const std::string &label) const
{
QString currentLabel = QString::fromStdString(label);
int counter = 0;
while (labelList.contains(currentLabel))
{
currentLabel = QString::fromStdString(label + std::to_string(counter));
counter++;
}
return currentLabel.toStdString();
}
QmitkChartWidget::QmitkChartWidget(QWidget *parent) : QWidget(parent), m_Impl(new Impl(this))
{
connect(this, &QmitkChartWidget::PageSuccessfullyLoaded, this, &QmitkChartWidget::OnPageSuccessfullyLoaded);
}
QmitkChartWidget::~QmitkChartWidget() {}
void QmitkChartWidget::SetColor(const std::string &label, const std::string &colorName)
{
m_Impl->SetColor(label, colorName);
}
void QmitkChartWidget::SetLineStyle(const std::string &label, LineStyle style)
{
m_Impl->SetLineStyle(label, style);
}
void QmitkChartWidget::SetMarkerSymbol(const std::string &label, MarkerSymbol symbol)
{
m_Impl->SetMarkerSymbol(label, symbol);
}
void QmitkChartWidget::SetYAxisScale(AxisScale scale)
{
m_Impl->SetYAxisScale(scale);
}
void QmitkChartWidget::AddData1D(const std::vector<double> &data1D, const std::string &label, ChartType type)
{
m_Impl->AddData1D(data1D, label, type);
}
void QmitkChartWidget::AddData2D(const std::vector< std::pair<double, double> >& data2D, const std::string& label, ChartType type)
{
m_Impl->AddData2D(data2D, label, type);
}
void QmitkChartWidget::AddChartExampleData(const std::vector< std::pair<double, double> >& data2D,
const std::string& label,
const std::string& type,
const std::string& color,
const std::string& lineStyle,
const std::string& pieLabelsData)
{
m_Impl->AddChartExampleData(data2D, label, type, color, lineStyle, pieLabelsData);
}
void QmitkChartWidget::UpdateData1D(const std::vector<double> &data1D, const std::string &label)
{
m_Impl->UpdateData1D(data1D, label);
}
void QmitkChartWidget::UpdateData2D(const std::vector< std::pair<double, double> > &data2D, const std::string &label)
{
m_Impl->UpdateData2D(data2D, label);
}
void QmitkChartWidget::UpdateChartExampleData(const std::vector< std::pair<double, double> >& data2D,
const std::string& label,
const std::string& type,
const std::string& color,
const std::string& lineStyle,
const std::string& pieLabelsData)
{
m_Impl->UpdateChartExampleData(data2D, label, type, color, lineStyle, pieLabelsData);
}
void QmitkChartWidget::RemoveData(const std::string &label)
{
m_Impl->RemoveData(label);
}
void QmitkChartWidget::UpdateLabel(const std::string &existingLabel, const std::string &newLabel) {
m_Impl->UpdateLabel(existingLabel, newLabel);
}
QmitkChartxyData* QmitkChartWidget::GetDataElementByLabel(const std::string& label) const
{
return m_Impl->GetDataElementByLabel(label);
}
void QmitkChartWidget::SetXAxisLabel(const std::string &label)
{
m_Impl->SetXAxisLabel(label);
}
void QmitkChartWidget::SetYAxisLabel(const std::string &label)
{
m_Impl->SetYAxisLabel(label);
}
void QmitkChartWidget::SetPieLabels(const std::vector<std::string> &pieLabels, const std::string &label)
{
m_Impl->SetPieLabels(pieLabels, label);
}
void QmitkChartWidget::SetTitle(const std::string &title)
{
m_Impl->SetTitle(title);
}
void QmitkChartWidget::SetShowDataPoints(bool showDataPoints)
{
m_Impl->SetShowDataPoints(showDataPoints);
}
void QmitkChartWidget::SetChartType(const std::string &label, ChartType type)
{
m_Impl->SetChartType(label, type);
}
void QmitkChartWidget::SetXErrorBars(const std::string &label,
const std::vector<double> &errorPlus,
const std::vector<double> &errorMinus)
{
m_Impl->SetXErrorBars(label, errorPlus, errorMinus);
}
void QmitkChartWidget::SetYErrorBars(const std::string &label,
const std::vector<double> &errorPlus,
const std::vector<double> &errorMinus)
{
m_Impl->SetYErrorBars(label, errorPlus, errorMinus);
}
void QmitkChartWidget::SetLegendPosition(LegendPosition position)
{
m_Impl->SetLegendPosition(position);
}
void QmitkChartWidget::SetShowLegend(bool show)
{
m_Impl->SetShowLegend(show);
}
void QmitkChartWidget::SetStackedData(bool stacked)
{
m_Impl->SetStackedData(stacked);
}
void QmitkChartWidget::Show(bool showSubChart)
{
m_Impl->Show(showSubChart);
}
void QmitkChartWidget::Clear()
{
m_Impl->ClearData();
m_Impl->ClearJavaScriptChart();
}
void QmitkChartWidget::OnLoadFinished(bool isLoadSuccessful)
{
if (isLoadSuccessful)
{
emit PageSuccessfullyLoaded();
}
}
void QmitkChartWidget::OnPageSuccessfullyLoaded()
{
auto themeName = m_Impl->GetThemeName();
QString command;
if (themeName == "dark")
{
command = QString("changeTheme('dark')");
}
else
{
command = QString("changeTheme('light')");
}
m_Impl->CallJavaScriptFuntion(command);
}
void QmitkChartWidget::SetTheme(ColorTheme themeEnabled)
{
m_Impl->SetThemeName(themeEnabled);
}
void QmitkChartWidget::SetShowSubchart(bool showSubChart)
{
m_Impl->SetShowSubchart(showSubChart);
}
void QmitkChartWidget::SetShowErrorBars(bool showErrorBars)
{
m_Impl->SetShowErrorBars(showErrorBars);
}
void QmitkChartWidget::SetMinMaxValueXView(double minValueX, double maxValueX)
{
m_Impl->SetMinMaxValueXView(minValueX, maxValueX);
}
void QmitkChartWidget::SetMinMaxValueYView(double minValueY, double maxValueY)
{
m_Impl->SetMinMaxValueYView(minValueY, maxValueY);
}
void QmitkChartWidget::Reload()
{
const QString command = QString("Reload()");
m_Impl->CallJavaScriptFuntion(command);
}
QSize QmitkChartWidget::sizeHint() const
{
return m_Impl->sizeHint();
}
void QmitkChartWidget::SavePlotAsImage()
{
m_Impl->GetImageUrl();
}
diff --git a/Modules/Classification/CLCore/CMakeLists.txt b/Modules/Classification/CLCore/CMakeLists.txt
index cdcd91eb6f..0dd8fad85e 100644
--- a/Modules/Classification/CLCore/CMakeLists.txt
+++ b/Modules/Classification/CLCore/CMakeLists.txt
@@ -1,5 +1,4 @@
MITK_CREATE_MODULE(
DEPENDS MitkCore MitkCommandLine
- PACKAGE_DEPENDS
- PUBLIC Eigen
+ PACKAGE_DEPENDS PUBLIC ITK|ITKEigen3
)
diff --git a/Modules/Classification/CLCore/include/mitkAbstractClassifier.h b/Modules/Classification/CLCore/include/mitkAbstractClassifier.h
index 40c55a9ceb..ff976df587 100644
--- a/Modules/Classification/CLCore/include/mitkAbstractClassifier.h
+++ b/Modules/Classification/CLCore/include/mitkAbstractClassifier.h
@@ -1,195 +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 mitkAbstractClassifier_h
#define mitkAbstractClassifier_h
#include <MitkCLCoreExports.h>
#include <mitkBaseData.h>
// Eigen
-#include <Eigen/Dense>
+#include <itkeigen/Eigen/Dense>
// STD Includes
// MITK includes
#include <mitkConfigurationHolder.h>
namespace mitk
{
class MITKCLCORE_EXPORT AbstractClassifier : public BaseData
{
public:
mitkClassMacro(AbstractClassifier,BaseData);
///
/// @brief Build a forest of trees from the training set (X, y).
/// @param X The training input samples. Matrix of shape = [n_samples, n_features]
/// @param Y The target values (class labels in classification, real numbers in regression). Matrix of shape = [n_samples, 1]
///
virtual void Train(const Eigen::MatrixXd &X, const Eigen::MatrixXi &Y) = 0;
///
/// @brief Predict class for X.
/// @param X The input samples.
/// @return The predicted classes. Y matrix of shape = [n_samples, 1]
///
virtual Eigen::MatrixXi Predict(const Eigen::MatrixXd &X) = 0;
///
/// @brief GetPointWiseWeightCopy
/// @return return label matrix of shape = [n_samples , 1]
///
Eigen::MatrixXi & GetLabels()
{
return m_OutLabel;
}
protected:
Eigen::MatrixXi m_OutLabel;
public:
// * --------------- *
// PointWiseWeight
// * --------------- *
///
/// @brief SupportsPointWiseWeight
/// @return True if the classifier supports pointwise weighting else false
///
virtual bool SupportsPointWiseWeight() = 0;
///
/// @brief GetPointWiseWeightCopy
/// @return Create and return a copy of W
///
virtual Eigen::MatrixXd & GetPointWiseWeight()
{
return m_PointWiseWeight;
}
///
/// @brief SetPointWiseWeight
/// @param W The pointwise weights. W matrix of shape = [n_samples, 1]
///
virtual void SetPointWiseWeight(const Eigen::MatrixXd& W)
{
this->m_PointWiseWeight = W;
}
///
/// @brief UsePointWiseWeight
/// @param value weighting on/off
///
virtual void UsePointWiseWeight(bool value)
{
this->m_IsUsingPointWiseWeight = value;
}
///
/// @brief IsUsingPointWiseWeight
/// @return true if pointewise weighting is enabled.
///
virtual bool IsUsingPointWiseWeight()
{
return this->m_IsUsingPointWiseWeight;
}
protected:
Eigen::MatrixXd m_PointWiseWeight;
bool m_IsUsingPointWiseWeight;
// * --------------- *
// PointWiseProbabilities
// * --------------- *
public:
///
/// @brief SupportsPointWiseProbability
/// @return True if the classifier supports pointwise class probability calculation else false
///
virtual bool SupportsPointWiseProbability() = 0;
///
/// @brief GetPointWiseWeightCopy
/// @return return probability matrix
///
virtual Eigen::MatrixXd & GetPointWiseProbabilities()
{
return m_OutProbability;
}
///
/// \brief UsePointWiseProbabilities
/// \param value
///
virtual void UsePointWiseProbability(bool value)
{
m_IsUsingPointWiseProbability = value;
}
///
/// \brief IsUsingPointWiseProbabilities
/// \return
///
virtual bool IsUsingPointWiseProbability()
{
return m_IsUsingPointWiseProbability;
}
protected:
Eigen::MatrixXd m_OutProbability;
bool m_IsUsingPointWiseProbability;
private:
void MethodForBuild();
public:
void SetNthItems(const char *val, unsigned int idx);
std::string GetNthItems(unsigned int idx) const;
void SetItemList(std::vector<std::string>);
std::vector<std::string> GetItemList() const;
#ifndef DOXYGEN_SKIP
void SetRequestedRegionToLargestPossibleRegion() override{}
bool RequestedRegionIsOutsideOfTheBufferedRegion() override{return true;}
bool VerifyRequestedRegion() override{return false;}
void SetRequestedRegion(const itk::DataObject* /*data*/) override{}
// Override
bool IsEmpty() const override
{
if(IsInitialized() == false)
return true;
const TimeGeometry* timeGeometry = const_cast<AbstractClassifier*>(this)->GetUpdatedTimeGeometry();
if(timeGeometry == nullptr)
return true;
return false;
}
#endif // Skip Doxygen
};
}
#endif
diff --git a/Modules/Classification/CLCore/include/mitkAbstractGlobalImageFeature.h b/Modules/Classification/CLCore/include/mitkAbstractGlobalImageFeature.h
index a1e36bc404..5f3234358e 100644
--- a/Modules/Classification/CLCore/include/mitkAbstractGlobalImageFeature.h
+++ b/Modules/Classification/CLCore/include/mitkAbstractGlobalImageFeature.h
@@ -1,342 +1,342 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkAbstractGlobalImageFeature_h
#define mitkAbstractGlobalImageFeature_h
#include <MitkCLCoreExports.h>
#include <mitkBaseData.h>
#include <mitkImage.h>
#include <mitkCommandLineParser.h>
#include <mitkIntensityQuantifier.h>
// STD Includes
// Eigen
-#include <Eigen/Dense>
+#include <itkeigen/Eigen/Dense>
// MITK includes
#include <mitkConfigurationHolder.h>
namespace mitk
{
/**Used as ID for features calculated by feature classes*/
struct MITKCLCORE_EXPORT FeatureID
{
/**Name of the feature*/
std::string name;
/**Name of the feature class*/
std::string featureClass;
/**ID for the setting that is represented by parameters and is specified by the feature class while calculating the features. It must be as unique as the parameters themself.*/
std::string settingID;
- /**Alternative name that containes the legacy naming of the feature that encodes the parametersetting directly in the string.*/
+ /**Alternative name that contains the legacy naming of the feature that encodes the parametersetting directly in the string.*/
std::string legacyName;
/**Version of the feature definition*/
std::string version = "1";
using ParametersType = std::map<std::string, us::Any>;
ParametersType parameters;
bool operator < (const FeatureID& rh) const;
bool operator ==(const FeatureID& rh) const;
};
- /**Helper that takes a pass templateID clones it and populates it with the also passed informations befor returning it.
+ /**Helper that takes a pass templateID clones it and populates it with the also passed informations before returning it.
* @param templateID reference ID that should be cloned.
* @param name Name of the feature.*/
MITKCLCORE_EXPORT FeatureID CreateFeatureID(FeatureID templateID, std::string name);
/**
*
*
* ## Histogram Configuration ##
* Most Feature Generation Classes that use histograms use the same parameters and
* initialization logic. In general, all information can be passed either by the corresponding
- * Setter (which does not differenciate between global setting and feature specific setting) and
+ * Setter (which does not differentiate between global setting and feature specific setting) and
* a parameter object which can be obtained from the command line arguments, for example.
*
- * If the image values are used for the initializiation of the histogram, it can be defined
+ * If the image values are used for the initialization of the histogram, it can be defined
* whether the whole image is used or only the masked areas to find minima and maxima. This is
- * done by the option <b>SetIgnoreMask</b> or the corrsponding options
+ * done by the option <b>SetIgnoreMask</b> or the corresponding options
* <b>-%NAME::ignore-mask-for-histogram</b> and <b>-ignore-mask-for-histogram</b>. If these are
* true, the whole image is used for the calculation.
*
* Depending on the passed arguments, different initialization methods are used. The initialization
* is in the following order:
* - If <b>Minimum Intensity</b>, <b>Maximum Intensity</b>, and <b>Binsize</b>: The histogram is
* initialized between the minimum and maximum intensity. the number of bins is determined by the
* binsize. If the distance between minimum and maximum is not a multiple of the binsize, the maximum
* is increase so that it is.
* - <b>Minimum Intensity</b>, <b>Bins</b>, and <b>Binsize</b>: The histogram is initialized with the
* given binsize, and the intensity range from the minimum to \f$maximum = minimum + binsize*bins\f$.
* - <b>Minimum Intensity</b>, <b>Maximum Intensity</b>, and <b>Bins</b>: The histogram is initialized
* between the given minimum and maximum intensity. The binsize is calculated so that the number
* of bins is equal to the given number of bins.
* - <b>Binsize</b>, and <b>Minimum Intensity</b>: The maximum is set to the maximum that
* occur in the given image. Depending if the mask is considered or not, either only masked voxels or
* the whole image is used for the calculation. The initialization is then equal as if the minimum
* and maximum would have been given right from the beginning.
* - <b>Binsize</b>, and <b>Maximum Intensity</b>: The minimum intensity is set to the minimum that
* occur in the given image. Depending if the mask is considered or not, either only masked voxels or
* the whole image is used for the calculation. The initialization is then equal as if the minimum
* and maximum would have been given right from the beginning.
* - <b>Binsize</b>: The maximum and the minimum intensity is set to the minimum and maximum that
* occur in the given image. Depending if the mask is considered or not, either only masked voxels or
* the whole image is used for the calculation. The initialization is then equal as if the minimum
* and maximum would have been given right from the beginning.
* - <b>Bins</b>, and <b>Minimum Intensity</b>: The maximum is calculated from the image. Depending
* if the mask is considered or not, either only masked voxels or the whole image is used for the calculation. The histogram is
* then initialized as if these values would have been given as minimum and maximum intensity.
* - <b>Bins</b>, and <b>Maximum Intensity</b>: The minimum is calculated from the image. Depending
* if the mask is considered or not, either only masked voxels or the whole image is used for the calculation. The histogram is
* then initialized as if these values would have been given as minimum and maximum intensity.
* - <b>Bins</b>: The minimum and the maximum is calculated from the image. Depending
* if the mask is considered or not, either only masked voxels or * the whole image is used for the calculation. The histogram is
* then initialized as if these values would have been given as minimum and maximum intensity.
* - <b>No Parameter given</b>:The minimum and maximum intensity from the whole image or masked image is calculated and
* the histogram then initialized to this with a standard number of bins (Is set by each filter on its own.)
*
* ### Remark about command line parameter####
* There are generally two options to set a parameter via the command line. A global one that works for
* all filters that use histograms and a local one that set this parameter specific for this filter. The
- * local parameters start with the filter name (Indiciated by NAME) followed by two colons, for example
+ * local parameters start with the filter name (Indicated by NAME) followed by two colons, for example
* <b>vol::min</b> to set the minimum intensity for the volume filter. The global parameter is overwritten
* by the local parameter, if it is specified. Otherwise, it is still valid. If this prevents the specification
* of an histogram initialization method (for example, because the binsize is globally specified but the histogram
- * should be initialized using a fixed numbe of bins), the parameter <b>NAME::ignore-global-histogram</b> can be passed.
+ * should be initialized using a fixed number of bins), the parameter <b>NAME::ignore-global-histogram</b> can be passed.
* Then, all global histogram parameters are ignored and only local ones are used.
*
* The maximum intensity can be set by different command line parameters: global for all filters that use histograms
* by <b>-minimum-intensity</b> and <b>-minimum</b>. Alternative it can be set only for this filter by
* <b>-%NAME::minimum</b> and <b>-%NAME::min</b>.
*
* The minimum intensity can be set by different command line parameters: global for all filters that use histograms
* by <b>-maximum-intensity</b> and <b>-maximum</b>. Alternative it can be set only for this filter by
* <b>-%NAME::maximum</b> and <b>-%NAME::max</b>.
*
* The binsize can be set by different command line parameters: global for all filters that use histograms
* by <b>-binsize</b>. Alternative it can be set only for this filter by
* <b>-%NAME::binsize</b>.
*
* The number of bins can be set by different command line parameters: global for all filters that use histograms
* by <b>-bins</b>. Alternative it can be set only for this filter by
* <b>-%NAME::bins</b>.
* ### Note to the developers ###
* All features are supposed to work the same way if a histogram is used somewhere in
* the code. For this, each derived class that makes use of a histogram should use
* the Quantifier object. In order to use this object correctly, the AddArguments-Function should
* contain the line <b>AddQuantifierArguments(parser);</b>, the CalculateFeaturesUsingParameters function
* should contain the line <b>InitializeQuantifierFromParameters(feature, mask);</b> and the CalculateFeatures function
- * sould contain the line <b>InitializeQuantifier(image, mask);</b>. These function
+ * should contain the line <b>InitializeQuantifier(image, mask);</b>. These function
* calls ensure that the necessary options are given to the configuration file, and that the initialization
- * of the quantifier is done correctly. This ensures an consistend behavior over all FeatureGeneration Classes.
+ * of the quantifier is done correctly. This ensures a consistent behavior over all FeatureGeneration Classes.
*
*/
class MITKCLCORE_EXPORT AbstractGlobalImageFeature : public BaseData
{
public:
mitkClassMacro(AbstractGlobalImageFeature, BaseData);
typedef std::vector< std::pair<FeatureID, double> > FeatureListType;
using ParametersType = FeatureID::ParametersType;
/**
- * \brief Calculates the feature of this abstact interface. Does not necessarily considers the parameter settings.
+ * \brief Calculates the feature of this abstract interface. Does not necessarily considers the parameter settings.
*/
FeatureListType CalculateFeatures(const Image* image, const Image* mask);
virtual FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) = 0;
/**
- * \brief Calculates the given feature Slice-wise. Might not be availble for an individual filter!
+ * \brief Calculates the given feature Slice-wise. Might not be available for an individual filter!
*/
FeatureListType CalculateFeaturesSlicewise(const Image::Pointer & image, const Image::Pointer &mask, int sliceID);
/**
- * \brief Calculates the feature of this abstact interface. Does not necessarily considers the parameter settings.
+ * \brief Calculates the feature of this abstract interface. Does not necessarily considers the parameter settings.
*/
virtual void CalculateAndAppendFeaturesSliceWise(const Image::Pointer & image, const Image::Pointer &mask, int sliceID, FeatureListType &featureList, bool checkParameterActivation = true);
/**
- * \brief Calculates the feature of this abstact interface. Does not necessarily considers the parameter settings.
+ * \brief Calculates the feature of this abstract interface. Does not necessarily considers the parameter settings.
* @param image
* @param mask
* @param maskNoNaN
* @param featureList
* @param checkParameterActivation Indicates if the features should only be calculated and added if the FeatureClass is activated in the parameters.
- * True: only append if activated in the parametes. False: always and append it.
+ * True: only append if activated in the parameters. False: always and append it.
*/
void CalculateAndAppendFeatures(const Image* image, const Image* mask, const Image* maskNoNaN, FeatureListType &featureList, bool checkParameterActivation = true);
itkSetMacro(Prefix, std::string);
itkSetMacro(ShortName, std::string);
itkSetMacro(LongName, std::string);
itkSetMacro(FeatureClassName, std::string);
itkSetMacro(Direction, int);
void SetParameters(ParametersType param)
{
m_Parameters = param;
this->ConfigureQuantifierSettingsByParameters();
this->ConfigureSettingsByParameters(param);
this->Modified();
};
itkGetConstMacro(Prefix, std::string);
itkGetConstMacro(ShortName, std::string);
itkGetConstMacro(LongName, std::string);
itkGetConstMacro(FeatureClassName, std::string);
itkGetConstMacro(Parameters, ParametersType);
itkGetMacro(Quantifier, IntensityQuantifier::Pointer);
itkGetConstMacro(Direction, int);
itkSetMacro(MinimumIntensity, double);
itkSetMacro(UseMinimumIntensity, bool);
itkSetMacro(MaximumIntensity, double);
itkSetMacro(UseMaximumIntensity, bool);
itkGetConstMacro(MinimumIntensity, double);
itkGetConstMacro(UseMinimumIntensity, bool);
itkGetConstMacro(MaximumIntensity, double);
itkGetConstMacro(UseMaximumIntensity, bool);
itkSetMacro(Binsize, double);
itkSetMacro(UseBinsize, bool);
itkGetConstMacro(Binsize, double);
itkGetConstMacro(UseBinsize, bool);
itkSetMacro(MorphMask, mitk::Image::Pointer);
itkGetConstMacro(MorphMask, mitk::Image::Pointer);
itkSetMacro(Bins, int);
itkSetMacro(UseBins, bool);
itkGetConstMacro(UseBins, bool);
itkGetConstMacro(Bins, int);
itkSetMacro(IgnoreMask, bool);
itkGetConstMacro(IgnoreMask, bool);
itkSetMacro(EncodeParametersInFeaturePrefix, bool);
itkGetConstMacro(EncodeParametersInFeaturePrefix, bool);
itkBooleanMacro(EncodeParametersInFeaturePrefix);
std::string GetOptionPrefix() const
{
if (!m_Prefix.empty())
return m_Prefix + "::" + m_ShortName;
return m_ShortName;
}
/** Can be called to add all relevant argument for configuring the feature instance to the passed parser instance.
Must be implemented be derived classes. For adding the quantifier arguments use AddQuantifierArguments(...) as
helper function.*/
virtual void AddArguments(mitkCommandLineParser &parser) const = 0;
/** Helper function that generates the legacy feature name without encoding of parameters; as it is used e.g.
in the unit tests.*/
static std::string GenerateLegacyFeatureNameWOEncoding(const FeatureID& id);
protected:
std::vector<double> SplitDouble(std::string str, char delimiter);
virtual FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) = 0;
void AddQuantifierArguments(mitkCommandLineParser& parser) const;
/** Ensures that all quantifier relevant variables of the instance are set correctly given the information in m_Parameters.*/
void ConfigureQuantifierSettingsByParameters();
/** Ensures that the instance is configured according to the information given in the passed parameters.
* This method will be called by SetParameters(...) after ConfigureQuantifierSettingsByParameters() was called.*/
virtual void ConfigureSettingsByParameters(const ParametersType& parameters);
/**Initializes the quantifier gigen the quantifier relevant variables and the passed arguments.*/
void InitializeQuantifier(const Image* image, const Image* mask, unsigned int defaultBins = 256);
/** Helper that encodes the quantifier parameters in a string (e.g. used for the legacy feature name)*/
std::string QuantifierParameterString() const;
/* Creates a template feature id.
* it will set the featureClass, the settingID (assuming that it is the featureClass with the passed suffix
* and all parameters that are global or have the option prefix of the instance.*/
FeatureID CreateTemplateFeatureID(std::string settingsSuffix = "", FeatureID::ParametersType additionalParams = {});
/** Helper that generates the legacy feature names for a passed FeatureID.
* Format of the legacy feature name is: \<ClassName\>::[\<LegacyFeatureEncoding\>::]\<LegacyFeatureNamePart\>
* Overwrite GenerateLegacyFeatureNamePart and GenerateLegacyFeatureEncoding to change behavior in
* derived classes.
*/
virtual std::string GenerateLegacyFeatureName(const FeatureID& id) const;
virtual std::string GenerateLegacyFeatureNamePart(const FeatureID& id) const;
virtual std::string GenerateLegacyFeatureEncoding(const FeatureID& id) const;
public:
//#ifndef DOXYGEN_SKIP
void SetRequestedRegionToLargestPossibleRegion() override {};
bool RequestedRegionIsOutsideOfTheBufferedRegion() override { return true; };
bool VerifyRequestedRegion() override { return false; };
void SetRequestedRegion (const itk::DataObject * /*data*/) override {};
// Override
bool IsEmpty() const override
{
if(IsInitialized() == false)
return true;
const TimeGeometry* timeGeometry = const_cast<AbstractGlobalImageFeature*>(this)->GetUpdatedTimeGeometry();
if(timeGeometry == nullptr)
return true;
return false;
}
private:
std::string m_Prefix; // Prefix before all input parameters
std::string m_ShortName; // Name of all variables
std::string m_LongName; // Long version of the name (For turning on)
std::string m_FeatureClassName;
ParametersType m_Parameters; // Parameter setting
mitk::Image::Pointer m_MorphMask = nullptr;
IntensityQuantifier::Pointer m_Quantifier;
//Quantifier relevant variables
double m_MinimumIntensity = 0;
bool m_UseMinimumIntensity = false;
double m_MaximumIntensity = 100;
bool m_UseMaximumIntensity = false;
bool m_EncodeParametersInFeaturePrefix = false;
double m_Binsize = 1;
bool m_UseBinsize = false;
int m_Bins = 256;
bool m_UseBins = true;
int m_Direction = 0;
bool m_IgnoreMask = false;
//#endif // Skip Doxygen
};
}
#endif
diff --git a/Modules/Classification/CLCore/src/mitkAbstractGlobalImageFeature.cpp b/Modules/Classification/CLCore/src/mitkAbstractGlobalImageFeature.cpp
index e5b0a156d9..0e2ae7ebf1 100644
--- a/Modules/Classification/CLCore/src/mitkAbstractGlobalImageFeature.cpp
+++ b/Modules/Classification/CLCore/src/mitkAbstractGlobalImageFeature.cpp
@@ -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.
============================================================================*/
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkImageCast.h>
#include <mitkITKImageImport.h>
#include <iterator>
bool mitk::FeatureID::operator < (const FeatureID& rh) const
{
if (this->featureClass < rh.featureClass) return true;
if (this->featureClass == rh.featureClass)
{
if (this->name < rh.name) return true;
if (this->name == rh.name && this->settingID < rh.settingID) return true;
}
return false;
}
bool mitk::FeatureID::operator ==(const FeatureID& rh) const
{
if (this->name != rh.name) return false;
if (this->settingID != rh.settingID) return false;
if (this->featureClass != rh.featureClass) return false;
return true;
}
mitk::FeatureID mitk::CreateFeatureID(FeatureID templateID, std::string name)
{
auto newID = templateID;
newID.name = name;
return newID;
}
static void
ExtractSlicesFromImages(mitk::Image::Pointer image, mitk::Image::Pointer mask,
int direction,
std::vector<mitk::Image::Pointer> &imageVector,
std::vector<mitk::Image::Pointer> &maskVector)
{
typedef itk::Image< double, 2 > FloatImage2DType;
typedef itk::Image< unsigned short, 2 > MaskImage2DType;
typedef itk::Image< double, 3 > FloatImageType;
typedef itk::Image< unsigned short, 3 > MaskImageType;
FloatImageType::Pointer itkFloat = FloatImageType::New();
MaskImageType::Pointer itkMask = MaskImageType::New();
mitk::CastToItkImage(mask, itkMask);
mitk::CastToItkImage(image, itkFloat);
int idxA, idxB, idxC;
switch (direction)
{
case 0:
idxA = 1; idxB = 2; idxC = 0;
break;
case 1:
idxA = 0; idxB = 2; idxC = 1;
break;
case 2:
idxA = 0; idxB = 1; idxC = 2;
break;
default:
idxA = 1; idxB = 2; idxC = 0;
break;
}
auto imageSize = image->GetLargestPossibleRegion().GetSize();
FloatImageType::IndexType index3D;
FloatImage2DType::IndexType index2D;
FloatImage2DType::SpacingType spacing2D;
spacing2D[0] = itkFloat->GetSpacing()[idxA];
spacing2D[1] = itkFloat->GetSpacing()[idxB];
for (unsigned int i = 0; i < imageSize[idxC]; ++i)
{
FloatImage2DType::RegionType region;
FloatImage2DType::IndexType start;
FloatImage2DType::SizeType size;
start[0] = 0; start[1] = 0;
size[0] = imageSize[idxA];
size[1] = imageSize[idxB];
region.SetIndex(start);
region.SetSize(size);
FloatImage2DType::Pointer image2D = FloatImage2DType::New();
image2D->SetRegions(region);
image2D->Allocate();
MaskImage2DType::Pointer mask2D = MaskImage2DType::New();
mask2D->SetRegions(region);
mask2D->Allocate();
unsigned long voxelsInMask = 0;
for (unsigned int a = 0; a < imageSize[idxA]; ++a)
{
for (unsigned int b = 0; b < imageSize[idxB]; ++b)
{
index3D[idxA] = a;
index3D[idxB] = b;
index3D[idxC] = i;
index2D[0] = a;
index2D[1] = b;
image2D->SetPixel(index2D, itkFloat->GetPixel(index3D));
mask2D->SetPixel(index2D, itkMask->GetPixel(index3D));
voxelsInMask += (itkMask->GetPixel(index3D) > 0) ? 1 : 0;
}
}
image2D->SetSpacing(spacing2D);
mask2D->SetSpacing(spacing2D);
mitk::Image::Pointer tmpFloatImage = mitk::Image::New();
tmpFloatImage->InitializeByItk(image2D.GetPointer());
mitk::GrabItkImageMemory(image2D, tmpFloatImage);
mitk::Image::Pointer tmpMaskImage = mitk::Image::New();
tmpMaskImage->InitializeByItk(mask2D.GetPointer());
mitk::GrabItkImageMemory(mask2D, tmpMaskImage);
if (voxelsInMask > 0)
{
imageVector.push_back(tmpFloatImage);
maskVector.push_back(tmpMaskImage);
}
}
}
std::vector<double> mitk::AbstractGlobalImageFeature::SplitDouble(std::string str, char delimiter) {
std::vector<double> internal;
std::stringstream ss(str); // Turn the string into a stream.
std::string tok;
double val;
while (std::getline(ss, tok, delimiter)) {
std::stringstream s2(tok);
s2 >> val;
internal.push_back(val);
}
return internal;
}
void mitk::AbstractGlobalImageFeature::AddQuantifierArguments(mitkCommandLineParser &parser) const
{
std::string name = GetOptionPrefix();
- parser.addArgument(name + "::minimum", name + "::min", mitkCommandLineParser::Float, "Minium Intensity for Quantification", "Defines the minimum Intensity used for Quantification", us::Any());
+ parser.addArgument(name + "::minimum", name + "::min", mitkCommandLineParser::Float, "Minimum Intensity for Quantification", "Defines the minimum Intensity used for Quantification", us::Any());
parser.addArgument(name + "::maximum", name + "::max", mitkCommandLineParser::Float, "Maximum Intensity for Quantification", "Defines the maximum Intensity used for Quantification", us::Any());
parser.addArgument(name + "::bins", name + "::bins", mitkCommandLineParser::Int, "Number of Bins", "Define the number of bins that is used ", us::Any());
parser.addArgument(name + "::binsize", name + "::binsize", mitkCommandLineParser::Float, "Binsize", "Define the size of the used bins", us::Any());
parser.addArgument(name + "::ignore-global-histogram", name + "::ignore-global-histogram", mitkCommandLineParser::Bool, "Ignore the global histogram Parameters", "Ignores the global histogram parameters", us::Any());
parser.addArgument(name + "::ignore-mask-for-histogram", name + "::ignore-mask", mitkCommandLineParser::Bool, "Ignore the global histogram Parameters", "Ignores the global histogram parameters", us::Any());
}
void mitk::AbstractGlobalImageFeature::ConfigureQuantifierSettingsByParameters()
{
unsigned int bins = 0;
double binsize = 0;
double minimum = 0;
double maximum = 0;
auto parsedArgs = GetParameters();
std::string name = GetOptionPrefix();
bool useGlobal = true;
if (parsedArgs.count(name + "::ignore-global-histogram"))
{
useGlobal = false;
SetUseMinimumIntensity(false);
SetUseMaximumIntensity(false);
SetUseBinsize(false);
SetUseBins(false);
}
if (useGlobal)
{
if (parsedArgs.count("ignore-mask-for-histogram"))
{
bool tmp = us::any_cast<bool>(parsedArgs["ignore-mask-for-histogram"]);
SetIgnoreMask(tmp);
}
if (parsedArgs.count("minimum-intensity"))
{
minimum = us::any_cast<float>(parsedArgs["minimum-intensity"]);
SetMinimumIntensity(minimum);
SetUseMinimumIntensity(true);
}
if (parsedArgs.count("maximum-intensity"))
{
maximum = us::any_cast<float>(parsedArgs["maximum-intensity"]);
SetMaximumIntensity(maximum);
SetUseMaximumIntensity(true);
}
if (parsedArgs.count("bins"))
{
bins = us::any_cast<int>(parsedArgs["bins"]);
SetBins(bins);
SetUseBins(true);
}
if (parsedArgs.count("binsize"))
{
binsize = us::any_cast<float>(parsedArgs["binsize"]);
SetBinsize(binsize);
SetUseBinsize(true);
}
}
if (parsedArgs.count(name+"::ignore-mask-for-histogram"))
{
bool tmp = us::any_cast<bool>(parsedArgs[name+"::ignore-mask-for-histogram"]);
SetIgnoreMask(tmp);
}
if (parsedArgs.count(name + "::minimum"))
{
minimum = us::any_cast<float>(parsedArgs[name + "::minimum"]);
SetMinimumIntensity(minimum);
SetUseMinimumIntensity(true);
}
if (parsedArgs.count(name + "::maximum"))
{
maximum = us::any_cast<float>(parsedArgs[name + "::maximum"]);
SetMaximumIntensity(maximum);
SetUseMaximumIntensity(true);
}
if (parsedArgs.count(name + "::bins"))
{
bins = us::any_cast<int>(parsedArgs[name + "::bins"]);
SetBins(bins);
}
if (parsedArgs.count(name + "::binsize"))
{
binsize = us::any_cast<float>(parsedArgs[name + "::binsize"]);
SetBinsize(binsize);
SetUseBinsize(true);
}
}
void mitk::AbstractGlobalImageFeature::ConfigureSettingsByParameters(const ParametersType& /*parameters*/)
{
//Default implementation does nothing.
//Override to change behavior.
}
void mitk::AbstractGlobalImageFeature::InitializeQuantifier(const Image* image, const Image* mask, unsigned int defaultBins)
{
m_Quantifier = IntensityQuantifier::New();
if (GetUseMinimumIntensity() && GetUseMaximumIntensity() && GetUseBinsize())
m_Quantifier->InitializeByBinsizeAndMaximum(GetMinimumIntensity(), GetMaximumIntensity(), GetBinsize());
else if (GetUseMinimumIntensity() && GetUseBins() && GetUseBinsize())
m_Quantifier->InitializeByBinsizeAndBins(GetMinimumIntensity(), GetBins(), GetBinsize());
else if (GetUseMinimumIntensity() && GetUseMaximumIntensity() && GetUseBins())
m_Quantifier->InitializeByMinimumMaximum(GetMinimumIntensity(), GetMaximumIntensity(), GetBins());
- // Intialize from Image and Binsize
+ // Initialize from Image and Binsize
else if (GetUseBinsize() && GetIgnoreMask() && GetUseMinimumIntensity())
m_Quantifier->InitializeByImageAndBinsizeAndMinimum(image, GetMinimumIntensity(), GetBinsize());
else if (GetUseBinsize() && GetIgnoreMask() && GetUseMaximumIntensity())
m_Quantifier->InitializeByImageAndBinsizeAndMaximum(image, GetMaximumIntensity(), GetBinsize());
else if (GetUseBinsize() && GetIgnoreMask())
m_Quantifier->InitializeByImageAndBinsize(image, GetBinsize());
// Initialize form Image, Mask and Binsize
else if (GetUseBinsize() && GetUseMinimumIntensity())
m_Quantifier->InitializeByImageRegionAndBinsizeAndMinimum(image, mask, GetMinimumIntensity(), GetBinsize());
else if (GetUseBinsize() && GetUseMaximumIntensity())
m_Quantifier->InitializeByImageRegionAndBinsizeAndMaximum(image, mask, GetMaximumIntensity(), GetBinsize());
else if (GetUseBinsize())
m_Quantifier->InitializeByImageRegionAndBinsize(image, mask, GetBinsize());
- // Intialize from Image and Bins
+ // Initialize from Image and Bins
else if (GetUseBins() && GetIgnoreMask() && GetUseMinimumIntensity())
m_Quantifier->InitializeByImageAndMinimum(image, GetMinimumIntensity(), GetBins());
else if (GetUseBins() && GetIgnoreMask() && GetUseMaximumIntensity())
m_Quantifier->InitializeByImageAndMaximum(image, GetMaximumIntensity(), GetBins());
else if (GetUseBins())
m_Quantifier->InitializeByImage(image, GetBins());
- // Intialize from Image, Mask and Bins
+ // Initialize from Image, Mask and Bins
else if (GetUseBins() && GetUseMinimumIntensity())
m_Quantifier->InitializeByImageRegionAndMinimum(image, mask, GetMinimumIntensity(), GetBins());
else if (GetUseBins() && GetUseMaximumIntensity())
m_Quantifier->InitializeByImageRegionAndMaximum(image, mask, GetMaximumIntensity(), GetBins());
else if (GetUseBins())
m_Quantifier->InitializeByImageRegion(image, mask, GetBins());
// Default
else if (GetIgnoreMask())
m_Quantifier->InitializeByImage(image, GetBins());
else
m_Quantifier->InitializeByImageRegion(image, mask, defaultBins);
}
std::string mitk::AbstractGlobalImageFeature::GenerateLegacyFeatureName(const FeatureID& id) const
{
std::string output;
output = m_FeatureClassName + "::";
if (m_EncodeParametersInFeaturePrefix)
{
output += this->GenerateLegacyFeatureEncoding(id) + "::";
}
return output + this->GenerateLegacyFeatureNamePart(id);
};
std::string mitk::AbstractGlobalImageFeature::GenerateLegacyFeatureNamePart(const FeatureID& id) const
{
return id.name;
}
std::string mitk::AbstractGlobalImageFeature::GenerateLegacyFeatureEncoding(const FeatureID& /*id*/) const
{
return this->QuantifierParameterString();
}
std::string mitk::AbstractGlobalImageFeature::QuantifierParameterString() const
{
std::stringstream ss;
ss.imbue(std::locale("c"));
if (GetUseMinimumIntensity() && GetUseMaximumIntensity() && GetUseBinsize())
ss << "Min-" << GetMinimumIntensity() << "_Max-" << GetMaximumIntensity() << "_BS-" << GetBinsize();
else if (GetUseMinimumIntensity() && GetUseBins() && GetUseBinsize())
ss << "Min-" << GetMinimumIntensity() << "_Bins-" << GetBins() << "_BS-" << GetBinsize();
else if (GetUseMinimumIntensity() && GetUseMaximumIntensity() && GetUseBins())
ss << "Min-" << GetMinimumIntensity() << "_Max-" << GetMaximumIntensity() << "_Bins-" << GetBins();
- // Intialize from Image and Binsize
+ // Initialize from Image and Binsize
else if (GetUseBinsize() && GetIgnoreMask() && GetUseMinimumIntensity())
ss << "Min-" << GetMinimumIntensity() << "_BS-" << GetBinsize() << "_FullImage";
else if (GetUseBinsize() && GetIgnoreMask() && GetUseMaximumIntensity())
ss << "Max-" << GetMaximumIntensity() << "_BS-" << GetBinsize() << "_FullImage";
else if (GetUseBinsize() && GetIgnoreMask())
ss << "BS-" << GetBinsize() << "_FullImage";
// Initialize form Image, Mask and Binsize
else if (GetUseBinsize() && GetUseMinimumIntensity())
ss << "Min-" << GetMinimumIntensity() << "_BS-" << GetBinsize();
else if (GetUseBinsize() && GetUseMaximumIntensity())
ss << "Max-" << GetMaximumIntensity() << "_BS-" << GetBinsize();
else if (GetUseBinsize())
ss << "BS-" << GetBinsize();
- // Intialize from Image and Bins
+ // Initialize from Image and Bins
else if (GetUseBins() && GetIgnoreMask() && GetUseMinimumIntensity())
ss << "Min-" << GetMinimumIntensity() << "_Bins-" << GetBins() << "_FullImage";
else if (GetUseBins() && GetIgnoreMask() && GetUseMaximumIntensity())
ss << "Max-" << GetMaximumIntensity() << "_Bins-" << GetBins() << "_FullImage";
else if (GetUseBins())
ss << "Bins-" << GetBins() << "_FullImage";
- // Intialize from Image, Mask and Bins
+ // Initialize from Image, Mask and Bins
else if (GetUseBins() && GetUseMinimumIntensity())
ss << "Min-" << GetMinimumIntensity() << "_Bins-" << GetBins();
else if (GetUseBins() && GetUseMaximumIntensity())
ss << "Max-" << GetMaximumIntensity() << "_Bins-" << GetBins();
else if (GetUseBins())
ss << "Bins-" << GetBins();
// Default
else if (GetIgnoreMask())
ss << "Bins-" << GetBins() << "_FullImage";
else
ss << "Bins-" << GetBins();
return ss.str();
}
mitk::FeatureID mitk::AbstractGlobalImageFeature::CreateTemplateFeatureID(std::string settingsSuffix, FeatureID::ParametersType additionalParams)
{
FeatureID newID;
newID.featureClass = this->GetFeatureClassName();
newID.settingID = this->GetShortName();
if (!settingsSuffix.empty())
{
newID.settingID += "_" + settingsSuffix;
}
std::string name = this->GetOptionPrefix();
for (const auto& paramIter : m_Parameters)
{
if (paramIter.first.find(name) == 0)
{
newID.parameters.insert(std::make_pair(paramIter.first, paramIter.second));
}
}
if (m_Quantifier.IsNotNull())
{ //feature class uses the quantifier. So store the information in the feature ID
if (GetUseMinimumIntensity())
{
newID.parameters["minimum-intensity"] = us::Any(GetMinimumIntensity());
}
if (GetUseMaximumIntensity())
{
newID.parameters["minimum-intensity"] = us::Any(GetMaximumIntensity());
}
if (GetUseBinsize())
{
newID.parameters["binsize"] = us::Any(GetBinsize());
}
if (GetBins())
{
newID.parameters["bins"] = us::Any(GetBins());
}
if (GetIgnoreMask())
{
newID.parameters["ignore-mask-for-histogram"] = us::Any(true);
}
}
for (const auto& paramIter : additionalParams)
{
newID.parameters[paramIter.first] = paramIter.second;
}
return newID;
}
void mitk::AbstractGlobalImageFeature::CalculateAndAppendFeaturesSliceWise(const Image::Pointer & image, const Image::Pointer &mask, int sliceID, FeatureListType &featureList, bool checkParameterActivation)
{
auto parsedArgs = GetParameters();
if (!checkParameterActivation || parsedArgs.count(GetLongName()))
{
auto result = CalculateFeaturesSlicewise(image, mask, sliceID);
featureList.insert(featureList.end(), result.begin(), result.end());
}
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::AbstractGlobalImageFeature::CalculateFeaturesSlicewise(const Image::Pointer & image, const Image::Pointer &mask, int sliceID)
{
std::vector<mitk::Image::Pointer> imageVector;
std::vector<mitk::Image::Pointer> maskVector;
ExtractSlicesFromImages(image, mask,sliceID, imageVector, maskVector);
std::vector<mitk::AbstractGlobalImageFeature::FeatureListType> statVector;
for (std::size_t index = 0; index < imageVector.size(); ++index)
{
auto stat = this->CalculateFeatures(imageVector[index], maskVector[index]);
statVector.push_back(stat);
}
if (statVector.size() < 1)
return FeatureListType();
FeatureListType statMean, statStd, result;
for (std::size_t i = 0; i < statVector[0].size(); ++i)
{
auto cElement1 = statVector[0][i];
cElement1.first.name = "SliceWise Mean " + cElement1.first.name;
cElement1.first.legacyName = this->GenerateLegacyFeatureName(cElement1.first);
cElement1.second = 0.0;
auto cElement2 = statVector[0][i];
cElement2.first.name = "SliceWise Var. " + cElement2.first.name;
cElement2.first.legacyName = this->GenerateLegacyFeatureName(cElement2.first);
cElement2.second = 0.0;
statMean.push_back(cElement1);
statStd.push_back(cElement2);
}
for (auto cStat : statVector)
{
for (std::size_t i = 0; i < cStat.size(); ++i)
{
statMean[i].second += cStat[i].second / (1.0*statVector.size());
}
}
for (auto cStat : statVector)
{
for (std::size_t i = 0; i < cStat.size(); ++i)
{
statStd[i].second += (cStat[i].second - statMean[i].second)*(cStat[i].second - statMean[i].second) / (1.0*statVector.size());
}
}
for (auto cStat : statVector)
{
std::copy(cStat.begin(), cStat.end(), std::back_inserter(result));
}
std::copy(statMean.begin(), statMean.end(), std::back_inserter(result));
std::copy(statStd.begin(), statStd.end(), std::back_inserter(result));
return result;
}
void mitk::AbstractGlobalImageFeature::CalculateAndAppendFeatures(const Image* image, const Image* mask, const Image* maskNoNAN, FeatureListType& featureList, bool checkParameterActivation)
{
auto parsedArgs = this->GetParameters();
if (!checkParameterActivation || parsedArgs.count(this->GetLongName()))
{
auto result = this->CalculateFeatures(image, mask, maskNoNAN);
featureList.insert(featureList.end(), result.begin(), result.end());
}
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::AbstractGlobalImageFeature::CalculateFeatures(const Image* image, const Image* mask)
{
auto result = this->DoCalculateFeatures(image, mask);
//ensure legacy names
for (auto& feature : result)
{
feature.first.legacyName = this->GenerateLegacyFeatureName(feature.first);
}
return result;
}
std::string mitk::AbstractGlobalImageFeature::GenerateLegacyFeatureNameWOEncoding(const mitk::FeatureID& id)
{
std::string result;
if (id.name.find("SliceWise") == 0)
{
result = id.name.substr(0, 14) + " " + id.featureClass + "::" + id.name.substr(15, -1);
}
else
{
result = id.featureClass + "::" + id.name;
}
return result;
}
diff --git a/Modules/Classification/CLCore/src/mitkConfigurationHolder.cpp b/Modules/Classification/CLCore/src/mitkConfigurationHolder.cpp
index c7c0bcbadf..9c5fdfd80b 100644
--- a/Modules/Classification/CLCore/src/mitkConfigurationHolder.cpp
+++ b/Modules/Classification/CLCore/src/mitkConfigurationHolder.cpp
@@ -1,308 +1,308 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkConfigurationHolder.h>
#include <mitkExceptionMacro.h>
#include <sstream>
mitk::ConfigurationHolder::ConfigurationHolder() :
m_ValueType(DT_UNINIZIALIZED)
{
m_GroupValue.clear();
}
void mitk::ConfigurationHolder::SetBool(bool value)
{
m_BoolValue = value;
m_ValueType = DT_BOOL;
}
void mitk::ConfigurationHolder::SetUnsignedInt(unsigned int value)
{
m_UIntValue = value;
m_ValueType = DT_UINT;
}
void mitk::ConfigurationHolder::SetInt(int value)
{
m_IntValue = value;
m_ValueType = DT_INT;
}
void mitk::ConfigurationHolder::SetDouble(double value)
{
m_DoubleValue = value;
m_ValueType = DT_DOUBLE;
}
void mitk::ConfigurationHolder::SetString(std::string value)
{
m_StringValue = value;
m_ValueType = DT_STRING;
}
void mitk::ConfigurationHolder::ClearGroup()
{
m_GroupValue.clear();
m_ValueType = DT_GROUP;
}
void mitk::ConfigurationHolder::AddToGroup(std::string id, const ConfigurationHolder &value)
{
m_GroupValue[id] = value;
m_ValueType = DT_GROUP;
}
bool mitk::ConfigurationHolder::AsBool()
{
switch (m_ValueType)
{
case DT_UNINIZIALIZED:
mitkThrow() << "No value stored";
break;
case DT_BOOL:
return m_BoolValue;
break;
case DT_UINT:
return m_UIntValue;
break;
case DT_INT:
return m_IntValue;
break;
case DT_DOUBLE:
return (m_DoubleValue > 1 || m_DoubleValue < -1);
break;
case DT_STRING:
return (m_StringValue != "0");
break;
case DT_GROUP:
mitkThrow() << "Cannot convert group data to bool";
break;
default:
- mitkThrow() << "Unkown Data Type.";
+ mitkThrow() << "Unknown Data Type.";
break;
}
}
unsigned int mitk::ConfigurationHolder::AsUnsignedInt()
{
unsigned int result;
std::istringstream ss(m_StringValue);
ss >> result;
switch (m_ValueType)
{
case DT_UNINIZIALIZED:
mitkThrow() << "No value stored";
break;
case DT_BOOL:
return m_BoolValue;
break;
case DT_UINT:
return m_UIntValue;
break;
case DT_INT:
return m_IntValue;
break;
case DT_DOUBLE:
return m_DoubleValue;
break;
case DT_STRING:
return result;
break;
case DT_GROUP:
mitkThrow() << "Cannot convert group data to bool";
break;
default:
- mitkThrow() << "Unkown Data Type.";
+ mitkThrow() << "Unknown Data Type.";
break;
}
}
int mitk::ConfigurationHolder::AsInt()
{
int result;
std::istringstream ss(m_StringValue);
ss >> result;
switch (m_ValueType)
{
case DT_UNINIZIALIZED:
mitkThrow() << "No value stored";
break;
case DT_BOOL:
return m_BoolValue;
break;
case DT_UINT:
return m_UIntValue;
break;
case DT_INT:
return m_IntValue;
break;
case DT_DOUBLE:
return m_DoubleValue;
break;
case DT_STRING:
return result;
break;
case DT_GROUP:
mitkThrow() << "Cannot convert group data to bool";
break;
default:
- mitkThrow() << "Unkown Data Type.";
+ mitkThrow() << "Unknown Data Type.";
break;
}
}
double mitk::ConfigurationHolder::AsDouble()
{
double result;
std::istringstream ss(m_StringValue);
ss >> result;
switch (m_ValueType)
{
case DT_UNINIZIALIZED:
mitkThrow() << "No value stored";
break;
case DT_BOOL:
return m_BoolValue;
break;
case DT_UINT:
return m_UIntValue;
break;
case DT_INT:
return m_IntValue;
break;
case DT_DOUBLE:
return m_DoubleValue > 1;
break;
case DT_STRING:
return result;
break;
case DT_GROUP:
mitkThrow() << "Cannot convert group data to bool";
break;
default:
- mitkThrow() << "Unkown Data Type.";
+ mitkThrow() << "Unknown Data Type.";
break;
}
}
std::string mitk::ConfigurationHolder::AsString()
{
std::ostringstream strs;
switch (m_ValueType)
{
case DT_UNINIZIALIZED:
mitkThrow() << "No value stored";
break;
case DT_BOOL:
strs << m_BoolValue;
break;
case DT_UINT:
strs << m_UIntValue;
break;
case DT_INT:
strs << m_IntValue;
break;
case DT_DOUBLE:
strs << m_DoubleValue;
break;
case DT_STRING:
return m_StringValue;
break;
case DT_GROUP:
mitkThrow() << "Cannot convert group data to bool";
break;
default:
- mitkThrow() << "Unkown Data Type.";
+ mitkThrow() << "Unknown Data Type.";
break;
}
return strs.str();
}
std::vector<std::string> mitk::ConfigurationHolder::AsStringVector()
{
if (m_ValueType != DT_GROUP)
mitkThrow() << "No Group Data, cannot convert to String Vector";
std::vector<std::string> result;
for (auto iter = m_GroupValue.begin(); iter != m_GroupValue.end(); ++iter)
{
result.push_back((*iter).second.AsString());
}
return result;
}
mitk::ConfigurationHolder& mitk::ConfigurationHolder::At(std::string id)
{
return m_GroupValue[id];
}
bool mitk::ConfigurationHolder::AsBool(bool value)
{
try {
return this->AsBool();
}
catch (const mitk::Exception &)
{
return value;
}
}
unsigned int mitk::ConfigurationHolder::AsUnsignedInt(unsigned int value)
{
try {
return this->AsUnsignedInt();
}
catch (const mitk::Exception &)
{
return value;
}
}
int mitk::ConfigurationHolder::AsInt(int value)
{
try {
return this->AsInt();
}
catch (const mitk::Exception &)
{
return value;
}
}
double mitk::ConfigurationHolder::AsDouble(double value)
{
try {
return this->AsDouble();
}
catch (const mitk::Exception &)
{
return value;
}
}
std::string mitk::ConfigurationHolder::AsString(std::string value)
{
try {
return this->AsString();
}
catch (const mitk::Exception &)
{
return value;
}
}
diff --git a/Modules/Classification/CLMRUtilities/CMakeLists.txt b/Modules/Classification/CLMRUtilities/CMakeLists.txt
index 6a56a7aedd..ea79076e36 100644
--- a/Modules/Classification/CLMRUtilities/CMakeLists.txt
+++ b/Modules/Classification/CLMRUtilities/CMakeLists.txt
@@ -1,4 +1,3 @@
MITK_CREATE_MODULE(
DEPENDS MitkCore MitkCLCore
- PACKAGE_DEPENDS PUBLIC Eigen
)
diff --git a/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp b/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp
index 4ff557a00d..b93f7d1d09 100644
--- a/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp
+++ b/Modules/Classification/CLMiniApps/CLGlobalImageFeatures.cpp
@@ -1,786 +1,786 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkCLPolyToNrrd_cpp
#define mitkCLPolyToNrrd_cpp
#include "time.h"
#include <sstream>
#include <fstream>
#include <mitkIOUtil.h>
#include "mitkCommandLineParser.h"
#include <mitkSplitParameterToVector.h>
#include <mitkGlobalImageFeaturesParameter.h>
#include <mitkGIFCooccurenceMatrix.h>
#include <mitkGIFCooccurenceMatrix2.h>
#include <mitkGIFGreyLevelRunLength.h>
#include <mitkGIFFirstOrderStatistics.h>
#include <mitkGIFFirstOrderHistogramStatistics.h>
#include <mitkGIFFirstOrderNumericStatistics.h>
#include <mitkGIFVolumetricStatistics.h>
#include <mitkGIFVolumetricDensityStatistics.h>
#include <mitkGIFGreyLevelSizeZone.h>
#include <mitkGIFGreyLevelDistanceZone.h>
#include <mitkGIFImageDescriptionFeatures.h>
#include <mitkGIFLocalIntensity.h>
#include <mitkGIFCurvatureStatistic.h>
#include <mitkGIFIntensityVolumeHistogramFeatures.h>
#include <mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h>
#include <mitkGIFNeighbouringGreyLevelDependenceFeatures.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <mitkITKImageImport.h>
#include <mitkConvert2Dto3DImageFilter.h>
#include <mitkCLResultWriter.h>
#include <mitkCLResultXMLWriter.h>
#include <mitkVersion.h>
#include <iostream>
#include <locale>
#include <itkImageDuplicator.h>
#include <itkImageRegionIterator.h>
#include "itkNearestNeighborInterpolateImageFunction.h"
#include "itkResampleImageFilter.h"
#include <QApplication>
#include <mitkStandaloneDataStorage.h>
#include "QmitkRegisterClasses.h"
#include "QmitkRenderWindow.h"
#include "vtkRenderLargeImage.h"
#include "vtkPNGWriter.h"
typedef itk::Image< double, 3 > FloatImageType;
typedef itk::Image< unsigned short, 3 > MaskImageType;
template <class charT>
class punct_facet : public std::numpunct<charT> {
public:
punct_facet(charT sep) :
m_Sep(sep)
{
}
protected:
charT do_decimal_point() const override { return m_Sep; }
private:
charT m_Sep;
};
template<typename TPixel, unsigned int VImageDimension>
void
ResampleImage(itk::Image<TPixel, VImageDimension>* itkImage, float resolution, mitk::Image::Pointer& newImage)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::ResampleImageFilter<ImageType, ImageType> ResampleFilterType;
typename ResampleFilterType::Pointer resampler = ResampleFilterType::New();
auto spacing = itkImage->GetSpacing();
auto size = itkImage->GetLargestPossibleRegion().GetSize();
for (unsigned int i = 0; i < VImageDimension; ++i)
{
size[i] = size[i] / (1.0*resolution)*(1.0*spacing[i])+1.0;
}
spacing.Fill(resolution);
resampler->SetInput(itkImage);
resampler->SetSize(size);
resampler->SetOutputSpacing(spacing);
resampler->SetOutputOrigin(itkImage->GetOrigin());
resampler->SetOutputDirection(itkImage->GetDirection());
resampler->Update();
newImage->InitializeByItk(resampler->GetOutput());
mitk::GrabItkImageMemory(resampler->GetOutput(), newImage);
}
template<typename TPixel, unsigned int VImageDimension>
static void
CreateNoNaNMask(itk::Image<TPixel, VImageDimension>* itkValue, mitk::Image::Pointer mask, mitk::Image::Pointer& newMask)
{
typedef itk::Image< TPixel, VImageDimension> LFloatImageType;
typedef itk::Image< unsigned short, VImageDimension> LMaskImageType;
typename LMaskImageType::Pointer itkMask = LMaskImageType::New();
mitk::CastToItkImage(mask, itkMask);
typedef itk::ImageDuplicator< LMaskImageType > DuplicatorType;
typename DuplicatorType::Pointer duplicator = DuplicatorType::New();
duplicator->SetInputImage(itkMask);
duplicator->Update();
auto tmpMask = duplicator->GetOutput();
itk::ImageRegionIterator<LMaskImageType> mask1Iter(itkMask, itkMask->GetLargestPossibleRegion());
itk::ImageRegionIterator<LMaskImageType> mask2Iter(tmpMask, tmpMask->GetLargestPossibleRegion());
itk::ImageRegionIterator<LFloatImageType> imageIter(itkValue, itkValue->GetLargestPossibleRegion());
while (!mask1Iter.IsAtEnd())
{
mask2Iter.Set(0);
if (mask1Iter.Value() > 0)
{
// Is not NaN
if (imageIter.Value() == imageIter.Value())
{
mask2Iter.Set(1);
}
}
++mask1Iter;
++mask2Iter;
++imageIter;
}
newMask->InitializeByItk(tmpMask);
mitk::GrabItkImageMemory(tmpMask, newMask);
}
template<typename TPixel, unsigned int VImageDimension>
static void
ResampleMask(itk::Image<TPixel, VImageDimension>* itkMoving, mitk::Image::Pointer ref, mitk::Image::Pointer& newMask)
{
typedef itk::Image< TPixel, VImageDimension> LMaskImageType;
typedef itk::NearestNeighborInterpolateImageFunction< LMaskImageType> NearestNeighborInterpolateImageFunctionType;
typedef itk::ResampleImageFilter<LMaskImageType, LMaskImageType> ResampleFilterType;
typename NearestNeighborInterpolateImageFunctionType::Pointer nn_interpolator = NearestNeighborInterpolateImageFunctionType::New();
typename LMaskImageType::Pointer itkRef = LMaskImageType::New();
mitk::CastToItkImage(ref, itkRef);
typename ResampleFilterType::Pointer resampler = ResampleFilterType::New();
resampler->SetInput(itkMoving);
resampler->SetReferenceImage(itkRef);
resampler->UseReferenceImageOn();
resampler->SetInterpolator(nn_interpolator);
resampler->Update();
newMask->InitializeByItk(resampler->GetOutput());
mitk::GrabItkImageMemory(resampler->GetOutput(), newMask);
}
static void
ExtractSlicesFromImages(mitk::Image::Pointer image, mitk::Image::Pointer mask,
mitk::Image::Pointer maskNoNaN, mitk::Image::Pointer morphMask,
int direction,
std::vector<mitk::Image::Pointer> &imageVector,
std::vector<mitk::Image::Pointer> &maskVector,
std::vector<mitk::Image::Pointer> &maskNoNaNVector,
std::vector<mitk::Image::Pointer> &morphMaskVector)
{
typedef itk::Image< double, 2 > FloatImage2DType;
typedef itk::Image< unsigned short, 2 > MaskImage2DType;
FloatImageType::Pointer itkFloat = FloatImageType::New();
MaskImageType::Pointer itkMask = MaskImageType::New();
MaskImageType::Pointer itkMaskNoNaN = MaskImageType::New();
MaskImageType::Pointer itkMorphMask = MaskImageType::New();
mitk::CastToItkImage(mask, itkMask);
mitk::CastToItkImage(maskNoNaN, itkMaskNoNaN);
mitk::CastToItkImage(image, itkFloat);
mitk::CastToItkImage(morphMask, itkMorphMask);
int idxA, idxB, idxC;
switch (direction)
{
case 0:
idxA = 1; idxB = 2; idxC = 0;
break;
case 1:
idxA = 0; idxB = 2; idxC = 1;
break;
case 2:
idxA = 0; idxB = 1; idxC = 2;
break;
default:
idxA = 1; idxB = 2; idxC = 0;
break;
}
auto imageSize = image->GetLargestPossibleRegion().GetSize();
FloatImageType::IndexType index3D;
FloatImage2DType::IndexType index2D;
FloatImage2DType::SpacingType spacing2D;
spacing2D[0] = itkFloat->GetSpacing()[idxA];
spacing2D[1] = itkFloat->GetSpacing()[idxB];
for (unsigned int i = 0; i < imageSize[idxC]; ++i)
{
FloatImage2DType::RegionType region;
FloatImage2DType::IndexType start;
FloatImage2DType::SizeType size;
start[0] = 0; start[1] = 0;
size[0] = imageSize[idxA];
size[1] = imageSize[idxB];
region.SetIndex(start);
region.SetSize(size);
FloatImage2DType::Pointer image2D = FloatImage2DType::New();
image2D->SetRegions(region);
image2D->Allocate();
MaskImage2DType::Pointer mask2D = MaskImage2DType::New();
mask2D->SetRegions(region);
mask2D->Allocate();
MaskImage2DType::Pointer masnNoNaN2D = MaskImage2DType::New();
masnNoNaN2D->SetRegions(region);
masnNoNaN2D->Allocate();
MaskImage2DType::Pointer morph2D = MaskImage2DType::New();
morph2D->SetRegions(region);
morph2D->Allocate();
unsigned long voxelsInMask = 0;
for (unsigned int a = 0; a < imageSize[idxA]; ++a)
{
for (unsigned int b = 0; b < imageSize[idxB]; ++b)
{
index3D[idxA] = a;
index3D[idxB] = b;
index3D[idxC] = i;
index2D[0] = a;
index2D[1] = b;
image2D->SetPixel(index2D, itkFloat->GetPixel(index3D));
mask2D->SetPixel(index2D, itkMask->GetPixel(index3D));
masnNoNaN2D->SetPixel(index2D, itkMaskNoNaN->GetPixel(index3D));
morph2D->SetPixel(index2D, itkMorphMask->GetPixel(index3D));
voxelsInMask += (itkMask->GetPixel(index3D) > 0) ? 1 : 0;
}
}
image2D->SetSpacing(spacing2D);
mask2D->SetSpacing(spacing2D);
masnNoNaN2D->SetSpacing(spacing2D);
morph2D->SetSpacing(spacing2D);
mitk::Image::Pointer tmpFloatImage = mitk::Image::New();
tmpFloatImage->InitializeByItk(image2D.GetPointer());
mitk::GrabItkImageMemory(image2D, tmpFloatImage);
mitk::Image::Pointer tmpMaskImage = mitk::Image::New();
tmpMaskImage->InitializeByItk(mask2D.GetPointer());
mitk::GrabItkImageMemory(mask2D, tmpMaskImage);
mitk::Image::Pointer tmpMaskNoNaNImage = mitk::Image::New();
tmpMaskNoNaNImage->InitializeByItk(masnNoNaN2D.GetPointer());
mitk::GrabItkImageMemory(masnNoNaN2D, tmpMaskNoNaNImage);
mitk::Image::Pointer tmpMorphMaskImage = mitk::Image::New();
tmpMorphMaskImage->InitializeByItk(morph2D.GetPointer());
mitk::GrabItkImageMemory(morph2D, tmpMorphMaskImage);
if (voxelsInMask > 0)
{
imageVector.push_back(tmpFloatImage);
maskVector.push_back(tmpMaskImage);
maskNoNaNVector.push_back(tmpMaskNoNaNImage);
morphMaskVector.push_back(tmpMorphMaskImage);
}
}
}
static
void SaveSliceOrImageAsPNG(mitk::Image::Pointer image, mitk::Image::Pointer mask, std::string path, int index)
{
// Create a Standalone Datastorage for the single purpose of saving screenshots..
mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New();
QmitkRenderWindow renderWindow;
renderWindow.GetRenderer()->SetDataStorage(ds);
auto nodeI = mitk::DataNode::New();
nodeI->SetData(image);
auto nodeM = mitk::DataNode::New();
nodeM->SetData(mask);
ds->Add(nodeI);
ds->Add(nodeM);
auto geo = ds->ComputeBoundingGeometry3D(ds->GetAll());
mitk::RenderingManager::GetInstance()->InitializeViews(geo);
mitk::SliceNavigationController::Pointer sliceNaviController = renderWindow.GetSliceNavigationController();
unsigned int numberOfSteps = 1;
if (sliceNaviController)
{
numberOfSteps = sliceNaviController->GetStepper()->GetSteps();
sliceNaviController->GetStepper()->SetPos(0);
}
renderWindow.show();
renderWindow.resize(256, 256);
for (unsigned int currentStep = 0; currentStep < numberOfSteps; ++currentStep)
{
if (sliceNaviController)
{
sliceNaviController->GetStepper()->SetPos(currentStep);
}
renderWindow.GetRenderer()->PrepareRender();
vtkRenderWindow* renderWindow2 = renderWindow.GetVtkRenderWindow();
mitk::BaseRenderer* baserenderer = mitk::BaseRenderer::GetInstance(renderWindow2);
auto vtkRender = baserenderer->GetVtkRenderer();
vtkRender->GetRenderWindow()->WaitForCompletion();
vtkRenderLargeImage* magnifier = vtkRenderLargeImage::New();
magnifier->SetInput(vtkRender);
magnifier->SetMagnification(3.0);
std::stringstream ss;
ss << path << "_Idx-" << index << "_Step-"<<currentStep<<".png";
std::string tmpImageName;
ss >> tmpImageName;
auto fileWriter = vtkPNGWriter::New();
fileWriter->SetInputConnection(magnifier->GetOutputPort());
fileWriter->SetFileName(tmpImageName.c_str());
fileWriter->Write();
fileWriter->Delete();
}
}
int main(int argc, char* argv[])
{
// Commented : Updated to a common interface, include, if possible, mask is type unsigned short, uses Quantification, Comments
// Name follows standard scheme with Class Name::Feature Name
// Commented 2: Updated to use automatic inclusion of list of parameters if required.
mitk::GIFImageDescriptionFeatures::Pointer ipCalculator = mitk::GIFImageDescriptionFeatures::New(); // Commented 2, Tested
mitk::GIFFirstOrderStatistics::Pointer firstOrderCalculator = mitk::GIFFirstOrderStatistics::New(); //Commented 2
mitk::GIFFirstOrderHistogramStatistics::Pointer firstOrderHistoCalculator = mitk::GIFFirstOrderHistogramStatistics::New(); // Commented 2, Tested
mitk::GIFFirstOrderNumericStatistics::Pointer firstOrderNumericCalculator = mitk::GIFFirstOrderNumericStatistics::New(); // Commented 2, Tested
mitk::GIFVolumetricStatistics::Pointer volCalculator = mitk::GIFVolumetricStatistics::New(); // Commented 2, Tested
mitk::GIFVolumetricDensityStatistics::Pointer voldenCalculator = mitk::GIFVolumetricDensityStatistics::New(); // Commented 2, Tested
mitk::GIFCooccurenceMatrix::Pointer coocCalculator = mitk::GIFCooccurenceMatrix::New(); // Commented 2, Will not be tested
mitk::GIFCooccurenceMatrix2::Pointer cooc2Calculator = mitk::GIFCooccurenceMatrix2::New(); //Commented 2
mitk::GIFNeighbouringGreyLevelDependenceFeature::Pointer ngldCalculator = mitk::GIFNeighbouringGreyLevelDependenceFeature::New(); //Commented 2, Tested
mitk::GIFGreyLevelRunLength::Pointer rlCalculator = mitk::GIFGreyLevelRunLength::New(); // Commented 2
mitk::GIFGreyLevelSizeZone::Pointer glszCalculator = mitk::GIFGreyLevelSizeZone::New(); // Commented 2, Tested
mitk::GIFGreyLevelDistanceZone::Pointer gldzCalculator = mitk::GIFGreyLevelDistanceZone::New(); //Commented 2, Tested
mitk::GIFLocalIntensity::Pointer lociCalculator = mitk::GIFLocalIntensity::New(); //Commented 2, Tested
mitk::GIFIntensityVolumeHistogramFeatures::Pointer ivohCalculator = mitk::GIFIntensityVolumeHistogramFeatures::New(); // Commented 2
mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::Pointer ngtdCalculator = mitk::GIFNeighbourhoodGreyToneDifferenceFeatures::New(); //Commented 2, Tested
mitk::GIFCurvatureStatistic::Pointer curvCalculator = mitk::GIFCurvatureStatistic::New(); //Commented 2, Tested
std::vector<mitk::AbstractGlobalImageFeature::Pointer> features;
features.push_back(volCalculator.GetPointer());
features.push_back(voldenCalculator.GetPointer());
features.push_back(curvCalculator.GetPointer());
features.push_back(firstOrderCalculator.GetPointer());
features.push_back(firstOrderNumericCalculator.GetPointer());
features.push_back(firstOrderHistoCalculator.GetPointer());
features.push_back(ivohCalculator.GetPointer());
features.push_back(lociCalculator.GetPointer());
features.push_back(coocCalculator.GetPointer());
features.push_back(cooc2Calculator.GetPointer());
features.push_back(ngldCalculator.GetPointer());
features.push_back(rlCalculator.GetPointer());
features.push_back(glszCalculator.GetPointer());
features.push_back(gldzCalculator.GetPointer());
features.push_back(ipCalculator.GetPointer());
features.push_back(ngtdCalculator.GetPointer());
mitkCommandLineParser parser;
parser.setArgumentPrefix("--", "-");
mitk::cl::GlobalImageFeaturesParameter param;
param.AddParameter(parser);
parser.addArgument("--","-", mitkCommandLineParser::String, "---", "---", us::Any(),true);
for (auto cFeature : features)
{
cFeature->AddArguments(parser);
}
parser.addArgument("--", "-", mitkCommandLineParser::String, "---", "---", us::Any(), true);
parser.addArgument("description","d",mitkCommandLineParser::String,"Text","Description that is added to the output",us::Any());
parser.addArgument("direction", "dir", mitkCommandLineParser::String, "Int", "Allows to specify the direction for Cooc and RL. 0: All directions, 1: Only single direction (Test purpose), 2,3,4... Without dimension 0,1,2... ", us::Any());
parser.addArgument("slice-wise", "slice", mitkCommandLineParser::String, "Int", "Allows to specify if the image is processed slice-wise (number giving direction) ", us::Any());
parser.addArgument("output-mode", "omode", mitkCommandLineParser::Int, "Int", "Defines the format of the output. 0: (Default) results of an image / slice are written in a single row;"
" 1: results of an image / slice are written in a single column; 2: store the result of on image as structured radiomocs report (XML).");
// Miniapp Infos
parser.setCategory("Classification Tools");
parser.setTitle("Global Image Feature calculator");
parser.setDescription("Calculates different global statistics for a given segmentation / image combination");
parser.setContributor("German Cancer Research Center (DKFZ)");
std::map<std::string, us::Any> parsedArgs = parser.parseArguments(argc, argv);
param.ParseParameter(parsedArgs);
if (parsedArgs.size()==0)
{
return EXIT_FAILURE;
}
if ( parsedArgs.count("help") || parsedArgs.count("h"))
{
return EXIT_SUCCESS;
}
std::string version = "Version: 1.23";
MITK_INFO << version;
std::ofstream log;
if (param.useLogfile)
{
log.open(param.logfilePath, std::ios::app);
log << std::endl;
log << version;
log << "Image: " << param.imagePath;
log << "Mask: " << param.maskPath;
}
if (param.useDecimalPoint)
{
std::cout.imbue(std::locale(std::cout.getloc(), new punct_facet<char>(param.decimalPoint)));
}
//representing the original loaded image data without any prepropcessing that might come.
mitk::Image::Pointer loadedImage = mitk::IOUtil::Load<mitk::Image>(param.imagePath);
//representing the original loaded mask data without any prepropcessing that might come.
mitk::Image::Pointer loadedMask = mitk::IOUtil::Load<mitk::Image>(param.maskPath);
mitk::Image::Pointer image = loadedImage;
mitk::Image::Pointer mask = loadedMask;
mitk::Image::Pointer tmpImage = loadedImage;
mitk::Image::Pointer tmpMask = loadedMask;
mitk::Image::Pointer morphMask = mask;
if (param.useMorphMask)
{
morphMask = mitk::IOUtil::Load<mitk::Image>(param.morphPath);
}
log << " Check for Dimensions -";
if ((image->GetDimension() != mask->GetDimension()))
{
MITK_INFO << "Dimension of image does not match. ";
MITK_INFO << "Correct one image, may affect the result";
if (image->GetDimension() == 2)
{
mitk::Convert2Dto3DImageFilter::Pointer multiFilter2 = mitk::Convert2Dto3DImageFilter::New();
multiFilter2->SetInput(tmpImage);
multiFilter2->Update();
image = multiFilter2->GetOutput();
}
if (mask->GetDimension() == 2)
{
mitk::Convert2Dto3DImageFilter::Pointer multiFilter3 = mitk::Convert2Dto3DImageFilter::New();
multiFilter3->SetInput(tmpMask);
multiFilter3->Update();
mask = multiFilter3->GetOutput();
}
}
int writeDirection = 0;
if (parsedArgs.count("output-mode"))
{
writeDirection = us::any_cast<int>(parsedArgs["output-mode"]);
}
log << " Check for Resolution -";
if (param.resampleToFixIsotropic)
{
mitk::Image::Pointer newImage = mitk::Image::New();
AccessByItk_2(image, ResampleImage, param.resampleResolution, newImage);
image = newImage;
}
log << " Resample if required -";
if (param.resampleMask)
{
mitk::Image::Pointer newMaskImage = mitk::Image::New();
AccessByItk_2(mask, ResampleMask, image, newMaskImage);
mask = newMaskImage;
}
if ( ! mitk::Equal(mask->GetGeometry(0)->GetOrigin(), image->GetGeometry(0)->GetOrigin()))
{
MITK_INFO << "Not equal Origins";
if (param.ensureSameSpace)
{
MITK_INFO << "Warning!";
MITK_INFO << "The origin of the input image and the mask do not match. They are";
MITK_INFO << "now corrected. Please check to make sure that the images still match";
image->GetGeometry(0)->SetOrigin(mask->GetGeometry(0)->GetOrigin());
} else
{
return -1;
}
}
log << " Check for Equality -";
if ( ! mitk::Equal(mask->GetGeometry(0)->GetSpacing(), image->GetGeometry(0)->GetSpacing()))
{
MITK_INFO << "Not equal Spacing";
if (param.ensureSameSpace)
{
MITK_INFO << "Warning!";
MITK_INFO << "The spacing of the mask was set to match the spacing of the input image.";
MITK_INFO << "This might cause unintended spacing of the mask image";
image->GetGeometry(0)->SetSpacing(mask->GetGeometry(0)->GetSpacing());
} else
{
MITK_INFO << "The spacing of the mask and the input images is not equal.";
- MITK_INFO << "Terminating the programm. You may use the '-fi' option";
+ MITK_INFO << "Terminating the program. You may use the '-fi' option";
return -1;
}
}
int direction = 0;
if (parsedArgs.count("direction"))
{
direction = mitk::cl::splitDouble(parsedArgs["direction"].ToString(), ';')[0];
}
MITK_INFO << "Start creating Mask without NaN";
mitk::Image::Pointer maskNoNaN = mitk::Image::New();
AccessByItk_2(image, CreateNoNaNMask, mask, maskNoNaN);
//CreateNoNaNMask(mask, image, maskNoNaN);
bool sliceWise = false;
int sliceDirection = 0;
unsigned int currentSlice = 0;
bool imageToProcess = true;
std::vector<mitk::Image::Pointer> floatVector;
std::vector<mitk::Image::Pointer> maskVector;
std::vector<mitk::Image::Pointer> maskNoNaNVector;
std::vector<mitk::Image::Pointer> morphMaskVector;
if ((parsedArgs.count("slice-wise")) && image->GetDimension() > 2)
{
MITK_INFO << "Enabled slice-wise";
sliceWise = true;
sliceDirection = mitk::cl::splitDouble(parsedArgs["slice-wise"].ToString(), ';')[0];
MITK_INFO << sliceDirection;
ExtractSlicesFromImages(image, mask, maskNoNaN, morphMask, sliceDirection, floatVector, maskVector, maskNoNaNVector, morphMaskVector);
MITK_INFO << "Slice";
}
log << " Configure features -";
for (auto cFeature : features)
{
if (param.defineGlobalMinimumIntensity)
{
cFeature->SetMinimumIntensity(param.globalMinimumIntensity);
cFeature->SetUseMinimumIntensity(true);
}
if (param.defineGlobalMaximumIntensity)
{
cFeature->SetMaximumIntensity(param.globalMaximumIntensity);
cFeature->SetUseMaximumIntensity(true);
}
if (param.defineGlobalNumberOfBins)
{
cFeature->SetBins(param.globalNumberOfBins);
MITK_INFO << param.globalNumberOfBins;
}
cFeature->SetParameters(parsedArgs);
cFeature->SetDirection(direction);
cFeature->SetEncodeParametersInFeaturePrefix(param.encodeParameter);
}
bool addDescription = parsedArgs.count("description");
mitk::cl::FeatureResultWriter writer(param.outputPath, writeDirection);
if (param.useDecimalPoint)
{
writer.SetDecimalPoint(param.decimalPoint);
}
std::string description = "";
if (addDescription)
{
description = parsedArgs["description"].ToString();
}
mitk::Image::Pointer cImage = image;
mitk::Image::Pointer cMask = mask;
mitk::Image::Pointer cMaskNoNaN = maskNoNaN;
mitk::Image::Pointer cMorphMask = morphMask;
if (param.useHeader)
{
writer.AddColumn("SoftwareVersion");
writer.AddColumn("Patient");
writer.AddColumn("Image");
writer.AddColumn("Segmentation");
}
// Create a QTApplication and a Datastorage
// This is necessary in order to save screenshots of
// each image / slice.
QApplication qtapplication(argc, argv);
QmitkRegisterClasses();
std::vector<mitk::AbstractGlobalImageFeature::FeatureListType> allStats;
log << " Begin Processing -";
while (imageToProcess)
{
if (sliceWise)
{
cImage = floatVector[currentSlice];
cMask = maskVector[currentSlice];
cMaskNoNaN = maskNoNaNVector[currentSlice];
cMorphMask = morphMaskVector[currentSlice];
imageToProcess = (floatVector.size()-1 > (currentSlice)) ? true : false ;
}
else
{
imageToProcess = false;
}
if (param.writePNGScreenshots)
{
SaveSliceOrImageAsPNG(cImage, cMask, param.pngScreenshotsPath, currentSlice);
}
if (param.writeAnalysisImage)
{
mitk::IOUtil::Save(cImage, param.anaylsisImagePath);
}
if (param.writeAnalysisMask)
{
mitk::IOUtil::Save(cMask, param.analysisMaskPath);
}
mitk::AbstractGlobalImageFeature::FeatureListType stats;
for (auto cFeature : features)
{
log << " Calculating " << cFeature->GetFeatureClassName() << " -";
cFeature->SetMorphMask(cMorphMask);
cFeature->CalculateAndAppendFeatures(cImage, cMask, cMaskNoNaN, stats, !param.calculateAllFeatures);
}
for (std::size_t i = 0; i < stats.size(); ++i)
{
std::cout << stats[i].first.legacyName << " - " << stats[i].second << std::endl;
}
writer.AddHeader(description, currentSlice, stats, param.useHeader, addDescription);
if (true)
{
writer.AddSubjectInformation(MITK_REVISION);
writer.AddSubjectInformation(param.imageFolder);
writer.AddSubjectInformation(param.imageName);
writer.AddSubjectInformation(param.maskName);
}
writer.AddResult(description, currentSlice, stats, param.useHeader, addDescription);
allStats.push_back(stats);
++currentSlice;
}
log << " Process Slicewise -";
if (sliceWise)
{
mitk::AbstractGlobalImageFeature::FeatureListType statMean, statStd;
for (std::size_t i = 0; i < allStats[0].size(); ++i)
{
auto cElement1 = allStats[0][i];
cElement1.first.legacyName = "SliceWise Mean " + cElement1.first.legacyName;
cElement1.second = 0.0;
auto cElement2 = allStats[0][i];
cElement2.first.legacyName = "SliceWise Var. " + cElement2.first.legacyName;
cElement2.second = 0.0;
statMean.push_back(cElement1);
statStd.push_back(cElement2);
}
for (auto cStat : allStats)
{
for (std::size_t i = 0; i < cStat.size(); ++i)
{
statMean[i].second += cStat[i].second / (1.0*allStats.size());
}
}
for (auto cStat : allStats)
{
for (std::size_t i = 0; i < cStat.size(); ++i)
{
statStd[i].second += (cStat[i].second - statMean[i].second)*(cStat[i].second - statMean[i].second) / (1.0*allStats.size());
}
}
for (std::size_t i = 0; i < statMean.size(); ++i)
{
std::cout << statMean[i].first.legacyName << " - " << statMean[i].second << std::endl;
std::cout << statStd[i].first.legacyName << " - " << statStd[i].second << std::endl;
}
if (true)
{
writer.AddSubjectInformation(MITK_REVISION);
writer.AddSubjectInformation(param.imageFolder);
writer.AddSubjectInformation(param.imageName);
writer.AddSubjectInformation(param.maskName + " - Mean");
}
writer.AddResult(description, currentSlice, statMean, param.useHeader, addDescription);
if (true)
{
writer.AddSubjectInformation(MITK_REVISION);
writer.AddSubjectInformation(param.imageFolder);
writer.AddSubjectInformation(param.imageName);
writer.AddSubjectInformation(param.maskName + " - Var.");
}
writer.AddResult(description, currentSlice, statStd, param.useHeader, addDescription);
}
int returnCode = EXIT_SUCCESS;
if (!param.outputXMLPath.empty())
{
if (sliceWise)
{
MITK_ERROR << "Xml output is not supported in slicewise mode";
returnCode = EXIT_FAILURE;
}
else
{
mitk::cl::CLResultXMLWriter xmlWriter;
xmlWriter.SetCLIArgs(parsedArgs);
xmlWriter.SetFeatures(allStats.front());
xmlWriter.SetImage(loadedImage);
xmlWriter.SetMask(loadedMask);
xmlWriter.SetMethodName("CLGlobalImageFeatures");
xmlWriter.SetMethodVersion(version + "(mitk: " MITK_VERSION_STRING+")");
xmlWriter.SetOrganisation("German Cancer Research Center (DKFZ)");
xmlWriter.SetPipelineUID(param.pipelineUID);
xmlWriter.write(param.outputXMLPath);
}
}
if (param.useLogfile)
{
log << "Finished calculation" << std::endl;
log.close();
}
return returnCode;
}
#endif
diff --git a/Modules/Classification/CLMiniApps/CLN4.cpp b/Modules/Classification/CLMiniApps/CLN4.cpp
index ba778fed55..a7aa9d061a 100644
--- a/Modules/Classification/CLMiniApps/CLN4.cpp
+++ b/Modules/Classification/CLMiniApps/CLN4.cpp
@@ -1,117 +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 "mitkCommandLineParser.h"
#include "mitkIOUtil.h"
#include <mitkImageCast.h>
#include "mitkCommandLineParser.h"
#include <itkN4BiasFieldCorrectionImageFilter.h>
#include <itkSTAPLEImageFilter.h>
int main(int argc, char* argv[])
{
typedef itk::Image<unsigned char, 3> MaskImageType;
typedef itk::Image<float, 3> ImageType;
typedef itk::N4BiasFieldCorrectionImageFilter < ImageType, MaskImageType, ImageType > FilterType;
mitkCommandLineParser parser;
parser.setTitle("N4 Bias Field Correction");
parser.setCategory("Classification Command Tools");
parser.setDescription("");
parser.setContributor("German Cancer Research Center (DKFZ)");
parser.setArgumentPrefix("--", "-");
// Add command line argument names
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.addArgument("input", "i", mitkCommandLineParser::Directory, "Input file:", "Input file", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("mask", "m", mitkCommandLineParser::File, "Output file:", "Mask file", us::Any(), false, false, false, mitkCommandLineParser::Output);
parser.addArgument("output", "o", mitkCommandLineParser::File, "Output file:", "Output file", us::Any(), false, false, false, mitkCommandLineParser::Output);
parser.addArgument("number-of-controllpoints", "noc", mitkCommandLineParser::Int, "Parameter", "The noc for the point grid size defining the B-spline estimate (default 4)", us::Any(), true);
parser.addArgument("number-of-fitting-levels", "nofl", mitkCommandLineParser::Int, "Parameter", "Number of fitting levels for the multi-scale approach (default 1)", us::Any(), true);
parser.addArgument("number-of-histogram-bins", "nofl", mitkCommandLineParser::Int, "Parameter", "number of bins defining the log input intensity histogram (default 200)", us::Any(), true);
parser.addArgument("spline-order", "so", mitkCommandLineParser::Int, "Parameter", "Define the spline order (default 3)", us::Any(), true);
parser.addArgument("winer-filter-noise", "wfn", mitkCommandLineParser::Float, "Parameter", "Noise estimate defining the Wiener filter (default 0.01)", us::Any(), true);
parser.addArgument("number-of-maximum-iterations", "nomi", mitkCommandLineParser::Int, "Parameter", "Spezifies the maximum number of iterations per run", us::Any(), true);
// ToDo: Number Of Maximum Iterations durchschleifen
std::map<std::string, us::Any> parsedArgs = parser.parseArguments(argc, argv);
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
MaskImageType::Pointer itkMsk = MaskImageType::New();
mitk::Image::Pointer img = mitk::IOUtil::Load<mitk::Image>(parsedArgs["mask"].ToString());
mitk::CastToItkImage(img, itkMsk);
ImageType::Pointer itkImage = ImageType::New();
mitk::Image::Pointer img2 = mitk::IOUtil::Load<mitk::Image>(parsedArgs["input"].ToString());
mitk::CastToItkImage(img2, itkImage);
FilterType::Pointer filter = FilterType::New();
filter->SetInput(itkImage);
filter->SetMaskImage(itkMsk);
if (parsedArgs.count("number-of-controllpoints") > 0)
{
int variable = us::any_cast<int>(parsedArgs["maximum-iterations"]);
- MITK_INFO << "Number of controll points: " << variable;
+ MITK_INFO << "Number of control points: " << variable;
filter->SetNumberOfControlPoints(variable);
}
if (parsedArgs.count("number-of-fitting-levels") > 0)
{
int variable = us::any_cast<int>(parsedArgs["number-of-fitting-levels"]);
MITK_INFO << "Number of fitting levels: " << variable;
filter->SetNumberOfFittingLevels(variable);
}
if (parsedArgs.count("number-of-histogram-bins") > 0)
{
int variable = us::any_cast<int>(parsedArgs["number-of-histogram-bins"]);
MITK_INFO << "Number of histogram bins: " << variable;
filter->SetNumberOfHistogramBins(variable);
}
if (parsedArgs.count("spline-order") > 0)
{
int variable = us::any_cast<int>(parsedArgs["spline-order"]);
MITK_INFO << "Spline Order " << variable;
filter->SetSplineOrder(variable);
}
if (parsedArgs.count("winer-filter-noise") > 0)
{
float variable = us::any_cast<float>(parsedArgs["winer-filter-noise"]);
MITK_INFO << "Number of histogram bins: " << variable;
filter->SetWienerFilterNoise(variable);
}
if (parsedArgs.count("number-of-maximum-iterations") > 0)
{
int variable = us::any_cast<int>(parsedArgs["number-of-maximum-iterations"]);
MITK_INFO << "Number of Maximum Iterations: " << variable;
auto list = filter->GetMaximumNumberOfIterations();
list.Fill(variable);
filter->SetMaximumNumberOfIterations(list);
}
filter->Update();
auto out = filter->GetOutput();
mitk::Image::Pointer outImg = mitk::Image::New();
mitk::CastToMitkImage(out, outImg);
mitk::IOUtil::Save(outImg, parsedArgs["output"].ToString());
return EXIT_SUCCESS;
}
diff --git a/Modules/Classification/CLUtilities/CMakeLists.txt b/Modules/Classification/CLUtilities/CMakeLists.txt
index 577381576a..c7766e1cad 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 ITK|MathematicalMorphology+Smoothing VTK|FiltersStatistics
+ PACKAGE_DEPENDS PUBLIC 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/itkCoocurenceMatrixFeatureFunctor.h b/Modules/Classification/CLUtilities/include/itkCoocurenceMatrixFeatureFunctor.h
index ab99b4415f..f3de451fac 100644
--- a/Modules/Classification/CLUtilities/include/itkCoocurenceMatrixFeatureFunctor.h
+++ b/Modules/Classification/CLUtilities/include/itkCoocurenceMatrixFeatureFunctor.h
@@ -1,267 +1,267 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 itkCooccurenceMatrixFeatureFunctor_h
#define itkCooccurenceMatrixFeatureFunctor_h
#include "itkConstNeighborhoodIterator.h"
#include <itkHistogramToTextureFeaturesFilter.h>
#include <itkHistogram.h>
#include <bitset>
/*
* To Do:
* - Enable normalization of GLCM
*/
namespace itk
{
namespace Functor
{
-/** \brief Functor for texture feature calculation based on the Cooccurence matrix
+/** \brief Functor for texture feature calculation based on the Cooccurrence matrix
*/
template< typename TNeighborhoodType, typename TPixelOutputType>
class NeighborhoodCooccurenceMatrix
{
public:
// Functor defines
typedef itk::Statistics::Histogram< double, itk::Statistics::DenseFrequencyContainer2 > HistogramType;
typedef typename TNeighborhoodType::OffsetType OffsetType;
typedef typename HistogramType::SizeType SizeType;
typedef typename HistogramType::MeasurementVectorType MeasurementVectorType;
typedef typename itk::Statistics::HistogramToTextureFeaturesFilter<HistogramType> HistoToFeatureFilter;
static const unsigned int OutputCount = 8;
typedef vnl_vector_fixed<TPixelOutputType, OutputCount> OutputVectorType;
typedef TNeighborhoodType NeighborhoodType;
enum OutputFeatures
{
ENERGY, ENTROPY, CORRELATION, INERTIA, CLUSTERSHADE, CLUSTERPROMINENCE, HARALICKCORRELATION, INVERSEDIFFERENCEMOMENT
};
static const char * GetFeatureName(unsigned int f )
{
static const char * const FeatureNames[] = {"ENERGY", "ENTROPY", "CORRELATION", "INERTIA", "CLUSTERSHADE", "CLUSTERPROMINENCE", "HARALICKCORRELATION", "INVERSEDIFFERENCEMOMENT"};
return FeatureNames[f];
}
enum OffsetDirection
{
DIR_x1_x0_x0 = 1,
DIR_x1_x1_x0 = 2,
DIR_x0_x1_x0 = 4,
DIR_n1_x1_x0 = 8,
DIR_x1_x0_x1 = 16,
DIR_x1_x1_x1 = 32,
DIR_x0_x1_x1 = 64,
DIR_n1_x1_x1 = 128,
DIR_x1_x0_n1 = 256,
DIR_x1_x1_n1 = 512,
DIR_x0_x1_n1 = 1024,
DIR_n1_x1_n1 = 2048,
DIR_x0_x0_x1 = 4096,
ROTATION_INVARIANT = 8191
};
static std::map<OffsetDirection, OffsetType > CreateStdOffsets()
{
std::map<OffsetDirection,OffsetType> offsetMap;
{
itk::Offset<3> off = {{1,0,0}};
offsetMap[DIR_x1_x0_x0] = off;
}
{
itk::Offset<3> off = {{1,1,0}};
offsetMap[DIR_x1_x1_x0] = off;
}
{
itk::Offset<3> off = {{0,1,0}};
offsetMap[DIR_x0_x1_x0] = off;
}
{
itk::Offset<3> off = {{-1,1,0}};
offsetMap[DIR_n1_x1_x0] = off;
}
{
itk::Offset<3> off = {{1,0,1}};
offsetMap[DIR_x1_x0_x1]= off;
}
{
itk::Offset<3> off = {{1,1,1}};
offsetMap[DIR_x1_x1_x1] = off;
}
{
itk::Offset<3> off = {{0,1,1}};
offsetMap[DIR_x0_x1_x1] = off;
}
{
itk::Offset<3> off = {{-1,1,1}};
offsetMap[DIR_n1_x1_x1] = off;
}
{
itk::Offset<3> off = {{1,0,-1}};
offsetMap[DIR_x1_x0_n1] = off;
}
{
itk::Offset<3> off = {{1,1,-1}};
offsetMap[DIR_x1_x1_n1] = off;
}
{
itk::Offset<3> off = {{0,1,-1}};
offsetMap[DIR_x0_x1_n1] = off;
}
{
itk::Offset<3> off = {{-1,1,-1}};
offsetMap[DIR_n1_x1_n1] = off;
}
{
itk::Offset<3> off = {{0,0,1}};
offsetMap[DIR_x0_x0_x1] = off;
}
return offsetMap;
}
int m_DirectionFlags;
std::map<OffsetDirection, OffsetType> m_offsetMap;
unsigned int m_levels;
void DirectionFlags(int flags)
{
DirectionFlags(static_cast<OffsetDirection>(flags));
}
void DirectionFlags(OffsetDirection flags)
{
m_DirectionFlags = flags;
}
void SetLevels(unsigned int lvl)
{
m_levels = lvl;
}
NeighborhoodCooccurenceMatrix()
{
m_offsetMap = CreateStdOffsets();
m_levels = 5;
m_DirectionFlags =
DIR_x1_x0_x0; /*| DIR_x1_x1_x0 | DIR_x0_x1_x0 |
DIR_n1_x1_x0 | DIR_x1_x0_x1 | DIR_x1_x1_x1 |
DIR_x0_x1_x1 | DIR_n1_x1_x1 | DIR_x1_x0_n1 |
DIR_x1_x1_n1 | DIR_x0_x1_n1 | DIR_n1_x1_n1 |
DIR_x0_x0_x1;*/
std::cout << "NeighborhoodCooccurenceMatrix" << std::endl;
}
inline OutputVectorType operator()(const TNeighborhoodType & it) const
{
double min = std::numeric_limits<double>::max();
double max = std::numeric_limits<double>::min();
for (unsigned int i = 0; i < it.Size(); ++i)
{
double value = it.GetPixel(i);
max = (value > max) ? value : max;
min = (value < min) ? value : min;
}
if ( min >= max)
{
OutputVectorType nullRes; nullRes.fill(0);
return nullRes;
}
SizeType size;
MeasurementVectorType minBorder;
MeasurementVectorType maxBorder;
OffsetType g1, g2;
- size.SetSize(2); // 2D Historgram
+ size.SetSize(2); // 2D Histogram
size.Fill(m_levels); // 5 bins each dim
minBorder.SetSize(2); // min range value
minBorder.Fill(min);
maxBorder.SetSize(2); // max range value
maxBorder.Fill(max);
MeasurementVectorType cooccur;
cooccur.SetSize(2);
OutputVectorType output_vector;
output_vector.fill(0);
double div_num_dirs = 0;
//std::bitset<14> x(m_DirectionFlags);
//std::cout << "Direction flag " << x;
for(typename std::map<OffsetDirection,OffsetType>::const_iterator dir_it = m_offsetMap.begin(),
end = m_offsetMap.end(); dir_it != end; dir_it ++){
if(! (dir_it->first & m_DirectionFlags))
continue;
div_num_dirs++;
HistogramType::Pointer histogram = HistogramType::New();
histogram->SetMeasurementVectorSize(2);
histogram->Initialize(size, minBorder, maxBorder);
for (unsigned int i = 0; i < it.Size(); ++i)
{
// grayvalue pair (g1,g2)
// g1 ~ g2 with ~ as relation (e.g. a offset calculation)
g1 = it.GetOffset(i);
g2 = g1 + dir_it->second;
cooccur[0] = it.GetPixel(i);
cooccur[1] = it.GetImagePointer()->GetPixel(it.GetIndex(i)+dir_it->second);
histogram->IncreaseFrequencyOfMeasurement(cooccur, 1);
std::swap(cooccur[0],cooccur[1]);
histogram->IncreaseFrequencyOfMeasurement(cooccur, 1);
}
//To do Normalize GLCM N_g Number of levels
HistoToFeatureFilter::Pointer filter = HistoToFeatureFilter::New();
filter->SetInput(histogram);
filter->Update();
output_vector[ENERGY] += filter->GetEnergy();
output_vector[ENTROPY] += filter->GetEntropy();
output_vector[CORRELATION] += filter->GetCorrelation();
output_vector[INERTIA] += filter->GetInertia();
output_vector[CLUSTERSHADE] += filter->GetClusterShade();
output_vector[CLUSTERPROMINENCE] += filter->GetClusterProminence();
output_vector[HARALICKCORRELATION] += filter->GetHaralickCorrelation();
output_vector[INVERSEDIFFERENCEMOMENT] += filter->GetInverseDifferenceMoment();
}
//std::cout << "Number of directions " << div_num_dirs << std::endl;
// output_vector /= div_num_dirs;
return output_vector;
}
};
}// end namespace functor
} // end namespace itk
#endif // itkNeighborhoodFunctors_h
diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.h
index f7992dd190..3d4180b029 100644
--- a/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.h
+++ b/Modules/Classification/CLUtilities/include/itkEnhancedHistogramToTextureFeaturesFilter.h
@@ -1,273 +1,273 @@
/*=========================================================================
*
* 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
*
* https://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 __itkEnhancedHistogramToTextureFeaturesFilter_h
#define __itkEnhancedHistogramToTextureFeaturesFilter_h
#include "itkHistogram.h"
#include "itkMacro.h"
#include "itkProcessObject.h"
#include "itkSimpleDataObjectDecorator.h"
/** Get built-in type. Creates member Get"name"() (e.g., GetVisibility()); */
#define itkMacroGLCMFeatureGetter(name) \
const MeasurementObjectType * Get##name##Output() const; \
\
MeasurementType Get##name() const;
namespace itk
{
namespace Statistics
{
/** \class EnhancedHistogramToTextureFeaturesFilter
* \brief This class computes texture feature coefficients from a grey level
* co-occurrence matrix.
*
* This class computes features that summarize image texture, given a grey level
* co-occurrence matrix (generated by a ScalarImageToCooccurrenceMatrixFilter
* or related class).
*
* The features calculated are as follows (where \f$ g(i, j) \f$ is the element in
* cell i, j of a a normalized GLCM):
*
* "Energy" \f$ = f_1 = \sum_{i,j}g(i, j)^2 \f$
*
* "Entropy" \f$ = f_2 = -\sum_{i,j}g(i, j) \log_2 g(i, j)\f$, or 0 if \f$g(i, j) = 0\f$
*
* "Correlation" \f$ = f_3 = \sum_{i,j}\frac{(i - \mu)(j - \mu)g(i, j)}{\sigma^2} \f$
*
* "Difference Moment" \f$= f_4 = \sum_{i,j}\frac{1}{1 + (i - j)^2}g(i, j) \f$
*
* "Inertia" \f$ = f_5 = \sum_{i,j}(i - j)^2g(i, j) \f$ (sometimes called "contrast.")
*
* "Cluster Shade" \f$ = f_6 = \sum_{i,j}((i - \mu) + (j - \mu))^3 g(i, j) \f$
*
* "Cluster Prominence" \f$ = f_7 = \sum_{i,j}((i - \mu) + (j - \mu))^4 g(i, j) \f$
*
* "Haralick's Correlation" \f$ = f_8 = \frac{\sum_{i,j}(i, j) g(i, j) -\mu_t^2}{\sigma_t^2} \f$
* where \f$\mu_t\f$ and \f$\sigma_t\f$ are the mean and standard deviation of the row
* (or column, due to symmetry) sums.
*
* Above, \f$ \mu = \f$ (weighted pixel average) \f$ = \sum_{i,j}i \cdot g(i, j) =
* \sum_{i,j}j \cdot g(i, j) \f$ (due to matrix summetry), and
*
* \f$ \sigma = \f$ (weighted pixel variance) \f$ = \sum_{i,j}(i - \mu)^2 \cdot g(i, j) =
* \sum_{i,j}(j - \mu)^2 \cdot g(i, j) \f$ (due to matrix summetry)
*
* A good texture feature set to use is the Conners, Trivedi and Harlow set:
* features 1, 2, 4, 5, 6, and 7. There is some correlation between the various
* features, so using all of them at the same time is not necessarialy a good idea.
*
* NOTA BENE: The input histogram will be forcably normalized!
* This algorithm takes three passes through the input
* histogram if the histogram was already normalized, and four if not.
*
* Print references:
*
* Haralick, R.M., K. Shanmugam and I. Dinstein. 1973. Textural Features for
* Image Classification. IEEE Transactions on Systems, Man and Cybernetics.
* SMC-3(6):610-620.
*
* Haralick, R.M. 1979. Statistical and Structural Approaches to Texture.
* Proceedings of the IEEE, 67:786-804.
*
- * R.W. Conners and C.A. Harlow. A Theoretical Comaprison of Texture Algorithms.
+ * R.W. Conners and C.A. Harlow. A Theoretical Comparison of Texture Algorithms.
* IEEE Transactions on Pattern Analysis and Machine Intelligence, 2:204-222, 1980.
*
* R.W. Conners, M.M. Trivedi, and C.A. Harlow. Segmentation of a High-Resolution
* Urban Scene using Texture Operators. Computer Vision, Graphics and Image
* Processing, 25:273-310, 1984.
*
* \sa ScalarImageToCooccurrenceMatrixFilter
* \sa ScalarImageToTextureFeaturesFilter
*
* Author: Zachary Pincus
* \ingroup ITKStatistics
*/
template< typename THistogram >
class EnhancedHistogramToTextureFeaturesFilter:public ProcessObject
{
public:
/** Standard typedefs */
typedef EnhancedHistogramToTextureFeaturesFilter Self;
typedef ProcessObject Superclass;
typedef SmartPointer< Self > Pointer;
typedef SmartPointer< const Self > ConstPointer;
/** Run-time type information (and related methods). */
itkTypeMacro(EnhancedHistogramToTextureFeaturesFilter, ProcessObject);
/** standard New() method support */
itkNewMacro(Self);
typedef THistogram HistogramType;
typedef typename HistogramType::Pointer HistogramPointer;
typedef typename HistogramType::ConstPointer HistogramConstPointer;
typedef typename HistogramType::MeasurementType MeasurementType;
typedef typename HistogramType::MeasurementVectorType MeasurementVectorType;
typedef typename HistogramType::IndexType IndexType;
typedef typename HistogramType::AbsoluteFrequencyType AbsoluteFrequencyType;
typedef typename HistogramType::RelativeFrequencyType RelativeFrequencyType;
typedef typename HistogramType::TotalAbsoluteFrequencyType
TotalAbsoluteFrequencyType;
typedef typename HistogramType::TotalRelativeFrequencyType
TotalRelativeFrequencyType;
/** Container to hold relative frequencies of the histogram */
typedef std::vector< RelativeFrequencyType > RelativeFrequencyContainerType;
/** Method to Set/Get the input Histogram */
using Superclass::SetInput;
void SetInput(const HistogramType *histogram);
const HistogramType * GetInput() const;
/** Smart Pointer type to a DataObject. */
typedef DataObject::Pointer DataObjectPointer;
/** Type of DataObjects used for scalar outputs */
typedef SimpleDataObjectDecorator< MeasurementType > MeasurementObjectType;
/** Return energy texture value. */
MeasurementType GetEnergy() const;
const MeasurementObjectType * GetEnergyOutput() const;
/** Return entropy texture value. */
MeasurementType GetEntropy() const;
const MeasurementObjectType * GetEntropyOutput() const;
/** return correlation texture value. */
MeasurementType GetCorrelation() const;
const MeasurementObjectType * GetCorrelationOutput() const;
/** Return inverse difference moment texture value. */
MeasurementType GetInverseDifferenceMoment() const;
const MeasurementObjectType * GetInverseDifferenceMomentOutput() const;
/** Return inertia texture value. */
MeasurementType GetInertia() const;
const MeasurementObjectType * GetInertiaOutput() const;
/** Return cluster shade texture value. */
MeasurementType GetClusterShade() const;
const MeasurementObjectType * GetClusterShadeOutput() const;
/** Return cluster prominence texture value. */
MeasurementType GetClusterProminence() const;
const MeasurementObjectType * GetClusterProminenceOutput() const;
/** Return Haralick correlation texture value. */
MeasurementType GetHaralickCorrelation() const;
const MeasurementObjectType * GetHaralickCorrelationOutput() const;
itkMacroGLCMFeatureGetter(Autocorrelation);
itkMacroGLCMFeatureGetter(Contrast);
itkMacroGLCMFeatureGetter(Dissimilarity);
itkMacroGLCMFeatureGetter(MaximumProbability);
itkMacroGLCMFeatureGetter(InverseVariance);
itkMacroGLCMFeatureGetter(Homogeneity1);
itkMacroGLCMFeatureGetter(ClusterTendency);
itkMacroGLCMFeatureGetter(Variance);
itkMacroGLCMFeatureGetter(SumAverage);
itkMacroGLCMFeatureGetter(SumEntropy);
itkMacroGLCMFeatureGetter(SumVariance);
itkMacroGLCMFeatureGetter(DifferenceAverage);
itkMacroGLCMFeatureGetter(DifferenceEntropy);
itkMacroGLCMFeatureGetter(DifferenceVariance);
itkMacroGLCMFeatureGetter(InverseDifferenceMomentNormalized);
itkMacroGLCMFeatureGetter(InverseDifferenceNormalized);
itkMacroGLCMFeatureGetter(InverseDifference);
itkMacroGLCMFeatureGetter(JointAverage);
itkMacroGLCMFeatureGetter(FirstMeasureOfInformationCorrelation);
itkMacroGLCMFeatureGetter(SecondMeasureOfInformationCorrelation);
/** Texture feature types */
typedef enum {
Energy,
Entropy,
Correlation,
InverseDifferenceMoment,
Inertia,
ClusterShade,
ClusterProminence,
HaralickCorrelation,
Autocorrelation,
Contrast,
Dissimilarity,
MaximumProbability,
InverseVariance,
Homogeneity1,
ClusterTendency,
Variance,
SumAverage,
SumEntropy,
SumVariance,
DifferenceAverage,
DifferenceEntropy,
DifferenceVariance,
InverseDifferenceMomentNormalized,
InverseDifferenceNormalized,
InverseDifference,
JointAverage,
FirstMeasureOfInformationCorrelation,
SecondMeasureOfInformationCorrelation,
InvalidFeatureName
} TextureFeatureName;
/** convenience method to access the texture values */
MeasurementType GetFeature(TextureFeatureName name);
protected:
EnhancedHistogramToTextureFeaturesFilter();
~EnhancedHistogramToTextureFeaturesFilter() override {}
void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE;
/** Make a DataObject to be used for output output. */
typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType;
using Superclass::MakeOutput;
DataObjectPointer MakeOutput(DataObjectPointerArraySizeType) ITK_OVERRIDE;
void GenerateData() ITK_OVERRIDE;
private:
EnhancedHistogramToTextureFeaturesFilter(const Self &); //purposely not implemented
void operator=(const Self &); //purposely not implemented
void ComputeMeansAndVariances(double & pixelMean, double & marginalMean,
double & marginalDevSquared, double & pixelVariance);
RelativeFrequencyContainerType m_RelativeFrequencyContainer;
};
} // end of namespace Statistics
} // end of namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkEnhancedHistogramToTextureFeaturesFilter.hxx"
#endif
#endif
diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.h
index 1f58958dd6..78bec89ce3 100644
--- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.h
+++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.h
@@ -1,295 +1,295 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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
*
* https://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 __itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter_h
#define __itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter_h
#include "itkImage.h"
#include "itkHistogram.h"
#include "itkNumericTraits.h"
#include "itkVectorContainer.h"
namespace itk
{
namespace Statistics
{
/** \class EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter
* \brief This class computes a run length matrix (histogram) from
* a given image and a mask image if provided. Run length matrces are
* used for image texture description.
*
* This filters creates a grey-level run length matrix from a N-D scalar
* image. This is another possible texture description. See the following
* references.
* M. M. Galloway. Texture analysis using gray level run lengths. Computer
* Graphics and Image Processing, 4:172-179, 1975.
*
* A. Chu, C. M. Sehgal, and J. F. Greenleaf. Use of gray value distribution of
* run lengths for texture analysis. Pattern Recognition Letters, 11:415-420,
* 1990.
*
* B. R. Dasarathy and E. B. Holder. Image characterizations based on joint
* gray-level run-length distributions. Pattern Recognition Letters, 12:490-502,
* 1991.
*
* The basic idea is as follows:
* Given an image and an offset (e.g. (1, -1) for a 2-d image), each element
* in the joint histogram describes the frequency for a particular distance/
* intensity pair within a given image. This distance/intensity pair can be
* described as follows: we start at a given voxel which has some intensity.
* We then "jump" to neighboring pixels in increments provided by the offset(s)
* as long as the pixel to which we are jumping is within the same intensity
* bin as the original voxel. The distance component is given by the distance
* from the original to the final voxel satisfying our jumping criteria.
*
* The offset (or offsets) along which the co-occurences are calculated can be
* set by the user. Traditionally, only one offset is used per histogram, and
* offset components in the range [-1, 1] are used. For rotation-invariant
* features averages of features computed over several histograms with different
* offsets are generally used, instead of computing features from one histogram
* create with several offsets. Additionally, instead of using offsets of two or
* more pixels in any direction, multi-resolution techniques (e.g. image
* pyramids) are generally used to deal with texture at different spatial
* resolutions.
*
* This class calculates a 2-d histogram of all the intensity/distance pairs in
* the given image's requested region, for a given set of offsets. That is, if
* a given offset falls outside of the requested region (or outside the mask)
* at a particular point, that distance/intensity pair will not be added to
* the matrix.
*
* The number of histogram bins on each axis can be set (defaults to 256). Also,
* by default the histogram min and max corresponds to the largest and smallest
* possible pixel value of that pixel type. To customize the histogram bounds
* for a given image, the max and min pixel values that will be placed in the
* histogram can be set manually. NB: The min and max are INCLUSIVE.
*
* Further, the type of histogram frequency container used is an optional
* template parameter. By default, a dense container is used, but for images
* with little texture or in cases where the user wants more histogram bins,
* a sparse container can be used for the histogram instead.
*
* WARNING: This probably won't work for pixels of double or long-double type
* unless you set the histogram min and max manually. This is because the largest
* histogram bin by default has max value of the largest possible pixel value
* plus 1. For double and long-double types, whose "RealType" as defined by the
* NumericTraits class is the same, and thus cannot hold any larger values,
* this would cause a float overflow.
*
* IJ article: https://hdl.handle.net/1926/1374
*
* \sa ScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter
* \sa EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter
* \sa HistogramToNeighbourhoodGreyLevelDifferenceFeaturesFilter
*
* \author: Nick Tustison
* \ingroup ITKStatistics
*/
template<typename TImageType, typename THistogramFrequencyContainer =
DenseFrequencyContainer2>
class EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter : public ProcessObject
{
public:
/** Standard typedefs */
typedef EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter Self;
typedef ProcessObject Superclass;
typedef SmartPointer<Self> Pointer;
typedef SmartPointer<const Self> ConstPointer;
/** Run-time type information (and related methods). */
itkTypeMacro( EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter, ProcessObject );
/** standard New() method support */
itkNewMacro( Self );
typedef TImageType ImageType;
typedef typename ImageType::Pointer ImagePointer;
typedef typename ImageType::ConstPointer ImageConstPointer;
typedef typename ImageType::PixelType PixelType;
typedef typename ImageType::IndexType IndexType;
typedef typename ImageType::RegionType RegionType;
typedef typename ImageType::SizeType RadiusType;
typedef typename ImageType::OffsetType OffsetType;
typedef VectorContainer<unsigned char, OffsetType> OffsetVector;
typedef typename OffsetVector::Pointer OffsetVectorPointer;
typedef typename ImageType::PointType PointType;
typedef typename NumericTraits<PixelType>::RealType MeasurementType;
typedef typename NumericTraits<PixelType>::RealType RealType;
typedef Histogram<MeasurementType, THistogramFrequencyContainer>
HistogramType;
typedef typename HistogramType::Pointer HistogramPointer;
typedef typename HistogramType::ConstPointer HistogramConstPointer;
typedef typename HistogramType::MeasurementVectorType MeasurementVectorType;
/** ImageDimension constants */
itkStaticConstMacro( ImageDimension, unsigned int,
TImageType::ImageDimension );
/** Specify the default number of bins per axis */
itkStaticConstMacro( DefaultBinsPerAxis, unsigned int, 256 );
/**
* Set the offsets over which the intensity/distance pairs will be computed.
* Invoking this function clears the previous offsets.
* Note: for each individual offset in the OffsetVector, the rightmost non-zero
* offset element must be positive. For example, in the offset list of a 2D image,
* (1, 0) means the offset along x-axis. (1, 0) has to be set instead
* of (-1, 0). This is required from the iterating order of pixel iterator.
*
*/
itkSetObjectMacro( Offsets, OffsetVector );
/**
* Set offset over which the intensity/distance pairs will be computed.
* Invoking this function clears the previous offset(s).
* Note: for each individual offset, the rightmost non-zero
* offset element must be positive. For example, in the offset list of a 2D image,
* (1, 0) means the offset along x-axis. (1, 0) has to be set instead
* of (-1, 0). This is required from the iterating order of pixel iterator.
*
*/
void SetOffset( const OffsetType offset );
void AddOffsets( const std::vector<OffsetType> offset );
/**
* Get the current offset(s).
*/
itkGetModifiableObjectMacro(Offsets, OffsetVector );
/** Set number of histogram bins along each axis */
itkSetMacro( NumberOfBinsPerAxis, unsigned int );
/** Get number of histogram bins along each axis */
itkGetConstMacro( NumberOfBinsPerAxis, unsigned int );
/**
* Set the min and max (inclusive) pixel value that will be used in
* generating the histogram.
*/
void SetPixelValueMinMax( PixelType min, PixelType max );
/** Get the min pixel value defining one dimension of the joint histogram. */
itkGetConstMacro( Min, PixelType );
/** Get the max pixel value defining one dimension of the joint histogram. */
itkGetConstMacro( Max, PixelType );
/**
* Set the min and max (inclusive) pixel value that will be used in
* generating the histogram.
*/
void SetDistanceValueMinMax( RealType min, RealType max );
/**
* Get the min distance value defining one dimension of the joint histogram.
*/
itkGetConstMacro( MinDistance, RealType );
/**
* Get the max distance value defining one dimension of the joint histogram.
*/
itkGetConstMacro( MaxDistance, RealType );
/** Method to set the input image */
using Superclass::SetInput;
void SetInput( const ImageType *image );
/** Method to get the input image */
const ImageType * GetInput() const;
/** Method to set the mask image */
void SetMaskImage( const ImageType *image );
/** Method to get the mask image */
const ImageType * GetMaskImage() const;
/** method to get the Histogram */
const HistogramType * GetOutput() const;
/** method to get the Histogram */
double* GetSiMatrix() const;
/**
* Set the pixel value of the mask that should be considered "inside" the
* object. Defaults to 1.
*/
itkSetMacro( InsidePixelValue, PixelType );
itkGetConstMacro( InsidePixelValue, PixelType );
protected:
EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter();
~EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter() override {};
void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE;
/** Standard itk::ProcessObject subclass method. */
typedef DataObject::Pointer DataObjectPointer;
typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType;
using Superclass::MakeOutput;
DataObjectPointer MakeOutput( DataObjectPointerArraySizeType idx ) ITK_OVERRIDE;
/** This method causes the filter to generate its output. */
void GenerateData() ITK_OVERRIDE;
/**
* Normalize the direction of the offset before it is applied.
- * The last non-zero dimension of the offest has to be positive in order
+ * The last non-zero dimension of the offset has to be positive in order
* to match to scanning order of the iterator. Only the sign is changed.
* For example, the input offset (-1, 0) will be normalized as
* (1, 0).
* */
void NormalizeOffsetDirection(OffsetType &offset);
private:
unsigned int m_NumberOfBinsPerAxis;
PixelType m_Min;
PixelType m_Max;
RealType m_MinDistance;
RealType m_MaxDistance;
PixelType m_InsidePixelValue;
MeasurementVectorType m_LowerBound;
MeasurementVectorType m_UpperBound;
OffsetVectorPointer m_Offsets;
double * m_siMatrix;
};
} // end of namespace Statistics
} // end of namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceMatrixFilter.hxx"
#endif
#endif
diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.h
index dfdaf31361..b5a7463414 100644
--- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.h
+++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.h
@@ -1,288 +1,288 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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
*
* https://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_h
#define __itkEnhancedScalarImageToRunLengthMatrixFilter_h
#include "itkImage.h"
#include "itkHistogram.h"
#include "itkNumericTraits.h"
#include "itkVectorContainer.h"
namespace itk
{
namespace Statistics
{
/** \class EnhancedScalarImageToRunLengthMatrixFilter
* \brief This class computes a run length matrix (histogram) from
* a given image and a mask image if provided. Run length matrces are
* used for image texture description.
*
* This filters creates a grey-level run length matrix from a N-D scalar
* image. This is another possible texture description. See the following
* references.
* M. M. Galloway. Texture analysis using gray level run lengths. Computer
* Graphics and Image Processing, 4:172-179, 1975.
*
* A. Chu, C. M. Sehgal, and J. F. Greenleaf. Use of gray value distribution of
* run lengths for texture analysis. Pattern Recognition Letters, 11:415-420,
* 1990.
*
* B. R. Dasarathy and E. B. Holder. Image characterizations based on joint
* gray-level run-length distributions. Pattern Recognition Letters, 12:490-502,
* 1991.
*
* The basic idea is as follows:
* Given an image and an offset (e.g. (1, -1) for a 2-d image), each element
* in the joint histogram describes the frequency for a particular distance/
* intensity pair within a given image. This distance/intensity pair can be
* described as follows: we start at a given voxel which has some intensity.
* We then "jump" to neighboring pixels in increments provided by the offset(s)
* as long as the pixel to which we are jumping is within the same intensity
* bin as the original voxel. The distance component is given by the distance
* from the original to the final voxel satisfying our jumping criteria.
*
* The offset (or offsets) along which the co-occurences are calculated can be
* set by the user. Traditionally, only one offset is used per histogram, and
* offset components in the range [-1, 1] are used. For rotation-invariant
* features averages of features computed over several histograms with different
* offsets are generally used, instead of computing features from one histogram
* create with several offsets. Additionally, instead of using offsets of two or
* more pixels in any direction, multi-resolution techniques (e.g. image
* pyramids) are generally used to deal with texture at different spatial
* resolutions.
*
* This class calculates a 2-d histogram of all the intensity/distance pairs in
* the given image's requested region, for a given set of offsets. That is, if
* a given offset falls outside of the requested region (or outside the mask)
* at a particular point, that distance/intensity pair will not be added to
* the matrix.
*
* The number of histogram bins on each axis can be set (defaults to 256). Also,
* by default the histogram min and max corresponds to the largest and smallest
* possible pixel value of that pixel type. To customize the histogram bounds
* for a given image, the max and min pixel values that will be placed in the
* histogram can be set manually. NB: The min and max are INCLUSIVE.
*
* Further, the type of histogram frequency container used is an optional
* template parameter. By default, a dense container is used, but for images
* with little texture or in cases where the user wants more histogram bins,
* a sparse container can be used for the histogram instead.
*
* WARNING: This probably won't work for pixels of double or long-double type
* unless you set the histogram min and max manually. This is because the largest
* histogram bin by default has max value of the largest possible pixel value
* plus 1. For double and long-double types, whose "RealType" as defined by the
* NumericTraits class is the same, and thus cannot hold any larger values,
* this would cause a float overflow.
*
* IJ article: https://hdl.handle.net/1926/1374
*
* \sa ScalarImageToRunLengthFeaturesFilter
* \sa EnhancedScalarImageToRunLengthMatrixFilter
* \sa HistogramToRunLengthFeaturesFilter
*
* \author: Nick Tustison
* \ingroup ITKStatistics
*/
template<typename TImageType, typename THistogramFrequencyContainer =
DenseFrequencyContainer2>
class EnhancedScalarImageToRunLengthMatrixFilter : public ProcessObject
{
public:
/** Standard typedefs */
typedef EnhancedScalarImageToRunLengthMatrixFilter Self;
typedef ProcessObject Superclass;
typedef SmartPointer<Self> Pointer;
typedef SmartPointer<const Self> ConstPointer;
/** Run-time type information (and related methods). */
itkTypeMacro( EnhancedScalarImageToRunLengthMatrixFilter, ProcessObject );
/** standard New() method support */
itkNewMacro( Self );
typedef TImageType ImageType;
typedef typename ImageType::Pointer ImagePointer;
typedef typename ImageType::ConstPointer ImageConstPointer;
typedef typename ImageType::PixelType PixelType;
typedef typename ImageType::IndexType IndexType;
typedef typename ImageType::RegionType RegionType;
typedef typename ImageType::SizeType RadiusType;
typedef typename ImageType::OffsetType OffsetType;
typedef VectorContainer<unsigned char, OffsetType> OffsetVector;
typedef typename OffsetVector::Pointer OffsetVectorPointer;
typedef typename ImageType::PointType PointType;
typedef typename NumericTraits<PixelType>::RealType MeasurementType;
typedef typename NumericTraits<PixelType>::RealType RealType;
typedef Histogram<MeasurementType, THistogramFrequencyContainer>
HistogramType;
typedef typename HistogramType::Pointer HistogramPointer;
typedef typename HistogramType::ConstPointer HistogramConstPointer;
typedef typename HistogramType::MeasurementVectorType MeasurementVectorType;
/** ImageDimension constants */
itkStaticConstMacro( ImageDimension, unsigned int,
TImageType::ImageDimension );
/** Specify the default number of bins per axis */
itkStaticConstMacro( DefaultBinsPerAxis, unsigned int, 256 );
/**
* Set the offsets over which the intensity/distance pairs will be computed.
* Invoking this function clears the previous offsets.
* Note: for each individual offset in the OffsetVector, the rightmost non-zero
* offset element must be positive. For example, in the offset list of a 2D image,
* (1, 0) means the offset along x-axis. (1, 0) has to be set instead
* of (-1, 0). This is required from the iterating order of pixel iterator.
*
*/
itkSetObjectMacro( Offsets, OffsetVector );
/**
* Set offset over which the intensity/distance pairs will be computed.
* Invoking this function clears the previous offset(s).
* Note: for each individual offset, the rightmost non-zero
* offset element must be positive. For example, in the offset list of a 2D image,
* (1, 0) means the offset along x-axis. (1, 0) has to be set instead
* of (-1, 0). This is required from the iterating order of pixel iterator.
*
*/
void SetOffset( const OffsetType offset );
/**
* Get the current offset(s).
*/
itkGetModifiableObjectMacro(Offsets, OffsetVector );
/** Set number of histogram bins along each axis */
itkSetMacro( NumberOfBinsPerAxis, unsigned int );
/** Get number of histogram bins along each axis */
itkGetConstMacro( NumberOfBinsPerAxis, unsigned int );
/**
* Set the min and max (inclusive) pixel value that will be used in
* generating the histogram.
*/
void SetPixelValueMinMax( PixelType min, PixelType max );
/** Get the min pixel value defining one dimension of the joint histogram. */
itkGetConstMacro( Min, PixelType );
/** Get the max pixel value defining one dimension of the joint histogram. */
itkGetConstMacro( Max, PixelType );
/**
* Set the min and max (inclusive) pixel value that will be used in
* generating the histogram.
*/
void SetDistanceValueMinMax( RealType min, RealType max );
/**
* Get the min distance value defining one dimension of the joint histogram.
*/
itkGetConstMacro( MinDistance, RealType );
/**
* Get the max distance value defining one dimension of the joint histogram.
*/
itkGetConstMacro( MaxDistance, RealType );
/** Method to set the input image */
using Superclass::SetInput;
void SetInput( const ImageType *image );
/** Method to get the input image */
const ImageType * GetInput() const;
/** Method to set the mask image */
void SetMaskImage( const ImageType *image );
/** Method to get the mask image */
const ImageType * GetMaskImage() const;
/** method to get the Histogram */
const HistogramType * GetOutput() const;
/**
* Set the pixel value of the mask that should be considered "inside" the
* object. Defaults to 1.
*/
itkSetMacro( InsidePixelValue, PixelType );
itkGetConstMacro( InsidePixelValue, PixelType );
protected:
EnhancedScalarImageToRunLengthMatrixFilter();
~EnhancedScalarImageToRunLengthMatrixFilter() override {};
void PrintSelf( std::ostream& os, Indent indent ) const ITK_OVERRIDE;
/** Standard itk::ProcessObject subclass method. */
typedef DataObject::Pointer DataObjectPointer;
typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType;
using Superclass::MakeOutput;
DataObjectPointer MakeOutput( DataObjectPointerArraySizeType idx ) ITK_OVERRIDE;
/** This method causes the filter to generate its output. */
void GenerateData() ITK_OVERRIDE;
/**
* Normalize the direction of the offset before it is applied.
- * The last non-zero dimension of the offest has to be positive in order
+ * The last non-zero dimension of the offset has to be positive in order
* to match to scanning order of the iterator. Only the sign is changed.
* For example, the input offset (-1, 0) will be normalized as
* (1, 0).
* */
void NormalizeOffsetDirection(OffsetType &offset);
private:
unsigned int m_NumberOfBinsPerAxis;
PixelType m_Min;
PixelType m_Max;
RealType m_MinDistance;
RealType m_MaxDistance;
PixelType m_InsidePixelValue;
MeasurementVectorType m_LowerBound;
MeasurementVectorType m_UpperBound;
OffsetVectorPointer m_Offsets;
};
} // end of namespace Statistics
} // end of namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkEnhancedScalarImageToRunLengthMatrixFilter.hxx"
#endif
#endif
diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.hxx
index 88389fd5d9..9a1d292ce5 100644
--- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.hxx
+++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToSizeZoneMatrixFilter.hxx
@@ -1,405 +1,405 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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
*
* https://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 __itkEnhancedScalarImageToSizeZoneMatrixFilter_hxx
#define __itkEnhancedScalarImageToSizeZoneMatrixFilter_hxx
#include "itkEnhancedScalarImageToSizeZoneMatrixFilter.h"
#include "itkConstNeighborhoodIterator.h"
#include "itkNeighborhood.h"
#include "vnl/vnl_math.h"
#include "itkMacro.h"
#include "itkRescaleIntensityImageFilter.h"
#include "itkMaskImageFilter.h"
#include "itkLabelStatisticsImageFilter.h"
#include "itkScalarConnectedComponentImageFilter.h"
#include "itkRelabelComponentImageFilter.h"
#include "itkCastImageFilter.h"
#include <mitkLog.h>
namespace itk
{
namespace Statistics
{
template<typename TImageType, typename THistogramFrequencyContainer>
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::EnhancedScalarImageToSizeZoneMatrixFilter() :
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
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::SetOffset( const OffsetType offset )
{
OffsetVectorPointer offsetVector = OffsetVector::New();
offsetVector->push_back( offset );
this->SetOffsets( offsetVector );
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToSizeZoneMatrixFilter<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
EnhancedScalarImageToSizeZoneMatrixFilter<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 *
EnhancedScalarImageToSizeZoneMatrixFilter<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 *
EnhancedScalarImageToSizeZoneMatrixFilter<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 EnhancedScalarImageToSizeZoneMatrixFilter<TImageType,
THistogramFrequencyContainer >::HistogramType *
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::GetOutput() const
{
const HistogramType *output =
static_cast<const HistogramType *>( this->ProcessObject::GetOutput( 0 ) );
return output;
}
template<typename TImageType, typename THistogramFrequencyContainer>
typename EnhancedScalarImageToSizeZoneMatrixFilter<TImageType,
THistogramFrequencyContainer>::DataObjectPointer
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::MakeOutput( DataObjectPointerArraySizeType itkNotUsed( idx ) )
{
return HistogramType::New().GetPointer();
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToSizeZoneMatrixFilter<TImageType, THistogramFrequencyContainer>
::GenerateData()
{
HistogramType *output =
static_cast<HistogramType *>( this->ProcessObject::GetOutput( 0 ) );
const ImageType * inputImage = this->GetInput();
const ImageType * maskImage = this->GetMaskImage();
// 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;
//Cast the image to a float image - with no respect to the incoming image
//to prevent some non-templated itk issues
typedef itk::Image<float, 3> FloatImageType;
typedef itk::CastImageFilter<ImageType, FloatImageType> CastFilterType;
typename CastFilterType::Pointer caster = CastFilterType::New();
caster->SetInput(inputImage);
caster->Update();
typename FloatImageType::Pointer floatImage = caster->GetOutput();
//MITK_WARN << "InputImage casted.";
//Cast the mask to an unsigned short image - with no respect to the incomimg maskimage
//to prevent some non-templated itk issues
typedef unsigned short LabelPixelType;
typedef itk::Image<LabelPixelType, 3 > LabelImageType;
typedef itk::CastImageFilter<ImageType, LabelImageType> MaskCastFilterType;
typename MaskCastFilterType::Pointer maskCaster = MaskCastFilterType::New();
maskCaster->SetInput(maskImage);
maskCaster->Update();
//MITK_WARN << "MaskImage casted.";
//Set all values out of the mask to (m_Min + m_Max) / 2.
typedef itk::MaskImageFilter< FloatImageType, LabelImageType, FloatImageType > MaskFilterType;
typename MaskFilterType::Pointer maskFilter = MaskFilterType::New();
maskFilter->SetInput(floatImage);
maskFilter->SetMaskImage(maskCaster->GetOutput());
maskFilter->SetOutsideValue((m_Max + m_Min) / 2);
maskFilter->Update();
//MITK_WARN << "InputImage masked.";
//Rescale intensity to match the size of the histogram
typedef itk::Image< unsigned int, 3 > OutputImageType;
typedef itk::RescaleIntensityImageFilter< FloatImageType,OutputImageType> RescalerType;
typename RescalerType::Pointer rescaler = RescalerType::New();
//We use 0 for nans, all valid numbers will be 1 < x < size
rescaler->SetOutputMinimum( 1 );
rescaler->SetOutputMaximum( size[0] );
rescaler->SetInput(maskFilter->GetOutput());
rescaler->Update();
typename OutputImageType::Pointer rescaled = rescaler->GetOutput();
//MITK_WARN << "Intensities rescaled.";
//Write back the nans because they get lost during rescaling
int xx = inputImage->GetLargestPossibleRegion().GetSize()[0];
int yy = inputImage->GetLargestPossibleRegion().GetSize()[1];
int zz = inputImage->GetLargestPossibleRegion().GetSize()[2];
for (int x = 0; x < xx; x++)
{
for (int y = 0; y < yy; y++)
{
for (int z = 0; z < zz; z++)
{
FloatImageType::IndexType indexF;
indexF[0] = x;
indexF[1] = y;
indexF[2] = z;
OutputImageType::IndexType indexO;
indexO[0] = x;
indexO[1] = y;
indexO[2] = z;
//Is Pixel NaN?
if(floatImage->GetPixel(indexF) != floatImage->GetPixel(indexF))
{
rescaled->SetPixel(indexO,0);
}
}
}
}
//All nans are now 0, the valid values are within [1,numberOfBins]
/*
OutputImageType::IndexType indexO;
indexO[0] = 0;
indexO[1] = 2;
indexO[2] = 1;
MITK_WARN << "is 0: " << rescaled->GetPixel(indexO);
indexO[0] = 0;
indexO[1] = 0;
indexO[2] = 0;
MITK_WARN << "is 1: " << rescaled->GetPixel(indexO);
*/
PixelType distanceThreshold = 1 - mitk::eps;
//Calculate the connected components
typedef itk::ScalarConnectedComponentImageFilter <OutputImageType, OutputImageType, LabelImageType >
ConnectedComponentImageFilterType;
typename ConnectedComponentImageFilterType::Pointer connected = ConnectedComponentImageFilterType::New ();
connected->SetInput(rescaled);
connected->SetMaskImage(maskCaster->GetOutput());
connected->SetDistanceThreshold(distanceThreshold);
connected->Update();
/*
indexO[0] = 0;
indexO[1] = 2;
indexO[2] = 1;
MITK_WARN << "is 0: " << (connected->GetOutput())->GetPixel(indexO);
indexO[0] = 0;
indexO[1] = 0;
indexO[2] = 0;
MITK_WARN << "is 1: " << (connected->GetOutput())->GetPixel(indexO);
MITK_WARN << "Connected components calculated.";
*/
//Relabel the components
typedef itk::RelabelComponentImageFilter <OutputImageType, OutputImageType > RelabelFilterType;
typename RelabelFilterType::Pointer relabel = RelabelFilterType::New();
typename RelabelFilterType::ObjectSizeType minSize = 1;
relabel->SetInput(connected->GetOutput());
relabel->SetMinimumObjectSize(minSize);
relabel->Update();
//MITK_WARN << "Components relabeled.";
- //Get the stats of the componentes
+ //Get the stats of the components
typedef itk::LabelStatisticsImageFilter< FloatImageType, OutputImageType> LabelStatisticsImageFilterType;
typename LabelStatisticsImageFilterType::Pointer labelStatisticsImageFilter =
LabelStatisticsImageFilterType::New();
labelStatisticsImageFilter->SetLabelInput( relabel->GetOutput() );
labelStatisticsImageFilter->SetInput(floatImage);
labelStatisticsImageFilter->UseHistogramsOn(); // needed to compute median
labelStatisticsImageFilter->Update();
/*
std::cout << "Number of labels: "
<< labelStatisticsImageFilter->GetNumberOfLabels() << std::endl;
std::cout << std::endl;
*/
typedef typename LabelStatisticsImageFilterType::ValidLabelValuesContainerType ValidLabelValuesType;
for(typename ValidLabelValuesType::const_iterator vIt = labelStatisticsImageFilter->GetValidLabelValues().begin();
vIt != labelStatisticsImageFilter->GetValidLabelValues().end();
++vIt)
{
if ( labelStatisticsImageFilter->HasLabel(*vIt) )
{
LabelPixelType labelValue = *vIt;
run[0] = labelStatisticsImageFilter->GetMean( labelValue );
run[1] = labelStatisticsImageFilter->GetCount( labelValue );
//Check for NaN and inf
if(run[0] == run[0] && !std::isinf(std::abs(run[0])))
{
output->GetIndex( run, hIndex );
output->IncreaseFrequencyOfIndex( hIndex, 1 );
/*
MITK_INFO << "Adding a region:";
MITK_INFO << "\tmin: "
<< labelStatisticsImageFilter->GetMinimum( labelValue );
MITK_INFO << "\tmax: "
<< labelStatisticsImageFilter->GetMaximum( labelValue );
MITK_INFO << "\tmean: "
<< labelStatisticsImageFilter->GetMean( labelValue );
MITK_INFO << "\tcount: "
<< labelStatisticsImageFilter->GetCount( labelValue );
*/
}
}
}
}
template<typename TImageType, typename THistogramFrequencyContainer>
void
EnhancedScalarImageToSizeZoneMatrixFilter<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
EnhancedScalarImageToSizeZoneMatrixFilter<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
EnhancedScalarImageToSizeZoneMatrixFilter<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;
}
} // end of namespace Statistics
} // end of namespace itk
#endif
diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToTextureFeaturesFilter.h b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToTextureFeaturesFilter.h
index a3383d9723..1a45d8c9fb 100644
--- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToTextureFeaturesFilter.h
+++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToTextureFeaturesFilter.h
@@ -1,245 +1,245 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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
*
* https://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 __itkEnhancedScalarImageToTextureFeaturesFilter_h
#define __itkEnhancedScalarImageToTextureFeaturesFilter_h
#include "itkDataObjectDecorator.h"
#include "itkEnhancedHistogramToTextureFeaturesFilter.h"
#include "itkScalarImageToCooccurrenceMatrixFilter.h"
namespace itk
{
namespace Statistics
{
/** \class EnhancedScalarImageToTextureFeaturesFilter
* \brief This class computes texture descriptions from an image.
*
* This class computes features that summarize the texture of a given image.
* The texture features are computed a la Haralick, and have proven to be useful
* in image classification for biological and medical imaging.
* This class computes the texture features of an image (optionally in a
* masked region), averaged across several spatial directions so that they are
* invariant to rotation.
*
- * By default, texure features are computed for each spatial
+ * By default, texture features are computed for each spatial
* direction and then averaged afterward, so it is possible to access the standard
* deviations of the texture features. These values give a clue as to texture
* anisotropy. However, doing this is much more work, because it involved computing
* one GLCM for each offset given. To compute a single GLCM using the first offset ,
* call FastCalculationsOn(). If this is called, then the texture standard deviations
* will not be computed (and will be set to zero), but texture computation will
* be much faster.
*
* This class is templated over the input image type.
*
* Template Parameters:
* The image type, and the type of histogram frequency container. If you are using
* a large number of bins per axis, a sparse frequency container may be advisable.
* The default is to use a dense frequency container.
*
* Inputs and parameters:
* -# An image
* -# A mask defining the region over which texture features will be
* calculated. (Optional)
* -# The pixel value that defines the "inside" of the mask. (Optional, defaults
* to 1 if a mask is set.)
* -# The set of features to be calculated. These features are defined
* in the GreyLevelCooccurrenceMatrixTextureCoefficientsCalculator class. (Optional,
* defaults to {Energy, Entropy, InverseDifferenceMoment, Inertia, ClusterShade,
* ClusterProminence}, as in Conners, Trivedi and Harlow.)
* -# The number of intensity bins. (Optional, defaults to 256.)
* -# The set of directions (offsets) to average across. (Optional, defaults to
* {(-1, 0), (-1, -1), (0, -1), (1, -1)} for 2D images and scales analogously for ND
* images.)
* -# The pixel intensity range over which the features will be calculated.
* (Optional, defaults to the full dynamic range of the pixel type.)
*
* In general, the default parameter values should be sufficient.
*
* Outputs:
* (1) The average value of each feature.
* (2) The standard deviation in the values of each feature.
*
* Web reference:
* https://prism.ucalgary.ca/handle/1880/51900
*
* Print references:
* Haralick, R.M., K. Shanmugam and I. Dinstein. 1973. Textural Features for
* Image Classification. IEEE Transactions on Systems, Man and Cybernetics.
* SMC-3(6):610-620.
*
* Haralick, R.M. 1979. Statistical and Structural Approaches to Texture.
* Proceedings of the IEEE, 67:786-804.
*
- * R.W. Conners and C.A. Harlow. A Theoretical Comaprison of Texture Algorithms.
+ * R.W. Conners and C.A. Harlow. A Theoretical Comparison of Texture Algorithms.
* IEEE Transactions on Pattern Analysis and Machine Intelligence, 2:204-222, 1980.
*
* R.W. Conners, M.M. Trivedi, and C.A. Harlow. Segmentation of a High-Resolution
* Urban Scene using Texture Operators. Computer Vision, Graphics and Image
* Processing, 25:273-310, 1984.
*
* \sa ScalarImageToCooccurrenceMatrixFilter
* \sa HistogramToTextureFeaturesFilter
*
* Author: Zachary Pincus
* \ingroup ITKStatistics
*/
template< typename TImageType,
typename THistogramFrequencyContainer = DenseFrequencyContainer2 >
class EnhancedScalarImageToTextureFeaturesFilter:public ProcessObject
{
public:
/** Standard typedefs */
typedef EnhancedScalarImageToTextureFeaturesFilter Self;
typedef ProcessObject Superclass;
typedef SmartPointer< Self > Pointer;
typedef SmartPointer< const Self > ConstPointer;
/** Run-time type information (and related methods). */
itkTypeMacro(EnhancedScalarImageToTextureFeaturesFilter, ProcessObject);
/** standard New() method support */
itkNewMacro(Self);
typedef THistogramFrequencyContainer FrequencyContainerType;
typedef TImageType ImageType;
typedef typename ImageType::Pointer ImagePointer;
typedef typename ImageType::PixelType PixelType;
typedef typename ImageType::OffsetType OffsetType;
typedef VectorContainer< unsigned char, OffsetType > OffsetVector;
typedef typename OffsetVector::Pointer OffsetVectorPointer;
typedef typename OffsetVector::ConstPointer OffsetVectorConstPointer;
typedef ScalarImageToCooccurrenceMatrixFilter<
ImageType, FrequencyContainerType > CooccurrenceMatrixFilterType;
typedef typename CooccurrenceMatrixFilterType::HistogramType HistogramType;
typedef EnhancedHistogramToTextureFeaturesFilter< HistogramType > TextureFeaturesFilterType;
typedef short TextureFeatureName;
typedef VectorContainer< unsigned char, TextureFeatureName > FeatureNameVector;
typedef typename FeatureNameVector::Pointer FeatureNameVectorPointer;
typedef typename FeatureNameVector::ConstPointer FeatureNameVectorConstPointer;
typedef VectorContainer< unsigned char, double > FeatureValueVector;
typedef typename FeatureValueVector::Pointer FeatureValueVectorPointer;
/** Smart Pointer type to a DataObject. */
typedef DataObject::Pointer DataObjectPointer;
/** Type of DataObjects used for scalar outputs */
typedef DataObjectDecorator< FeatureValueVector >
FeatureValueVectorDataObjectType;
const FeatureValueVectorDataObjectType * GetFeatureMeansOutput() const;
const FeatureValueVectorDataObjectType * GetFeatureStandardDeviationsOutput() const;
/** Connects the input image for which the features are going to be computed
*/
using Superclass::SetInput;
void SetInput(const ImageType *);
const ImageType * GetInput() const;
/** Return the feature means and deviations. */
itkGetConstReferenceObjectMacro(FeatureMeans, FeatureValueVector);
itkGetConstReferenceObjectMacro(FeatureStandardDeviations, FeatureValueVector);
/** Set the desired feature set. Optional, for default value see above. */
itkSetConstObjectMacro(RequestedFeatures, FeatureNameVector);
itkGetConstObjectMacro(RequestedFeatures, FeatureNameVector);
/** Set the offsets over which the co-occurrence pairs will be computed.
Optional; for default value see above. */
itkSetConstObjectMacro(Offsets, OffsetVector);
itkGetConstObjectMacro(Offsets, OffsetVector);
/** Set number of histogram bins along each axis.
Optional; for default value see above. */
void SetNumberOfBinsPerAxis(unsigned int);
/** Set the min and max (inclusive) pixel value that will be used for
feature calculations. Optional; for default value see above. */
void SetPixelValueMinMax(PixelType min, PixelType max);
/** Connects the mask image for which the histogram is going to be computed.
Optional; for default value see above. */
void SetMaskImage(const ImageType *);
const ImageType * GetMaskImage() const;
/** Set the pixel value of the mask that should be considered "inside" the
object. Optional; for default value see above. */
void SetInsidePixelValue(PixelType InsidePixelValue);
itkGetConstMacro(FastCalculations, bool);
itkSetMacro(FastCalculations, bool);
itkBooleanMacro(FastCalculations);
protected:
EnhancedScalarImageToTextureFeaturesFilter();
~EnhancedScalarImageToTextureFeaturesFilter() override {}
void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE;
void FastCompute();
void FullCompute();
/** This method causes the filter to generate its output. */
void GenerateData() ITK_OVERRIDE;
/** Make a DataObject to be used for output output. */
typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType;
using Superclass::MakeOutput;
DataObjectPointer MakeOutput(DataObjectPointerArraySizeType) ITK_OVERRIDE;
private:
typename CooccurrenceMatrixFilterType::Pointer m_GLCMGenerator;
typename TextureFeaturesFilterType::Pointer m_GLCMCalculator;
FeatureValueVectorPointer m_FeatureMeans;
FeatureValueVectorPointer m_FeatureStandardDeviations;
FeatureNameVectorConstPointer m_RequestedFeatures;
OffsetVectorConstPointer m_Offsets;
bool m_FastCalculations;
};
} // end of namespace Statistics
} // end of namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkEnhancedScalarImageToTextureFeaturesFilter.hxx"
#endif
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkCLUtil.h b/Modules/Classification/CLUtilities/include/mitkCLUtil.h
index 7241b2d4af..1ab2c82b5a 100644
--- a/Modules/Classification/CLUtilities/include/mitkCLUtil.h
+++ b/Modules/Classification/CLUtilities/include/mitkCLUtil.h
@@ -1,581 +1,581 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkCLUtil_h
#define mitkCLUtil_h
-#include <Eigen/Dense>
+#include <itkeigen/Eigen/Dense>
#include <MitkCLUtilitiesExports.h>
#include <itkImageRegionIterator.h>
#include <mitkImage.h>
#include <mitkImageCast.h>
#include <mitkITKImageImport.h>
#include <itkConnectedComponentImageFilter.h>
namespace mitk
{
class MITKCLUTILITIES_EXPORT CLUtil
{
public:
///
/// \brief The MorphologicalDimensions enum
///
enum MorphologicalDimensions
{
Axial,Coronal,Sagittal,All
};
///
/// \brief CreateCheckerBoardPredictionMask
/// \param image
/// \param outimage
///
static void CreateCheckerboardMask(mitk::Image::Pointer image, mitk::Image::Pointer & outimage);
///
/// \brief InterpolateCreateCheckerboardPrediction
/// \param checkerboard_prediction
/// \param checkerboard_mask
/// \param outimage
///
static void InterpolateCheckerboardPrediction(mitk::Image::Pointer checkerboard_prediction, mitk::Image::Pointer & checkerboard_mask, mitk::Image::Pointer & outimage);
///
/// \brief CountVoxel
/// \param image
/// \param map
///
static void CountVoxel(mitk::Image::Pointer image, std::map<unsigned int, unsigned int> & map);
///
/// \brief CountVoxel
/// \param image
/// \param label
/// \param count
///
static void CountVoxel(mitk::Image::Pointer image, unsigned int label, unsigned int & count);
///
/// \brief CountVoxel
/// \param image
/// \param count
///
static void CountVoxel(mitk::Image::Pointer image, unsigned int & count);
///
/// \brief SumVoxelForLabel
/// \param image
/// \param source
/// \param label
/// \param val
///
static void SumVoxelForLabel(mitk::Image::Pointer image, const mitk::Image::Pointer & source , unsigned int label, double & val );
///
/// \brief SqSumVoxelForLabel
/// \param image
/// \param source
/// \param label
/// \param val
///
static void SqSumVoxelForLabel(mitk::Image::Pointer image, const mitk::Image::Pointer & source, unsigned int label, double & val );
///
/// \brief LogicalAndImages
/// \param image1
/// \param image2
/// \param outimage
///
static void LogicalAndImages(const Image::Pointer &image1, const Image::Pointer &image2, Image::Pointer &outimage);
///
/// \brief GaussianFilter
/// \param image
/// \param smoothed
/// \param sigma
///
static void GaussianFilter(mitk::Image::Pointer image, mitk::Image::Pointer & smoothed ,double sigma);
///
/// \brief SubtractGaussianFilter
/// \param image
/// \param smoothed (Result is sigma1-sigma2)
/// \param sigma1
/// \param sigma2
///
static void DifferenceOfGaussianFilter(mitk::Image::Pointer image, mitk::Image::Pointer & smoothed, double sigma1, double sigma2);
///
/// \brief Laplacian of Gaussian
/// \param image
/// \param smoothed (Result is sigma1-sigma2)
/// \param sigma1
///
static void LaplacianOfGaussianFilter(mitk::Image::Pointer image, mitk::Image::Pointer & smoothed, double sigma1);
///
/// \brief SubtractGaussianFilter
/// \param image
/// \param out
/// \param sigma
///
static void HessianOfGaussianFilter(mitk::Image::Pointer image, std::vector<mitk::Image::Pointer> &out, double sigma);
///
/// \brief Local Histogram
/// \param image
/// \param out
/// \param Bins
/// \param NeighbourhoodSize
///
static void LocalHistogram(mitk::Image::Pointer image, std::vector<mitk::Image::Pointer> &out, int Bins, int NeighbourhoodSize);
///
/// \brief transform
/// \param matrix
/// \param mask
///
template<typename TMatrixElementType>
static mitk::Image::Pointer Transform(const Eigen::Matrix<TMatrixElementType, Eigen::Dynamic, Eigen::Dynamic> & matrix, const mitk::Image::Pointer & mask)
{
itk::Image<unsigned int, 3>::Pointer itkMask;
mitk::CastToItkImage(mask,itkMask);
typename itk::Image<TMatrixElementType, 3>::Pointer itk_img = itk::Image<TMatrixElementType, 3>::New();
itk_img->SetRegions(itkMask->GetLargestPossibleRegion());
itk_img->SetOrigin(itkMask->GetOrigin());
itk_img->SetSpacing(itkMask->GetSpacing());
itk_img->SetDirection(itkMask->GetDirection());
itk_img->Allocate();
unsigned int n_numSamples = 0;
mitk::CLUtil::CountVoxel(mask,n_numSamples);
if(n_numSamples != matrix.rows())
MITK_ERROR << "Number of samples in matrix and number of points under the masks is not the same!";
auto mit = itk::ImageRegionConstIterator<itk::Image<unsigned int, 3> >(itkMask, itkMask->GetLargestPossibleRegion());
auto oit = itk::ImageRegionIterator<itk::Image<TMatrixElementType, 3> >(itk_img, itk_img->GetLargestPossibleRegion());
unsigned int current_row = 0;
while(!mit.IsAtEnd())
{
if(mit.Value() > 0)
oit.Set(matrix(current_row++,0));
else
oit.Set(0.0);
++mit;
++oit;
}
mitk::Image::Pointer out_img = mitk::Image::New();
mitk::GrabItkImageMemory(itk_img,out_img);
return out_img;
}
///
/// \brief TransformImageToMatrix
/// \param img
/// \param mask
///
template<typename TMatrixElementType>
static Eigen::Matrix<TMatrixElementType, Eigen::Dynamic, Eigen::Dynamic> Transform(const mitk::Image::Pointer & img, const mitk::Image::Pointer & mask)
{
itk::Image<unsigned int, 3>::Pointer current_mask;
mitk::CastToItkImage(mask,current_mask);
unsigned int n_numSamples = 0;
mitk::CLUtil::CountVoxel(mask,n_numSamples);
typename itk::Image<TMatrixElementType, 3>::Pointer current_img;
mitk::CastToItkImage(img,current_img);
Eigen::Matrix<TMatrixElementType, Eigen::Dynamic, Eigen::Dynamic> out_matrix(n_numSamples,1);
auto mit = itk::ImageRegionConstIterator<itk::Image<unsigned int, 3> >(current_mask, current_mask->GetLargestPossibleRegion());
auto iit = itk::ImageRegionConstIterator<itk::Image<TMatrixElementType, 3> >(current_img,current_img->GetLargestPossibleRegion());
unsigned int current_row = 0;
while (!mit.IsAtEnd()) {
if(mit.Value() > 0)
out_matrix(current_row++) = iit.Value();
++mit;
++iit;
}
return out_matrix;
}
///
/// \brief DilateBinary
/// \param sourceImage
/// \param resultImage
/// \param radius Size of the StructuringElement
/// \param d
///
static void DilateBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int radius , MorphologicalDimensions d);
///
/// \brief ErodeBinary
/// \param sourceImage
/// \param resultImage
/// \param radius Size of the StructuringElement
/// \param d
///
static void ErodeBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int radius, MorphologicalDimensions d);
///
/// \brief ClosingBinary
/// \param sourceImage
/// \param resultImage
/// \param radius Size of the StructuringElement
/// \param d
///
static void ClosingBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int radius, MorphologicalDimensions d);
///
/// \brief MergeLabels
/// \param img
/// \param map merge instruction where each map entry defines a mapping instruction. Key \c \<sourcelabel\> - Value \c \<targetlabel\>
///
static void MergeLabels(mitk::Image::Pointer & img, const std::map<unsigned int, unsigned int> & map);
///
/// \brief ConnectedComponentsImage
/// \param image
/// \param mask
/// \param outimage
/// \param num_components Number of components found in the image
///
static void ConnectedComponentsImage(mitk::Image::Pointer & image, mitk::Image::Pointer& mask, mitk::Image::Pointer &outimage, unsigned int& num_components);
///
/// \brief GrabLabel
/// \param image
/// \param outimage
/// \param label
///
static void GrabLabel(mitk::Image::Pointer & image, mitk::Image::Pointer & outimage, unsigned int label);
///
/// \brief itkInsertLabel
/// \param image
/// \param maskImage
/// \param label
///
static void InsertLabel(mitk::Image::Pointer & image, mitk::Image::Pointer & maskImage, unsigned int label);
///
/// \brief ErodeGrayscale
/// \param image
/// \param outimage
/// \param radius
/// \param d
///
static void ErodeGrayscale(mitk::Image::Pointer & image, unsigned int radius, mitk::CLUtil::MorphologicalDimensions d, mitk::Image::Pointer & outimage );
///
/// \brief DilateGrayscale
/// \param image
/// \param outimage
/// \param radius
/// \param d
///
static void DilateGrayscale(mitk::Image::Pointer & image, unsigned int radius, mitk::CLUtil::MorphologicalDimensions d, mitk::Image::Pointer & outimage );
///
/// \brief FillHoleGrayscale
/// \param image
/// \param outimage
///
static void FillHoleGrayscale(mitk::Image::Pointer & image, mitk::Image::Pointer & outimage);
///
/// \brief ProbabilityMap
/// \param sourceImage
/// \param mean
/// \param std_dev
/// \param resultImage
///
static void ProbabilityMap(const mitk::Image::Pointer& sourceImage, double mean, double std_dev, mitk::Image::Pointer& resultImage);
template<class TImageType>
static void itkCountVoxel( TImageType * image, std::map<unsigned int, unsigned int> & map)
{
auto it = itk::ImageRegionIterator< TImageType >(image,image->GetLargestPossibleRegion());
while(!it.IsAtEnd())
{
if(map.find(it.Value()) == map.end())
map[it.Value()] = 0;
map[it.Value()]++;
++it;
}
}
template <class TImageType>
static void itkCountVoxel(TImageType* image, typename TImageType::PixelType label, unsigned int & count )
{
itk::ImageRegionConstIterator<TImageType> inputIter(image, image->GetLargestPossibleRegion());
while(!inputIter.IsAtEnd())
{
if(inputIter.Value() == label) ++count;
++inputIter;
}
}
template<typename TImageType>
static inline void itkCountVoxel(TImageType * mask, unsigned int & n_numSamples)
{
auto mit = itk::ImageRegionConstIterator<TImageType>(mask, mask->GetLargestPossibleRegion());
while (!mit.IsAtEnd())
{
if(mit.Value() > 0)
n_numSamples++;
++mit;
}
}
template <class TImageType1, class TImageType2>
static void itkSampleLabel(TImageType1* image, TImageType2* output, double acceptrate, unsigned int label)
{
std::srand (time(nullptr));
itk::ImageRegionConstIterator< TImageType1 > inputIter(image, image->GetLargestPossibleRegion());
itk::ImageRegionIterator< TImageType2 > outputIter(output, output->GetLargestPossibleRegion());
while (!inputIter.IsAtEnd())
{
double r = (double)(rand()) / RAND_MAX;
if(inputIter.Get() == label && r < acceptrate)
outputIter.Set(label);
++inputIter;
++outputIter;
}
}
template <class TImageType>
static void itkSampleLabel(TImageType* image, mitk::Image::Pointer & output, unsigned int n_samples_drawn)
{
std::srand (time(nullptr));
typename TImageType::Pointer itk_out = TImageType::New();
itk_out->SetRegions(image->GetLargestPossibleRegion());
itk_out->SetDirection(image->GetDirection());
itk_out->SetOrigin(image->GetOrigin());
itk_out->SetSpacing(image->GetSpacing());
itk_out->Allocate();
itk_out->FillBuffer(0);
itk::ImageRegionConstIterator< TImageType > inputIter(image, image->GetLargestPossibleRegion());
itk::ImageRegionIterator< TImageType > outputIter(itk_out, itk_out->GetLargestPossibleRegion());
for(unsigned int i = 0 ; i < n_samples_drawn ;)
{
double r = (double)(rand()) / RAND_MAX;
if(inputIter.Value() != 0 && r < 0.01 && outputIter.Value() == 0)
{
outputIter.Set(inputIter.Value());
i++;
}
++inputIter;
++outputIter;
if(inputIter.IsAtEnd())
{
inputIter.GoToBegin();
outputIter.GoToBegin();
}
}
mitk::CastToMitkImage(itk_out, output);
}
private:
template<class TImageType>
static void itkErodeGrayscale(TImageType * image, mitk::Image::Pointer & outimage , unsigned int radius, mitk::CLUtil::MorphologicalDimensions d);
template<class TImageType>
static void itkDilateGrayscale(TImageType * image, mitk::Image::Pointer & outimage , unsigned int radius, mitk::CLUtil::MorphologicalDimensions d);
template<class TImageType>
static void itkFillHoleGrayscale(TImageType * image, mitk::Image::Pointer & outimage);
template< typename TImageType >
static void itkInsertLabel(TImageType * maskImage, mitk::Image::Pointer & outimage, unsigned int label)
{
typename TImageType::Pointer itk_out;
if(outimage.IsNull()) // create if necessary
{
MITK_INFO << "Initialize new image";
itk_out = TImageType::New();
itk_out->SetSpacing(maskImage->GetSpacing());
itk_out->SetDirection(maskImage->GetDirection());
itk_out->SetOrigin(maskImage->GetOrigin());
itk_out->SetRegions(maskImage->GetLargestPossibleRegion());
itk_out->Allocate();
itk_out->FillBuffer(0);
}else
{
mitk::CastToItkImage(outimage, itk_out);
}
itk::ImageRegionIterator<TImageType> oit(itk_out,itk_out->GetLargestPossibleRegion());
itk::ImageRegionConstIterator<TImageType> mit(maskImage,maskImage->GetLargestPossibleRegion());
while(!mit.IsAtEnd())
{
if(mit.Value() != 0)
{
oit.Set(label);
}
++oit;
++mit;
}
mitk::CastToMitkImage(itk_out,outimage);
}
template< typename TImageType >
static void itkGrabLabel(TImageType * image, mitk::Image::Pointer & outimage, unsigned int label)
{
typedef itk::Image<unsigned short, 3> TOutType;
TOutType::Pointer itk_out = TOutType::New();
itk_out->SetRegions(image->GetLargestPossibleRegion());
itk_out->SetDirection(image->GetDirection());
itk_out->SetOrigin(image->GetOrigin());
itk_out->SetSpacing(image->GetSpacing());
itk_out->Allocate();
itk::ImageRegionConstIterator<TImageType> iit(image, image->GetLargestPossibleRegion());
itk::ImageRegionIterator<TOutType> oit(itk_out,itk_out->GetLargestPossibleRegion());
while(!iit.IsAtEnd())
{
if(iit.Value() == static_cast<typename TImageType::PixelType>(label))
oit.Set(1);
else
oit.Set(0);
++iit;
++oit;
}
mitk::CastToMitkImage(itk_out, outimage);
}
template<class TImagetype>
static void itkMergeLabels(TImagetype * img, const std::map<unsigned int, unsigned int> & map)
{
auto it = itk::ImageRegionIterator<TImagetype>(img,img->GetLargestPossibleRegion());
while(!it.IsAtEnd())
{
if(map.find(it.Value())!=map.end())
it.Set( map.at(it.Value()) );
++it;
}
}
template<typename TImageType>
static void itkConnectedComponentsImage(TImageType * image, mitk::Image::Pointer& mask, mitk::Image::Pointer &outimage, unsigned int& num_components)
{
typedef itk::Image<unsigned short, 3> MaskImageType;
MaskImageType::Pointer itk_mask;
if(mask.IsNull())
{
itk_mask = MaskImageType::New();
itk_mask->SetRegions(image->GetLargestPossibleRegion());
itk_mask->SetDirection(image->GetDirection());
itk_mask->SetOrigin(image->GetOrigin());
itk_mask->SetSpacing(image->GetSpacing());
itk_mask->Allocate();
itk_mask->FillBuffer(1);
}else{
mitk::CastToItkImage(mask,itk_mask);
}
typedef itk::ConnectedComponentImageFilter<TImageType, MaskImageType, MaskImageType > FilterType;
typename FilterType::Pointer cc_filter = FilterType::New();
cc_filter->SetMaskImage(itk_mask.GetPointer());
cc_filter->SetInput(image);
cc_filter->SetBackgroundValue(0);
cc_filter->Update();
num_components = cc_filter->GetObjectCount();
mitk::CastToMitkImage(cc_filter->GetOutput(), outimage);
}
template< typename TImageType >
static void itkCreateCheckerboardMask(TImageType * image, mitk::Image::Pointer & outimage);
template< typename TImageType >
static void itkInterpolateCheckerboardPrediction(TImageType * checkerboard_prediction, mitk::Image::Pointer & checkerboard_mask, mitk::Image::Pointer & outimage);
template <class TImageType>
static void itkSumVoxelForLabel(TImageType* image, const mitk::Image::Pointer & source , typename TImageType::PixelType label, double & val );
template <class TImageType>
static void itkSqSumVoxelForLabel(TImageType* image, const mitk::Image::Pointer & source, typename TImageType::PixelType label, double & val );
template<typename TStructuringElement>
static void itkFitStructuringElement(TStructuringElement & se, MorphologicalDimensions d, int radius);
template<typename TImageType>
static void itkDilateBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int radius , MorphologicalDimensions d);
template<typename TImageType>
static void itkErodeBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int radius, MorphologicalDimensions d);
template<typename TImageType>
static void itkClosingBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int radius, MorphologicalDimensions d);
template<typename TPixel, unsigned int VDimension>
static void itkFillHolesBinary(itk::Image<TPixel, VDimension>* sourceImage, mitk::Image::Pointer& resultImage);
template<typename TImageType>
static void itkLogicalAndImages(const TImageType * image1, const mitk::Image::Pointer & image2, mitk::Image::Pointer & outimage);
template<class TImageType>
static void itkGaussianFilter(TImageType * image, mitk::Image::Pointer & smoothed ,double sigma);
template<class TImageType>
static void itkDifferenceOfGaussianFilter(TImageType * image, mitk::Image::Pointer & smoothed, double sigma1, double sigma2);
template<typename TImageType>
static void itkProbabilityMap(const TImageType * sourceImage, double mean, double std_dev, mitk::Image::Pointer& resultImage);
template<typename TPixel, unsigned int VImageDimension>
static void itkHessianOfGaussianFilter(itk::Image<TPixel, VImageDimension>* itkImage, double variance, std::vector<mitk::Image::Pointer> &out);
template<typename TPixel, unsigned int VImageDimension>
static void itkLaplacianOfGaussianFilter(itk::Image<TPixel, VImageDimension>* itkImage, double variance, mitk::Image::Pointer &output);
template<typename TPixel, unsigned int VImageDimension>
static void itkLocalHistograms(itk::Image<TPixel, VImageDimension>* itkImage, std::vector<mitk::Image::Pointer> &out, int size, int bins);
};
} //namespace MITK
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix.h b/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix.h
index d10e127e0d..8340cc5d80 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix.h
@@ -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.
============================================================================*/
#ifndef mitkGIFCooccurenceMatrix_h
#define mitkGIFCooccurenceMatrix_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
class MITKCLUTILITIES_EXPORT GIFCooccurenceMatrix : public AbstractGlobalImageFeature
{
/**
- * \brief Calculates features based on the co-occurence matrix.
+ * \brief Calculates features based on the co-occurrence matrix.
*
- * This filter calculates features based on the Co-Occurence Matrix.
+ * This filter calculates features based on the Co-Occurrence Matrix.
*
* \warning{ This is a legacy class only. If possible, avoid to use it. Use
* GIFCooccurenceMatrix2 instead.}
*/
public:
mitkClassMacro(GIFCooccurenceMatrix,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFCooccurenceMatrix();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
itkGetConstMacro(Ranges, std::vector<double>);
void SetRanges(std::vector<double> ranges);
void SetRange(double range);
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
std::string GenerateLegacyFeatureNamePart(const FeatureID& id) const override;
std::string GenerateLegacyFeatureEncoding(const FeatureID& id) const override;
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
void ConfigureSettingsByParameters(const ParametersType& parameters) override;
private:
std::vector<double> m_Ranges;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix2.h b/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix2.h
index f3190da6f4..907fcb2929 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix2.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFCooccurenceMatrix2.h
@@ -1,162 +1,162 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkGIFCooccurenceMatrix2_h
#define mitkGIFCooccurenceMatrix2_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
-#include <Eigen/src/Core/Array.h>
+#include <itkeigen/Eigen/src/Core/Array.h>
namespace mitk
{
/**
- * \brief Calculates features based on the co-occurence matrix.
+ * \brief Calculates features based on the co-occurrence matrix.
*
- * The co-occurence matrix describes the relations between voxels in a specific direction. The elements \f$m_{i,k} \f$ of the
+ * The co-occurrence matrix describes the relations between voxels in a specific direction. The elements \f$m_{i,k} \f$ of the
* matrix count how often a voxel with the intensity \f$i \f$ has a neighbour in a certain direction with the intensity \f$ k \f$.
* The direction for each matrix is given by a directed vector \f$ \overrightarrow{d} \f$.
*
* It is important to calculate the matrices for all possible directions in order to obtain a rotation invariant feature.
- * For the 3D case, this means that there are 26 possible directions. Using the symmetrical properties of the co-occurence
+ * For the 3D case, this means that there are 26 possible directions. Using the symmetrical properties of the co-occurrence
* matrix, it is then possible to calculate the features in all directions looking at 13 different directions.
*
* The standard length of the vector is 1, e.g. looking at direct neighbours. It is possible to look at more
* distance neighbours. This is achieved using the parameter <b>range</b> which defines the distance between
* two neighbouring voxels in number of voxels. The default value for this is 1. It can be changes using the Method
* SetRange() or by passing the option <b>cooc2::range</b>.
*
* There are two possible ways of combining the information obtained from the multiple directions. The first option
* is to calculate a common matrix for all directions and then use this matrix to calculate the describing features.
* The second method is to calculate a matrix for each direction, obtain the features and then report the mean and
- * standard value of these features. Both mehtods are calcuated by this filters and reported, distinguisehd by either
+ * standard value of these features. Both methods are calculated by this filters and reported, distinguisehd by either
* an "Overall" if a single matrix is used, a "Mean" for the mean Value, or an "Std.Dev." for the standard deviation.
*
* The connected areas are based on the binned image, the binning parameters can be set via the default
* parameters as described in AbstractGlobalImageFeature. The intensity used for the calculation is
* always equal to the bin number. It is also possible to determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
* No other options are possible beside these two options.
*
* This feature calculator is activated by the option <b>-cooccurence2</b> or <b>-cooc2</b>.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image. All voxels with the value 1 are treated as masked.
*
* The following features are defined. We always give the notation for the overall matrix feature
* although those for the mean and std.dev. are basically equal. In the name, \<Range\> is replace
* by the distance of the neighbours. For the definitions of the feature, the probability of each
* intensity pair (i,k) \f$ p_{i,k} = \frac{m_{i,k}}{\sum_i \sum_k m_{i,k}} \f$.
*
* In addition, the marginal sum \f$ p_{i,\cdot} = p_{\cdot,k=i} = \sum_k p_{i,k} \f$, which is
- * identical for both axis due to the symetrical nature of the matrix. Furthermore, the diagonal and
- * cross diagnoal features are used:
+ * identical for both axis due to the symmetrical nature of the matrix. Furthermore, the diagonal and
+ * cross diagonal features are used:
* \f[ p_{i-k}(l) = \sum_i \sum_k p_{i,k} \delta(l - \| i -k \| ) \enspace \enspace l = 0, \dots, N_g -1 \f]
* \f[ p_{i+k}(l) = \sum_i \sum_k p_{i,k} \delta(l - ( i + k ) ) \enspace \enspace l = 2, \dots, 2 N_g \f]
* Here, \f$ \delta(x) \f$ is the dirac function, which is one for \f$x=0 \f$ and zero otherwise.
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Joint Maximum</b>:
* \f[ \textup{Joint Maximum}= \textup{max}(p_{i,k}) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Joint Average</b>:
* \f[ \textup{Joint Average} = \mu_{ja} = \sum_i \sum_k i p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Joint Variance</b>:
* \f[ \textup{Joint Variance} = \sum_i \sum_k (i - \mu_{ja})^2 p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Joint Entropy</b>:
* \f[ \textup{Joint Entropy} = e_j = - \sum_i \sum_k p_{i,k} \textup{log}_2 p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Row Maximum</b>:
* \f[ \textup{Row Maximum}= \textup{max}(p_{i,\cdot}) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Row Average</b>:
* \f[ \textup{Row Average} = \mu_{ra} = \sum_i i p_{i,\cdot} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Row Variance</b>:
* \f[ \textup{Row Variance} = \sigma^2_{i, \cdot} = \sum_i (i - \mu_{ra})^2 p_{i,\cdot} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Row Entropy</b>:
* \f[ \textup{Row Entropy} = e_r = - \sum_i p_{i,\cdot} \textup{log}_2 p_{i,\cdot} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall First Row-Column Entropy</b>:
* \f[ \textup{First Row-Column Entropy} = e_1 = - \sum_i \sum_k p_{i,k} \textup{log}_2 ( p_{i,\cdot} p_{\cdot,k}) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Second Row-Column Entropy</b>:
* \f[ \textup{Second Row-Column Entropy} = e_2 = - \sum_i \sum_k p_{i,\cdot} p_{\cdot,k} \textup{log}_2 ( p_{i,\cdot} p_{\cdot,k}) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Difference Average</b>:
* \f[ \textup{Difference Average} = \mu_{da} = \sum_l l p_{i-k}(l) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Difference Variance</b>:
* \f[ \textup{Difference Variance} = \sum_l (i - \mu_{da})^2 p_{i-k}(l) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Difference Entropy</b>:
* \f[ \textup{Difference Entropy} = - \sum_l p_{i-k}(l) \textup{log}_2 p_{i-k}(l) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Sum Average</b>:
* \f[ \textup{Sum Average} = \mu_{sa} = \sum_l l p_{i+k}(l) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Sum Variance</b>:
* \f[ \textup{Sum Variance} = \sum_l (i - \mu_{sa})^2 p_{i+k}(l) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Sum Entropy</b>:
* \f[ \textup{Sum Entropy} = - \sum_l p_{i+k}(l) \textup{log}_2 p_{i+k}(l) \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Angular Second Moment</b>:
* \f[ \textup{Angular Second Moment} = \sum_i \sum_k p^2_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Contrast</b>:
* \f[ \textup{Contrast} = \sum_i \sum_k (i-k)^2 p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Dissimilarity</b>:
* \f[ \textup{Dissimilarity} = \sum_i \sum_k \| i-k\| p^2_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Inverse Difference</b>:
* \f[ \textup{Inverse Difference} = \sum_i \sum_k \frac{p_{i,k}}{1+\| i-k\|} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Inverse Difference Normalized</b>:
* \f[ \textup{Inverse Difference Normalized} = \sum_i \sum_k \frac{p_{i,k}}{1+\frac{\| i-k\|}{N_g}} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Inverse Difference Moment</b>:
* \f[ \textup{Inverse Difference Moment} = \sum_i \sum_k \frac{p_{i,k}}{1+ ( i-k )^2} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Inverse Difference Moment Normalized</b>:
* \f[ \textup{Inverse Difference Moment Normalized} = \sum_i \sum_k \frac{p_{i,k}}{1+\frac{( i-k ) ^2}{N_g}} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Inverse Variance</b>:
* \f[ \textup{Inverse Difference Moment Normalized} = \sum_i \sum_k \frac{p_{i,k}}{(i-k)^2} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Correlation</b>:
* \f[ \textup{Correlation} = \frac{1}{\sigma^2_{i,\cdot}} \sum_i \sum_k (i - \mu_{ra})(k - \mu_{ra}) p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Autocorrelation</b>:
* \f[ \textup{Autocorrelation} = \sum_i \sum_k i k p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Cluster Tendency</b>:
* \f[ \textup{Cluster Tendency} = \sum_i \sum_k (i + k - 2\mu_{ra})^2 p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Cluster Shade</b>:
* \f[ \textup{Cluster Shade} = \sum_i \sum_k (i + k - 2\mu_{ra})^3 p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Cluster Prominence</b>:
* \f[ \textup{Cluster Prominence} = \sum_i \sum_k (i + k - 2\mu_{ra})^4 p_{i,k} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall First Measure of Information Correlation</b>:
* \f[ \textup{First Measure of Information Correlation} = \frac{ e_j- e_1}{e_r} \f]
* - <b>Co-occurenced Based Features (\<Range\>)::%Overall Second Measure of Information Correlation</b>:
* \f[ \textup{Second Measure of Information Correlation} = \sqrt{1- \exp(-2 (e_2 - e_j)} \f]
*/
class MITKCLUTILITIES_EXPORT GIFCooccurenceMatrix2 : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFCooccurenceMatrix2, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFCooccurenceMatrix2();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
itkGetConstMacro(Ranges, std::vector<double>);
void SetRanges(std::vector<double> ranges);
void SetRange(double range);
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
std::string GenerateLegacyFeatureEncoding(const FeatureID& id) const override;
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
void ConfigureSettingsByParameters(const ParametersType& parameters) override;
private:
std::vector<double> m_Ranges;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFCurvatureStatistic.h b/Modules/Classification/CLUtilities/include/mitkGIFCurvatureStatistic.h
index 601d9a1d45..cc87ba77d3 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFCurvatureStatistic.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFCurvatureStatistic.h
@@ -1,98 +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 mitkGIFCurvatureStatistic_h
#define mitkGIFCurvatureStatistic_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
- * \brief Calculates features based on the co-occurence matrix.
+ * \brief Calculates features based on the co-occurrence matrix.
*
* The Curvature is a measure for the bending of a surface and is therefore a measure for the description of the
* surface of an segmentation.
*
* THe curvature is calculated for each point of the surface of the given object and then a combined measure is
* produced. It measures the divergence of the orientation of an curve from the
* tangent of the curve. There are multiple ways to calculate the Curvature:
*
* <b> Gaussian Curvature</b>: The discrete gaussian curvature (K) is computed as \f$K(\textup{Corner Point v}) = 2 * \pi - \sum_{\textup{Neighoubring Voxel Surfaces f of v}} (\textup{Angle}_f \textup{at} v) \f$.
* <b> Mean Curvature</b>:The mean curvature (H) is computed as \f$H(\textup{Corner Point v}) = \textup{average over edges e neighbouring v of H(e)} \f$.
* with \f$H(edge e) = length(e)*dihedral_angle(e)\f$
* <b>Maximum</b> (\f$k_max\f$) and <b>Minimum</b> (\f$k_min\f$) Principal Curvatures
* \f$k_max = H + sqrt(H^2 - K)\f$
* \f$k_min = H - sqrt(H^2 - K)\f$
* Excepting spherical and planar surfaces which have equal principal curvatures,
* the curvature at a point on a surface varies with the direction one "sets off"
* from the point. For all directions, the curvature will pass through two extrema:
* a minimum (\f$k_min\f$) and a maximum (\f$k_max\f$) which occur at mutually orthogonal
* directions to each other.
*
* This method does not take any parameters.
*
* This feature calculator is activated by the option <b>-curvature</b> or <b>-cur</b>.
*
* The features are calculated based on a mask, which is converted into a mesh.
*
* The following features are defined. All features are calculated for all four possible
* curvation calculation methods (Gaussian, Mean, Minimum, Maximum). The principal way
* of calculating these features is the same, the used curvation is indicated by \<name\> in the
* feature name:
*
* - <b>Curvature Feature::Minimum \<name\> Curvature</b>:
* The minimum curvature for the whole given mask
* - <b>Curvature Feature::Maximum \<name\> Curvature</b>:
* The maximum curvature for the whole given mask
* - <b>Curvature Feature::Mean \<name\> Curvature</b>:
* The mean curvature for the whole given mask
* - <b>Curvature Feature::Standard Deviation \<name\> Curvature</b>:
* The standard deviation curvature for the whole given mask
* - <b>Curvature Feature::Skewness \<name\> Curvature</b>:
* The skewness curvature for the whole given mask
* - <b>Curvature Feature::Mean Positive \<name\> Curvature</b>:
* The mean curvature of all positive curvatures from the whole given mask
* - <b>Curvature Feature::Standard Deviation Positive \<name\> Curvature</b>:
* The Standard Deviation curvature of all positive curvatures from the whole given mask
* - <b>Curvature Feature::Skewness Positive \<name\> Curvature</b>:
* The Skewness curvature of all positive curvatures from the whole given mask
* - <b>Curvature Feature::Mean Negative \<name\> Curvature</b>:
* The mean curvature of all Negative curvatures from the whole given mask
* - <b>Curvature Feature::Standard Deviation Negative \<name\> Curvature</b>:
* The Standard Deviation curvature of all Negative curvatures from the whole given mask
* - <b>Curvature Feature::Skewness Negative \<name\> Curvature</b>:
* The Skewness curvature of all Negative curvatures from the whole given mask
*/
class MITKCLUTILITIES_EXPORT GIFCurvatureStatistic : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFCurvatureStatistic,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFCurvatureStatistic();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser &parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderHistogramStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderHistogramStatistics.h
index 61d9682dfe..c86a2b53bc 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderHistogramStatistics.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderHistogramStatistics.h
@@ -1,109 +1,109 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkGIFFirstOrderHistogramStatistics_h
#define mitkGIFFirstOrderHistogramStatistics_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
- * \brief Calulates first order features based on a histogram.
+ * \brief Calculates first order features based on a histogram.
*
* This class can be used to calculate first order features based on a histogram.
* For each feature, two variations are given, once the value of the feature that is
* obtained if the mean intensity of the histogram bins is used and the
* histogram bin that corresponds to the feature value. See AbstractGlobalImageFeature for more
* information on the histogram initialization. The histogram gives a probability \f$p_i\f$ for the
* intensity \f$x_i\f$ that is linked to the bin \f$i\f$. The histogram bins start at index 1.
*
* This feature calculator is activated by the option "<b>-first-order-histogram</b>" or "<b>-foh</b>".
* Beside the options for the histogram definitions, which are given in the description of AbstractGlobalImageFeature , no
* additional parameters are available.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image and all voxels with an value of 1
* are treated as masked.
*
* The resulting features are:
- * - <b>First Order Histogram::Mean Value</b>: The mean intensity of all voxels, calulated by \f$ \mu_x = \sum p_i x_i\f$.
+ * - <b>First Order Histogram::Mean Value</b>: The mean intensity of all voxels, calculated by \f$ \mu_x = \sum p_i x_i\f$.
* - <b>First Order Histogram::Variance Value</b> The variance intensity is calculated as : \f$ \sigma^2 = \sum p_i (x_i - \mu_x)^2\f$.
* - <b>First Order Histogram::Skewness Value</b>: \f[ skewness = \frac{\sum p_i (x_i - \mu_x)^3}{\sigma^3} \f]
* - <b>First Order Histogram::Excess Kurtosis Value</b>: \f[ skewness = \frac{\sum p_i (x_i - \mu_x)^4}{\sigma^4} - 3 \f]
* - <b>First Order Histogram::Median Value</b>: The median intensity value based on the histogram values.
* - <b>First Order Histogram::Minimum Value</b>: The minimum observed intensity value.
* - <b>First Order Histogram::Percentile 10 Value</b>: The intensity that is equal or greater than 10% of all observed intensities.
* - <b>First Order Histogram::Percentile 90 Value</b>: The intensity that is equal or greater than 90% of all observed intensities.
* - <b>First Order Histogram::Maximum Value</b>: The maximum observerd intensity value.
* - <b>First Order Histogram::Mode Value</b>: The most common intensity value, i.e. the value of the bin with the highest probability.
* - <b>First Order Histogram::Interquantile Range Value</b>: The intensity difference between Percentile 75% (\f$ P75\f$) and Percentile 25% (\f$ P25\f$).
* - <b>First Order Histogram::Range Value</b>: The difference between the observed maximum and minimum intensity.
* - <b>First Order Histogram::Mean Absolute Deviation Value</b>: \f[ \textup{mean absolute deviation} = \sum p_i \left \| (x_i - \mu_x) \right \| \f]
* - <b>First Order Histogram::Robust Mean Value</b>: The mean of all intensities between the 10% and 90% quantile.
* - <b>First Order Histogram::Robust Mean Absolute Deviation Value</b>: The Mean absolute deviation for all values between the 10% and 90% quantile. It is based on the robust mean value.
* - <b>First Order Histogram::Median Absolute Deviation Value</b>: \f[ \textup{mean absolute deviation} = \sum p_i \left \| (x_i - \textup{median}) \right \| \f]
* - <b>First Order Histogram::Coefficient of Variation Value</b>: \f[ \frac{\sigma_x}{\mu_x} \f]
* - <b>First Order Histogram::Quantile coefficient of Dispersion Value</b>: \f[ \textup{Quantile coefficient of Dispersion} = \frac{P75 - P25}{P75 + P25} \f]
* - <b>First Order Histogram::Entropy Value</b>: The entropy is only based on histogram bins with a probability greater than 0.0000001: \f[ \textup{entropy} = - \sum p_i \textup{log}_2 p_i \f]
* - <b>First Order Histogram::Uniformity Value</b>: \f$ \sum p_i^2 \f$
- * - <b>First Order Histogram::Mean Index</b>: The mean index of all voxels, calulated by \f$ \mu_i = \sum p_i i\f$.
+ * - <b>First Order Histogram::Mean Index</b>: The mean index of all voxels, calculated by \f$ \mu_i = \sum p_i i\f$.
* - <b>First Order Histogram::Variance Index</b>: The variance index is calculated as : \f$ \sigma_i^2 = \sum p_i (i - \mu_i)^2\f$.
* - <b>First Order Histogram::Skewness Index</b>: \f[ skewness = \frac{\sum p_i (i - \mu_i)^3}{\sigma_i^3} \f]
* - <b>First Order Histogram::Excess Kurtosis Index</b>: \f[ skewness = \frac{\sum p_i (i - \mu_i)^4}{\sigma_i^4} - 3 \f]
* - <b>First Order Histogram::Median Index</b>: The median index value based on the histogram values.
* - <b>First Order Histogram::Minimum Index</b>: The index of the minimum observed intensity value.
* - <b>First Order Histogram::Percentile 10 Index</b>: The index oft the intensity that is equal or greater than 10% of all observed intensities.
* - <b>First Order Histogram::Percentile 90 Index</b>: The index of the intensity that is equal or greater than 90% of all observed intensities.
* - <b>First Order Histogram::Maximum Index</b>: The index of the maximum observerd intensity value.
* - <b>First Order Histogram::Mode Index</b>: The index of the most common intensity value, i.e. the index of the bin with the highest probability.
* - <b>First Order Histogram::Interquantile Range Index</b>: The index difference between Percentile 75% (\f$ P75\f$) and Percentile 25% (\f$ P25\f$).
* - <b>First Order Histogram::Range Index</b>: The index difference between the index of the observed maximum and minimum intensity.
* - <b>First Order Histogram::Mean Absolute Deviation Index</b>: \f[ \textup{mean absolute deviation} = \sum p_i \left \| (i - \mu_i) \right \| \f]
* - <b>First Order Histogram::Robust Mean Absolute Deviation Index</b>: The Mean absolute deviation for all values between the 10% and 90% quantile. It is based on the robust mean value.
* - <b>First Order Histogram::Median Absolute Deviation Index</b>: \f[ \textup{mean absolute deviation} = \sum p_i \left \| (i - \textup{median}) \right \| \f]
* - <b>First Order Histogram::Coefficient of Variation Index</b>: \f[ \frac{\sigma_i}{\mu_i} \f]
* - <b>First Order Histogram::Quantile coefficient of Dispersion Index</b>: \f[ \textup{Quantile coefficient of Dispersion} = \frac{P75 - P25}{P75 + P25} \f]
* - <b>First Order Histogram::Entropy Index</b>: The entropy is only based on histogram bins with a probability greater than 0.0000001: \f$ \textup{entropy} = - \sum p_i \textup{log}_2 p_i \f$. Note that this is the same as the entropy value.
* - <b>First Order Histogram::Uniformity Index</b>: \f$ \sum p_i^2 \f$. Note that this is the same as the uniformity value.
- * - <b>First Order Histogram::Maximum Gradient</b>: The maximum difference between the probability of three neighbouring bins. For bins at the edge of the histogram, only two bins are used for the calulation.
+ * - <b>First Order Histogram::Maximum Gradient</b>: The maximum difference between the probability of three neighbouring bins. For bins at the edge of the histogram, only two bins are used for the calculation.
* - <b>First Order Histogram::Maximum Gradient Index</b>: The index of the bin that belongs to the maximum gradient.
- * - <b>First Order Histogram::Minimum Gradient</b>: The minimum difference between the probability of three neighbouring bins. For bins at the edge of the histogram, only two bins are used for the calulation.
+ * - <b>First Order Histogram::Minimum Gradient</b>: The minimum difference between the probability of three neighbouring bins. For bins at the edge of the histogram, only two bins are used for the calculation.
* - <b>First Order Histogram::Minimum Gradient Index</b>:The index of the bin that belongs to the minimum gradient.
* - <b>First Order Histogram::Robust Mean Index</b>: The mean index of all intensities between the 10% and 90% quantile.
* - <b>First Order Histogram::Number of Bins</b>: The number of bins in the histogram. This is rather for control, as this parameter is likely to be determined by the configuration rather than the image.
* - <b>First Order Histogram::Bin Size</b>: The binsize of the bins from the histogram. This is rather for control, as this parameter is likely to be determined by the configuration rather than the image.
*/
class MITKCLUTILITIES_EXPORT GIFFirstOrderHistogramStatistics : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFFirstOrderHistogramStatistics,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFFirstOrderHistogramStatistics();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderNumericStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderNumericStatistics.h
index 87f2660c49..7e79804a95 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderNumericStatistics.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderNumericStatistics.h
@@ -1,146 +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.
============================================================================*/
#ifndef mitkGIFFirstOrderNumericStatistics_h
#define mitkGIFFirstOrderNumericStatistics_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
class MITKCLUTILITIES_EXPORT GIFFirstOrderNumericStatistics : public AbstractGlobalImageFeature
{
public:
/**
* \brief Calculates first order statistics of the given image.
*
* The first order statistics for the intensity distribution within a given Region of Interest (ROI)
- * is caluclated. The ROI is defined using a mask.
+ * is calculated. The ROI is defined using a mask.
*
* The features are calculated on a quantified image. If the bin-size is too big, the obtained values
- * can be errornous and missleading. It is therefore important to use enough bins. The binned approach is
+ * can be erroneous and misleading. It is therefore important to use enough bins. The binned approach is
* used in order to avoid floating-point related errors.
*
* This feature calculator is activated by the option <b>-first-order</b> or <b>-fo</b>.
*
* The connected areas are based on the binned image, the binning parameters can be set via the default
* parameters as described in AbstractGlobalImageFeature. It is also possible to determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
* No other options are possible beside these two options.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image. All voxels with the value 1 are treated as masked.
*
* The following features are then defined using the (binned) voxel intensity \f$ x_i \f$ of each voxel, the probability
* an intensity \f$ p_x \f$, and the overall number of voxels within the mask \f$ N_v \f$:
* - <b>First Order::Mean</b>: The mean intensity within the ROI
* \f[ \textup{Mean}= \mu = \frac{1}{N_v} \sum x_i \f]
* - <b>First Order::Unbiased Variance</b>: An unbiased estimation of the variance:
* \f[ \textup{Unbiased Variance} = \frac{1}{N_v - 1} \sum \left( x_i - \mu \right)^2 \f]
* - <b>First Order::Biased Variance</b>: An biased estimation of the variance. If not specified otherwise, this is
* used as the variance:
* \f[ \textup{Biased Variance} = \sigma^2 = \frac{1}{N_v} \sum \left( x_i - \mu \right)^2 \f]
* - <b>First Order::Unbiased Standard Deviation</b>: Estimation of diversity within the intensity values
* \f[ \textup{Unbiased Standard Deviation} = \sqrt{\frac{1}{N_v-1} \sum \left( x_i - \mu \right)^2} \f]
* - <b>First Order::Biased Standard Deviation</b>: Estimation of diversity within the intensity values
* \f[ \textup{Biased Standard Deviation} = \sigma = \sqrt{\frac{1}{N_v} \sum \left( x_i - \mu \right)^2} \f]
* - <b>First Order::Skewness</b>:
* \f[ \textup{Skewness} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^3}{\sigma^3} \f]
* - <b>First Order::Kurtosis</b>: The kurtosis is a measurement of the peakness of the given
- * distirbution:
+ * distribution:
* \f[ \textup{Kurtosis} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^4}{\sigma^4} \f]
* - <b>First Order::Excess Kurtosis</b>: The kurtosis is a measurement of the peakness of the given
- * distirbution. The excess kurtosis is similar to the kurtosis, but is corrected by a fisher correction,
+ * distribution. The excess kurtosis is similar to the kurtosis, but is corrected by a fisher correction,
* ensuring that a gaussian distribution has an excess kurtosis of 0.
* \f[ \textup{Excess Kurtosis} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^4}{\sigma^4} - 3 \f]
* - <b>First Order::Median</b>: The median is defined as the median of the all intensities in the ROI.
* - <b>First Order::Minimum</b>: The minimum is defined as the minimum of the all intensities in the ROI.
* - <b>First Order::05th Percentile</b>: \f$ P_{5\%} \f$ The 5% percentile. 5% of all voxel do have this or a lower intensity.
* - <b>First Order::10th Percentile</b>: \f$ P_{10\%} \f$ The 10% percentile. 10% of all voxel do have this or a lower intensity.
* - <b>First Order::15th Percentile</b>: \f$ P_{15\%} \f$ The 15% percentile. 15% of all voxel do have this or a lower intensity.
* - <b>First Order::20th Percentile</b>: \f$ P_{20\%} \f$ The 20% percentile. 20% of all voxel do have this or a lower intensity.
* - <b>First Order::25th Percentile</b>: \f$ P_{25\%} \f$ The 25% percentile. 25% of all voxel do have this or a lower intensity.
* - <b>First Order::30th Percentile</b>: \f$ P_{30\%} \f$ The 30% percentile. 30% of all voxel do have this or a lower intensity.
* - <b>First Order::35th Percentile</b>: \f$ P_{35\%} \f$ The 35% percentile. 35% of all voxel do have this or a lower intensity.
* - <b>First Order::40th Percentile</b>: \f$ P_{40\%} \f$ The 40% percentile. 40% of all voxel do have this or a lower intensity.
* - <b>First Order::45th Percentile</b>: \f$ P_{45\%} \f$ The 45% percentile. 45% of all voxel do have this or a lower intensity.
* - <b>First Order::50th Percentile</b>: \f$ P_{50\%} \f$ The 50% percentile. 50% of all voxel do have this or a lower intensity.
* - <b>First Order::55th Percentile</b>: \f$ P_{55\%} \f$ The 55% percentile. 55% of all voxel do have this or a lower intensity.
* - <b>First Order::60th Percentile</b>: \f$ P_{60\%} \f$ The 60% percentile. 60% of all voxel do have this or a lower intensity.
* - <b>First Order::65th Percentile</b>: \f$ P_{65\%} \f$ The 65% percentile. 65% of all voxel do have this or a lower intensity.
* - <b>First Order::70th Percentile</b>: \f$ P_{70\%} \f$ The 70% percentile. 70% of all voxel do have this or a lower intensity.
* - <b>First Order::75th Percentile</b>: \f$ P_{75\%} \f$ The 75% percentile. 75% of all voxel do have this or a lower intensity.
* - <b>First Order::80th Percentile</b>: \f$ P_{80\%} \f$ The 80% percentile. 80% of all voxel do have this or a lower intensity.
* - <b>First Order::85th Percentile</b>: \f$ P_{85\%} \f$ The 85% percentile. 85% of all voxel do have this or a lower intensity.
* - <b>First Order::90th Percentile</b>: \f$ P_{90\%} \f$ The 90% percentile. 90% of all voxel do have this or a lower intensity.
* - <b>First Order::95th Percentile</b>: \f$ P_{95\%} \f$ The 95% percentile. 95% of all voxel do have this or a lower intensity.
* - <b>First Order::Maximum</b>: The maximum is defined as the minimum of the all intensities in the ROI.
* - <b>First Order::Range</b>: The range of intensity values is defined as the difference between the maximum
* and minimum intensity in the ROI.
* - <b>First Order::Interquartile Range</b>: The difference between the 75% and 25% quantile.
* - <b>First Order::Mean Absolute Deviation</b>: The mean absolute deviation gives the mean distance of each
- * voxel intensity to the overal mean intensity and is a measure of the dispersion of the intensity form the
+ * voxel intensity to the overall mean intensity and is a measure of the dispersion of the intensity form the
* mean value:
* \f[ \textup{Mean Absolute Deviation} = \frac{1}{N_v} \sum \left \| x_i - \mu \right \| \f]
* - <b>First Order::Robust Mean</b>: The mean intensity within the ROI for all voxels between the 10% and 90% quantile:
* \f[ \textup{Robust Mean}= \mu_R = \frac{1}{N_{vr}} \sum x_i \f]
* - <b>First Order::Robust Mean Absolute Deviation</b>: The absolute deviation of all intensities within the ROI for
* all voxels between the 10% and 90% quantilefrom the robust mean intensity:
* \f[ \textup{Robust Mean Absolute Deviation}= \mu_R = \frac{1}{N_{vr}} \sum \left \| x_i - \mu_R \right \| \f]
* - <b>First Order::Median Absolute Deviation</b>: Similar to the mean absolute deviation, but uses the median
* instead of the mean to measure the center of the distribution.
* - <b>First Order::Coefficient Of Variation</b>: Measures the dispersion of the intensity distribution:
* \f[ \textup{Coefficient Of Variation} = \frac{sigma}{\mu} \f]
- * - <b>First Order::Quantile Coefficient Of Dispersion</b>: A robust alternative to teh coefficient of variance:
+ * - <b>First Order::Quantile Coefficient Of Dispersion</b>: A robust alternative to the coefficient of variance:
* \f[ \textup{Quantile Coefficient Of Dispersion} = \frac{P_{75\%} - P_{25\%} }{P_{75\%} + P_{25\%}} \f]
* - <b>First Order::Energy</b>: The intensity energy:
* \f[ \textup{Energy} = \sum x_i ^2 \f]
* - <b>First Order::Root Mean Square</b>: Root mean square is an important measure for the error.
* \f[ \textup{Root Mean Square} = \sqrt{\frac{\sum x_i ^2}{N_v}} \f]
* - <b>First Order::Uniformity</b>:
* \f[ \textup{Uniformity} = \sum p_x^2 \f]
* - <b>First Order::Entropy</b>:
* \f[ \textup{Entropy} = - \sum p_x \textup{log}_2(p_x) \f]
* - <b>First Order::Entropy</b>:
* \f[ \textup{Entropy} = - \sum p_x \textup{log}_2(p_x) \f]
* - <b>First Order::Covered Image Intensity Range</b>: Percentage of the image intensity range (maximum - minimum in whole
* image) that is covered by the ROI.
* - <b>First Order::Sum</b>: The sum of all intensities. It is correlated to the mean intensity.
* \f[ \textup{Sum} = \sum x_i \f]
* - <b>First Order::Mode</b>: The most common intensity.
* - <b>First Order::Mode Probability</b>: The likelihood of the most common intensity.
* - <b>First Order::Number Of Voxels</b>: \f$ N_v \f$ the number of voxels covered by the ROI.
* - <b>First Order::Image Dimension</b>: The dimensionality of the image (e.g. 2D, 3D, etc.).
* - <b>First Order::Number Of Voxels</b>: The product of all spacing along all dimensions. In 3D, this is equal to the
* volume.
* - <b>First Order::Number Of Voxels</b>: The volume of a single voxel. If the dimensionality is only 2D, this is the
* surface of an voxel.
*/
mitkClassMacro(GIFFirstOrderNumericStatistics,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFFirstOrderNumericStatistics();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderStatistics.h
index 6c913ba0a2..c49a0425cf 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderStatistics.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFFirstOrderStatistics.h
@@ -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.
============================================================================*/
#ifndef mitkGIFFirstOrderStatistics_h
#define mitkGIFFirstOrderStatistics_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
class MITKCLUTILITIES_EXPORT GIFFirstOrderStatistics : public AbstractGlobalImageFeature
{
public:
/**
* \brief Calculates first order statistics of the given image.
*
* The first order statistics for the intensity distribution within a given Region of Interest (ROI)
- * is caluclated. The ROI is defined using a mask.
+ * is calculated. The ROI is defined using a mask.
*
* The features are calculated on a quantified image. If the bin-size is too big, the obtained values
- * can be errornous and missleading. It is therefore important to use enough bins. The binned approach is
+ * can be erroneous and misleading. It is therefore important to use enough bins. The binned approach is
* used in order to avoid floating-point related errors.
*
* This feature calculator is activated by the option <b>-first-order</b> or <b>-fo</b>.
*
* The connected areas are based on the binned image, the binning parameters can be set via the default
* parameters as described in AbstractGlobalImageFeature. It is also possible to determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
* No other options are possible beside these two options.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image. All voxels with the value 1 are treated as masked.
*
* The following features are then defined using the (binned) voxel intensity \f$ x_i \f$ of each voxel, the probability
* an intensity \f$ p_x \f$, and the overall number of voxels within the mask \f$ N_v \f$:
* - <b>First Order::Mean</b>: The mean intensity within the ROI
* \f[ \textup{Mean}= \mu = \frac{1}{N_v} \sum x_i \f]
* - <b>First Order::Unbiased Variance</b>: An unbiased estimation of the variance:
* \f[ \textup{Unbiased Variance} = \frac{1}{N_v - 1} \sum \left( x_i - \mu \right)^2 \f]
* - <b>First Order::Biased Variance</b>: An biased estimation of the variance. If not specified otherwise, this is
* used as the variance:
* \f[ \textup{Biased Variance} = \sigma^2 = \frac{1}{N_v} \sum \left( x_i - \mu \right)^2 \f]
* - <b>First Order::Unbiased Standard Deviation</b>: Estimation of diversity within the intensity values
* \f[ \textup{Unbiased Standard Deviation} = \sqrt{\frac{1}{N_v-1} \sum \left( x_i - \mu \right)^2} \f]
* - <b>First Order::Biased Standard Deviation</b>: Estimation of diversity within the intensity values
* \f[ \textup{Biased Standard Deviation} = \sigma = \sqrt{\frac{1}{N_v} \sum \left( x_i - \mu \right)^2} \f]
* - <b>First Order::Skewness</b>:
* \f[ \textup{Skewness} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^3}{\sigma^3} \f]
* - <b>First Order::Kurtosis</b>: The kurtosis is a measurement of the peakness of the given
- * distirbution:
+ * distribution:
* \f[ \textup{Kurtosis} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^4}{\sigma^4} \f]
* - <b>First Order::Excess Kurtosis</b>: The kurtosis is a measurement of the peakness of the given
- * distirbution. The excess kurtosis is similar to the kurtosis, but is corrected by a fisher correction,
+ * distribution. The excess kurtosis is similar to the kurtosis, but is corrected by a fisher correction,
* ensuring that a gaussian distribution has an excess kurtosis of 0.
* \f[ \textup{Excess Kurtosis} = \frac{\frac{1}{N_v} \sum \left( x_i - \mu \right)^4}{\sigma^4} - 3 \f]
* - <b>First Order::Median</b>: The median is defined as the median of the all intensities in the ROI.
* - <b>First Order::Minimum</b>: The minimum is defined as the minimum of the all intensities in the ROI.
* - <b>First Order::05th Percentile</b>: \f$ P_{5\%} \f$ The 5% percentile. 5% of all voxel do have this or a lower intensity.
* - <b>First Order::10th Percentile</b>: \f$ P_{10\%} \f$ The 10% percentile. 10% of all voxel do have this or a lower intensity.
* - <b>First Order::15th Percentile</b>: \f$ P_{15\%} \f$ The 15% percentile. 15% of all voxel do have this or a lower intensity.
* - <b>First Order::20th Percentile</b>: \f$ P_{20\%} \f$ The 20% percentile. 20% of all voxel do have this or a lower intensity.
* - <b>First Order::25th Percentile</b>: \f$ P_{25\%} \f$ The 25% percentile. 25% of all voxel do have this or a lower intensity.
* - <b>First Order::30th Percentile</b>: \f$ P_{30\%} \f$ The 30% percentile. 30% of all voxel do have this or a lower intensity.
* - <b>First Order::35th Percentile</b>: \f$ P_{35\%} \f$ The 35% percentile. 35% of all voxel do have this or a lower intensity.
* - <b>First Order::40th Percentile</b>: \f$ P_{40\%} \f$ The 40% percentile. 40% of all voxel do have this or a lower intensity.
* - <b>First Order::45th Percentile</b>: \f$ P_{45\%} \f$ The 45% percentile. 45% of all voxel do have this or a lower intensity.
* - <b>First Order::50th Percentile</b>: \f$ P_{50\%} \f$ The 50% percentile. 50% of all voxel do have this or a lower intensity.
* - <b>First Order::55th Percentile</b>: \f$ P_{55\%} \f$ The 55% percentile. 55% of all voxel do have this or a lower intensity.
* - <b>First Order::60th Percentile</b>: \f$ P_{60\%} \f$ The 60% percentile. 60% of all voxel do have this or a lower intensity.
* - <b>First Order::65th Percentile</b>: \f$ P_{65\%} \f$ The 65% percentile. 65% of all voxel do have this or a lower intensity.
* - <b>First Order::70th Percentile</b>: \f$ P_{70\%} \f$ The 70% percentile. 70% of all voxel do have this or a lower intensity.
* - <b>First Order::75th Percentile</b>: \f$ P_{75\%} \f$ The 75% percentile. 75% of all voxel do have this or a lower intensity.
* - <b>First Order::80th Percentile</b>: \f$ P_{80\%} \f$ The 80% percentile. 80% of all voxel do have this or a lower intensity.
* - <b>First Order::85th Percentile</b>: \f$ P_{85\%} \f$ The 85% percentile. 85% of all voxel do have this or a lower intensity.
* - <b>First Order::90th Percentile</b>: \f$ P_{90\%} \f$ The 90% percentile. 90% of all voxel do have this or a lower intensity.
* - <b>First Order::95th Percentile</b>: \f$ P_{95\%} \f$ The 95% percentile. 95% of all voxel do have this or a lower intensity.
* - <b>First Order::Maximum</b>: The maximum is defined as the minimum of the all intensities in the ROI.
* - <b>First Order::Range</b>: The range of intensity values is defined as the difference between the maximum
* and minimum intensity in the ROI.
* - <b>First Order::Interquartile Range</b>: The difference between the 75% and 25% quantile.
* - <b>First Order::Mean Absolute Deviation</b>: The mean absolute deviation gives the mean distance of each
- * voxel intensity to the overal mean intensity and is a measure of the dispersion of the intensity form the
+ * voxel intensity to the overall mean intensity and is a measure of the dispersion of the intensity form the
* mean value:
* \f[ \textup{Mean Absolute Deviation} = \frac{1}{N_v} \sum \left \| x_i - \mu \right \| \f]
* - <b>First Order::Robust Mean</b>: The mean intensity within the ROI for all voxels between the 10% and 90% quantile:
* \f[ \textup{Robust Mean}= \mu_R = \frac{1}{N_{vr}} \sum x_i \f]
* - <b>First Order::Robust Mean Absolute Deviation</b>: The absolute deviation of all intensities within the ROI for
* all voxels between the 10% and 90% quantilefrom the robust mean intensity:
* \f[ \textup{Robust Mean Absolute Deviation}= \mu_R = \frac{1}{N_{vr}} \sum \left \| x_i - \mu_R \right \| \f]
* - <b>First Order::Median Absolute Deviation</b>: Similar to the mean absolute deviation, but uses the median
* instead of the mean to measure the center of the distribution.
* - <b>First Order::Coefficient Of Variation</b>: Measures the dispersion of the intensity distribution:
* \f[ \textup{Coefficient Of Variation} = \frac{sigma}{\mu} \f]
- * - <b>First Order::Quantile Coefficient Of Dispersion</b>: A robust alternative to teh coefficient of variance:
+ * - <b>First Order::Quantile Coefficient Of Dispersion</b>: A robust alternative to the coefficient of variance:
* \f[ \textup{Quantile Coefficient Of Dispersion} = \frac{P_{75\%} - P_{25\%} }{P_{75\%} + P_{25\%}} \f]
* - <b>First Order::Energy</b>: The intensity energy:
* \f[ \textup{Energy} = \sum x_i ^2 \f]
* - <b>First Order::Root Mean Square</b>: Root mean square is an important measure for the error.
* \f[ \textup{Root Mean Square} = \sqrt{\frac{\sum x_i ^2}{N_v}} \f]
* - <b>First Order::Uniformity</b>:
* \f[ \textup{Uniformity} = \sum p_x^2 \f]
* - <b>First Order::Entropy</b>:
* \f[ \textup{Entropy} = - \sum p_x \textup{log}_2(p_x) \f]
* - <b>First Order::Entropy</b>:
* \f[ \textup{Entropy} = - \sum p_x \textup{log}_2(p_x) \f]
* - <b>First Order::Covered Image Intensity Range</b>: Percentage of the image intensity range (maximum - minimum in whole
* image) that is covered by the ROI.
* - <b>First Order::Sum</b>: The sum of all intensities. It is correlated to the mean intensity.
* \f[ \textup{Sum} = \sum x_i \f]
* - <b>First Order::Mode</b>: The most common intensity.
* - <b>First Order::Mode Probability</b>: The likelihood of the most common intensity.
* - <b>First Order::Number Of Voxels</b>: \f$ N_v \f$ the number of voxels covered by the ROI.
* - <b>First Order::Image Dimension</b>: The dimensionality of the image (e.g. 2D, 3D, etc.).
* - <b>First Order::Number Of Voxels</b>: The product of all spacing along all dimensions. In 3D, this is equal to the
* volume.
* - <b>First Order::Number Of Voxels</b>: The volume of a single voxel. If the dimensionality is only 2D, this is the
* surface of an voxel.
*/
mitkClassMacro(GIFFirstOrderStatistics,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFFirstOrderStatistics();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelDistanceZone.h b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelDistanceZone.h
index fa3cbd29bd..20a792f609 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelDistanceZone.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelDistanceZone.h
@@ -1,168 +1,168 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkGIFGreyLevelDistanceZone_h
#define mitkGIFGreyLevelDistanceZone_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
-#include <Eigen/src/Core/Array.h>
+#include <itkeigen/Eigen/src/Core/Array.h>
namespace mitk
{
struct GreyLevelDistanceZoneFeatures
{
GreyLevelDistanceZoneFeatures() :
SmallDistanceEmphasis(0),
LargeDistanceEmphasis(0),
LowGreyLevelEmphasis(0),
HighGreyLevelEmphasis(0),
SmallDistanceLowGreyLevelEmphasis(0),
SmallDistanceHighGreyLevelEmphasis(0),
LargeDistanceLowGreyLevelEmphasis(0),
LargeDistanceHighGreyLevelEmphasis(0),
GreyLevelNonUniformity(0),
GreyLevelNonUniformityNormalized(0),
ZoneDistanceNonUniformity(0),
ZoneDistanceNoneUniformityNormalized(0),
ZonePercentage(0),
GreyLevelMean(0),
GreyLevelVariance(0),
ZoneDistanceMean(0),
ZoneDistanceVariance(0),
ZoneDistanceEntropy(0)
{
}
public:
double SmallDistanceEmphasis;
double LargeDistanceEmphasis;
double LowGreyLevelEmphasis;
double HighGreyLevelEmphasis;
double SmallDistanceLowGreyLevelEmphasis;
double SmallDistanceHighGreyLevelEmphasis;
double LargeDistanceLowGreyLevelEmphasis;
double LargeDistanceHighGreyLevelEmphasis;
double GreyLevelNonUniformity;
double GreyLevelNonUniformityNormalized;
double ZoneDistanceNonUniformity;
double ZoneDistanceNoneUniformityNormalized;
double ZonePercentage;
double GreyLevelMean;
double GreyLevelVariance;
double ZoneDistanceMean;
double ZoneDistanceVariance;
double ZoneDistanceEntropy;
};
class MITKCLUTILITIES_EXPORT GIFGreyLevelDistanceZone : public AbstractGlobalImageFeature
{
/**
* \brief Calculates the Grey Level Distance Zone
*
* This class can be used to calculate Grey Level Distance Zone as presented in Thibault et al. 2014.
*
* The basic idea behind the Grey Level Distance Zone based features is to count the connected areas
* with a given intensity value \f$x_i\f$ and a given distance to the border of each segmentation \f$d_i\f$.
- * Several features are then calculated based on a matrix, that gives the number of occurence for each
+ * Several features are then calculated based on a matrix, that gives the number of occurrence for each
* combination of \f$x_i\f$ and \f$ d_i \f$ as \f$m_{x,d}\f$.
*
* This feature calculator is activated by the option <b>-grey-level-distance-zone</b> or <b>-gldz</b>.
*
* The connected areas are based on the binned image, the binning parameters can be set via the default
* parameters as described in AbstractGlobalImageFeature. It is also possible to determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
* No other options are possible beside these two options.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image. It is expected that the image contains only voxels with value 0 and 1,
* of which all voxels with an value equal to one are treated as masked.
*
* The features depend on the distance to the border of the segmentation ROI. In some cases, the border
* definition might be different from the definition of the masked area, for example, if small openings
* in the mask should not influence the distance. Due to that, it is possible to submit a second mask,
* named Morphological Mask to the features that is then used to calculate the distance of each voxel to
* border of the segmented area. The morpological mask can be either set by the function call SetMorphMask()
* or by the corresponding global option. (Not parsed by the filter itself, but by the command line tool).
*
* Beside the complete matrix, which is represented by its individual elements \f$m_{x,d}\f$, som eadditional
* values are used for the definition. \f$N_g\f$ is the number of discrete grey levels, \f$ N_d\f$ the number
* (or maximum value) of possible distances, and \f$N_s\f$ the total number of zones.
* \f$m_{x,d}\f$ gives the number of connected areas with the discrete
* grey level x and distance to the boarder of d. Corresponding, \f$p_{x,d} = \frac{m_{x,d}}{N_s} gives the relativ
* probability of this matrix cell. \f$ \f$N_v\f$ is the number of voxels. In addition, the marginal
* sums \f$m_{x,\cdot} = m_x = \sum_d m_{x,d} \f$ , representing the sum of all zones with a given intensity, and
* sums \f$m_{\cdot, d} = m_d = \sum_x m_{x,d} \f$ , representing the sum of all zones with a given distance, are used.
* The distance are given as the number of voxels to the border and the voxel intensity is given as the
* bin number of the binned image, starting with 1.
*
* The following features are then defined:
* - <b>Grey Level Distance Zone::Small Distance Emphasis</b>:
* \f[ \textup{Small Distance Emphasis}= \frac{1}{N_s} \sum_d \frac{m_d}{d^2} \f]
* - <b>Grey Level Distance Zone::Large Distance Emphasis</b>:
* \f[ \textup{Large Distance Emphasis}= \frac{1}{N_s} \sum_d d^2 m_d \f]
* - <b>Grey Level Distance Zone::Low Grey Level Emphasis</b>:
* \f[ \textup{Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \frac{m_x}{x^2} \f]
* - <b>Grey Level Distance Zone::High Grey Level Emphasis</b>:
* \f[ \textup{High Grey Level Emphasis}= \frac{1}{N_s} \sum_x x^2 m_x \f]
* - <b>Grey Level Distance Zone::Small Distance Low Grey Level Emphasis</b>:
* \f[ \textup{Small Distance Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d \frac{ m_{x,d}}{x^2 d^2}\f]
* - <b>Grey Level Distance Zone::Small Distance High Grey Level Emphasis</b>:
* \f[ \textup{Small Distance High Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d \frac{x^2 m_{x,d}}{d^2}\f]
* - <b>Grey Level Distance Zone::Large Distance Low Grey Level Emphasis</b>:
* \f[ \textup{Large Distance Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d \frac{d^2 m_{x,d}}{x^2}\f]
* - <b>Grey Level Distance Zone::Large Distance High Grey Level Emphasis</b>:
* \f[ \textup{Large Distance High Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d \x^2 d^2 m_{x,d} \f]
* - <b>Grey Level Distance Zone::Grey Level Non-Uniformity</b>:
* \f[ \textup{Grey Level Non-Uniformity}= \frac{1}{N_s} \sum_x m_x^2 \f]
* - <b>Grey Level Distance Zone::Grey Level Non-Uniformity Normalized</b>:
* \f[ \textup{Grey Level Non-Uniformity Normalized}= \frac{1}{N_s^2} \sum_x m_x^2 \f]
* - <b>Grey Level Distance Zone::Zone Distance Non-Uniformity</b>:
* \f[ \textup{Grey Level Non-Uniformity}= \frac{1}{N_s} \sum_d m_d^2 \f]
* - <b>Grey Level Distance Zone::Zone Distance Non-Uniformity Normalized</b>:
* \f[ \textup{Grey Level Non-Uniformity Normalized}= \frac{1}{N_s^2} \sum_d m_d^2 \f]
* - <b>Grey Level Distance Zone::Zone Percentage</b>: The ratio of zones to the possible zones:
* \f[ \textup{Zone Percentage}= \frac{N_s}{N_v} \f]
* - <b>Grey Level Distance Zone::Grey Level Mean</b>:
* \f[ \textup{Grey Level Mean}= \mu_x = \sum_x \sum_d x p_{x,d} \f]
* - <b>Grey Level Distance Zone::Grey Level Variance</b>:
* \f[ \textup{Grey Level Variance} = \sum_x \sum_d \left(x - \mu_x \right)^2 p_{x,d} \f]
* - <b>Grey Level Distance Zone::Zone Distance Mean</b>:
* \f[ \textup{Zone Distance Mean}= \mu_d = \sum_x \sum_d d p_{x,d} \f]
* - <b>Grey Level Distance Zone::Zone Distance Variance</b>:
* \f[ \textup{Zone Distance Variance} = \sum_x \sum_d \left(d - \mu_d \right)^2 p_{x,d} \f]
* - <b>Grey Level Distance Zone::Zone Distance Entropy </b>:
* \f[ \textup{Zone Distance Entropy} = - \sum_x \sum_d p_{x,d} \textup{log}_2 ( p_{x,d} ) \f]
* - <b>Grey Level Distance Zone::Grey Level Entropy </b>:
* \f[ \textup{Grey Level Entropy} = - \sum_x \sum_d p_{x,d} \textup{log}_2 ( p_{x,d} ) \f]
*/
public:
mitkClassMacro(GIFGreyLevelDistanceZone, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFGreyLevelDistanceZone();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelRunLength.h b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelRunLength.h
index 480ddb760a..a2d15f05a8 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelRunLength.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelRunLength.h
@@ -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.
============================================================================*/
#ifndef mitkGIFGreyLevelRunLength_h
#define mitkGIFGreyLevelRunLength_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
* \brief Calculates the Run Length based features.
*
- * Grey Level Run Length based features are calcualted using the Run-Length-Matrix and were originally
+ * Grey Level Run Length based features are calculated using the Run-Length-Matrix and were originally
* defined by Galloway (1975). The basic idea behind this feature is to measure the length of
* lines with similar intensity in an image. This allows to asses line-based structures in an image.
* For this, the Run-Length-Matrix created that gives the number of lines \f$ m_{x,l} \f$ with the intensity \f$ x \f$ and
* the length of \f$ l \f$ with the given intensity.
*
* The image is quantified prior to the calculation of the features. This reduces the number of
* available intensity values. Instead of using the pure intensity value, the features are
* calculated using the number of the bins as intensity value \f$ x_i \f$. The parameter of the
* quantification of the image can be controlled using the general binning parameters as defined
* in AbstractGlobalImageFeature.
*
- * By default, the calculation is based on a 26 neighourhood for 3D and a 8 neighbourhood in 2D. It is further
+ * By default, the calculation is based on a 26 neighbourhood for 3D and a 8 neighbourhood in 2D. It is further
* possible to exclude directions from the calculation, e.g. calculating the feature in 2D, even if a
* 3D image is passed. This is controlled by determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
* No other options are possible beside these two options.
*
* This feature calculator is activated by the option <b>-run-length</b> or <b>-rl</b>.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the same type as the input image. All voxels with a value greater 0.5 are treated as masked.
*
- * Several values are definied for the definition of the features. \f$ N_v \f$ is the number of masked voxels,
+ * Several values are defined for the definition of the features. \f$ N_v \f$ is the number of masked voxels,
* \f$N_s \f$ is the number of different lines, \f$ m_{x,\cdot} = \sum_l m{x,l} \f$ is the number of all lines
* with a given intensity value, and likewise \f$ m_{\cdot, l} = \sum_x m{x,l} \f$ is the number of all lines
* with a given length. There are two options how to make this feature orientation-invariant. Either calculating a
* single matrix for all directions and then extracting the features or by calculating a matrix per direction,
* calculating all features and then reporting the mean and standard deviation for each feature. The result
* of the first option is given with the extension "Comb.", the results for the second with "Std." and "Means".
* All three options are always calculated, although we state only one in the following list:
* - <b>Run Length::Short run emphasis Comb.</b>:
* \f[ \textup{Short run emphasis}= \frac{1}{N_s} \sum_l { \frac{m_{\cdot, l}}{l^2} } \f]
* - <b>Run Length::Long run emphasis Comb.</b>:
* \f[ \textup{Long run emphasis}= \frac{1}{N_s} \sum_l { m_{\cdot, l} l^2} \f]
* - <b>Run Length::Low grey level run emphasis Comb.</b>:
* \f[ \textup{Low grey level run emphasis}= \frac{1}{N_s} \sum_x { \frac{m_{x,\cdot}}{x^2} } \f]
* - <b>Run Length::High grey level run emphasis Comb.</b>:
* \f[ \textup{High grey level run emphasis}= \frac{1}{N_s} \sum_x { m_{x,\cdot} x^2} \f]
* - <b>Run Length::Short run low grey level emphasis Comb.</b>:
* \f[ \textup{Short run low grey level emphasis}= \frac{1}{N_s} \sum_x \sum_l { \frac{m_{x,l}}{x^2 l^2} } \f]
* - <b>Run Length::Short run high grey level emphasis Comb.</b>:
* \f[ \textup{Short run high grey level emphasis}= \frac{1}{N_s} \sum_x \sum_l { \frac{x^2 m_{x,s}}{l^2} } \f]
* - <b>Run Length::Long run low grey level emphasis Comb.</b>:
* \f[ \textup{Long run low grey level emphasis}= \frac{1}{N_s} \sum_x \sum_l { \frac{l^2 m_{x,l}}{x^2} } \f]
* - <b>Run Length::Long run high grey level emphasis Comb.</b>:
* \f[ \textup{Long run high grey level emphasis}= \frac{1}{N_s} \sum_x \sum_l { x^2 l^2 m_{x,l} } \f]
* - <b>Run Length::Grey level nonuniformity Comb.</b>:
* \f[ \textup{Grey level nonuniformity}= \frac{1}{N_s} \sum_x m_{x,\cdot}^2 \f]
* - <b>Run Length::Grey level nonuniformity normalized Comb.</b>:
* \f[ \textup{Grey level nonuniformity normalized}= \frac{1}{N_s^2} \sum_x m_{x,\cdot}^2 \f]
* - <b>Run Length::Run length nonuniformity</b>:
* \f[ \textup{Run length nonuniformity}= \frac{1}{N_s} \sum_l m_{\cdot, l}^2 \f]
* - <b>Run Length::Run length nonuniformity normalized</b>:
* \f[ \textup{Run length nonuniformity normalized}= \frac{1}{N_s^2} \sum_l m_{\cdot, l}^2 \f]
* - <b>Run Length::Run percentage</b>: The ratio of realized runs to the theoretical limit of zones:
* \f[ \textup{Run percentage}= \frac{N_s}{N_v} \f]
* - <b>Run Length::Grey level variance</b>:
* \f[ \textup{Grey level variance} = \frac{1}{N_s} \sum_x (x -mu_x)^2 m_{x, \cdot} \f]
* - <b>Run Length::Run length variance</b>:
* \f[ \textup{Run length variance} = \frac{1}{N_s} \sum_l (l -mu_l)^2 m_{\cdot, l} \f]
* - <b>Run Length::Run length entropy</b>: This feature would be equivalent with
* the Grey Level Entropy, which is therefore not included. It is based on the likelihood
* for a given intensity- size combination \f$ p_{x,s} = \frac{m_{x,s}}{N_s} \f$. :
* \f[ \textup{Run length entropy} = \sum_x \sum_s p_{x,s} \textup{log}_2 \left( p{_x,s} \right) \f]
* - <b>Run Length::Number of runs</b>: The overall number of realized runs:
* \f[ \textup{Number of runs} = \sum m_{x, l} \f]
*/
class MITKCLUTILITIES_EXPORT GIFGreyLevelRunLength : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFGreyLevelRunLength,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFGreyLevelRunLength();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser &parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelSizeZone.h b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelSizeZone.h
index 9f900b22fd..72a136d412 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelSizeZone.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFGreyLevelSizeZone.h
@@ -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.
============================================================================*/
#ifndef mitkGIFGreyLevelSizeZone_h
#define mitkGIFGreyLevelSizeZone_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
-#include <Eigen/src/Core/Array.h>
+#include <itkeigen/Eigen/src/Core/Array.h>
namespace mitk
{
class MITKCLUTILITIES_EXPORT GIFGreyLevelSizeZone : public AbstractGlobalImageFeature
{
/**
* \brief Calculates the Grey level size zone based features.
*
- * Grey level size zone based features are similar to Grey Level Cooccurence features. But instead
+ * Grey level size zone based features are similar to Grey Level Cooccurrence features. But instead
* of measuring the similarity within a given neighbourhood, the size of areas with the same intensity
* is assessed. For this, a matrix is created that gives the number of areas \f$ m_{x,s} \f$ with the intensity \f$ x \f$ and
* the size of \f$ s \f$. Each area is specified as connected voxels with the given intensity.
*
* The image is quantified prior to the calculation of the features. This reduces the number of
* available intensity values. Instead of using the pure intensity value, the features are
* calculated using the number of the bins as intensity value \f$ x_i \f$. The parameter of the
* quantification of the image can be controlled using the general binning parameters as defined
* in AbstractGlobalImageFeature.
*
- * By default, the calculation is based on a 26 neighourhood for 3D and a 8 neighbourhood in 2D. It is further
+ * By default, the calculation is based on a 26 neighbourhood for 3D and a 8 neighbourhood in 2D. It is further
* possible to exclude directions from the calculation, e.g. calculating the feature in 2D, even if a
* 3D image is passed. This is controlled by determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
* No other options are possible beside these two options.
*
* This feature calculator is activated by the option <b>-grey-level-sizezone</b> or <b>-glsz</b>.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image. All voxels with the value 1 are treated as masked.
*
- * Several values are definied for the definition of the features. \f$ N_v \f$ is the number of masked voxels,
+ * Several values are defined for the definition of the features. \f$ N_v \f$ is the number of masked voxels,
* \f$N_s \f$ is the number of different zones, \f$ m_{x,\cdot} = \sum_s m{x,s} \f$ is the number of all areas
* with a given intensity value, and likewise \f$ m_{\cdot, s} = \sum_x m{x,s} \f$ is the number of all areas
* with a given size. The features are then defined as:
* - <b>Grey Level Size Zone::Small Zone Emphasis</b>:
* \f[ \textup{Small Zone Emphasis}= \frac{1}{N_s} \sum_s { \frac{m_{\cdot, s}}{s^2} } \f]
* - <b>Grey Level Size Zone::Large Zone Emphasis</b>:
* \f[ \textup{Large Zone Emphasis}= \frac{1}{N_s} \sum_s { m_{\cdot, s} s^2} \f]
* - <b>Grey Level Size Zone::Low Grey Level Zone Emphasis</b>:
* \f[ \textup{Low Grey Level Zone Emphasis}= \frac{1}{N_s} \sum_x { \frac{m_{x,\cdot}}{x^2} } \f]
* - <b>Grey Level Size Zone::High Grey Level Zone Emphasis</b>:
* \f[ \textup{High Grey Level Zone Emphasis}= \frac{1}{N_s} \sum_x { m_{x,\cdot} x^2} \f]
* - <b>Grey Level Size Zone::Small Zone Low Grey Level Emphasis</b>:
* \f[ \textup{Small Zone Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_s { \frac{m_{x,s}}{x^2 s^2} } \f]
* - <b>Grey Level Size Zone::Small Zone High Grey Level Emphasis</b>:
* \f[ \textup{Small Zone High Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_s { \frac{x^2 m_{x,s}}{s^2} } \f]
* - <b>Grey Level Size Zone::Large Zone Low Grey Level Emphasis</b>:
* \f[ \textup{Large Zone Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_s { \frac{s^2 m_{x,s}}{x^2} } \f]
* - <b>Grey Level Size Zone::Large Zone High Grey Level Emphasis</b>:
* \f[ \textup{Large Zone High Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_s { x^2 s^2 m_{x,s} } \f]
* - <b>Grey Level Size Zone::Grey Level Non-Uniformity</b>:
* \f[ \textup{Grey Level Non-Uniformity}= \frac{1}{N_s} \sum_x m_{x,\cdot}^2 \f]
* - <b>Grey Level Size Zone::Grey Level Non-Uniformity Normalized</b>:
* \f[ \textup{Grey Level Non-Uniformity Normalized}= \frac{1}{N_s^2} \sum_x m_{x,\cdot}^2 \f]
* - <b>Grey Level Size Zone::Zone Size Non-Uniformity</b>:
* \f[ \textup{Zone Size Non-Uniformity}= \frac{1}{N_s} \sum_s m_{\cdot, s}^2 \f]
* - <b>Grey Level Size Zone::Zone Size Non-Uniformity Normalized</b>:
* \f[ \textup{Zone Size Non-Uniformity Normalized}= \frac{1}{N_s^2} \sum_s m_{\cdot, s}^2 \f]
* - <b>Grey Level Size Zone::Zone Percentage</b>: The ratio of realized areas to the theoretical limit of zones:
* \f[ \textup{Zone Percentage}= \frac{N_s}{N_v} \f]
* - <b>Grey Level Size Zone::Grey Level Mean</b>:
* \f[ \textup{Grey Level Mean} = \mu_x = \frac{1}{N_s} \sum_x x m_{x, \cdot} \f]
* - <b>Grey Level Size Zone::Grey Level Variance</b>:
* \f[ \textup{Grey Level Variance} = \frac{1}{N_s} \sum_x (x -mu_x)^2 m_{x, \cdot} \f]
* - <b>Grey Level Size Zone::Zone Size Mean</b>:
* \f[ \textup{Zone Size Mean} = \mu_s = \frac{1}{N_s} \sum_s s m_{\cdot, s} \f]
* - <b>Grey Level Size Zone::Grey Level Variance</b>:
* \f[ \textup{Grey Level Variance} = \frac{1}{N_s} \sum_s (s -mu_s)^2 m_{\cdot, s} \f]
* - <b>Grey Level Size Zone::Zone Size Entropy</b>: This feature would be equivalent with
* the Grey Level Entropy, which is therefore not included. It is based on the likelihood
* for a given intensity- size combination \f$ p_{x,s} = \frac{m_{x,s}}{N_s} \f$. :
* \f[ \textup{Zone Size Entropy} = \sum_x \sum_s p_{x,s} \textup{log}_2 \left( p{_x,s} \right) \f]
*/
public:
mitkClassMacro(GIFGreyLevelSizeZone, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFGreyLevelSizeZone();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFImageDescriptionFeatures.h b/Modules/Classification/CLUtilities/include/mitkGIFImageDescriptionFeatures.h
index e1b8bff712..8dff6b58f6 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFImageDescriptionFeatures.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFImageDescriptionFeatures.h
@@ -1,82 +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 mitkGIFImageDescriptionFeatures_h
#define mitkGIFImageDescriptionFeatures_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
* \brief Calculates simple features that describe the given image / mask
*
* This class can be used to calculated simple image describing features that
* can be used to compare different images.
*
* This feature calculator is activated by the option "<b>-image-diagnostic</b>" or "<b>-id</b>".
* There are no parameters to further define the behavior of this feature calculator.
*
- * The paramters for this image are calculated from two images, a mask and an intenstiy
+ * The parameters for this image are calculated from two images, a mask and an intenstiy
* image. It is assumed that the mask is
* of the type of an unsigned short image and all voxels with an value larger or equal
* to one are treated as masked. (Standard MITK mask)
*
* The definition of X, Y, and Z axis depends on the image format and does not necessarily
* correlates with axial, coronal, and sagittal.
*
* The features of this calculator are:
* - <b>Diagnostic::Dimension X</b>: The number of voxels in the intensity image along the first image dimension.
* - <b>Diagnostic::Image Dimension Y</b>: The number of voxels in the intensity image along the second image dimension.
* - <b>Diagnostic::Image Dimension Z</b>: The number of voxels in the intensity image along the third image dimension.
* - <b>Diagnostic::Image Spacing X</b>: The spacing of the intensity image in the first image dimension.
* - <b>Diagnostic::Image Spacing Y</b>: The spacing of the intensity image in the second image dimension.
* - <b>Diagnostic::Image Spacing Z</b>: The spacing of the intensity image in the third image dimension.
* - <b>Diagnostic::Image Mean intensity</b>: The mean intensity of the whole image.
* - <b>Diagnostic::Image Minimum intensity</b>: The minimum intensity that occurs in the whole image.
* - <b>Diagnostic::Image Maximum intensity</b>: The maximum intensity that occurs in the whole image.
* - <b>Diagnostic::Mask Dimension X</b>: The number of voxels in the mask image along the first image dimension.
* - <b>Diagnostic::Mask Dimension Y</b>: The number of voxels in the mask image along the second image dimension.
* - <b>Diagnostic::Mask Dimension Z</b>: The number of voxels in the mask image along the third image dimension.
* - <b>Diagnostic::Mask bounding box X</b>: The distance between the maximum and minimum position of a masked voxel along the first axis.
* - <b>Diagnostic::Mask bounding box X</b>: The distance between the maximum and minimum position of a masked voxel along the second axis.
* - <b>Diagnostic::Mask bounding box X</b>: The distance between the maximum and minimum position of a masked voxel along the third axis.
* - <b>Diagnostic::Mask Spacing X</b>: The spacing of the mask image in the first image dimension.
* - <b>Diagnostic::Mask Spacing Y</b>: The spacing of the mask image in the second image dimension.
* - <b>Diagnostic::Mask Spacing Z</b>: The spacing of the mask image in the third image dimension.
* - <b>Diagnostic::Mask Voxel count</b>: The number of voxels that are masked.
* - <b>Diagnostic::Mask Mean intensity</b>: The mean intensity of all voxel that are masked. Also depends on the intensity image.
* - <b>Diagnostic::Mask Minimum intensity</b> The minimum masked intensity in the intensity image.
* - <b>Diagnostic::Mask Maximum intensity</b> The maximum masked intensity in the intensity image.
*/
class MITKCLUTILITIES_EXPORT GIFImageDescriptionFeatures : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFImageDescriptionFeatures,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFImageDescriptionFeatures();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFIntensityVolumeHistogramFeatures.h b/Modules/Classification/CLUtilities/include/mitkGIFIntensityVolumeHistogramFeatures.h
index e529d8f04b..49bb94e781 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFIntensityVolumeHistogramFeatures.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFIntensityVolumeHistogramFeatures.h
@@ -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.
============================================================================*/
#ifndef mitkGIFIntensityVolumeHistogramFeatures_h
#define mitkGIFIntensityVolumeHistogramFeatures_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
* \brief Calculates the Intensity Volume Histogram features
*
* This class can be used to calculate the volume histogram and features that are calculated from
* it. It is based on the intensity-volume histogram (IVH) which describes the relation between the
* grey level index i (and the corresponding intensity \f$x_i\f$) and the volume fraction \f$f\f$ that
* with an intensity that is equal or greater than \f$x_i\f$. This feature is original proposed in
* El Naqa et al. Exploring feature-based approaches in PET images for prediciting cancer treatment outcomes.
* Pattern recognition 2009.
*
* This feature calculator is activated by the option "<b>-intensity-volume-histogram</b>" or "<b>-ivoh</b>".
- * Beside the configuration of the histogram, which follows the describtion given in AbstractGlobalImageFeature
+ * Beside the configuration of the histogram, which follows the description given in AbstractGlobalImageFeature
* there are no additional parameters to configure this feature. Remark that, different from other features,
* the number of bins is 1000 by default and not 256.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image and all voxels with an value of greater than zero
* are treated as masked.
*
* The resulting features are:
- * - <b>Intensity Volume Histogram::Volume fration at 0.10 intensity</b>: The volume fraction with an intensity
+ * - <b>Intensity Volume Histogram::Volume fraction at 0.10 intensity</b>: The volume fraction with an intensity
* of greater or equal to 10% of the maximum intensity.
- * - <b>Intensity Volume Histogram::Volume fration at 0.90 intensity</b>: The volume fraction with an intensity
+ * - <b>Intensity Volume Histogram::Volume fraction at 0.90 intensity</b>: The volume fraction with an intensity
* of greater or equal to 90% of the maximum intensity.
* - <b>Intensity Volume Histogram::Intensity at 0.10 volume</b>: The highest intensity so that at least
* 10% of the masked image area has the same or higher intensity.
* - <b>Intensity Volume Histogram::Intensity at 0.90 volume</b>: The highest intensity so that at least
* 10% of the masked image area has the same or higher intensity.
- * - <b>Intensity Volume Histogram::Difference volume fration at 0.10 and 0.90 intensity</b>: The difference
+ * - <b>Intensity Volume Histogram::Difference volume fraction at 0.10 and 0.90 intensity</b>: The difference
* between the volume fraction at 10% intensity and 90% intensity.
* - <b>Intensity Volume Histogram::Difference intensity at 0.10 and 0.90 volume</b>: The intensity difference
* between the intenstiy of 90% of the volume and 10% volume.
* - <b>Intensity Volume Histogram::Area under IVH curve</b>: If the IVH is interpreted as curve, this value represents
* the area under the curve. It is calculated using the bin indexes rather than the intensity values.
*/
class MITKCLUTILITIES_EXPORT GIFIntensityVolumeHistogramFeatures : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFIntensityVolumeHistogramFeatures, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFIntensityVolumeHistogramFeatures();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFLocalIntensity.h b/Modules/Classification/CLUtilities/include/mitkGIFLocalIntensity.h
index a2f91501a6..48a9661179 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFLocalIntensity.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFLocalIntensity.h
@@ -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.
============================================================================*/
#ifndef mitkGIFLocalIntensity_h
#define mitkGIFLocalIntensity_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
* \brief Calculates the local intensity features
*
- * This class can be used to calcualte the local intensity. The local intensity is defined as the
+ * This class can be used to calculate the local intensity. The local intensity is defined as the
* mean intensity in a cube with the volume \f$ 1 \textup{cm}^3\f$ which correspond to a radius
* of approximate 0.62 cm.
*
* This feature calculator is activated by the option <b>-local-intensity</b> or <b>-loci</b>.
*
* The range that is used to calculate the local intensity can be set using the function <b>SetRange</b>.
* To set it with parameters set the option <b>loci::range</b> which expects an float value that is
* interpreted as mm length of the radius. The default value is 6.2.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image and all voxels with an value of greater than zero
* are treated as masked.
*
* The resulting features are:
* - <b>Local Intensity::Local Intensity Peak</b>: This value is defined as the local intensity of the
* voxel with the highest intensity. If there are multiple voxels with the highest intensity, the mean
* of all local intensites of these voxels are reported.
* - <b>Local Intensity::Global Intensity Peak</b>: This value is defined as the highest local intensity
* that occur for all voxels within the mask.
*/
class MITKCLUTILITIES_EXPORT GIFLocalIntensity : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFLocalIntensity, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFLocalIntensity();
itkGetConstMacro(Range, double);
itkSetMacro(Range, double);
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
std::string GenerateLegacyFeatureEncoding(const FeatureID& id) const override;
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
void ConfigureSettingsByParameters(const ParametersType& parameters) override;
private:
double m_Range;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h b/Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h
index e09c99e56c..b0775258d6 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.h
@@ -1,90 +1,90 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkGIFNeighbourhoodGreyToneDifferenceFeatures_h
#define mitkGIFNeighbourhoodGreyToneDifferenceFeatures_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
* \brief Calculates the Neighbourhood Grey Tone Difference Features.
*
* This class can be used to calculate Neighbourhood Grey Tone Difference Features which have been introduced in
* Amadasun and King: Textural features corresponding to textural properties. IEEE Transactions on Systems, Man and Cybernetricy, 1989.
*
- * The Neighbourhood Grey Tone Difference (NGTD) is based on a table and is calcualted using
+ * The Neighbourhood Grey Tone Difference (NGTD) is based on a table and is calculated using
* a defined neighbourhood for each voxel. Within this neighbourhood, the mean intensity of the neighbouring
* voxel is calculated \f$A_i\f$, i.e. the mean intensity of the neighbourhood excluding the center voxel.
* Based on this a table with four columns is calculated. The first column represents the voxel
* value, or in our implementation, the histogram bin index \f$i\f$. The second column represents the
* number of voxel with this intensity value \f$n_i\f$. The proability for each intensity value \f$p_i\f$, which
- * is equal to the bin probability. And the sum of the absolut differences of the intensity of all voxels with this
+ * is equal to the bin probability. And the sum of the absolute differences of the intensity of all voxels with this
* intensity and the mean intensity of their neighbourhood: \f[s_i = \sum \left \| i- A_i \right \| \f].
* Additional \f$ N_v\f$ is the number of voxels, \f$ N_g \f$ the number of bins, and \f$N_{g,p}\f$ the number of
* bins with a non-zero probability.
*
* This feature calculator is activated by the option <b>-neighbourhood-grey-tone-difference</b> or <b>-ngtd</b>.
*
* The range that is used to calculate the local intensity can be set using the function <b>SetRange</b>.
* To set it with parameters set the option <b>ngtd::range</b> which expects an int value n that is
- * interpreted as voxel count. The neighbourhood includes symetrical n voxels additional
+ * interpreted as voxel count. The neighbourhood includes symmetrical n voxels additional
* to the center voxel in each directions. The default value for this parameter is 1.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image and all voxels with an value of greater than zero
* are treated as masked.
*
* For the definition of the features we use the sum, this is always the sum over the bins of the histogram.
* If the denominator of a feature evaluates to zero, the feature is defined as zero.
* The resulting features are:
* - <b>Neighbourhood Grey Tone Difference::Coarsness</b>: The coarsness is defined as :
* \f[ \textup{Coarsness}= \frac{1}{\sum p_i s_i} \f]
* - <b>Neighbourhood Grey Tone Difference::Contrast</b>: The contrast is defined as :
* \f[ \textup{contrast}= \left( \frac{1}{N_{g,p} ( N_{g,p} - 1) } \sum_i \sum_j p_i p_j (i-j)^2 \right) \left( \frac{1}{N_v} \sum s_i \right) \f]
* - <b>Neighbourhood Grey Tone Difference::Busyness</b>: for all bins with a non-zero probability
* \f[ \textup{busyness} = \frac{\sum p_i s_i}{\sum_i \sum_j \left \| i p_i - j p_j \right \| } \f]
* - <b>Neighbourhood Grey Tone Difference::Complexity</b>: for all bins with a non-zero probability
* \f[ \textup{complexity} = \frac{1}{N_v} \sum_i \sum_j \left \| i - j \right \| \frac{p_i s_i + p_j s_j}{p_i + p_j} \f]
* - <b>Neighbourhood Grey Tone Difference::Strength</b>: for all bins with a non-zero probability
* \f[ \textup{strength} = \frac{\sum_i \sum_j (p_i + p_j) (i + j)^2}{\sum_i s_i } \f]
*/
class MITKCLUTILITIES_EXPORT GIFNeighbourhoodGreyToneDifferenceFeatures : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFNeighbourhoodGreyToneDifferenceFeatures, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFNeighbourhoodGreyToneDifferenceFeatures();
itkSetMacro(Range, int);
itkGetConstMacro(Range, int);
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
std::string GenerateLegacyFeatureEncoding(const FeatureID& id) const override;
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
void ConfigureSettingsByParameters(const ParametersType& parameters) override;
private:
int m_Range;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFNeighbouringGreyLevelDependenceFeatures.h b/Modules/Classification/CLUtilities/include/mitkGIFNeighbouringGreyLevelDependenceFeatures.h
index 5a7d2889b0..d9f0f60c11 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFNeighbouringGreyLevelDependenceFeatures.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFNeighbouringGreyLevelDependenceFeatures.h
@@ -1,149 +1,149 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkGIFNeighbouringGreyLevelDependenceFeatures_h
#define mitkGIFNeighbouringGreyLevelDependenceFeatures_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
-#include <Eigen/src/Core/Array.h>
+#include <itkeigen/Eigen/src/Core/Array.h>
namespace mitk
{
/**
* \brief Calculates the Neighbouring Grey Level Dependence Features
*
* The Neighbouring Grey Level Dependence Features were proposed by Sun and Wee (1983) and
* capture the coarsness of the image texture. They are rotational invariant.
*
* The features are calculated on a matrix \f$ m \f$. To obtain the matrix, a neighbourhood
* around each feature is calculated and the number of voxels within the neighbourhood that
* are greater than the center voxel plus \f$ \alpha \f$ is counted. This is called the
* number of dependence voxels. The matrix gives the
- * number of voxels with an intesity \f$ x \f$ and \f$ d \f$ dependence neighbourhood voxels.
+ * number of voxels with an intensity \f$ x \f$ and \f$ d \f$ dependence neighbourhood voxels.
*
* The image is quantified prior to the calculation of the features. This reduces the number of
* available intensity values. Instead of using the pure intensity value, the features are
* calculated using the number of the bins as intensity value \f$ x_i \f$. The parameter of the
* quantification of the image can be controlled using the general binning parameters as defined
* in AbstractGlobalImageFeature.
*
- * By default, the calculation is based on a 26 neighourhood for 3D and a 8 neighbourhood in 2D. It is further
+ * By default, the calculation is based on a 26 neighbourhood for 3D and a 8 neighbourhood in 2D. It is further
* possible to exclude directions from the calculation, e.g. calculating the feature in 2D, even if a
* 3D image is passed. This is controlled by determine the
* dimensionality of the neighbourhood using direction-related commands as described in AbstractGlobalImageFeature.
*
* In addition to this, the size of the neighbourhood can be controlled by setting the parameter
* <b>ngld::range</b>. By default it is one. To pass more than one range, separate the ranges with
* a semicolon. E.g. 1;2;3 would calculate the features for the ranges 1, 2, and 3.
*
* This feature calculator is activated by the option <b>-neighbouring-grey-level-dependence</b>
* or <b>-ngld</b>.
*
* The features are calculated based on a mask. It is assumed that the mask is
* a unsigned short image. All voxels with a value greater 0 are treated as masked.
*
- * Several values are definied for the definition of the features. \f$ N_v \f$ is the number of masked voxels,
+ * Several values are defined for the definition of the features. \f$ N_v \f$ is the number of masked voxels,
* \f$N_s \f$ is the number of neighbourhoods, \f$ m_{x,\cdot} = \sum_d m{x,d} \f$ is the number of neighbourhoods
* with a given intensity value, and likewise \f$ m_{\cdot, d} = \sum_x m{x,d} \f$ is the number of neighbourhoods
* with a given number of dependence features:
* - <b>Neighbouring Grey Level Dependence::Low Dependence Emphasis</b>:
* \f[ \textup{Low dependence emphasis}= \frac{1}{N_s} \sum_d { \frac{m_{\cdot, d}}{d^2} } \f]
* - <b>Neighbouring Grey Level Dependence::High Dependence Emphasis</b>:
* \f[ \textup{High dependence emphasis}= \frac{1}{N_s} \sum_d { m_{\cdot, d} d^2} \f]
* - <b>Neighbouring Grey Level Dependence::Low Grey Level Count Emphasis</b>:
* \f[ \textup{Low grey level count emphasis}= \frac{1}{N_s} \sum_x { \frac{m_{x,\cdot}}{x^2} } \f]
* - <b>Neighbouring Grey Level Dependence::High Grey Level Count Emphasis</b>:
* \f[ \textup{High grey level count emphasis}= \frac{1}{N_s} \sum_x { m_{x,\cdot} x^2} \f]
* - <b>Neighbouring Grey Level Dependence::Low Dependence Low Grey Level Emphasis</b>:
* \f[ \textup{Low Dependence Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d { \frac{m_{x,d}}{x^2 d^2} } \f]
* - <b>Neighbouring Grey Level Dependence::Low Dependence High Grey Level Emphasis</b>:
* \f[ \textup{Low dependence high grey level emphasis}= \frac{1}{N_s} \sum_x \sum_d { \frac{x^2 m_{x,d}}{d^2} } \f]
* - <b>Neighbouring Grey Level Dependence::High Dependence Low Grey Level Emphasis</b>:
* \f[ \textup{High Dependence Low Grey Level Emphasis}= \frac{1}{N_s} \sum_x \sum_d { \frac{d^2 m_{x,d}}{x^2} } \f]
* - <b>Neighbouring Grey Level Dependence::High Dependence High Grey Level Emphasis</b>:
* \f[ \textup{High dependence high grey level emphasis}= \frac{1}{N_s} \sum_x \sum_d { x^2 d^2 m_{x,d} } \f]
* - <b>Neighbouring Grey Level Dependence::Grey level nonuniformity</b>:
* \f[ \textup{Grey level nonuniformity}= \frac{1}{N_s} \sum_x m_{x,\cdot}^2 \f]
* - <b>Neighbouring Grey Level Dependence::Grey level nonuniformity normalized</b>:
* \f[ \textup{Grey level nonuniformity normalized}= \frac{1}{N_s^2} \sum_x m_{x,\cdot}^2 \f]
* - <b>Neighbouring Grey Level Dependence::Dependence Count Nonuniformity</b>:
* \f[ \textup{Dependence count nonuniformity}= \frac{1}{N_s} \sum_d m_{\cdot, d}^2 \f]
* - <b>Neighbouring Grey Level Dependence::Dependence Count Nonuniformity Normalized</b>:
* \f[ \textup{Dependence count nonuniformity normalized}= \frac{1}{N_s^2} \sum_d m_{\cdot, d}^2 \f]
* - <b>Neighbouring Grey Level Dependence::DEpendence Count Percentage</b> THe number of realized
* neighbourhoods relativ to the theoretical maximum of realized neighbourhoods. This feature is always
* one for this implementation as partial neighbourhoods are still considered.
* - <b>Neighbouring Grey Level Dependence::Grey Level Mean</b>: The mean value of all grey level.
* \f[ \textup{Grey Level Mean} = \mu_x = \frac{1}{N_s} \sum_x x m_{x,\cdot} \f]
* - <b>Neighbouring Grey Level Dependence::Grey Level Variance</b>:
* \f[ \textup{Grey level variance} = \frac{1}{N_s} \sum_x (x -mu_x)^2 m_{x, \cdot} \f]
* - <b>Neighbouring Grey Level Dependence::Dependence Count Mean</b>: The mean value of all dependence counts.
* \f[ \textup{Dependence count mean} = \mu_d = \frac{1}{N_s} \sum_d d m_{\cdot,d} \f]
* - <b>Neighbouring Grey Level Dependence::Dependence Count Variance</b>:
* \f[ \textup{Dependence count variance} = \frac{1}{N_s} \sum_d (d -mu_d)^2 m_{\cdot, d} \f]
* - <b>Neighbouring Grey Level Dependence::Dependence Count Entropy</b>: This feature would be equivalent with
* the Grey Level Entropy, which is therefore not included. It is based on the likelihood
* for a given intensity- size combination \f$ p_{x,d} = \frac{m_{x,d}}{N_s} \f$. :
* \f[ \textup{Dependence count entropy} = \sum_x \sum_d p_{x,d} \textup{log}_2 \left( p_{x,d} \right) \f]
* - <b>Neighbouring Grey Level Dependence::Dependence Count Energy</b>: This feature would be equivalent with
* the Grey Level Energy, which is therefore not included. It is based on the likelihood
* for a given intensity- size combination \f$ p_{x,d} = \frac{m_{x,d}}{N_s} \f$. :
* \f[ \textup{Dependence count energy} = \sum_x \sum_d p_{x,d}^2 \f]
* - <b>Neighbouring Grey Level Dependence::Expected Neighbourhood Size</b>: The expected size of a
* full neighbourhood. It depends on the dimension of the area that is looked at.
* - <b>Neighbouring Grey Level Dependence::Average Neighbourhood Size</b>: The feature calculation
* allows to consider partially masked neighbourhoods. Due to that, some neighbourhoods might be smaller.
* This feature gives not the theoretical neighbourhood size but the average realized neighbourhood sizes.
* - <b>Neighbouring Grey Level Dependence::Average Incomplete Neighbourhood Size</b>: Gives the average
* size of all neighbourhoods that are not complete.
* - <b>Neighbouring Grey Level Dependence::Percentage of complete Neighbourhoods</b>: Gives the percentage
* of all complete neighbourhoods from all realized neighbourhoods.
* - <b>Neighbouring Grey Level Dependence::Percentage of Dependence Neighbour Voxels</b>: Gives the
* percentage of voxels in all neighbourhoods compared to the expected number of voxels.
*/
class MITKCLUTILITIES_EXPORT GIFNeighbouringGreyLevelDependenceFeature : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFNeighbouringGreyLevelDependenceFeature, AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFNeighbouringGreyLevelDependenceFeature();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
itkGetConstMacro(Ranges, std::vector<double>);
void SetRanges(std::vector<double> ranges);
void SetRange(double range);
itkGetConstMacro(Alpha, int);
itkSetMacro(Alpha, int);
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
std::string GenerateLegacyFeatureEncoding(const FeatureID& id) const override;
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
void ConfigureSettingsByParameters(const ParametersType& parameters) override;
private:
std::vector<double> m_Ranges;
int m_Alpha;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFVolumetricDensityStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFVolumetricDensityStatistics.h
index 044a2342bb..4d97c84c33 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFVolumetricDensityStatistics.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFVolumetricDensityStatistics.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 mitkGIFVolumetricDensityStatistics_h
#define mitkGIFVolumetricDensityStatistics_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
* \brief Calculates Volumetric Density Features
*
* These features characterize the compactness of the volume and shape by comparing the volumes
* of different volume and shape estimation methods.
*
* This feature calculator is activated by the option <b>-volume-density</b> or <b>-volden</b>.
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image. All voxels with the value equal or greater than 1 are treated as masked.
*
* The volume and surface are compared to the volume \f$ V \f$ and surface \f$ A \f$ that is calculated
* directly from the mask. The following features are then defined:
* - <b>Morphological Density::Volume density axis-aligned bounding box</b>: The axis-aligned bounding
* box is defined as the minimum axis aligned box in 3D space that encloses all masked voxels.
- * It is calculated by using the maximum spacial extension of the mask. Based on the volume of the
+ * It is calculated by using the maximum spatial extension of the mask. Based on the volume of the
* bounding box, \f$ V_{aabb} \f$, the feature is defined as:
* \f[ \textup{Volume density axis-aligned bounding box}= \frac{V}{V_{aabb}} \f]
* - <b>Morphological Density::Surface density axis-aligned bounding box</b>: As for the previous
* feature, the axis-aligned bounding box is compared to the mask, this time using the surface of the
* bounding box \f$ A_{aabb} \f$:
* \f[ \textup{Surface density axis-aligned bounding box}= \frac{A}{A_{aabb}} \f]
* - <b>Morphological Density::Volume density oriented minimum bounding box</b>: A three-dimensional
* bounding box is defined using the box with the minimum volume. We do not use an estimation
* for this feature, which makes the calculation of this feature slow. Based on the volume of the
* bounding box, \f$ V_{ombb} \f$, the feature is defined as:
* \f[ \textup{Volume density oriented minimum bounding box}= \frac{V}{V_{ombb}} \f]
* - <b>Morphological Density::Surface density axis-aligned bounding box</b>: As for the previous
* feature, theminimum oriented bounding box is compared to the mask, this time using the surface of the
* bounding box \f$ A_{ombb} \f$:
* \f[ \textup{Surface density axis-aligned bounding box}= \frac{A}{A_{ombb}} \f]
* - <b>Morphological Density::Volume density approx. enclosing ellipsoid</b>: Using a Principal Component Analysis (PCA)
- * of the spacial coordinates gives the three main axis of the mask. They correspond to the length of
+ * of the spatial coordinates gives the three main axis of the mask. They correspond to the length of
* a eclipse enclosing the mask. The length of the axis of the eclipse are given by the eigenvalues of the
* decomposition: \f$ a = 2 \sqrt{\lambda_1} \f$, \f$ b = 2 \sqrt{\lambda_2} \f$, and \f$ c = 2 \sqrt{\lambda_3} \f$
* with \f$\lambda_x\f$ being the sorted eigenvalues (higher number indicates larger values). The volume
* of the enclosing eclipse can be estimated by \f$ V_{aee} = 4 \pi a b c \f$:
* \f[ \textup{Volume density approx. enclosing ellipsoid}= \frac{V}{V_{aee}} \f]
* - <b>Morphological Density::Surface density approx. enclosing ellipsoid</b>: As for the previous
- * feature, the surface of the enclosing ellipsoid is used. To simplify the calulation of it, an approximation (20 iterations)
+ * feature, the surface of the enclosing ellipsoid is used. To simplify the calculation of it, an approximation (20 iterations)
* for the surface is used (\f$ \alpha = \sqrt{1-\frac{b^2}{a^2}} \f$, \f$ \beta = \sqrt{1-\frac{c^2}{a^2}} \f$):
* \f[ A_{aee} = 2 \pi a b \frac{\alpha^2 + \beta^2}{\alpha \beta} \sum_v^\infty \frac{(a \beta)^v}{1-a v^2} \f]
* \f[ \textup{Surface density approx. enclosing ellipsoid}= \frac{A}{A_{aee}} \f]
* - <b>Morphological Density::Volume density approx. minimum volume enclosing ellipsoid</b>:
* The volume is compared to the volume of the minimum enclosing ellipsoid. While this ellipsoid can be
* found by brute-force calculation, this is quite time-consuming. It is therefore estimated using
* Khachiyan's Algorithm (Khachiyan, Rounding of Polytopes in the Real Number Model of Computation. Mathematics of Operations Research 1996)
* The so-found ellipsoid is described by the lengths \f$a, b, c \f$ of its axis. The volume is then
* defined as \f$ V_{mvee} = 4 \pi a b c \f$ and the feature given by:
* \f[ \textup{Volume density approx. minimum volume enclosing ellipsoid}= \frac{V}{V_{mvee}} \f]
* - <b>Morphological Density::Surface density approx. minimum volume enclosing ellipsoid</b>: As for the previous
- * feature, the surface of the minimum volume enclosing ellipsoid is used. To simplify the calulation of it,
+ * feature, the surface of the minimum volume enclosing ellipsoid is used. To simplify the calculation of it,
* an approximation with 20 iterations instead of infinite iterations is used for the calculation of the
* the surface (\f$ \alpha = \sqrt{1-\frac{b^2}{a^2}} \f$, \f$ \beta = \sqrt{1-\frac{c^2}{a^2}} \f$):
* \f[ A_{mvee} = 2 \pi a b \frac{\alpha^2 + \beta^2}{\alpha \beta} \sum_v^\infty \frac{(a \beta)^v}{1-a v^2} \f]
* \f[ \textup{Surface density approx. minimum volume enclosing ellipsoid}= \frac{A}{A_{mvee}} \f]
* - <b>Morphological Density::Volume density convex hull</b>: The volume of the density
* hull is calculated using a convex mesh and then calculating the volume of this mesh \f$V_{convex} \f$.
* The feature is then calculated using:
* \f[ \textup{Volume density convex hull}= \frac{V}{V_{convex}} \f]
* - <b>Morphological Density::Surface density convex hull</b>: The surface of the density
* hull is calculated using a convex mesh and then calculating the surface of this mesh \f$A_{convex} \f$.
* The feature is then calculated using:
* \f[ \textup{Volume density convex hull}= \frac{A}{A_{convex}} \f]
* - <b>Morphological Density::Volume integrated intensity</b>: Integrated intensity is the
* average intensity times the volume. It is often used in conjunction with PET-images, where
* this feature is also called "total legion glycolysis". It is defined using the volume \f$V \f$, the
* number of masked voxels \f$ N_v \f$ and the intensity of each voxel \f$ x_i \f$:
* \f[ \textup{Volume integrated intensity}= V \frac{1}{N_v} \sum x_i \f]
* - <b>Morphological Density::Volume Moran's I index</b>: Moran's I index is an measure for
- * the spacial autocorrelation. It is defined using the inverse spacial distance between two voxels \f$i, j \f$ \f$w_{ij} \f$,
+ * the spatial autocorrelation. It is defined using the inverse spatial distance between two voxels \f$i, j \f$ \f$w_{ij} \f$,
* the number of masked voxels \f$ N_v \f$, the intensity of each voxel \f$ x_i \f$,
* and the mean intensity of all masked voxels \f$ \mu = \frac{1}{N_v} sum x_i \f$:
* \f[ \textup{Volume Moran's I index}= \frac{N_v}{\sum_i \sum_j w_{ij}} \frac{\sum_i \sum_j (x_i - \mu) (x_j -\mu)}{\sum_i (x_i - \mu)^2 } \enspace \enspace {; i \neq j} \f]
* - <b>Morphological Density::Volume Geary's C measure</b>: Geary's C meansure is similar to Moran's I index.
- * However, it is more sensitive to grey level differences and spacial autocorrelation:
- * the spacial autocorrelation. It is defined using the inverse spacial distance between two voxels \f$i, j \f$ \f$w_{ij} \f$,
+ * However, it is more sensitive to grey level differences and spatial autocorrelation:
+ * the spatial autocorrelation. It is defined using the inverse spatial distance between two voxels \f$i, j \f$ \f$w_{ij} \f$,
* the number of masked voxels \f$ N_v \f$, the intensity of each voxel \f$ x_i \f$,
* and the mean intensity of all masked voxels \f$ \mu = \frac{1}{N_v} sum x_i \f$:
* \f[ \textup{Volume Geary's C measure}= \frac{N_v - 1}{2 \sum_i \sum_j w_{ij}} \frac{ \sum_i \sum_j w_{ij} (x_i - x_j)^2 }{\sum_i (x_i - \mu)^2 } \enspace \enspace {; i \neq j} \f]
*/
class MITKCLUTILITIES_EXPORT GIFVolumetricDensityStatistics : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFVolumetricDensityStatistics,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFVolumetricDensityStatistics();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/include/mitkGIFVolumetricStatistics.h b/Modules/Classification/CLUtilities/include/mitkGIFVolumetricStatistics.h
index 19f3c0e42d..69b68f3da2 100644
--- a/Modules/Classification/CLUtilities/include/mitkGIFVolumetricStatistics.h
+++ b/Modules/Classification/CLUtilities/include/mitkGIFVolumetricStatistics.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 mitkGIFVolumetricStatistics_h
#define mitkGIFVolumetricStatistics_h
#include <mitkAbstractGlobalImageFeature.h>
#include <mitkBaseData.h>
#include <MitkCLUtilitiesExports.h>
namespace mitk
{
/**
- * \brief Calulates simpel shape-related features.
+ * \brief Calculates simpel shape-related features.
*
* This class can be used to calculate simple, shape-related features describing
- * a given segmentation. There are no parameters that can be externaly set.
+ * a given segmentation. There are no parameters that can be externally set.
*
* This feature calculator is activated by the option "<b>-volume</b>" or "<b>-vol</b>"
*
* The features are calculated based on a mask. It is assumed that the mask is
* of the type of an unsigned short image and all voxels with an value larger or equal
* to one are treated as masked. (Standard MITK mask)
*
* Some of the features are calculated twice using different methods. For voxel-
- * based approaches, the corresponding parameter is calcualted using the voxel,
+ * based approaches, the corresponding parameter is calculated using the voxel,
* for example the volume is then calculated by multiplying the volume of a
* single volume with the number of voxels in the mask. In the second method, the
- * mesh based appraoch, a mesh is created prior to the feature calculation which
+ * mesh based approach, a mesh is created prior to the feature calculation which
* is then done using the features.
*
* Another difference between two features might be the evaluation of invalid
* values within the image. There are two possibilities: By default, only those
* voxels are used with an valid intensity value, i.e. where the value is not
* infinite or NaN. The second possibility is not correcting for these voxels
* and only looking at the mask. Features that use these method are marked as
* "(uncorrected)"
*
* The resulting features are:
* - <b>Volumetric Features:: Voxel Volume</b>: \f$ V_{single\_voxel} \f$ , the volume of an single volume, calculated as the
* multiplication of the voxel spacing in all directions.
- * - <b>Volumetric Features:: Volume (voxel based)</b>: \f$ V_{voxel} \f$, the volume of the masked area. Calulated by
- * multiplying the numer of voxels with the Voxel Volume.
+ * - <b>Volumetric Features:: Volume (voxel based)</b>: \f$ V_{voxel} \f$, the volume of the masked area. Calculated by
+ * multiplying the number of voxels with the Voxel Volume.
* - <b>Volumetric Features:: Volume (mesh based)</b>: \f$ V_{shape} \f$, The volume based on the mesh-representation of
* the mask.
- * - <b>Volumetric Features:: Surface (voxel based)</b>: \f$ A_{voxel} \f$, the surface of the given mask. It is calulated
+ * - <b>Volumetric Features:: Surface (voxel based)</b>: \f$ A_{voxel} \f$, the surface of the given mask. It is calculated
* by summing the surfaces between a masked and an unmasked voxel.
* - <b>Volumetric Features:: Surface (mesh based)</b>: \f$ A_{mesh} \f$, the surface of the given mask calculated using
* the mask representation
* - <b>Volumetric Features:: Surface to volume ration (voxel based)</b>: The ratio between voxel based surface and voxel based
* volume given as: \f[ F_{av\_voxel}=\frac{A_{voxel}}{V_{voxel}} \f]
* - <b>Volumetric Features:: Surface to volume ration (mesh based)</b>: The ratio between voxel based surface and voxel based
* volume given as: \f[ F_{av\_mesh}=\frac{A_{mesh}}{V_{mesh}} \f]
* - <b>Volumetric Features:: Compactness 1 (voxel based)</b>:
* - <b>Volumetric Features:: Compactness 1 (mesh based)</b>:
The compatness is a measure how spheric a shape is given.
* Compactness 1 is defined as:
* \f[ F_{compactness\_1} = \frac{V}{\pi^{1/2} A^{3/2}}\f]
* - <b>Volumetric Features:: Compactness 1 old (voxel based)</b>:
* - <b>Volumetric Features:: Compactness 1 old (mesh based)</b>: Some implementations use a slightly different definition of
* compactness 1. Although this is most likely an error and leads to an non-dimensionless feature,
- * this defition is still calculated as:
+ * this definition is still calculated as:
* \f[ F_{compactness\_1\_old} = \frac{V}{\pi^{1/2} A^{2/3}}\f]
* - <b>Volumetric Features:: Compactness 2 (voxel based)</b>:
* - <b>Volumetric Features:: Compactness 2 (mesh based)</b>: The compatness is a measure how spheric a shape is given.
* Compactness 2 is defined as:
* \f[ F_{compactness\_1} = 36 \pi \frac{V^2}{A^3}\f]
* - <b>Volumetric Features::Sphericity (voxel based)</b>:
* - <b>Volumetric Features::Sphericity (mesh based)</b>: Sphericity is measure of how sphere-like a shape is:
* \f[ F_{sphericity} = \frac{(36 \pi V^2)^{1/3}}{A} \f]
* - <b>Volumetric Features::Asphericity (voxel based)</b>:
* - <b>Volumetric Features::Asphericity (mesh based)</b>: Sphericity is measure of how sphere-like a shape is:
* \f[ F_{asphericity} = \left(\frac{1}{36 \pi }\frac{(A^3}{V^2}\right)^{1/3} - 1 \f]
* - <b>Volumetric Features::Spherical disproportion (voxel based)</b>:
* - <b>Volumetric Features::Spherical disproportion (mesh based)</b>: Sphericity is measure of how sphere-like a shape is:
* \f[ F_{spherical\_disproportion} = \frac{A}{4\pi R^2}= \frac{A}{\left(36\pi V^2\right)^{1/3}} \f]
* - <b>Volumetric Features:: Maximum 3D diameter</b>: This is the largest distance between the centers of two voxels that
* are masked.
* - <b>Volumetric Features::Bounding box volume</b>: The bounding box volume is the volume of the smallest axis-aligned box
* that encapuslates all voxel centres.
* - <b>Volumetric Features::Centre of mass shift</b>:
* - <b>Volumetric Features::Centre of mass shift (uncorrected)</b>: This is the distance between two centres of mass,
* namely the geometric centre and the weighted centre. The geometric centre is the mean position
* of all masked voxels, and the weighted centre is the mean position if the position of each
* voxel is weighted according to its intensity.
* - <b>Volumetric Features::PCA Major Axis length</b>:
* - <b>Volumetric Features::PCA Major Axis length (uncorrected)</b>: A Principal component analysis (PCA) of the masekd voxel
* positions will give the main orientation and elongation of the masked area. The resulting
* eigenvectors of the PCA are sorted so that \f$ \lambda_{major}\geq \lambda_{minor} \geq \lambda_{least}\f$.
* The major axis length is defined as:
* \f[ F_{pca\_major} = 4 \sqrt{\lambda_{major}} \f]
* - <b>Volumetric Features::PCA Minor axis length</b>:
* - <b>Volumetric Features::PCA Minor axis length</b>: The Minor axis length is defined as:
* \f[ F_{pca\_minor} = 4 \sqrt{\lambda_{minor}} \f]
* - <b>Volumetric Features::PCA Least axis length</b>:
* - <b>Volumetric Features::PCA Least axis length</b>: The Minor axis length is defined as:
* \f[ F_{pca\_Least} = 4 \sqrt{\lambda_{Least}} \f]
*/
class MITKCLUTILITIES_EXPORT GIFVolumetricStatistics : public AbstractGlobalImageFeature
{
public:
mitkClassMacro(GIFVolumetricStatistics,AbstractGlobalImageFeature);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
GIFVolumetricStatistics();
FeatureListType CalculateFeatures(const Image* image, const Image* mask, const Image* maskNoNAN) override;
using Superclass::CalculateFeatures;
void AddArguments(mitkCommandLineParser& parser) const override;
protected:
FeatureListType DoCalculateFeatures(const Image* image, const Image* mask) override;
};
}
#endif
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix.cpp
index d806cf63a3..7fe7a3ec46 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix.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 <mitkGIFCooccurenceMatrix.h>
// MITK
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
// ITK
#include <itkEnhancedScalarImageToTextureFeaturesFilter.h>
#include <itkMinimumMaximumImageCalculator.h>
// STL
#include <sstream>
struct GIFCooccurenceMatrixConfiguration
{
double range;
unsigned int direction;
double MinimumIntensity;
bool UseMinimumIntensity;
double MaximumIntensity;
bool UseMaximumIntensity;
int Bins;
mitk::FeatureID id;
};
template<typename TPixel, unsigned int VImageDimension>
void
CalculateCoocurenceFeatures(const itk::Image<TPixel, VImageDimension>* itkImage, const mitk::Image* mask, mitk::GIFCooccurenceMatrix::FeatureListType & featureList, GIFCooccurenceMatrixConfiguration config)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<TPixel, VImageDimension> MaskType;
typedef itk::Statistics::EnhancedScalarImageToTextureFeaturesFilter<ImageType> FilterType;
typedef itk::MinimumMaximumImageCalculator<ImageType> MinMaxComputerType;
typedef typename FilterType::TextureFeaturesFilterType TextureFilterType;
typename MaskType::Pointer maskImage = MaskType::New();
mitk::CastToItkImage(mask, maskImage);
typename FilterType::Pointer filter = FilterType::New();
typename FilterType::OffsetVector::Pointer newOffset = FilterType::OffsetVector::New();
auto oldOffsets = filter->GetOffsets();
auto oldOffsetsIterator = oldOffsets->Begin();
while(oldOffsetsIterator != oldOffsets->End())
{
bool continueOuterLoop = false;
typename FilterType::OffsetType offset = oldOffsetsIterator->Value();
for (unsigned int i = 0; i < VImageDimension; ++i)
{
offset[i] *= config.range;
if (config.direction == i + 2 && offset[i] != 0)
{
continueOuterLoop = true;
}
}
if (config.direction == 1)
{
offset[0] = 0;
offset[1] = 0;
offset[2] = 1;
newOffset->push_back(offset);
break;
}
oldOffsetsIterator++;
if (continueOuterLoop)
continue;
newOffset->push_back(offset);
}
filter->SetOffsets(newOffset);
// All features are required
typename FilterType::FeatureNameVectorPointer requestedFeatures = FilterType::FeatureNameVector::New();
requestedFeatures->push_back(TextureFilterType::Energy);
requestedFeatures->push_back(TextureFilterType::Entropy);
requestedFeatures->push_back(TextureFilterType::Correlation);
requestedFeatures->push_back(TextureFilterType::InverseDifferenceMoment);
requestedFeatures->push_back(TextureFilterType::Inertia);
requestedFeatures->push_back(TextureFilterType::ClusterShade);
requestedFeatures->push_back(TextureFilterType::ClusterProminence);
requestedFeatures->push_back(TextureFilterType::HaralickCorrelation);
requestedFeatures->push_back(TextureFilterType::Autocorrelation);
requestedFeatures->push_back(TextureFilterType::Contrast);
requestedFeatures->push_back(TextureFilterType::Dissimilarity);
requestedFeatures->push_back(TextureFilterType::MaximumProbability);
requestedFeatures->push_back(TextureFilterType::InverseVariance);
requestedFeatures->push_back(TextureFilterType::Homogeneity1);
requestedFeatures->push_back(TextureFilterType::ClusterTendency);
requestedFeatures->push_back(TextureFilterType::Variance);
requestedFeatures->push_back(TextureFilterType::SumAverage);
requestedFeatures->push_back(TextureFilterType::SumEntropy);
requestedFeatures->push_back(TextureFilterType::SumVariance);
requestedFeatures->push_back(TextureFilterType::DifferenceAverage);
requestedFeatures->push_back(TextureFilterType::DifferenceEntropy);
requestedFeatures->push_back(TextureFilterType::DifferenceVariance);
requestedFeatures->push_back(TextureFilterType::InverseDifferenceMomentNormalized);
requestedFeatures->push_back(TextureFilterType::InverseDifferenceNormalized);
requestedFeatures->push_back(TextureFilterType::InverseDifference);
requestedFeatures->push_back(TextureFilterType::JointAverage);
requestedFeatures->push_back(TextureFilterType::FirstMeasureOfInformationCorrelation);
requestedFeatures->push_back(TextureFilterType::SecondMeasureOfInformationCorrelation);
typename MinMaxComputerType::Pointer minMaxComputer = MinMaxComputerType::New();
minMaxComputer->SetImage(itkImage);
minMaxComputer->Compute();
filter->SetInput(itkImage);
filter->SetMaskImage(maskImage);
filter->SetRequestedFeatures(requestedFeatures);
double min = minMaxComputer->GetMinimum() - 0.5;
double max = minMaxComputer->GetMaximum() + 0.5;
if (config.UseMinimumIntensity)
min = config.MinimumIntensity;
if (config.UseMaximumIntensity)
max = config.MaximumIntensity;
filter->SetPixelValueMinMax(min,max);
//filter->SetPixelValueMinMax(-1024,3096);
//filter->SetNumberOfBinsPerAxis(5);
filter->Update();
auto featureMeans = filter->GetFeatureMeans ();
auto featureStd = filter->GetFeatureStandardDeviations();
std::ostringstream ss;
ss << config.range;
std::string strRange = ss.str();
for (std::size_t i = 0; i < featureMeans->size(); ++i)
{
switch (i)
{
case TextureFilterType::Energy :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Energy Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Energy Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Entropy :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Entropy Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Entropy Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Correlation :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Correlation Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Correlation Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::InverseDifferenceMoment :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifferenceMoment Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifferenceMoment Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Inertia :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Inertia Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Inertia Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::ClusterShade :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. ClusterShade Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. ClusterShade Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::ClusterProminence :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. ClusterProminence Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. ClusterProminence Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::HaralickCorrelation :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. HaralickCorrelation Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. HaralickCorrelation Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Autocorrelation :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Autocorrelation Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Autocorrelation Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Contrast :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Contrast Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Contrast Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Dissimilarity :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Dissimilarity Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Dissimilarity Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::MaximumProbability :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. MaximumProbability Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. MaximumProbability Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::InverseVariance :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseVariance Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseVariance Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Homogeneity1 :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Homogeneity1 Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Homogeneity1 Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::ClusterTendency :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. ClusterTendency Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. ClusterTendency Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::Variance :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Variance Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. Variance Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::SumAverage :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SumAverage Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SumAverage Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::SumEntropy :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SumEntropy Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SumEntropy Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::SumVariance :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SumVariance Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SumVariance Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::DifferenceAverage :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. DifferenceAverage Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. DifferenceAverage Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::DifferenceEntropy :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. DifferenceEntropy Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. DifferenceEntropy Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::DifferenceVariance :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. DifferenceVariance Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. DifferenceVariance Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::InverseDifferenceMomentNormalized :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifferenceMomentNormalized Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifferenceMomentNormalized Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::InverseDifferenceNormalized :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifferenceNormalized Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifferenceNormalized Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::InverseDifference :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifference Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. InverseDifference Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::JointAverage :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. JointAverage Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. JointAverage Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::FirstMeasureOfInformationCorrelation :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. FirstMeasureOfInformationCorrelation Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. FirstMeasureOfInformationCorrelation Std."), featureStd->ElementAt(i)));
break;
case TextureFilterType::SecondMeasureOfInformationCorrelation :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SecondMeasureOfInformationCorrelation Means"), featureMeans->ElementAt(i)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, "co-occ. SecondMeasureOfInformationCorrelation Std."), featureStd->ElementAt(i)));
break;
default:
break;
}
}
}
mitk::GIFCooccurenceMatrix::GIFCooccurenceMatrix():
m_Ranges({ 1.0 })
{
SetShortName("deprecated-cooc");
- SetLongName("deprecated-cooccurence");
- SetFeatureClassName("Deprecated Co-occurence Features");
+ SetLongName("deprecated-cooccurrence");
+ SetFeatureClassName("Deprecated Co-occurrence Features");
}
void mitk::GIFCooccurenceMatrix::SetRanges(std::vector<double> ranges)
{
m_Ranges = ranges;
this->Modified();
}
void mitk::GIFCooccurenceMatrix::SetRange(double range)
{
m_Ranges.resize(1);
m_Ranges[0] = range;
this->Modified();
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFCooccurenceMatrix::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
InitializeQuantifier(image, mask);
for (const auto& range : m_Ranges)
{
MITK_INFO << "Start calculating coocurence with range " << range << "....";
GIFCooccurenceMatrixConfiguration config;
config.direction = GetDirection();
config.range = range;
config.MinimumIntensity = GetQuantifier()->GetMinimum();
config.MaximumIntensity = GetQuantifier()->GetMaximum();
config.UseMinimumIntensity = GetUseMinimumIntensity();
config.UseMaximumIntensity = GetUseMaximumIntensity();
config.Bins = GetBins();
config.id = this->CreateTemplateFeatureID(std::to_string(range), { {GetOptionPrefix() + "::range", range} });
AccessByItk_3(image, CalculateCoocurenceFeatures, mask, featureList, config);
MITK_INFO << "Finished calculating coocurence with range " << range << "....";
}
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFCooccurenceMatrix::CalculateFeatures(const Image* image, const Image*, const Image* maskNoNAN)
{
return Superclass::CalculateFeatures(image, maskNoNAN);
}
void mitk::GIFCooccurenceMatrix::AddArguments(mitkCommandLineParser& parser) const
{
std::string name = this->GetOptionPrefix();
- parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurence matrix", "calculates Co-occurence based features", us::Any());
+ parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurrence matrix", "calculates Co-occurrence based features", us::Any());
parser.addArgument(name + "::range", name + "::range", mitkCommandLineParser::String, "Cooc Range", "Define the range that is used (Semicolon-separated)", us::Any());
}
std::string mitk::GIFCooccurenceMatrix::GenerateLegacyFeatureNamePart(const FeatureID& id) const
{
auto result = id.name;
result.insert(7, " (" + id.parameters.at(this->GetOptionPrefix() + "::range").ToString() + ")");
return result;
}
std::string mitk::GIFCooccurenceMatrix::GenerateLegacyFeatureEncoding(const FeatureID& /*id*/) const
{
//deprecated GIFCooccurenceMatrix does not support feature encoding
return "";
}
void mitk::GIFCooccurenceMatrix::ConfigureSettingsByParameters(const ParametersType& parameters)
{
auto name = GetOptionPrefix() + "::range";
if (parameters.count(name))
{
m_Ranges = SplitDouble(parameters.at(name).ToString(), ';');
}
}
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix2.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix2.cpp
index e64aee3fb4..b2fe3c7d3b 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix2.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFCooccurenceMatrix2.cpp
@@ -1,702 +1,702 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkGIFCooccurenceMatrix2.h>
// MITK
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
// ITK
#include <itkEnhancedScalarImageToTextureFeaturesFilter.h>
#include <itkShapedNeighborhoodIterator.h>
#include <itkImageRegionConstIterator.h>
// STL
#include <sstream>
#include <cmath>
namespace mitk
{
struct GIFCooccurenceMatrix2Configuration
{
double range;
unsigned int direction;
double MinimumIntensity;
double MaximumIntensity;
int Bins;
FeatureID id;
};
struct CoocurenceMatrixHolder
{
public:
CoocurenceMatrixHolder(double min, double max, int number);
int IntensityToIndex(double intensity);
double IndexToMinIntensity(int index);
double IndexToMeanIntensity(int index);
double IndexToMaxIntensity(int index);
double m_MinimumRange;
double m_MaximumRange;
double m_Stepsize;
int m_NumberOfBins;
Eigen::MatrixXd m_Matrix;
};
struct CoocurenceMatrixFeatures
{
CoocurenceMatrixFeatures() :
JointMaximum(0),
JointAverage(0),
JointVariance(0),
JointEntropy(0),
RowMaximum(0),
RowAverage(0),
RowVariance(0),
RowEntropy(0),
FirstRowColumnEntropy(0),
SecondRowColumnEntropy(0),
DifferenceAverage(0),
DifferenceVariance(0),
DifferenceEntropy(0),
SumAverage(0),
SumVariance(0),
SumEntropy(0),
AngularSecondMoment(0),
Contrast(0),
Dissimilarity(0),
InverseDifference(0),
InverseDifferenceNormalised(0),
InverseDifferenceMoment(0),
InverseDifferenceMomentNormalised(0),
InverseVariance(0),
Correlation(0),
Autocorrelation(0),
ClusterTendency(0),
ClusterShade(0),
ClusterProminence(0),
FirstMeasureOfInformationCorrelation(0),
SecondMeasureOfInformationCorrelation(0)
{
}
public:
double JointMaximum;
double JointAverage;
double JointVariance;
double JointEntropy;
double RowMaximum;
double RowAverage;
double RowVariance;
double RowEntropy;
double FirstRowColumnEntropy;
double SecondRowColumnEntropy;
double DifferenceAverage;
double DifferenceVariance;
double DifferenceEntropy;
double SumAverage;
double SumVariance;
double SumEntropy;
double AngularSecondMoment;
double Contrast;
double Dissimilarity;
double InverseDifference;
double InverseDifferenceNormalised;
double InverseDifferenceMoment;
double InverseDifferenceMomentNormalised;
double InverseVariance;
double Correlation;
double Autocorrelation;
double ClusterTendency;
double ClusterShade;
double ClusterProminence;
double FirstMeasureOfInformationCorrelation;
double SecondMeasureOfInformationCorrelation;
};
}
static
void MatrixFeaturesTo(const mitk::CoocurenceMatrixFeatures& features,
const std::string& prefix,
const mitk::GIFCooccurenceMatrix2Configuration& config,
mitk::GIFCooccurenceMatrix2::FeatureListType& featureList)
{
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Joint Maximum"), features.JointMaximum));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Joint Average"), features.JointAverage));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Joint Variance"), features.JointVariance));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Joint Entropy"), features.JointEntropy));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Difference Average"), features.DifferenceAverage));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Difference Variance"), features.DifferenceVariance));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Difference Entropy"), features.DifferenceEntropy));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Sum Average"), features.SumAverage));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Sum Variance"), features.SumVariance));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Sum Entropy"), features.SumEntropy));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Angular Second Moment"), features.AngularSecondMoment));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Contrast"), features.Contrast));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Dissimilarity"), features.Dissimilarity));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Inverse Difference"), features.InverseDifference));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Inverse Difference Normalized"), features.InverseDifferenceNormalised));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Inverse Difference Moment"), features.InverseDifferenceMoment));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Inverse Difference Moment Normalized"), features.InverseDifferenceMomentNormalised));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Inverse Variance"), features.InverseVariance));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Correlation"), features.Correlation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Autocorrelation"), features.Autocorrelation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Cluster Tendency"), features.ClusterTendency));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Cluster Shade"), features.ClusterShade));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Cluster Prominence"), features.ClusterProminence));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "First Measure of Information Correlation"), features.FirstMeasureOfInformationCorrelation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Second Measure of Information Correlation"), features.SecondMeasureOfInformationCorrelation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Row Maximum"), features.RowMaximum));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Row Average"), features.RowAverage));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Row Variance"), features.RowVariance));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Row Entropy"), features.RowEntropy));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "First Row-Column Entropy"), features.FirstRowColumnEntropy));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(config.id, prefix + "Second Row-Column Entropy"), features.SecondRowColumnEntropy));
}
static
void CalculateMeanAndStdDevFeatures(std::vector<mitk::CoocurenceMatrixFeatures> featureList,
mitk::CoocurenceMatrixFeatures &meanFeature,
mitk::CoocurenceMatrixFeatures &stdFeature);
static
void NormalizeMatrixFeature(mitk::CoocurenceMatrixFeatures &features,
std::size_t number);
mitk::CoocurenceMatrixHolder::CoocurenceMatrixHolder(double min, double max, int number) :
m_MinimumRange(min),
m_MaximumRange(max),
m_NumberOfBins(number)
{
m_Matrix.resize(number, number);
m_Matrix.fill(0);
m_Stepsize = (max - min) / (number);
}
int mitk::CoocurenceMatrixHolder::IntensityToIndex(double intensity)
{
int index = std::floor((intensity - m_MinimumRange) / m_Stepsize);
return std::max(0, std::min(index, m_NumberOfBins - 1));
}
double mitk::CoocurenceMatrixHolder::IndexToMinIntensity(int index)
{
return m_MinimumRange + index * m_Stepsize;
}
double mitk::CoocurenceMatrixHolder::IndexToMeanIntensity(int index)
{
return m_MinimumRange + (index+0.5) * m_Stepsize;
}
double mitk::CoocurenceMatrixHolder::IndexToMaxIntensity(int index)
{
return m_MinimumRange + (index + 1) * m_Stepsize;
}
template<typename TPixel, unsigned int VImageDimension>
void
CalculateCoOcMatrix(const itk::Image<TPixel, VImageDimension>* itkImage,
const itk::Image<unsigned short, VImageDimension>* mask,
itk::Offset<VImageDimension> offset,
int range,
mitk::CoocurenceMatrixHolder &holder)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<unsigned short, VImageDimension> MaskImageType;
typedef itk::ShapedNeighborhoodIterator<ImageType> ShapeIterType;
typedef itk::ShapedNeighborhoodIterator<MaskImageType> ShapeMaskIterType;
typedef itk::ImageRegionConstIterator<ImageType> ConstIterType;
typedef itk::ImageRegionConstIterator<MaskImageType> ConstMaskIterType;
itk::Size<VImageDimension> radius;
radius.Fill(range+1);
ShapeIterType imageOffsetIter(radius, itkImage, itkImage->GetLargestPossibleRegion());
ShapeMaskIterType maskOffsetIter(radius, mask, mask->GetLargestPossibleRegion());
imageOffsetIter.ActivateOffset(offset);
maskOffsetIter.ActivateOffset(offset);
ConstIterType imageIter(itkImage, itkImage->GetLargestPossibleRegion());
ConstMaskIterType maskIter(mask, mask->GetLargestPossibleRegion());
// iterator.GetIndex() + ci.GetNeighborhoodOffset()
auto region = mask->GetLargestPossibleRegion();
while (!maskIter.IsAtEnd())
{
auto ciMask = maskOffsetIter.Begin();
auto ciValue = imageOffsetIter.Begin();
if (maskIter.Value() > 0 &&
ciMask.Get() > 0 &&
imageIter.Get() == imageIter.Get() &&
ciValue.Get() == ciValue.Get() &&
region.IsInside(maskOffsetIter.GetIndex() + ciMask.GetNeighborhoodOffset()))
{
int i = holder.IntensityToIndex(imageIter.Get());
int j = holder.IntensityToIndex(ciValue.Get());
holder.m_Matrix(i, j) += 1;
holder.m_Matrix(j, i) += 1;
}
++imageOffsetIter;
++maskOffsetIter;
++imageIter;
++maskIter;
}
}
void CalculateFeatures(
mitk::CoocurenceMatrixHolder &holder,
mitk::CoocurenceMatrixFeatures & results
)
{
auto pijMatrix = holder.m_Matrix;
auto piMatrix = holder.m_Matrix;
auto pjMatrix = holder.m_Matrix;
double Ng = holder.m_NumberOfBins;
int NgSize = holder.m_NumberOfBins;
pijMatrix /= pijMatrix.sum();
piMatrix.rowwise().normalize();
pjMatrix.colwise().normalize();
for (int i = 0; i < holder.m_NumberOfBins; ++i)
for (int j = 0; j < holder.m_NumberOfBins; ++j)
{
if (pijMatrix(i, j) != pijMatrix(i, j))
pijMatrix(i, j) = 0;
if (piMatrix(i, j) != piMatrix(i, j))
piMatrix(i, j) = 0;
if (pjMatrix(i, j) != pjMatrix(i, j))
pjMatrix(i, j) = 0;
}
Eigen::VectorXd piVector = pijMatrix.colwise().sum();
Eigen::VectorXd pjVector = pijMatrix.rowwise().sum();
double sigmai = 0;;
for (int i = 0; i < holder.m_NumberOfBins; ++i)
{
double iInt = i + 1;// holder.IndexToMeanIntensity(i);
results.RowAverage += iInt * piVector(i);
if (piVector(i) > 0)
{
results.RowEntropy -= piVector(i) * std::log(piVector(i)) / std::log(2);
}
}
for (int i = 0; i < holder.m_NumberOfBins; ++i)
{
double iInt = i + 1; // holder.IndexToMeanIntensity(i);
results.RowVariance += (iInt - results.RowAverage)*(iInt - results.RowAverage) * piVector(i);
}
results.RowMaximum = piVector.maxCoeff();
sigmai = std::sqrt(results.RowVariance);
Eigen::VectorXd pimj(NgSize);
pimj.fill(0);
Eigen::VectorXd pipj(2*NgSize);
pipj.fill(0);
results.JointMaximum += pijMatrix.maxCoeff();
for (int i = 0; i < holder.m_NumberOfBins; ++i)
{
for (int j = 0; j < holder.m_NumberOfBins; ++j)
{
//double iInt = holder.IndexToMeanIntensity(i);
//double jInt = holder.IndexToMeanIntensity(j);
double iInt = i + 1;// holder.IndexToMeanIntensity(i);
double jInt = j + 1;// holder.IndexToMeanIntensity(j);
double pij = pijMatrix(i, j);
int deltaK = (i - j)>0?(i-j) : (j-i);
pimj(deltaK) += pij;
pipj(i + j) += pij;
results.JointAverage += iInt * pij;
if (pij > 0)
{
results.JointEntropy -= pij * std::log(pij) / std::log(2);
results.FirstRowColumnEntropy -= pij * std::log(piVector(i)*pjVector(j)) / std::log(2);
}
if (piVector(i) > 0 && pjVector(j) > 0 )
{
results.SecondRowColumnEntropy -= piVector(i)*pjVector(j) * std::log(piVector(i)*pjVector(j)) / std::log(2);
}
results.AngularSecondMoment += pij*pij;
results.Contrast += (iInt - jInt)* (iInt - jInt) * pij;
results.Dissimilarity += std::abs<double>(iInt - jInt) * pij;
results.InverseDifference += pij / (1 + (std::abs<double>(iInt - jInt)));
results.InverseDifferenceNormalised += pij / (1 + (std::abs<double>(iInt - jInt) / Ng));
results.InverseDifferenceMoment += pij / (1 + (iInt - jInt)*(iInt - jInt));
results.InverseDifferenceMomentNormalised += pij / (1 + (iInt - jInt)*(iInt - jInt)/Ng/Ng);
results.Autocorrelation += iInt*jInt * pij;
double cluster = (iInt + jInt - 2 * results.RowAverage);
results.ClusterTendency += cluster*cluster * pij;
results.ClusterShade += cluster*cluster*cluster * pij;
results.ClusterProminence += cluster*cluster*cluster*cluster * pij;
if (iInt != jInt)
{
results.InverseVariance += pij / (iInt - jInt) / (iInt - jInt);
}
}
}
results.Correlation = 1 / sigmai / sigmai * (-results.RowAverage*results.RowAverage+ results.Autocorrelation);
results.FirstMeasureOfInformationCorrelation = (results.JointEntropy - results.FirstRowColumnEntropy) / results.RowEntropy;
if (results.JointEntropy < results.SecondRowColumnEntropy)
{
results.SecondMeasureOfInformationCorrelation = sqrt(1 - exp(-2 * (results.SecondRowColumnEntropy - results.JointEntropy)));
}
else
{
results.SecondMeasureOfInformationCorrelation = 0;
}
for (int i = 0; i < holder.m_NumberOfBins; ++i)
{
for (int j = 0; j < holder.m_NumberOfBins; ++j)
{
//double iInt = holder.IndexToMeanIntensity(i);
//double jInt = holder.IndexToMeanIntensity(j);
double iInt = i + 1;
double pij = pijMatrix(i, j);
results.JointVariance += (iInt - results.JointAverage)* (iInt - results.JointAverage)*pij;
}
}
for (int k = 0; k < NgSize; ++k)
{
results.DifferenceAverage += k* pimj(k);
if (pimj(k) > 0)
{
results.DifferenceEntropy -= pimj(k) * log(pimj(k)) / std::log(2);
}
}
for (int k = 0; k < NgSize; ++k)
{
results.DifferenceVariance += (results.DifferenceAverage-k)* (results.DifferenceAverage-k)*pimj(k);
}
for (int k = 0; k <2* NgSize ; ++k)
{
results.SumAverage += (2+k)* pipj(k);
if (pipj(k) > 0)
{
results.SumEntropy -= pipj(k) * log(pipj(k)) / std::log(2);
}
}
for (int k = 0; k < 2*NgSize; ++k)
{
results.SumVariance += (2+k - results.SumAverage)* (2+k - results.SumAverage)*pipj(k);
}
//MITK_INFO << std::endl << holder.m_Matrix;
//MITK_INFO << std::endl << pijMatrix;
//MITK_INFO << std::endl << piMatrix;
//MITK_INFO << std::endl << pjMatrix;
//for (int i = 0; i < holder.m_NumberOfBins; ++i)
//{
// MITK_INFO << "Bin " << i << " Min: " << holder.IndexToMinIntensity(i) << " Max: " << holder.IndexToMaxIntensity(i);
//}
//MITK_INFO << pimj;
//MITK_INFO << pipj;
}
template<typename TPixel, unsigned int VImageDimension>
void
CalculateCoocurenceFeatures(const itk::Image<TPixel, VImageDimension>* itkImage, const mitk::Image* mask, mitk::GIFCooccurenceMatrix2::FeatureListType & featureList, mitk::GIFCooccurenceMatrix2Configuration config)
{
typedef itk::Image<unsigned short, VImageDimension> MaskType;
typedef itk::Neighborhood<TPixel, VImageDimension > NeighborhoodType;
typedef itk::Offset<VImageDimension> OffsetType;
///////////////////////////////////////////////////////////////////////////////////////////////
double rangeMin = config.MinimumIntensity;
double rangeMax = config.MaximumIntensity;
int numberOfBins = config.Bins;
typename MaskType::Pointer maskImage = MaskType::New();
mitk::CastToItkImage(mask, maskImage);
//Find possible directions
std::vector < itk::Offset<VImageDimension> > offsetVector;
NeighborhoodType hood;
hood.SetRadius(1);
unsigned int centerIndex = hood.GetCenterNeighborhoodIndex();
OffsetType offset;
for (unsigned int d = 0; d < centerIndex; d++)
{
offset = hood.GetOffset(d);
bool useOffset = true;
for (unsigned int i = 0; i < VImageDimension; ++i)
{
offset[i] *= config.range;
if (config.direction == i + 2 && offset[i] != 0)
{
useOffset = false;
}
}
if (useOffset)
{
offsetVector.push_back(offset);
}
}
if (config.direction == 1)
{
offsetVector.clear();
offset[0] = 0;
offset[1] = 0;
offset[2] = 1;
}
std::vector<mitk::CoocurenceMatrixFeatures> resultVector;
mitk::CoocurenceMatrixHolder holderOverall(rangeMin, rangeMax, numberOfBins);
mitk::CoocurenceMatrixFeatures overallFeature;
for (std::size_t i = 0; i < offsetVector.size(); ++i)
{
if (config.direction > 1)
{
if (offsetVector[i][config.direction - 2] != 0)
{
continue;
}
}
offset = offsetVector[i];
mitk::CoocurenceMatrixHolder holder(rangeMin, rangeMax, numberOfBins);
mitk::CoocurenceMatrixFeatures coocResults;
CalculateCoOcMatrix<TPixel, VImageDimension>(itkImage, maskImage, offset, config.range, holder);
holderOverall.m_Matrix += holder.m_Matrix;
CalculateFeatures(holder, coocResults);
resultVector.push_back(coocResults);
}
CalculateFeatures(holderOverall, overallFeature);
//NormalizeMatrixFeature(overallFeature, offsetVector.size());
mitk::CoocurenceMatrixFeatures featureMean;
mitk::CoocurenceMatrixFeatures featureStd;
CalculateMeanAndStdDevFeatures(resultVector, featureMean, featureStd);
std::ostringstream ss;
ss << config.range;
std::string strRange = ss.str();
MatrixFeaturesTo(overallFeature, "Overall ", config, featureList);
MatrixFeaturesTo(featureMean, "Mean ", config, featureList);
MatrixFeaturesTo(featureStd, "Std.Dev. ", config, featureList);
}
static
void CalculateMeanAndStdDevFeatures(std::vector<mitk::CoocurenceMatrixFeatures> featureList,
mitk::CoocurenceMatrixFeatures &meanFeature,
mitk::CoocurenceMatrixFeatures &stdFeature)
{
#define ADDFEATURE(a) \
if ( ! (featureList[i].a == featureList[i].a)) featureList[i].a = 0; \
meanFeature.a += featureList[i].a;stdFeature.a += featureList[i].a*featureList[i].a
#define CALCVARIANCE(a) stdFeature.a =sqrt(stdFeature.a - meanFeature.a*meanFeature.a)
for (std::size_t i = 0; i < featureList.size(); ++i)
{
ADDFEATURE(JointMaximum);
ADDFEATURE(JointAverage);
ADDFEATURE(JointVariance);
ADDFEATURE(JointEntropy);
ADDFEATURE(RowMaximum);
ADDFEATURE(RowAverage);
ADDFEATURE(RowVariance);
ADDFEATURE(RowEntropy);
ADDFEATURE(FirstRowColumnEntropy);
ADDFEATURE(SecondRowColumnEntropy);
ADDFEATURE(DifferenceAverage);
ADDFEATURE(DifferenceVariance);
ADDFEATURE(DifferenceEntropy);
ADDFEATURE(SumAverage);
ADDFEATURE(SumVariance);
ADDFEATURE(SumEntropy);
ADDFEATURE(AngularSecondMoment);
ADDFEATURE(Contrast);
ADDFEATURE(Dissimilarity);
ADDFEATURE(InverseDifference);
ADDFEATURE(InverseDifferenceNormalised);
ADDFEATURE(InverseDifferenceMoment);
ADDFEATURE(InverseDifferenceMomentNormalised);
ADDFEATURE(InverseVariance);
ADDFEATURE(Correlation);
ADDFEATURE(Autocorrelation);
ADDFEATURE(ClusterShade);
ADDFEATURE(ClusterTendency);
ADDFEATURE(ClusterProminence);
ADDFEATURE(FirstMeasureOfInformationCorrelation);
ADDFEATURE(SecondMeasureOfInformationCorrelation);
}
NormalizeMatrixFeature(meanFeature, featureList.size());
NormalizeMatrixFeature(stdFeature, featureList.size());
CALCVARIANCE(JointMaximum);
CALCVARIANCE(JointAverage);
CALCVARIANCE(JointVariance);
CALCVARIANCE(JointEntropy);
CALCVARIANCE(RowMaximum);
CALCVARIANCE(RowAverage);
CALCVARIANCE(RowVariance);
CALCVARIANCE(RowEntropy);
CALCVARIANCE(FirstRowColumnEntropy);
CALCVARIANCE(SecondRowColumnEntropy);
CALCVARIANCE(DifferenceAverage);
CALCVARIANCE(DifferenceVariance);
CALCVARIANCE(DifferenceEntropy);
CALCVARIANCE(SumAverage);
CALCVARIANCE(SumVariance);
CALCVARIANCE(SumEntropy);
CALCVARIANCE(AngularSecondMoment);
CALCVARIANCE(Contrast);
CALCVARIANCE(Dissimilarity);
CALCVARIANCE(InverseDifference);
CALCVARIANCE(InverseDifferenceNormalised);
CALCVARIANCE(InverseDifferenceMoment);
CALCVARIANCE(InverseDifferenceMomentNormalised);
CALCVARIANCE(InverseVariance);
CALCVARIANCE(Correlation);
CALCVARIANCE(Autocorrelation);
CALCVARIANCE(ClusterShade);
CALCVARIANCE(ClusterTendency);
CALCVARIANCE(ClusterProminence);
CALCVARIANCE(FirstMeasureOfInformationCorrelation);
CALCVARIANCE(SecondMeasureOfInformationCorrelation);
#undef ADDFEATURE
#undef CALCVARIANCE
}
static
void NormalizeMatrixFeature(mitk::CoocurenceMatrixFeatures &features,
std::size_t number)
{
features.JointMaximum = features.JointMaximum / number;
features.JointAverage = features.JointAverage / number;
features.JointVariance = features.JointVariance / number;
features.JointEntropy = features.JointEntropy / number;
features.RowMaximum = features.RowMaximum / number;
features.RowAverage = features.RowAverage / number;
features.RowVariance = features.RowVariance / number;
features.RowEntropy = features.RowEntropy / number;
features.FirstRowColumnEntropy = features.FirstRowColumnEntropy / number;
features.SecondRowColumnEntropy = features.SecondRowColumnEntropy / number;
features.DifferenceAverage = features.DifferenceAverage / number;
features.DifferenceVariance = features.DifferenceVariance / number;
features.DifferenceEntropy = features.DifferenceEntropy / number;
features.SumAverage = features.SumAverage / number;
features.SumVariance = features.SumVariance / number;
features.SumEntropy = features.SumEntropy / number;
features.AngularSecondMoment = features.AngularSecondMoment / number;
features.Contrast = features.Contrast / number;
features.Dissimilarity = features.Dissimilarity / number;
features.InverseDifference = features.InverseDifference / number;
features.InverseDifferenceNormalised = features.InverseDifferenceNormalised / number;
features.InverseDifferenceMoment = features.InverseDifferenceMoment / number;
features.InverseDifferenceMomentNormalised = features.InverseDifferenceMomentNormalised / number;
features.InverseVariance = features.InverseVariance / number;
features.Correlation = features.Correlation / number;
features.Autocorrelation = features.Autocorrelation / number;
features.ClusterShade = features.ClusterShade / number;
features.ClusterTendency = features.ClusterTendency / number;
features.ClusterProminence = features.ClusterProminence / number;
features.FirstMeasureOfInformationCorrelation = features.FirstMeasureOfInformationCorrelation / number;
features.SecondMeasureOfInformationCorrelation = features.SecondMeasureOfInformationCorrelation / number;
}
mitk::GIFCooccurenceMatrix2::GIFCooccurenceMatrix2():
m_Ranges({ 1.0 })
{
SetShortName("cooc2");
SetLongName("cooccurence2");
SetFeatureClassName("Co-occurenced Based Features");
}
void mitk::GIFCooccurenceMatrix2::SetRanges(std::vector<double> ranges)
{
m_Ranges = ranges;
this->Modified();
}
void mitk::GIFCooccurenceMatrix2::SetRange(double range)
{
m_Ranges.resize(1);
m_Ranges[0] = range;
this->Modified();
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFCooccurenceMatrix2::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
InitializeQuantifier(image, mask);
for (const auto& range: m_Ranges)
{
MITK_INFO << "Start calculating coocurence with range " << range << "....";
GIFCooccurenceMatrix2Configuration config;
config.direction = GetDirection();
config.range = range;
config.MinimumIntensity = GetQuantifier()->GetMinimum();
config.MaximumIntensity = GetQuantifier()->GetMaximum();
config.Bins = GetQuantifier()->GetBins();
config.id = this->CreateTemplateFeatureID(std::to_string(range), { {GetOptionPrefix() + "::range", range} });
AccessByItk_3(image, CalculateCoocurenceFeatures, mask, featureList, config);
MITK_INFO << "Finished calculating coocurence with range " << range << "....";
}
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFCooccurenceMatrix2::CalculateFeatures(const Image* image, const Image*, const Image* maskNoNAN)
{
return Superclass::CalculateFeatures(image, maskNoNAN);
}
void mitk::GIFCooccurenceMatrix2::AddArguments(mitkCommandLineParser &parser) const
{
this->AddQuantifierArguments(parser);
std::string name = this->GetOptionPrefix();
- parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurence matrix", "calculates Co-occurence based features (new implementation)", us::Any());
+ parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurrence matrix", "calculates Co-occurrence based features (new implementation)", us::Any());
parser.addArgument(name+"::range", name+"::range", mitkCommandLineParser::String, "Cooc 2 Range", "Define the range that is used (Semicolon-separated)", us::Any());
}
std::string mitk::GIFCooccurenceMatrix2::GenerateLegacyFeatureEncoding(const FeatureID& id) const
{
return QuantifierParameterString() + "_Range-" + id.parameters.at(this->GetOptionPrefix()+"::range").ToString();
}
void mitk::GIFCooccurenceMatrix2::ConfigureSettingsByParameters(const ParametersType& parameters)
{
auto name = GetOptionPrefix()+"::range";
if (parameters.count(name))
{
m_Ranges = SplitDouble(parameters.at(name).ToString(), ';');
}
}
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderHistogramStatistics.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderHistogramStatistics.cpp
index dd2d2b789f..8913274d43 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderHistogramStatistics.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFFirstOrderHistogramStatistics.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.
============================================================================*/
#include <mitkGIFFirstOrderHistogramStatistics.h>
// MITK
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
// ITK
#include <itkLabelStatisticsImageFilter.h>
#include <itkMinimumMaximumImageCalculator.h>
#include <itkImageRegionConstIteratorWithIndex.h>
// STL
#include <sstream>
#include <limits>
#include <math.h>
namespace mitk
{
struct GIFFirstOrderHistogramStatisticsConfiguration
{
double MinimumIntensity;
double MaximumIntensity;
int Bins;
FeatureID id;
};
}
#define GET_VARIABLE_INDEX(value) \
mv[0] = value; \
histogram->GetIndex(mv, resultingIndex);
template<typename TPixel, unsigned int VImageDimension>
void
CalculateFirstOrderHistogramStatistics(const itk::Image<TPixel, VImageDimension>* itkImage, const mitk::Image* mask, mitk::GIFFirstOrderHistogramStatistics::FeatureListType & featureList, mitk::GIFFirstOrderHistogramStatisticsConfiguration 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;
typename MaskType::Pointer maskImage = MaskType::New();
mitk::CastToItkImage(mask, maskImage);
typename FilterType::Pointer labelStatisticsImageFilter = FilterType::New();
labelStatisticsImageFilter->SetInput(itkImage);
labelStatisticsImageFilter->SetLabelInput(maskImage);
labelStatisticsImageFilter->SetUseHistograms(true);
labelStatisticsImageFilter->SetHistogramParameters(params.Bins, params.MinimumIntensity, params.MaximumIntensity);
labelStatisticsImageFilter->Update();
typename HistogramType::MeasurementVectorType mv(1);
mv[0] = 4.1;
typename HistogramType::IndexType resultingIndex;
auto histogram = labelStatisticsImageFilter->GetHistogram(1);
double meanValue = 0; // labelStatisticsImageFilter->GetMean(1);
GET_VARIABLE_INDEX(meanValue);
double meanIndex = 0; // resultingIndex[0];
double medianValue = labelStatisticsImageFilter->GetMedian(1);
GET_VARIABLE_INDEX(medianValue);
double medianIndex = resultingIndex[0];
double minimumValue = labelStatisticsImageFilter->GetMinimum(1);
GET_VARIABLE_INDEX(minimumValue);
double minimumIndex = resultingIndex[0];
double p10Value = histogram->Quantile(0, 0.10);
GET_VARIABLE_INDEX(p10Value);
double p10Index = resultingIndex[0];
double p25Value = histogram->Quantile(0, 0.25);
GET_VARIABLE_INDEX(p25Value);
double p25Index = resultingIndex[0];
double p75Value = histogram->Quantile(0, 0.75);
GET_VARIABLE_INDEX(p75Value);
double p75Index = resultingIndex[0];
double p90Value = histogram->Quantile(0, 0.90);
GET_VARIABLE_INDEX(p90Value);
double p90Index = resultingIndex[0];
double maximumValue = labelStatisticsImageFilter->GetMaximum(1);
GET_VARIABLE_INDEX(maximumValue);
double maximumIndex = resultingIndex[0];
double Log2 = log(2);
HIndexType index;
HIndexType index2;
index.SetSize(1);
index2.SetSize(1);
double binWidth = histogram->GetBinMax(0, 0) - histogram->GetBinMin(0, 0);
double count = labelStatisticsImageFilter->GetCount(1);
double robustMeanValue = 0;
double robustMeanIndex = 0;
double robustCount = 0;
for (int i = 0; i < (int)(histogram->GetSize(0)); ++i)
{
index[0] = i;
double frequence = histogram->GetFrequency(index);
double probability = frequence / count;
double voxelValue = histogram->GetBinMin(0, i) + binWidth * 0.5;
meanValue += probability * voxelValue;
meanIndex += probability * (i);
if ((i >= p10Index) && (i <= p90Index))
{
robustMeanValue += frequence * voxelValue;
robustMeanIndex += frequence * i;
robustCount += frequence;
}
}
robustMeanValue /= robustCount;
robustMeanIndex /= robustCount;
double varianceValue = 0;
double varianceIndex = 0;
double skewnessValue = 0;
double skewnessIndex = 0;
double kurtosisValue = 0;
double kurtosisIndex = 0;
double modeValue = 0;
double modeIndex = 0;
double modeFrequence = 0;
double meanAbsoluteDeviationValue = 0;
double meanAbsoluteDeviationIndex = 0;
double robustMeanAbsoluteDeviationValue = 0;
double robustMeanAbsoluteDeivationIndex = 0;
double medianAbsoluteDeviationValue = 0;
double medianAbsoluteDeviationIndex = 0;
double coefficientOfVariationValue = 0;
double coefficientOfVariationIndex = 0;
double quantileCoefficientOfDispersionValue = 0;
double quantileCoefficientOfDispersionIndex = 0;
double entropyValue = 0;
double entropyIndex = 0;
double uniformityValue = 0;
double uniformityIndex = 0;
double maximumGradientValue = std::numeric_limits<double>::min();
double maximumGradientIndex = 0;
double minimumGradientValue = std::numeric_limits<double>::max();
double minimumGradientIndex = 0;
double gradient = 0;
for (int i = 0; i < (int)(histogram->GetSize(0)); ++i)
{
index[0] = i;
double frequence = histogram->GetFrequency(index);
double probability = frequence / count;
double voxelValue = histogram->GetBinMin(0, i) + binWidth * 0.5;
double deltaValue = (voxelValue - meanValue);
double deltaIndex = (i - meanIndex);
varianceValue += probability * deltaValue * deltaValue;
varianceIndex += probability * deltaIndex * deltaIndex;
skewnessValue += probability * deltaValue * deltaValue * deltaValue;
skewnessIndex += probability * deltaIndex * deltaIndex * deltaIndex;
kurtosisValue += probability * deltaValue * deltaValue * deltaValue * deltaValue;
kurtosisIndex += probability * deltaIndex * deltaIndex * deltaIndex * deltaIndex;
if (modeFrequence < frequence)
{
modeFrequence = frequence;
modeValue = voxelValue;
modeIndex = i;
}
meanAbsoluteDeviationValue += probability * std::abs(deltaValue);
meanAbsoluteDeviationIndex += probability * std::abs(deltaIndex);
if ((i >= p10Index) && (i <= p90Index))
{
robustMeanAbsoluteDeviationValue += frequence * std::abs<double>(voxelValue - robustMeanValue);
robustMeanAbsoluteDeivationIndex += frequence * std::abs<double>(i*1.0 - robustMeanIndex*1.0);
}
medianAbsoluteDeviationValue += probability * std::abs(voxelValue - medianValue);
medianAbsoluteDeviationIndex += probability * std::abs(i*1.0 - medianIndex);
if (probability > 0.0000001)
{
entropyValue -= probability * std::log(probability) / Log2;
entropyIndex = entropyValue;
}
uniformityValue += probability*probability;
uniformityIndex = uniformityValue;
if (i == 0)
{
index[0] = 1; index2[0] = 0;
gradient = histogram->GetFrequency(index)*1.0 - histogram->GetFrequency(index2)*1.0;
}
else if (i == (int)(histogram->GetSize(0)) - 1)
{
index[0] = i; index2[0] = i - 1;
gradient = histogram->GetFrequency(index)*1.0 - histogram->GetFrequency(index2)*1.0;
}
else
{
index[0] = i+1; index2[0] = i - 1;
gradient = (histogram->GetFrequency(index)*1.0 - histogram->GetFrequency(index2)*1.0) / 2.0;
}
if (gradient > maximumGradientValue)
{
maximumGradientValue = gradient;
maximumGradientIndex = i + 1;
}
if (gradient < minimumGradientValue)
{
minimumGradientValue = gradient;
minimumGradientIndex = i + 1;
}
}
skewnessValue = skewnessValue / (varianceValue * std::sqrt(varianceValue));
skewnessIndex = skewnessIndex / (varianceIndex * std::sqrt(varianceIndex));
kurtosisValue = kurtosisValue / (varianceValue * varianceValue) - 3; // Excess Kurtosis
kurtosisIndex = kurtosisIndex / (varianceIndex * varianceIndex) - 3; // Excess Kurtosis
coefficientOfVariationValue = std::sqrt(varianceValue) / meanValue;
coefficientOfVariationIndex = std::sqrt(varianceIndex) / (meanIndex+1);
quantileCoefficientOfDispersionValue = (p75Value - p25Value) / (p75Value + p25Value);
quantileCoefficientOfDispersionIndex = (p75Index - p25Index) / (p75Index + 1.0 + p25Index + 1.0);
robustMeanAbsoluteDeviationValue /= robustCount;
robustMeanAbsoluteDeivationIndex /= robustCount;
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mean Value"), meanValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Variance Value"), varianceValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Skewness Value"), skewnessValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Excess Kurtosis Value"), kurtosisValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Median Value"), medianValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Minimum Value"), minimumValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Percentile 10 Value"), p10Value));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Percentile 90 Value"), p90Value));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Maximum Value"), maximumValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mode Value"), modeValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Interquantile Range Value"), p75Value - p25Value));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Range Value"), maximumValue - minimumValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mean Absolute Deviation Value"), meanAbsoluteDeviationValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Robust Mean Absolute Deviation Value"), robustMeanAbsoluteDeviationValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Median Absolute Deviation Value"), medianAbsoluteDeviationValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Coefficient of Variation Value"), coefficientOfVariationValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Quantile coefficient of Dispersion Value"), quantileCoefficientOfDispersionValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Entropy Value"), entropyValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Uniformity Value"), uniformityValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Robust Mean Value"), robustMeanValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mean Index"), meanIndex + 1 ));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Variance Index"), varianceIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Skewness Index"), skewnessIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Excess Kurtosis Index"), kurtosisIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Median Index"), medianIndex + 1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Minimum Index"), minimumIndex + 1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Percentile 10 Index"), p10Index + 1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Percentile 90 Index"), p90Index + 1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Maximum Index"), maximumIndex + 1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mode Index"), modeIndex + 1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Interquantile Range Index"), p75Index - p25Index));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Range Index"), maximumIndex - minimumIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Mean Absolute Deviation Index"), meanAbsoluteDeviationIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Robust Mean Absolute Deviation Index"), robustMeanAbsoluteDeivationIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Median Absolute Deviation Index"), medianAbsoluteDeviationIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Coefficient of Variation Index"), coefficientOfVariationIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Quantile coefficient of Dispersion Index"), quantileCoefficientOfDispersionIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Entropy Index"), entropyIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Uniformity Index"), uniformityIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Maximum Gradient"), maximumGradientValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Maximum Gradient Index"), maximumGradientIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Minimum Gradient"), minimumGradientValue));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Minimum Gradient Index"), minimumGradientIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Robust Mean Index"), robustMeanIndex));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Number of Bins"), histogram->GetSize(0)));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Bin Size"), binWidth));
}
mitk::GIFFirstOrderHistogramStatistics::GIFFirstOrderHistogramStatistics()
{
SetShortName("foh");
SetLongName("first-order-histogram");
SetFeatureClassName("First Order Histogram");
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFFirstOrderHistogramStatistics::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
this->InitializeQuantifier(image, mask);
MITK_INFO << "Start calculating first order histogram features ....";
GIFFirstOrderHistogramStatisticsConfiguration config;
config.MinimumIntensity = GetQuantifier()->GetMinimum();
config.MaximumIntensity = GetQuantifier()->GetMaximum();
config.Bins = GetQuantifier()->GetBins();
config.id = this->CreateTemplateFeatureID();
AccessByItk_3(image, CalculateFirstOrderHistogramStatistics, mask, featureList, config);
MITK_INFO << "Finished calculating first order histogram features....";
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFFirstOrderHistogramStatistics::CalculateFeatures(const Image* image, const Image*, const Image* maskNoNAN)
{
return Superclass::CalculateFeatures(image, maskNoNAN);
}
void mitk::GIFFirstOrderHistogramStatistics::AddArguments(mitkCommandLineParser& parser) const
{
this->AddQuantifierArguments(parser);
std::string name = this->GetOptionPrefix();
- parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurence matrix", "calculates Co-occurence based features (new implementation)", us::Any());
+ parser.addArgument(this->GetLongName(), name, mitkCommandLineParser::Bool, "Use Co-occurrence matrix", "calculates Co-occurrence based features (new implementation)", us::Any());
}
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyLevelDifference.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyLevelDifference.cpp
index ea0b3c9a46..5604a20f7a 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyLevelDifference.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFNeighbourhoodGreyLevelDifference.cpp
@@ -1,227 +1,227 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkGIFNeighbourhoodGreyLevelDifference.h>
// MITK
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
// ITK
#include <itkEnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter.h>
#include <itkMinimumMaximumImageCalculator.h>
// STL
#include <sstream>
struct GIFNeighbourhoodGreyLevelDifferenceParameterStruct
{
bool m_UseCTRange;
double m_Range;
unsigned int m_Direction;
mitk::FeatureID id;
};
template<typename TPixel, unsigned int VImageDimension>
void
CalculateGrayLevelNeighbourhoodGreyLevelDifferenceFeatures(const itk::Image<TPixel, VImageDimension>* itkImage, const mitk::Image* mask, mitk::GIFNeighbourhoodGreyLevelDifference::FeatureListType & featureList, GIFNeighbourhoodGreyLevelDifferenceParameterStruct params)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<TPixel, VImageDimension> MaskType;
typedef itk::Statistics::EnhancedScalarImageToNeighbourhoodGreyLevelDifferenceFeaturesFilter<ImageType> FilterType;
typedef itk::MinimumMaximumImageCalculator<ImageType> MinMaxComputerType;
typedef typename FilterType::NeighbourhoodGreyLevelDifferenceFeaturesFilterType TextureFilterType;
typename MaskType::Pointer maskImage = MaskType::New();
mitk::CastToItkImage(mask, maskImage);
typename FilterType::Pointer filter = FilterType::New();
typename FilterType::OffsetVector::Pointer newOffset = FilterType::OffsetVector::New();
auto oldOffsets = filter->GetOffsets();
auto oldOffsetsIterator = oldOffsets->Begin();
while (oldOffsetsIterator != oldOffsets->End())
{
bool continueOuterLoop = false;
typename FilterType::OffsetType offset = oldOffsetsIterator->Value();
for (unsigned int i = 0; i < VImageDimension; ++i)
{
if (params.m_Direction == i + 2 && offset[i] != 0)
{
continueOuterLoop = true;
}
}
if (params.m_Direction == 1)
{
offset[0] = 0;
offset[1] = 0;
offset[2] = 1;
newOffset->push_back(offset);
break;
}
oldOffsetsIterator++;
if (continueOuterLoop)
continue;
newOffset->push_back(offset);
}
filter->SetOffsets(newOffset);
// All features are required
typename FilterType::FeatureNameVectorPointer requestedFeatures = FilterType::FeatureNameVector::New();
requestedFeatures->push_back(TextureFilterType::Coarseness);
requestedFeatures->push_back(TextureFilterType::Contrast);
requestedFeatures->push_back(TextureFilterType::Busyness);
requestedFeatures->push_back(TextureFilterType::Complexity);
requestedFeatures->push_back(TextureFilterType::Strength);
typename MinMaxComputerType::Pointer minMaxComputer = MinMaxComputerType::New();
minMaxComputer->SetImage(itkImage);
minMaxComputer->Compute();
filter->SetInput(itkImage);
filter->SetMaskImage(maskImage);
filter->SetRequestedFeatures(requestedFeatures);
int rangeOfPixels = params.m_Range;
if (rangeOfPixels < 2)
rangeOfPixels = 256;
if (params.m_UseCTRange)
{
filter->SetPixelValueMinMax((TPixel)(-1024.5),(TPixel)(3096.5));
filter->SetNumberOfBinsPerAxis(3096.5+1024.5);
} else
{
filter->SetPixelValueMinMax(minMaxComputer->GetMinimum(),minMaxComputer->GetMaximum());
filter->SetNumberOfBinsPerAxis(rangeOfPixels);
}
filter->SetDistanceValueMinMax(0,rangeOfPixels);
filter->Update();
auto featureMeans = filter->GetFeatureMeans ();
auto featureStd = filter->GetFeatureStandardDeviations();
std::ostringstream ss;
ss << rangeOfPixels;
std::string strRange = ss.str();
for (std::size_t i = 0; i < featureMeans->size(); ++i)
{
switch (i)
{
case TextureFilterType::Coarseness :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Coarseness Means"),featureMeans->ElementAt(i)));
break;
case TextureFilterType::Contrast :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Contrast Means"),featureMeans->ElementAt(i)));
break;
case TextureFilterType::Busyness :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Busyness Means"),featureMeans->ElementAt(i)));
break;
case TextureFilterType::Complexity :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Complexity Means"),featureMeans->ElementAt(i)));
break;
case TextureFilterType::Strength :
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Strength Means"),featureMeans->ElementAt(i)));
break;
default:
break;
}
}
}
mitk::GIFNeighbourhoodGreyLevelDifference::GIFNeighbourhoodGreyLevelDifference():
m_Ranges({ 1.0 }), m_UseCTRange(false)
{
SetShortName("ngld");
SetLongName("NeighbourhoodGreyLevelDifference");
}
void mitk::GIFNeighbourhoodGreyLevelDifference::SetRanges(std::vector<double> ranges)
{
m_Ranges = ranges;
this->Modified();
}
void mitk::GIFNeighbourhoodGreyLevelDifference::SetRange(double range)
{
m_Ranges.resize(1);
m_Ranges[0] = range;
this->Modified();
}
void mitk::GIFNeighbourhoodGreyLevelDifference::AddArguments(mitkCommandLineParser &parser) const
{
std::string name = GetOptionPrefix();
- parser.addArgument(GetLongName(), name, mitkCommandLineParser::String, "Use Co-occurence matrix", "calculates Co-occurence based features (new implementation)", us::Any());
+ parser.addArgument(GetLongName(), name, mitkCommandLineParser::String, "Use Co-occurrence matrix", "calculates Co-occurrence based features (new implementation)", us::Any());
parser.addArgument(name + "::range", name + "::range", mitkCommandLineParser::String, "Cooc 2 Range", "Define the range that is used (Semicolon-separated)", us::Any());
parser.addArgument(name + "::direction", name + "::dir", mitkCommandLineParser::Int, "Int", "Allows to specify the direction for Cooc and RL. 0: All directions, 1: Only single direction (Test purpose), 2,3,4... without dimension 0,1,2... ", us::Any());
parser.addArgument(name + "::useCTRange", name + "::ct", mitkCommandLineParser::Bool, "Use CT range", "If flag is set only value in the CT range will be used for feature computation.", us::Any());
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFNeighbourhoodGreyLevelDifference::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
for (const auto& range : m_Ranges)
{
MITK_INFO << "Start calculating Neighbourhood Grey Level Difference with range " << range << "....";
GIFNeighbourhoodGreyLevelDifferenceParameterStruct params;
params.m_UseCTRange = m_UseCTRange;
params.m_Range = range;
params.m_Direction = GetDirection();
params.id = this->CreateTemplateFeatureID(std::to_string(range), { {GetOptionPrefix() + "::range", range} });
AccessByItk_3(image, CalculateGrayLevelNeighbourhoodGreyLevelDifferenceFeatures, mask, featureList, params);
MITK_INFO << "Finished calculating coocurence with range " << range << "....";
}
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFNeighbourhoodGreyLevelDifference::CalculateFeatures(const Image* image, const Image*, const Image* maskNoNAN)
{
return Superclass::CalculateFeatures(image, maskNoNAN);
}
std::string mitk::GIFNeighbourhoodGreyLevelDifference::GenerateLegacyFeatureName(const FeatureID& id) const
{
return "NeighbourhoodGreyLevelDifference (" + id.parameters.at(this->GetOptionPrefix() + "::range").ToString() + ") " + id.name;
}
void mitk::GIFNeighbourhoodGreyLevelDifference::ConfigureSettingsByParameters(const ParametersType& parameters)
{
auto prefixname = GetOptionPrefix();
auto name = prefixname + "::range";
if (parameters.count(name))
{
m_Ranges = SplitDouble(parameters.at(name).ToString(), ';');
}
name = prefixname + "::direction";
if (parameters.count(name))
{
int direction = us::any_cast<int>(parameters.at(name));
this->SetDirection(direction);
}
name = prefixname + "::useCTRange";
if (parameters.count(name))
{
bool tmp = us::any_cast<bool>(parameters.at(name));
m_UseCTRange = tmp;
}
}
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricDensityStatistics.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricDensityStatistics.cpp
index 026f64a6d2..2fb8536e1c 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricDensityStatistics.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricDensityStatistics.cpp
@@ -1,513 +1,513 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkGIFVolumetricDensityStatistics.h>
// MITK
#include <mitkITKImageImport.h>
#include <mitkImageCast.h>
#include <mitkImageAccessByItk.h>
#include <mitkPixelTypeMultiplex.h>
#include <mitkImagePixelReadAccessor.h>
// ITK
#include <itkLabelStatisticsImageFilter.h>
#include <itkNeighborhoodIterator.h>
#include <itkImageRegionConstIteratorWithIndex.h>
#include <itkLabelGeometryImageFilter.h>
// VTK
#include <vtkSmartPointer.h>
#include <vtkImageMarchingCubes.h>
#include <vtkMassProperties.h>
#include <vtkDelaunay3D.h>
#include <vtkGeometryFilter.h>
#include <vtkDoubleArray.h>
#include <vtkPCAStatistics.h>
#include <vtkTable.h>
// STL
#include <limits>
#include <vnl/vnl_math.h>
// Eigen
-#include <Eigen/Dense>
+#include <itkeigen/Eigen/Dense>
struct GIFVolumetricDensityStatisticsParameters
{
double volume;
mitk::FeatureID id;
};
template<typename TPixel, unsigned int VImageDimension>
void
CalculateVolumeDensityStatistic(const itk::Image<TPixel, VImageDimension>* itkImage, const mitk::Image* mask, GIFVolumetricDensityStatisticsParameters params, mitk::GIFVolumetricDensityStatistics::FeatureListType & featureList)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<unsigned short, VImageDimension> MaskType;
double volume = params.volume;
typename MaskType::Pointer maskImage = MaskType::New();
mitk::CastToItkImage(mask, maskImage);
itk::ImageRegionConstIteratorWithIndex<ImageType> imgA(itkImage, itkImage->GetLargestPossibleRegion());
itk::ImageRegionConstIteratorWithIndex<ImageType> imgB(itkImage, itkImage->GetLargestPossibleRegion());
itk::ImageRegionConstIteratorWithIndex<MaskType> maskA(maskImage, maskImage->GetLargestPossibleRegion());
itk::ImageRegionConstIteratorWithIndex<MaskType> maskB(maskImage, maskImage->GetLargestPossibleRegion());
double moranA = 0;
double moranB = 0;
double geary = 0;
double Nv = 0;
double w_ij = 0;
double mean = 0;
typename ImageType::PointType pointA;
typename ImageType::PointType pointB;
while (!imgA.IsAtEnd())
{
if (maskA.Get() > 0)
{
Nv += 1;
mean += imgA.Get();
}
++imgA;
++maskA;
}
mean /= Nv;
imgA.GoToBegin();
maskA.GoToBegin();
while (!imgA.IsAtEnd())
{
if (maskA.Get() > 0)
{
imgB.GoToBegin();
maskB.GoToBegin();
while (!imgB.IsAtEnd())
{
if ((imgA.GetIndex() == imgB.GetIndex()) ||
(maskB.Get() < 1))
{
++imgB;
++maskB;
continue;
}
itkImage->TransformIndexToPhysicalPoint(maskA.GetIndex(), pointA);
itkImage->TransformIndexToPhysicalPoint(maskB.GetIndex(), pointB);
double w = 1 / pointA.EuclideanDistanceTo(pointB);
moranA += w*(imgA.Get() - mean)* (imgB.Get() - mean);
geary += w * (imgA.Get() - imgB.Get()) * (imgA.Get() - imgB.Get());
w_ij += w;
++imgB;
++maskB;
}
moranB += (imgA.Get() - mean)* (imgA.Get() - mean);
}
++imgA;
++maskA;
}
MITK_INFO << "Volume: " << volume;
MITK_INFO << " Mean: " << mean;
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Volume integrated intensity"), volume* mean));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Volume Moran's I index"), Nv / w_ij * moranA / moranB));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Volume Geary's C measure"), ( Nv -1 ) / 2 / w_ij * geary/ moranB));
}
void calculateMOBB(vtkPointSet *pointset, double &volume, double &surface)
{
volume = std::numeric_limits<double>::max();
for (int cellID = 0; cellID < pointset->GetNumberOfCells(); ++cellID)
{
auto cell = pointset->GetCell(cellID);
for (int edgeID = 0; edgeID < 3; ++edgeID)
{
auto edge = cell->GetEdge(edgeID);
double pA[3], pB[3];
double pAA[3], pBB[3];
vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
transform->PostMultiply();
pointset->GetPoint(edge->GetPointId(0), pA);
pointset->GetPoint(edge->GetPointId(1), pB);
double angleZ = std::atan2((- pA[2] + pB[2]) ,(pA[1] - pB[1]));
angleZ *= 180 / vnl_math::pi;
if (pA[2] == pB[2])
angleZ = 0;
transform->RotateX(angleZ);
transform->TransformPoint(pA, pAA);
transform->TransformPoint(pB, pBB);
double angleY = std::atan2((pAA[1] -pBB[1]) ,-(pAA[0] - pBB[0]));
angleY *= 180 / vnl_math::pi;
if (pAA[1] == pBB[1])
angleY = 0;
transform->RotateZ(angleY);
double p0[3];
pointset->GetPoint(edge->GetPointId(0), p0);
double curMinX = std::numeric_limits<double>::max();
double curMaxX = std::numeric_limits<double>::lowest();
double curMinY = std::numeric_limits<double>::max();
double curMaxY = std::numeric_limits<double>::lowest();
double curMinZ = std::numeric_limits<double>::max();
double curMaxZ = std::numeric_limits<double>::lowest();
for (int pointID = 0; pointID < pointset->GetNumberOfPoints(); ++pointID)
{
double p[3];
double p2[3];
pointset->GetPoint(pointID, p);
p[0] -= p0[0]; p[1] -= p0[1]; p[2] -= p0[2];
transform->TransformPoint(p, p2);
curMinX = std::min<double>(p2[0], curMinX);
curMaxX = std::max<double>(p2[0], curMaxX);
curMinY = std::min<double>(p2[1], curMinY);
curMaxY = std::max<double>(p2[1], curMaxY);
curMinZ = std::min<double>(p2[2], curMinZ);
curMaxZ = std::max<double>(p2[2], curMaxZ);
}
if ((curMaxX - curMinX)*(curMaxY - curMinY)*(curMaxZ - curMinZ) < volume)
{
volume = (curMaxX - curMinX)*(curMaxY - curMinY)*(curMaxZ - curMinZ);
surface = (curMaxX - curMinX)*(curMaxX - curMinX) + (curMaxY - curMinY)*(curMaxY - curMinY) + (curMaxZ - curMinZ)*(curMaxZ - curMinZ);
surface *= 2;
}
}
}
}
void calculateMEE(vtkPointSet *pointset, double &vol, double &surf, double tolerance=0.0001)
{
// Inspired by https://github.com/smdabdoub/ProkaryMetrics/blob/master/calc/fitting.py
int numberOfPoints = pointset->GetNumberOfPoints();
int dimension = 3;
Eigen::MatrixXd points(3, numberOfPoints);
Eigen::MatrixXd Q(3+1, numberOfPoints);
double p[3];
std::cout << "Initialize Q " << std::endl;
for (int i = 0; i < numberOfPoints; ++i)
{
pointset->GetPoint(i, p);
points(0, i) = p[0];
points(1, i) = p[1];
points(2, i) = p[2];
Q(0, i) = p[0];
Q(1, i) = p[1];
Q(2, i) = p[2];
Q(3, i) = 1.0;
}
double error = 1;
Eigen::VectorXd u_vector(numberOfPoints);
u_vector.fill(1.0 / numberOfPoints);
Eigen::DiagonalMatrix<double, Eigen::Dynamic> u = u_vector.asDiagonal();
Eigen::VectorXd ones(dimension + 1);
ones.fill(1);
Eigen::MatrixXd Ones = ones.asDiagonal();
// Khachiyan Algorithm
while (error > tolerance)
{
auto Qt = Q.transpose();
Eigen::MatrixXd X = Q*u*Qt;
Eigen::FullPivHouseholderQR<Eigen::MatrixXd> qr(X);
Eigen::MatrixXd Xi = qr.solve(Ones);
Eigen::MatrixXd M = Qt * Xi * Q;
double maximumValue = M(0, 0);
int maximumPosition = 0;
for (int i = 0; i < numberOfPoints; ++i)
{
if (maximumValue < M(i, i))
{
maximumValue = M(i, i);
maximumPosition = i;
}
}
double stepsize = (maximumValue - dimension - 1) / ((dimension + 1) * (maximumValue - 1));
Eigen::DiagonalMatrix<double, Eigen::Dynamic> new_u = (1.0 - stepsize) * u;
new_u.diagonal()[maximumPosition] = (new_u.diagonal())(maximumPosition) + stepsize;
error = (new_u.diagonal() - u.diagonal()).norm();
u.diagonal() = new_u.diagonal();
}
// U = u
Eigen::MatrixXd Ai = points * u * points.transpose() - points * u *(points * u).transpose();
Eigen::FullPivHouseholderQR<Eigen::MatrixXd> qr(Ai);
Eigen::VectorXd ones2(dimension);
ones2.fill(1);
Eigen::MatrixXd Ones2 = ones2.asDiagonal();
Eigen::MatrixXd A = qr.solve(Ones2)*1.0/dimension;
Eigen::JacobiSVD<Eigen::MatrixXd> svd(A);
double c = 1 / sqrt(svd.singularValues()[0]);
double b = 1 / sqrt(svd.singularValues()[1]);
double a = 1 / sqrt(svd.singularValues()[2]);
double V = 4 * vnl_math::pi*a*b*c / 3;
double ad_mvee= 0;
double alpha = std::sqrt(1 - b*b / a / a);
double beta = std::sqrt(1 - c*c / a / a);
for (int i = 0; i < 20; ++i)
{
ad_mvee += 4 * vnl_math::pi*a*b*(alpha*alpha + beta*beta) / (2 * alpha*beta) * (std::pow(alpha*beta, i)) / (1 - 4 * i*i);
}
vol = V;
surf = ad_mvee;
}
mitk::GIFVolumetricDensityStatistics::GIFVolumetricDensityStatistics()
{
SetLongName("volume-density");
SetShortName("volden");
SetFeatureClassName("Morphological Density");
}
void mitk::GIFVolumetricDensityStatistics::AddArguments(mitkCommandLineParser& parser) const
{
std::string name = GetOptionPrefix();
parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Volume-Density Statistic", "calculates volume density based features", us::Any());
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFVolumetricDensityStatistics::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
if (image->GetDimension() < 3)
{
MITK_INFO << "Skipped calculating volumetric density features; only 3D images are supported ....";
return featureList;
}
MITK_INFO << "Start calculating volumetric density features ....";
vtkSmartPointer<vtkImageMarchingCubes> mesher = vtkSmartPointer<vtkImageMarchingCubes>::New();
vtkSmartPointer<vtkMassProperties> stats = vtkSmartPointer<vtkMassProperties>::New();
vtkSmartPointer<vtkMassProperties> stats2 = vtkSmartPointer<vtkMassProperties>::New();
auto nonconstVtkData = const_cast<vtkImageData*>(mask->GetVtkImageData());
mesher->SetInputData(nonconstVtkData);
mesher->SetValue(0, 0.5);
stats->SetInputConnection(mesher->GetOutputPort());
stats->Update();
vtkSmartPointer<vtkDelaunay3D> delaunay =
vtkSmartPointer< vtkDelaunay3D >::New();
delaunay->SetInputConnection(mesher->GetOutputPort());
delaunay->SetAlpha(0);
delaunay->Update();
vtkSmartPointer<vtkGeometryFilter> geometryFilter =
vtkSmartPointer<vtkGeometryFilter>::New();
geometryFilter->SetInputConnection(delaunay->GetOutputPort());
geometryFilter->Update();
stats2->SetInputConnection(geometryFilter->GetOutputPort());
stats2->Update();
double vol_mvee;
double surf_mvee;
calculateMEE(mesher->GetOutput(), vol_mvee, surf_mvee);
double vol_mobb;
double surf_mobb;
calculateMOBB(geometryFilter->GetOutput(), vol_mobb, surf_mobb);
double pi = vnl_math::pi;
double meshVolume = stats->GetVolume();
double meshSurf = stats->GetSurfaceArea();
GIFVolumetricDensityStatisticsParameters params;
params.volume = meshVolume;
params.id = this->CreateTemplateFeatureID();
AccessByItk_3(image, CalculateVolumeDensityStatistic, mask, params, featureList);
//Calculate center of mass shift
int xx = mask->GetDimensions()[0];
int yy = mask->GetDimensions()[1];
int zz = mask->GetDimensions()[2];
double xd = mask->GetGeometry()->GetSpacing()[0];
double yd = mask->GetGeometry()->GetSpacing()[1];
double zd = mask->GetGeometry()->GetSpacing()[2];
int minimumX = xx;
int maximumX = 0;
int minimumY = yy;
int maximumY = 0;
int minimumZ = zz;
int maximumZ = 0;
vtkSmartPointer<vtkDoubleArray> dataset1Arr = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkDoubleArray> dataset2Arr = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkDoubleArray> dataset3Arr = vtkSmartPointer<vtkDoubleArray>::New();
dataset1Arr->SetNumberOfComponents(1);
dataset2Arr->SetNumberOfComponents(1);
dataset3Arr->SetNumberOfComponents(1);
dataset1Arr->SetName("M1");
dataset2Arr->SetName("M2");
dataset3Arr->SetName("M3");
vtkSmartPointer<vtkDoubleArray> dataset1ArrU = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkDoubleArray> dataset2ArrU = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkDoubleArray> dataset3ArrU = vtkSmartPointer<vtkDoubleArray>::New();
dataset1ArrU->SetNumberOfComponents(1);
dataset2ArrU->SetNumberOfComponents(1);
dataset3ArrU->SetNumberOfComponents(1);
dataset1ArrU->SetName("M1");
dataset2ArrU->SetName("M2");
dataset3ArrU->SetName("M3");
vtkSmartPointer<vtkPoints> points =
vtkSmartPointer< vtkPoints >::New();
for (int x = 0; x < xx; x++)
{
for (int y = 0; y < yy; y++)
{
for (int z = 0; z < zz; z++)
{
itk::Image<int, 3>::IndexType index;
index[0] = x;
index[1] = y;
index[2] = z;
mitk::ScalarType pxImage;
mitk::ScalarType pxMask;
mitkPixelTypeMultiplex5(
mitk::FastSinglePixelAccess,
image->GetChannelDescriptor().GetPixelType(),
image,
image->GetVolumeData(),
index,
pxImage,
0);
mitkPixelTypeMultiplex5(
mitk::FastSinglePixelAccess,
mask->GetChannelDescriptor().GetPixelType(),
mask,
mask->GetVolumeData(),
index,
pxMask,
0);
//Check if voxel is contained in segmentation
if (pxMask > 0)
{
minimumX = std::min<int>(x, minimumX);
minimumY = std::min<int>(y, minimumY);
minimumZ = std::min<int>(z, minimumZ);
maximumX = std::max<int>(x, maximumX);
maximumY = std::max<int>(y, maximumY);
maximumZ = std::max<int>(z, maximumZ);
points->InsertNextPoint(x * xd, y * yd, z * zd);
if (pxImage == pxImage)
{
dataset1Arr->InsertNextValue(x * xd);
dataset2Arr->InsertNextValue(y * yd);
dataset3Arr->InsertNextValue(z * zd);
}
}
}
}
}
vtkSmartPointer<vtkTable> datasetTable = vtkSmartPointer<vtkTable>::New();
datasetTable->AddColumn(dataset1Arr);
datasetTable->AddColumn(dataset2Arr);
datasetTable->AddColumn(dataset3Arr);
vtkSmartPointer<vtkPCAStatistics> pcaStatistics = vtkSmartPointer<vtkPCAStatistics>::New();
pcaStatistics->SetInputData(vtkStatisticsAlgorithm::INPUT_DATA, datasetTable);
pcaStatistics->SetColumnStatus("M1", 1);
pcaStatistics->SetColumnStatus("M2", 1);
pcaStatistics->SetColumnStatus("M3", 1);
pcaStatistics->RequestSelectedColumns();
pcaStatistics->SetDeriveOption(true);
pcaStatistics->Update();
vtkSmartPointer<vtkDoubleArray> eigenvalues = vtkSmartPointer<vtkDoubleArray>::New();
pcaStatistics->GetEigenvalues(eigenvalues);
std::vector<double> eigen_val(3);
eigen_val[2] = eigenvalues->GetValue(0);
eigen_val[1] = eigenvalues->GetValue(1);
eigen_val[0] = eigenvalues->GetValue(2);
double major = 2 * sqrt(eigen_val[2]);
double minor = 2 * sqrt(eigen_val[1]);
double least = 2 * sqrt(eigen_val[0]);
double alpha = std::sqrt(1 - minor * minor / major / major);
double beta = std::sqrt(1 - least * least / major / major);
double a = (maximumX - minimumX + 1) * xd;
double b = (maximumY - minimumY + 1) * yd;
double c = (maximumZ - minimumZ + 1) * zd;
double vd_aabb = meshVolume / (a * b * c);
double ad_aabb = meshSurf / (2 * a * b + 2 * a * c + 2 * b * c);
double vd_aee = 3 * meshVolume / (4.0 * pi * major * minor * least);
double ad_aee = 0;
for (int i = 0; i < 20; ++i)
{
ad_aee += 4 * pi * major * minor * (alpha * alpha + beta * beta) / (2 * alpha * beta) * (std::pow(alpha * beta, i)) / (1 - 4 * i * i);
}
ad_aee = meshSurf / ad_aee;
double vd_ch = meshVolume / stats2->GetVolume();
double ad_ch = meshSurf / stats2->GetSurfaceArea();
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Volume density axis-aligned bounding box"), vd_aabb));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Surface density axis-aligned bounding box"), ad_aabb));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Volume density oriented minimum bounding box"), meshVolume / vol_mobb));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Surface density oriented minimum bounding box"), meshSurf / surf_mobb));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Volume density approx. enclosing ellipsoid"), vd_aee));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Surface density approx. enclosing ellipsoid"), ad_aee));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Volume density approx. minimum volume enclosing ellipsoid"), meshVolume / vol_mvee));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Surface density approx. minimum volume enclosing ellipsoid"), meshSurf / surf_mvee));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Volume density convex hull"), vd_ch));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(params.id, "Surface density convex hull"), ad_ch));
MITK_INFO << "Finished calculating volumetric density features....";
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFVolumetricDensityStatistics::CalculateFeatures(const Image* image, const Image* mask, const Image*)
{
return Superclass::CalculateFeatures(image, mask);
}
diff --git a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp
index 860265ced9..0f2ee167fe 100644
--- a/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp
+++ b/Modules/Classification/CLUtilities/src/GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp
@@ -1,456 +1,456 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkGIFVolumetricStatistics.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 <vtkMassProperties.h>
#include <vtkDoubleArray.h>
#include <vtkPCAStatistics.h>
#include <vtkTable.h>
// STL
#include <vnl/vnl_math.h>
template<typename TPixel, unsigned int VImageDimension>
void
CalculateVolumeStatistic(const itk::Image<TPixel, VImageDimension>* itkImage, const mitk::Image* mask, mitk::GIFVolumetricStatistics::FeatureListType & featureList, const mitk::FeatureID& featureID)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<int, VImageDimension> MaskType;
typedef itk::LabelStatisticsImageFilter<ImageType, MaskType> FilterType;
typename MaskType::Pointer maskImage = MaskType::New();
mitk::CastToItkImage(mask, maskImage);
typename FilterType::Pointer labelStatisticsImageFilter = FilterType::New();
labelStatisticsImageFilter->SetInput( itkImage );
labelStatisticsImageFilter->SetLabelInput(maskImage);
labelStatisticsImageFilter->Update();
double volume = labelStatisticsImageFilter->GetCount(1);
double voxelVolume = 1;
for (int i = 0; i < (int)(VImageDimension); ++i)
{
volume *= itkImage->GetSpacing()[i];
voxelVolume *= itkImage->GetSpacing()[i];
}
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Voxel Volume"), voxelVolume));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Volume (voxel based)"), volume));
}
template<typename TPixel, unsigned int VImageDimension>
void
CalculateLargestDiameter(const itk::Image<TPixel, VImageDimension>* mask, const mitk::Image* valueImage, mitk::GIFVolumetricStatistics::FeatureListType & featureList, mitk::FeatureID featureID)
{
typedef itk::Image<double, VImageDimension> ValueImageType;
typename ValueImageType::Pointer itkValueImage = ValueImageType::New();
mitk::CastToItkImage(valueImage, itkValueImage);
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef typename ImageType::PointType PointType;
typename ImageType::SizeType radius;
for (int i=0; i < (int)VImageDimension; ++i)
radius[i] = 1;
itk::ConstNeighborhoodIterator<ImageType> iterator(radius, mask, mask->GetRequestedRegion());
itk::NeighborhoodIterator<ValueImageType> valueIter(radius, itkValueImage, itkValueImage->GetRequestedRegion());
std::vector<PointType> borderPoints;
unsigned int maskDimensionX = mask->GetLargestPossibleRegion().GetSize()[0];
unsigned int maskDimensionY = mask->GetLargestPossibleRegion().GetSize()[1];
unsigned int maskDimensionZ = mask->GetLargestPossibleRegion().GetSize()[2];
double maskVoxelSpacingX = mask->GetSpacing()[0];
double maskVoxelSpacingY = mask->GetSpacing()[1];
double maskVoxelSpacingZ = mask->GetSpacing()[2];
int maskMinimumX = maskDimensionX;
int maskMaximumX = 0;
int maskMinimumY = maskDimensionY;
int maskMaximumY = 0;
int maskMinimumZ = maskDimensionZ;
int maskMaximumZ = 0;
//
// Calculate surface in different directions
//
double surface = 0;
std::vector<double> directionSurface;
for (int i = 0; i < (int)(iterator.Size()); ++i)
{
auto offset = iterator.GetOffset(i);
double deltaS = 1;
int nonZeros = 0;
for (unsigned int j = 0; j < VImageDimension; ++j)
{
if (offset[j] != 0 && nonZeros == 0)
{
for (unsigned int k = 0; k < VImageDimension; ++k)
{
if (k != j)
deltaS *= mask->GetSpacing()[k];
}
nonZeros++;
}
else if (offset[j] != 0)
{
deltaS = 0;
}
}
if (nonZeros < 1)
deltaS = 0;
directionSurface.push_back(deltaS);
}
//
- // Prepare calulation of Centre of mass shift
+ // Prepare calculation of Centre of mass shift
//
PointType normalCenter(0);
PointType normalCenterUncorrected(0);
PointType weightedCenter(0);
PointType currentPoint;
int numberOfPoints = 0;
int numberOfPointsUncorrected = 0;
double sumOfPoints = 0;
while(!iterator.IsAtEnd())
{
if (iterator.GetCenterPixel() == 0)
{
++iterator;
++valueIter;
continue;
}
maskMinimumX = (maskMinimumX > iterator.GetIndex()[0]) ? iterator.GetIndex()[0] : maskMinimumX;
maskMaximumX = (maskMaximumX < iterator.GetIndex()[0]) ? iterator.GetIndex()[0] : maskMaximumX;
maskMinimumY = (maskMinimumY > iterator.GetIndex()[1]) ? iterator.GetIndex()[1] : maskMinimumY;
maskMaximumY = (maskMaximumY < iterator.GetIndex()[1]) ? iterator.GetIndex()[1] : maskMaximumY;
maskMinimumZ = (maskMinimumZ > iterator.GetIndex()[2]) ? iterator.GetIndex()[2] : maskMinimumZ;
maskMaximumZ = (maskMaximumZ < iterator.GetIndex()[2]) ? iterator.GetIndex()[2] : maskMaximumZ;
mask->TransformIndexToPhysicalPoint(iterator.GetIndex(), currentPoint);
normalCenterUncorrected += currentPoint.GetVectorFromOrigin();
++numberOfPointsUncorrected;
double intensityValue = valueIter.GetCenterPixel();
if (intensityValue == intensityValue)
{
normalCenter += currentPoint.GetVectorFromOrigin();
weightedCenter += currentPoint.GetVectorFromOrigin() * intensityValue;
sumOfPoints += intensityValue;
++numberOfPoints;
}
bool border = false;
for (int i = 0; i < (int)(iterator.Size()); ++i)
{
if (iterator.GetPixel(i) == 0 || ( ! iterator.IndexInBounds(i)))
{
border = true;
surface += directionSurface[i];
//break;
}
}
if (border)
{
auto centerIndex = iterator.GetIndex();
PointType centerPoint;
mask->TransformIndexToPhysicalPoint(centerIndex, centerPoint );
borderPoints.push_back(centerPoint);
}
++iterator;
++valueIter;
}
auto normalCenterVector = normalCenter.GetVectorFromOrigin() / numberOfPoints;
auto normalCenterVectorUncorrected = normalCenter.GetVectorFromOrigin() / numberOfPointsUncorrected;
auto weightedCenterVector = weightedCenter.GetVectorFromOrigin() / sumOfPoints;
auto differenceOfCentersUncorrected = (normalCenterVectorUncorrected - weightedCenterVector).GetNorm();
auto differenceOfCenters = (normalCenterVector - weightedCenterVector).GetNorm();
double longestDiameter = 0;
unsigned long numberOfBorderPoints = borderPoints.size();
for (int i = 0; i < (int)numberOfBorderPoints; ++i)
{
auto point = borderPoints[i];
for (int j = i; j < (int)numberOfBorderPoints; ++j)
{
double newDiameter=point.EuclideanDistanceTo(borderPoints[j]);
if (newDiameter > longestDiameter)
longestDiameter = newDiameter;
}
}
double boundingBoxVolume = maskVoxelSpacingX* (maskMaximumX - maskMinimumX) * maskVoxelSpacingY* (maskMaximumY - maskMinimumY) * maskVoxelSpacingZ* (maskMaximumZ - maskMinimumZ);
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Maximum 3D diameter"), longestDiameter));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Surface (voxel based)"), surface));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Centre of mass shift"), differenceOfCenters));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Centre of mass shift (uncorrected)"), differenceOfCentersUncorrected));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Bounding Box Volume"), boundingBoxVolume));
}
mitk::GIFVolumetricStatistics::GIFVolumetricStatistics()
{
SetLongName("volume");
SetShortName("vol");
SetFeatureClassName("Volumetric Features");
}
void mitk::GIFVolumetricStatistics::AddArguments(mitkCommandLineParser& parser) const
{
std::string name = GetOptionPrefix();
parser.addArgument(GetLongName(), name, mitkCommandLineParser::Bool, "Use Volume-Statistic", "calculates volume based features", us::Any());
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFVolumetricStatistics::DoCalculateFeatures(const Image* image, const Image* mask)
{
FeatureListType featureList;
MITK_INFO << "Start calculating Volumetric Features:....";
if (image->GetDimension() < 3)
{
return featureList;
}
auto featureID = this->CreateTemplateFeatureID();
AccessByItk_3(image, CalculateVolumeStatistic, mask, featureList, featureID);
AccessByItk_3(mask, CalculateLargestDiameter, image, featureList, featureID);
vtkSmartPointer<vtkImageMarchingCubes> mesher = vtkSmartPointer<vtkImageMarchingCubes>::New();
vtkSmartPointer<vtkMassProperties> stats = vtkSmartPointer<vtkMassProperties>::New();
auto nonconstVtkData = const_cast<vtkImageData*>(mask->GetVtkImageData());
mesher->SetInputData(nonconstVtkData);
mesher->SetValue(0, 0.5);
stats->SetInputConnection(mesher->GetOutputPort());
stats->Update();
double pi = vnl_math::pi;
double meshVolume = stats->GetVolume();
double meshSurf = stats->GetSurfaceArea();
double pixelVolume = featureList[1].second;
double pixelSurface = featureList[3].second;
MITK_INFO << "Surface: " << pixelSurface << " Volume: " << pixelVolume;
double compactness1 = pixelVolume / (std::sqrt(pi) * std::pow(meshSurf, 2.0 / 3.0));
double compactness1Pixel = pixelVolume / (std::sqrt(pi) * std::pow(pixelSurface, 2.0 / 3.0));
//This is the definition used by Aertz. However, due to 2/3 this feature is not demensionless. Use compactness3 instead.
double compactness2 = 36 * pi * pixelVolume * pixelVolume / meshSurf / meshSurf / meshSurf;
double compactness2MeshMesh = 36 * pi * meshVolume * meshVolume / meshSurf / meshSurf / meshSurf;
double compactness2Pixel = 36 * pi * pixelVolume * pixelVolume / pixelSurface / pixelSurface / pixelSurface;
double compactness3 = pixelVolume / (std::sqrt(pi) * std::pow(meshSurf, 3.0 / 2.0));
double compactness3MeshMesh = meshVolume / (std::sqrt(pi) * std::pow(meshSurf, 3.0 / 2.0));
double compactness3Pixel = pixelVolume / (std::sqrt(pi) * std::pow(pixelSurface, 3.0 / 2.0));
double sphericity = std::pow(pi, 1 / 3.0) * std::pow(6 * pixelVolume, 2.0 / 3.0) / meshSurf;
double sphericityMesh = std::pow(pi, 1 / 3.0) * std::pow(6 * meshVolume, 2.0 / 3.0) / meshSurf;
double sphericityPixel = std::pow(pi, 1 / 3.0) * std::pow(6 * pixelVolume, 2.0 / 3.0) / pixelSurface;
double surfaceToVolume = meshSurf / meshVolume;
double surfaceToVolumePixel = pixelSurface / pixelVolume;
double sphericalDisproportion = meshSurf / 4 / pi / std::pow(3.0 / 4.0 / pi * pixelVolume, 2.0 / 3.0);
double sphericalDisproportionMesh = meshSurf / 4 / pi / std::pow(3.0 / 4.0 / pi * meshVolume, 2.0 / 3.0);
double sphericalDisproportionPixel = pixelSurface / 4 / pi / std::pow(3.0 / 4.0 / pi * pixelVolume, 2.0 / 3.0);
double asphericity = std::pow(1.0 / compactness2, (1.0 / 3.0)) - 1;
double asphericityMesh = std::pow(1.0 / compactness2MeshMesh, (1.0 / 3.0)) - 1;
double asphericityPixel = std::pow(1.0 / compactness2Pixel, (1.0 / 3.0)) - 1;
//Calculate center of mass shift
int xx = mask->GetDimensions()[0];
int yy = mask->GetDimensions()[1];
int zz = mask->GetDimensions()[2];
double xd = mask->GetGeometry()->GetSpacing()[0];
double yd = mask->GetGeometry()->GetSpacing()[1];
double zd = mask->GetGeometry()->GetSpacing()[2];
vtkSmartPointer<vtkDoubleArray> dataset1Arr = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkDoubleArray> dataset2Arr = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkDoubleArray> dataset3Arr = vtkSmartPointer<vtkDoubleArray>::New();
dataset1Arr->SetNumberOfComponents(1);
dataset2Arr->SetNumberOfComponents(1);
dataset3Arr->SetNumberOfComponents(1);
dataset1Arr->SetName("M1");
dataset2Arr->SetName("M2");
dataset3Arr->SetName("M3");
vtkSmartPointer<vtkDoubleArray> dataset1ArrU = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkDoubleArray> dataset2ArrU = vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkDoubleArray> dataset3ArrU = vtkSmartPointer<vtkDoubleArray>::New();
dataset1ArrU->SetNumberOfComponents(1);
dataset2ArrU->SetNumberOfComponents(1);
dataset3ArrU->SetNumberOfComponents(1);
dataset1ArrU->SetName("M1");
dataset2ArrU->SetName("M2");
dataset3ArrU->SetName("M3");
for (int x = 0; x < xx; x++)
{
for (int y = 0; y < yy; y++)
{
for (int z = 0; z < zz; z++)
{
itk::Image<int, 3>::IndexType index;
index[0] = x;
index[1] = y;
index[2] = z;
mitk::ScalarType pxImage;
mitk::ScalarType pxMask;
mitkPixelTypeMultiplex5(
mitk::FastSinglePixelAccess,
image->GetChannelDescriptor().GetPixelType(),
image,
image->GetVolumeData(),
index,
pxImage,
0);
mitkPixelTypeMultiplex5(
mitk::FastSinglePixelAccess,
mask->GetChannelDescriptor().GetPixelType(),
mask,
mask->GetVolumeData(),
index,
pxMask,
0);
//Check if voxel is contained in segmentation
if (pxMask > 0)
{
dataset1ArrU->InsertNextValue(x * xd);
dataset2ArrU->InsertNextValue(y * yd);
dataset3ArrU->InsertNextValue(z * zd);
if (pxImage == pxImage)
{
dataset1Arr->InsertNextValue(x * xd);
dataset2Arr->InsertNextValue(y * yd);
dataset3Arr->InsertNextValue(z * zd);
}
}
}
}
}
vtkSmartPointer<vtkTable> datasetTable = vtkSmartPointer<vtkTable>::New();
datasetTable->AddColumn(dataset1Arr);
datasetTable->AddColumn(dataset2Arr);
datasetTable->AddColumn(dataset3Arr);
vtkSmartPointer<vtkTable> datasetTableU = vtkSmartPointer<vtkTable>::New();
datasetTableU->AddColumn(dataset1ArrU);
datasetTableU->AddColumn(dataset2ArrU);
datasetTableU->AddColumn(dataset3ArrU);
vtkSmartPointer<vtkPCAStatistics> pcaStatistics = vtkSmartPointer<vtkPCAStatistics>::New();
pcaStatistics->SetInputData(vtkStatisticsAlgorithm::INPUT_DATA, datasetTable);
pcaStatistics->SetColumnStatus("M1", 1);
pcaStatistics->SetColumnStatus("M2", 1);
pcaStatistics->SetColumnStatus("M3", 1);
pcaStatistics->RequestSelectedColumns();
pcaStatistics->SetDeriveOption(true);
pcaStatistics->Update();
vtkSmartPointer<vtkDoubleArray> eigenvalues = vtkSmartPointer<vtkDoubleArray>::New();
pcaStatistics->GetEigenvalues(eigenvalues);
pcaStatistics->SetInputData(vtkStatisticsAlgorithm::INPUT_DATA, datasetTableU);
pcaStatistics->Update();
vtkSmartPointer<vtkDoubleArray> eigenvaluesU = vtkSmartPointer<vtkDoubleArray>::New();
pcaStatistics->GetEigenvalues(eigenvaluesU);
std::vector<double> eigen_val(3);
std::vector<double> eigen_valUC(3);
eigen_val[2] = eigenvalues->GetValue(0);
eigen_val[1] = eigenvalues->GetValue(1);
eigen_val[0] = eigenvalues->GetValue(2);
eigen_valUC[2] = eigenvaluesU->GetValue(0);
eigen_valUC[1] = eigenvaluesU->GetValue(1);
eigen_valUC[0] = eigenvaluesU->GetValue(2);
double major = 4 * sqrt(eigen_val[2]);
double minor = 4 * sqrt(eigen_val[1]);
double least = 4 * sqrt(eigen_val[0]);
double elongation = (major == 0) ? 0 : sqrt(eigen_val[1] / eigen_val[2]);
double flatness = (major == 0) ? 0 : sqrt(eigen_val[0] / eigen_val[2]);
double majorUC = 4 * sqrt(eigen_valUC[2]);
double minorUC = 4 * sqrt(eigen_valUC[1]);
double leastUC = 4 * sqrt(eigen_valUC[0]);
double elongationUC = majorUC == 0 ? 0 : sqrt(eigen_valUC[1] / eigen_valUC[2]);
double flatnessUC = majorUC == 0 ? 0 : sqrt(eigen_valUC[0] / eigen_valUC[2]);
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Volume (mesh based)"), meshVolume));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Surface (mesh based)"), meshSurf));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Surface to volume ratio (mesh based)"), surfaceToVolume));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Sphericity (mesh based)"), sphericity));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Sphericity (mesh, mesh based)"), sphericityMesh));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Asphericity (mesh based)"), asphericity));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Asphericity (mesh, mesh based)"), asphericityMesh));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 1 (mesh based)"), compactness3));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 1 old (mesh based)"), compactness1));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 2 (mesh based)"), compactness2));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 1 (mesh, mesh based)"), compactness3MeshMesh));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 2 (mesh, mesh based)"), compactness2MeshMesh));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Spherical disproportion (mesh based)"), sphericalDisproportion));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Spherical disproportion (mesh, mesh based)"), sphericalDisproportionMesh));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Surface to volume ratio (voxel based)"), surfaceToVolumePixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Sphericity (voxel based)"), sphericityPixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Asphericity (voxel based)"), asphericityPixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 1 (voxel based)"), compactness3Pixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 1 old (voxel based)"), compactness1Pixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Compactness 2 (voxel based)"), compactness2Pixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "Spherical disproportion (voxel based)"), sphericalDisproportionPixel));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Major axis length"), major));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Minor axis length"), minor));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Least axis length"), least));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Elongation"), elongation));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Flatness"), flatness));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Major axis length (uncorrected)"), majorUC));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Minor axis length (uncorrected)"), minorUC));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Least axis length (uncorrected)"), leastUC));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Elongation (uncorrected)"), elongationUC));
featureList.push_back(std::make_pair(mitk::CreateFeatureID(featureID, "PCA Flatness (uncorrected)"), flatnessUC));
MITK_INFO << "Finished calculating volumetric features....";
return featureList;
}
mitk::AbstractGlobalImageFeature::FeatureListType mitk::GIFVolumetricStatistics::CalculateFeatures(const Image* image, const Image* mask, const Image* )
{
return Superclass::CalculateFeatures(image, mask);
}
diff --git a/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkGlobalImageFeaturesParameter.cpp b/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkGlobalImageFeaturesParameter.cpp
index 8b12aeca34..cc02fcb505 100644
--- a/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkGlobalImageFeaturesParameter.cpp
+++ b/Modules/Classification/CLUtilities/src/MiniAppUtils/mitkGlobalImageFeaturesParameter.cpp
@@ -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.
============================================================================*/
#include <mitkGlobalImageFeaturesParameter.h>
#include <fstream>
#include <itkFileTools.h>
#include <itksys/SystemTools.hxx>
static bool fileExists(const std::string& filename)
{
std::ifstream infile(filename.c_str());
bool isGood = infile.good();
infile.close();
return isGood;
}
void mitk::cl::GlobalImageFeaturesParameter::AddParameter(mitkCommandLineParser &parser)
{
// Required Parameter
parser.addArgument("image", "i", mitkCommandLineParser::Image, "Input Image", "Path to the input image file", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("mask", "m", mitkCommandLineParser::Image, "Input Mask", "Path to the mask Image that specifies the area over for the statistic (Values = 1)", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("morph-mask", "morph", mitkCommandLineParser::Image, "Morphological Image Mask", "Path to the mask Image that specifies the area over for the statistic (Values = 1)", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument("output", "o", mitkCommandLineParser::File, "Output text file", "Path to output file. The output statistic is appended to this file.", us::Any(), false, false, false, mitkCommandLineParser::Output);
// Optional Parameter
parser.addArgument("xml-output", "x", mitkCommandLineParser::File, "XML result file", "Path where the results should be stored as XML result file. ", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument("logfile", "log", mitkCommandLineParser::File, "Text Logfile", "Path to the location of the target log file. ", us::Any(), true, false, false, mitkCommandLineParser::Input);
- parser.addArgument("save-image", "save-image", mitkCommandLineParser::File, "Output Image", "If spezified, the image that is used for the analysis is saved to this location.", us::Any(), true, false, false, mitkCommandLineParser::Output);
- parser.addArgument("save-mask", "save-mask", mitkCommandLineParser::File, "Output Image", "If spezified, the mask that is used for the analysis is saved to this location. ", us::Any(), true, false, false, mitkCommandLineParser::Output);
- parser.addArgument("save-image-screenshots", "save-screenshot", mitkCommandLineParser::File, "Output Image", "If spezified, a screenshot of each slice is saved. Specify an EXISTING folder with prefix (for example ~/demo/ or ~/demo/image-", us::Any(), true, false, false, mitkCommandLineParser::Output);
+ parser.addArgument("save-image", "save-image", mitkCommandLineParser::File, "Output Image", "If specified, the image that is used for the analysis is saved to this location.", us::Any(), true, false, false, mitkCommandLineParser::Output);
+ parser.addArgument("save-mask", "save-mask", mitkCommandLineParser::File, "Output Image", "If specified, the mask that is used for the analysis is saved to this location. ", us::Any(), true, false, false, mitkCommandLineParser::Output);
+ parser.addArgument("save-image-screenshots", "save-screenshot", mitkCommandLineParser::File, "Output Image", "If specified, a screenshot of each slice is saved. Specify an EXISTING folder with prefix (for example ~/demo/ or ~/demo/image-", us::Any(), true, false, false, mitkCommandLineParser::Output);
parser.addArgument("header", "head", mitkCommandLineParser::Bool, "Add Header (Labels) to output", "", us::Any());
parser.addArgument("first-line-header", "fl-head", mitkCommandLineParser::Bool, "Add Header (Labels) to first line of output", "", us::Any());
parser.addArgument("decimal-point", "decimal", mitkCommandLineParser::String, "Decima Point that is used in Conversion", "", us::Any());
parser.addArgument("resample-mask", "rm", mitkCommandLineParser::Bool, "Bool", "Resamples the mask to the resolution of the input image ", us::Any());
parser.addArgument("same-space", "sp", mitkCommandLineParser::Bool, "Bool", "Set the spacing of all images to equal. Otherwise an error will be thrown. ", us::Any());
parser.addArgument("fixed-isotropic", "fi", mitkCommandLineParser::Float, "Float", "Input image resampled to fixed isotropic resolution given in mm. Should be used with resample-mask ", us::Any());
parser.addArgument("minimum-intensity", "minimum", mitkCommandLineParser::Float, "Float", "Minimum intensity. If set, it is overwritten by more specific intensity minima", us::Any());
parser.addArgument("maximum-intensity", "maximum", mitkCommandLineParser::Float, "Float", "Maximum intensity. If set, it is overwritten by more specific intensity maxima", us::Any());
parser.addArgument("bins", "bins", mitkCommandLineParser::Int, "Int", "Number of bins if bins are used. If set, it is overwritten by more specific bin count", us::Any());
parser.addArgument("binsize", "binsize", mitkCommandLineParser::Float, "Int", "Size of bins that is used. If set, it is overwritten by more specific bin count", us::Any());
parser.addArgument("ignore-mask-for-histogram", "ignore-mask", mitkCommandLineParser::Bool, "Bool", "If the whole image is used to calculate the histogram. ", us::Any());
parser.addArgument("encode-parameter-in-name", "encode-parameter", mitkCommandLineParser::Bool, "Bool", "If true, the parameters used for each feature is encoded in its name.", us::Any());
parser.addArgument("pipeline-uid", "p", mitkCommandLineParser::String, "Pipeline UID", "UID that is stored in the XML output and identifies the processing pipeline the app is used in.", us::Any());
parser.addArgument("all-features", "a", mitkCommandLineParser::Bool, "Calculate all features", "If true, all features will be calculated and the feature specific activation will be ignored.", us::Any());
}
void mitk::cl::GlobalImageFeaturesParameter::ParseParameter(std::map<std::string, us::Any> parsedArgs)
{
ParseFileLocations(parsedArgs);
ParseAdditionalOutputs(parsedArgs);
ParseHeaderInformation(parsedArgs);
ParseMaskAdaptation(parsedArgs);
ParseGlobalFeatureParameter(parsedArgs);
}
void mitk::cl::GlobalImageFeaturesParameter::ParseFileLocations(std::map<std::string, us::Any> &parsedArgs)
{
//
// Read input and output file informations
//
imagePath = parsedArgs["image"].ToString();
maskPath = parsedArgs["mask"].ToString();
outputPath = parsedArgs["output"].ToString();
imageFolder = itksys::SystemTools::GetFilenamePath(imagePath);
imageName = itksys::SystemTools::GetFilenameName(imagePath);
maskFolder = itksys::SystemTools::GetFilenamePath(maskPath);
maskName = itksys::SystemTools::GetFilenameName(maskPath);
useMorphMask = false;
if (parsedArgs.count("morph-mask"))
{
useMorphMask = true;
morphPath = parsedArgs["morph-mask"].ToString();
morphName = itksys::SystemTools::GetFilenameName(morphPath);
}
outputXMLPath = "";
if (parsedArgs.count("xml-output"))
{
outputXMLPath = parsedArgs["xml-output"].ToString();
}
}
void mitk::cl::GlobalImageFeaturesParameter::ParseAdditionalOutputs(std::map<std::string, us::Any> &parsedArgs)
{
//
// Read input and output file informations
//
useLogfile = false;
if (parsedArgs.count("logfile"))
{
useLogfile = true;
logfilePath = us::any_cast<std::string>(parsedArgs["logfile"]);
}
writeAnalysisImage = false;
if (parsedArgs.count("save-image"))
{
writeAnalysisImage = true;
anaylsisImagePath = us::any_cast<std::string>(parsedArgs["save-image"]);
}
writeAnalysisMask = false;
if (parsedArgs.count("save-mask"))
{
writeAnalysisMask = true;
analysisMaskPath = us::any_cast<std::string>(parsedArgs["save-mask"]);
}
writePNGScreenshots = false;
if (parsedArgs.count("save-image-screenshots"))
{
writePNGScreenshots = true;
pngScreenshotsPath = us::any_cast<std::string>(parsedArgs["save-image-screenshots"]);
std::string pngScrenshotFolderPath = itksys::SystemTools::GetFilenamePath(pngScreenshotsPath);
if (pngScreenshotsPath.back() == '/' || pngScreenshotsPath.back() == '\\')
{
pngScrenshotFolderPath = pngScreenshotsPath;
}
itk::FileTools::CreateDirectory(pngScrenshotFolderPath.c_str());
}
useDecimalPoint = false;
if (parsedArgs.count("decimal-point"))
{
auto tmpDecimalPoint = us::any_cast<std::string>(parsedArgs["decimal-point"]);
if (tmpDecimalPoint.length() > 0)
{
useDecimalPoint = true;
decimalPoint = tmpDecimalPoint.at(0);
}
}
pipelineUID = "";
if (parsedArgs.count("pipeline-uid"))
{
pipelineUID = us::any_cast<std::string>(parsedArgs["pipeline-uid"]);
}
calculateAllFeatures = parsedArgs.count("all-features");
}
void mitk::cl::GlobalImageFeaturesParameter::ParseHeaderInformation(std::map<std::string, us::Any> &parsedArgs)
{
//
// Check if an header is required or not. Consider also first line header option.
//
useHeader = false;
useHeaderForFirstLineOnly = false;
if (parsedArgs.count("header"))
{
useHeader = us::any_cast<bool>(parsedArgs["header"]);
}
if (parsedArgs.count("first-line-header"))
{
useHeaderForFirstLineOnly = us::any_cast<bool>(parsedArgs["first-line-header"]);
}
if (useHeaderForFirstLineOnly)
{
useHeader = !fileExists(outputPath);
}
}
void mitk::cl::GlobalImageFeaturesParameter::ParseMaskAdaptation(std::map<std::string, us::Any> &parsedArgs)
{
//
// Parse parameters that control how the input mask is adapted to the input image
//
resampleMask = false;
ensureSameSpace = false;
resampleToFixIsotropic = false;
resampleResolution = 1.0;
if (parsedArgs.count("resample-mask"))
{
resampleMask = us::any_cast<bool>(parsedArgs["resample-mask"]);
}
if (parsedArgs.count("same-space"))
{
ensureSameSpace = us::any_cast<bool>(parsedArgs["same-space"]);
}
if (parsedArgs.count("fixed-isotropic"))
{
resampleToFixIsotropic = true;
resampleResolution = us::any_cast<float>(parsedArgs["fixed-isotropic"]);
}
}
void mitk::cl::GlobalImageFeaturesParameter::ParseGlobalFeatureParameter(std::map<std::string, us::Any> &parsedArgs)
{
//
// Parse parameters that control how the input mask is adapted to the input image
//
defineGlobalMinimumIntensity = false;
defineGlobalMaximumIntensity = false;
defineGlobalNumberOfBins = false;
encodeParameter = false;
if (parsedArgs.count("minimum-intensity"))
{
defineGlobalMinimumIntensity = true;
globalMinimumIntensity = us::any_cast<float>(parsedArgs["minimum-intensity"]);
}
if (parsedArgs.count("maximum-intensity"))
{
defineGlobalMaximumIntensity = true;
globalMaximumIntensity = us::any_cast<float>(parsedArgs["maximum-intensity"]);
}
if (parsedArgs.count("bins"))
{
defineGlobalNumberOfBins = true;
globalNumberOfBins = us::any_cast<int>(parsedArgs["bins"]);
}
if (parsedArgs.count("encode-parameter-in-name"))
{
encodeParameter = true;
}
}
diff --git a/Modules/Classification/CLUtilities/src/mitkCLUtil.cpp b/Modules/Classification/CLUtilities/src/mitkCLUtil.cpp
index d67040b3f6..2b78ecf3d4 100644
--- a/Modules/Classification/CLUtilities/src/mitkCLUtil.cpp
+++ b/Modules/Classification/CLUtilities/src/mitkCLUtil.cpp
@@ -1,642 +1,642 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 _mitkCLUtil_HXX
#define _mitkCLUtil_HXX
#include <mitkCLUtil.h>
#include <mitkImageAccessByItk.h>
-#include <Eigen/Dense>
+#include <itkeigen/Eigen/Dense>
#include <itkImage.h>
// itk includes
#include <itkCheckerBoardImageFilter.h>
#include <itkShapedNeighborhoodIterator.h>
#include "itkHessianRecursiveGaussianImageFilter.h"
#include "itkUnaryFunctorImageFilter.h"
#include "vnl/algo/vnl_symmetric_eigensystem.h"
#include <itkLaplacianRecursiveGaussianImageFilter.h>
#include <itkMultiHistogramFilter.h>
// Morphologic Operations
#include <itkBinaryBallStructuringElement.h>
#include <itkBinaryDilateImageFilter.h>
#include <itkBinaryErodeImageFilter.h>
#include <itkBinaryFillholeImageFilter.h>
#include <itkBinaryMorphologicalClosingImageFilter.h>
#include <itkGrayscaleErodeImageFilter.h>
#include <itkGrayscaleDilateImageFilter.h>
#include <itkGrayscaleFillholeImageFilter.h>
// Image Filter
#include <itkDiscreteGaussianImageFilter.h>
#include <itkSubtractImageFilter.h>
void mitk::CLUtil::ProbabilityMap(const mitk::Image::Pointer & image , double mean, double stddev, mitk::Image::Pointer & outimage)
{
AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkProbabilityMap, 3, mean, stddev, outimage);
}
void mitk::CLUtil::ErodeGrayscale(mitk::Image::Pointer & image , unsigned int radius, mitk::CLUtil::MorphologicalDimensions d, mitk::Image::Pointer & outimage)
{
AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkErodeGrayscale, 3, outimage, radius, d);
}
void mitk::CLUtil::DilateGrayscale(mitk::Image::Pointer & image, unsigned int radius, mitk::CLUtil::MorphologicalDimensions d, mitk::Image::Pointer & outimage)
{
AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkDilateGrayscale, 3, outimage, radius, d);
}
void mitk::CLUtil::FillHoleGrayscale(mitk::Image::Pointer & image, mitk::Image::Pointer & outimage)
{
AccessFixedDimensionByItk_1(image, mitk::CLUtil::itkFillHoleGrayscale, 3, outimage);
}
void mitk::CLUtil::InsertLabel(mitk::Image::Pointer & image, mitk::Image::Pointer & maskImage, unsigned int label)
{
AccessByItk_2(image, mitk::CLUtil::itkInsertLabel, maskImage, label);
}
void mitk::CLUtil::GrabLabel(mitk::Image::Pointer & image, mitk::Image::Pointer & outimage, unsigned int label)
{
AccessFixedDimensionByItk_2(image, mitk::CLUtil::itkGrabLabel, 3, outimage, label);
}
void mitk::CLUtil::ConnectedComponentsImage(mitk::Image::Pointer & image, mitk::Image::Pointer& mask, mitk::Image::Pointer &outimage, unsigned int& num_components)
{
AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkConnectedComponentsImage,3, mask, outimage, num_components);
}
void mitk::CLUtil::MergeLabels(mitk::Image::Pointer & img, const std::map<unsigned int, unsigned int> & map)
{
AccessByItk_1(img, mitk::CLUtil::itkMergeLabels, map);
}
void mitk::CLUtil::CountVoxel(mitk::Image::Pointer image, std::map<unsigned int, unsigned int> & map)
{
AccessByItk_1(image, mitk::CLUtil::itkCountVoxel, map);
}
void mitk::CLUtil::CountVoxel(mitk::Image::Pointer image, unsigned int label, unsigned int & count)
{
AccessByItk_2(image, mitk::CLUtil::itkCountVoxel, label, count);
}
void mitk::CLUtil::CountVoxel(mitk::Image::Pointer image, unsigned int & count)
{
AccessByItk_1(image, mitk::CLUtil::itkCountVoxel, count);
}
void mitk::CLUtil::CreateCheckerboardMask(mitk::Image::Pointer image, mitk::Image::Pointer & outimage)
{
AccessFixedDimensionByItk_1(image, mitk::CLUtil::itkCreateCheckerboardMask,3, outimage);
}
void mitk::CLUtil::LogicalAndImages(const mitk::Image::Pointer & image1, const mitk::Image::Pointer & image2, mitk::Image::Pointer & outimage)
{
AccessFixedDimensionByItk_2(image1,itkLogicalAndImages, 3, image2, outimage);
}
void mitk::CLUtil::InterpolateCheckerboardPrediction(mitk::Image::Pointer checkerboard_prediction, mitk::Image::Pointer & checkerboard_mask, mitk::Image::Pointer & outimage)
{
AccessFixedDimensionByItk_2(checkerboard_prediction, mitk::CLUtil::itkInterpolateCheckerboardPrediction,3, checkerboard_mask, outimage);
}
void mitk::CLUtil::GaussianFilter(mitk::Image::Pointer image, mitk::Image::Pointer & smoothed ,double sigma)
{
AccessFixedDimensionByItk_2(image, mitk::CLUtil::itkGaussianFilter,3, smoothed, sigma);
}
void mitk::CLUtil::DifferenceOfGaussianFilter(mitk::Image::Pointer image, mitk::Image::Pointer & smoothed, double sigma1, double sigma2)
{
AccessFixedDimensionByItk_3(image, mitk::CLUtil::itkDifferenceOfGaussianFilter, 3, smoothed, sigma1, sigma2);
}
void mitk::CLUtil::LaplacianOfGaussianFilter(mitk::Image::Pointer image, mitk::Image::Pointer & smoothed, double sigma1)
{
AccessByItk_2(image, mitk::CLUtil::itkLaplacianOfGaussianFilter, sigma1, smoothed);
}
void mitk::CLUtil::HessianOfGaussianFilter(mitk::Image::Pointer image, std::vector<mitk::Image::Pointer> &out, double sigma)
{
AccessByItk_2(image, mitk::CLUtil::itkHessianOfGaussianFilter, sigma, out);
}
void mitk::CLUtil::LocalHistogram(mitk::Image::Pointer image, std::vector<mitk::Image::Pointer> &out, int Bins, int NeighbourhoodSize)
{
AccessByItk_3(image, mitk::CLUtil::itkLocalHistograms, out, NeighbourhoodSize, Bins);
}
void mitk::CLUtil::DilateBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int factor , MorphologicalDimensions d)
{
AccessFixedDimensionByItk_3(sourceImage, mitk::CLUtil::itkDilateBinary, 3, resultImage, factor, d);
}
void mitk::CLUtil::ErodeBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d)
{
AccessFixedDimensionByItk_3(sourceImage, mitk::CLUtil::itkErodeBinary, 3, resultImage, factor, d);
}
void mitk::CLUtil::ClosingBinary(mitk::Image::Pointer & sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d)
{
AccessFixedDimensionByItk_3(sourceImage, mitk::CLUtil::itkClosingBinary, 3, resultImage, factor, d);
}
template<typename TImageType>
void mitk::CLUtil::itkProbabilityMap(const TImageType * sourceImage, double mean, double std_dev, mitk::Image::Pointer& resultImage)
{
itk::Image<double, 3>::Pointer itk_img = itk::Image<double, 3>::New();
itk_img->SetRegions(sourceImage->GetLargestPossibleRegion());
itk_img->SetOrigin(sourceImage->GetOrigin());
itk_img->SetSpacing(sourceImage->GetSpacing());
itk_img->SetDirection(sourceImage->GetDirection());
itk_img->Allocate();
itk::ImageRegionConstIterator<TImageType> it(sourceImage,sourceImage->GetLargestPossibleRegion());
itk::ImageRegionIterator<itk::Image<double, 3> > outit(itk_img,itk_img->GetLargestPossibleRegion());
while(!it.IsAtEnd())
{
double x = it.Value();
double prob = (1.0/(std_dev*std::sqrt(2.0*itk::Math::pi))) * std::exp(-(((x-mean)*(x-mean))/(2.0*std_dev*std_dev)));
outit.Set(prob);
++it;
++outit;
}
mitk::CastToMitkImage(itk_img, resultImage);
}
template< typename TImageType >
void mitk::CLUtil::itkInterpolateCheckerboardPrediction(TImageType * checkerboard_prediction, Image::Pointer &checkerboard_mask, mitk::Image::Pointer & outimage)
{
typename TImageType::Pointer itk_checkerboard_mask;
mitk::CastToItkImage(checkerboard_mask,itk_checkerboard_mask);
typename TImageType::Pointer itk_outimage = TImageType::New();
itk_outimage->SetRegions(checkerboard_prediction->GetLargestPossibleRegion());
itk_outimage->SetDirection(checkerboard_prediction->GetDirection());
itk_outimage->SetOrigin(checkerboard_prediction->GetOrigin());
itk_outimage->SetSpacing(checkerboard_prediction->GetSpacing());
itk_outimage->Allocate();
itk_outimage->FillBuffer(0);
//typedef typename itk::ShapedNeighborhoodIterator<TImageType>::SizeType SizeType;
typedef itk::Size<3> SizeType;
SizeType size;
size.Fill(1);
itk::ShapedNeighborhoodIterator<TImageType> iit(size,checkerboard_prediction,checkerboard_prediction->GetLargestPossibleRegion());
itk::ShapedNeighborhoodIterator<TImageType> mit(size,itk_checkerboard_mask,itk_checkerboard_mask->GetLargestPossibleRegion());
itk::ImageRegionIterator<TImageType> oit(itk_outimage,itk_outimage->GetLargestPossibleRegion());
typedef typename itk::ShapedNeighborhoodIterator<TImageType>::OffsetType OffsetType;
OffsetType offset;
offset.Fill(0);
offset[0] = 1; // {1,0,0}
iit.ActivateOffset(offset);
mit.ActivateOffset(offset);
offset[0] = -1; // {-1,0,0}
iit.ActivateOffset(offset);
mit.ActivateOffset(offset);
offset[0] = 0; offset[1] = 1; //{0,1,0}
iit.ActivateOffset(offset);
mit.ActivateOffset(offset);
offset[1] = -1; //{0,-1,0}
iit.ActivateOffset(offset);
mit.ActivateOffset(offset);
// iit.ActivateOffset({{0,0,1}});
// iit.ActivateOffset({{0,0,-1}});
// mit.ActivateOffset({{0,0,1}});
// mit.ActivateOffset({{0,0,-1}});
while(!iit.IsAtEnd())
{
if(mit.GetCenterPixel() == 0)
{
typename TImageType::PixelType mean = 0;
for (auto i = iit.Begin(); ! i.IsAtEnd(); i++)
{ mean += i.Get(); }
//std::sort(list.begin(),list.end(),[](const typename TImageType::PixelType x,const typename TImageType::PixelType y){return x<=y;});
oit.Set((mean+0.5)/6.0);
}
else
{
oit.Set(iit.GetCenterPixel());
}
++iit;
++mit;
++oit;
}
mitk::CastToMitkImage(itk_outimage,outimage);
}
template< typename TImageType >
void mitk::CLUtil::itkCreateCheckerboardMask(TImageType * image, mitk::Image::Pointer & outimage)
{
typename TImageType::Pointer zeroimg = TImageType::New();
zeroimg->SetRegions(image->GetLargestPossibleRegion());
zeroimg->SetDirection(image->GetDirection());
zeroimg->SetOrigin(image->GetOrigin());
zeroimg->SetSpacing(image->GetSpacing());
zeroimg->Allocate();
zeroimg->FillBuffer(0);
typedef itk::CheckerBoardImageFilter<TImageType> FilterType;
typename FilterType::Pointer filter = FilterType::New();
filter->SetInput1(image);
filter->SetInput2(zeroimg);
typename FilterType::PatternArrayType pattern;
pattern.SetElement(0,(image->GetLargestPossibleRegion().GetSize()[0]));
pattern.SetElement(1,(image->GetLargestPossibleRegion().GetSize()[1]));
pattern.SetElement(2,(image->GetLargestPossibleRegion().GetSize()[2]));
filter->SetCheckerPattern(pattern);
filter->Update();
mitk::CastToMitkImage(filter->GetOutput(), outimage);
}
template <class TImageType>
void mitk::CLUtil::itkSumVoxelForLabel(TImageType* image, const mitk::Image::Pointer & source , typename TImageType::PixelType label, double & val )
{
itk::Image<double,3>::Pointer itk_source;
mitk::CastToItkImage(source,itk_source);
itk::ImageRegionConstIterator<TImageType> inputIter(image, image->GetLargestPossibleRegion());
itk::ImageRegionConstIterator< itk::Image<double,3> > sourceIter(itk_source, itk_source->GetLargestPossibleRegion());
while(!inputIter.IsAtEnd())
{
if(inputIter.Value() == label) val += sourceIter.Value();
++inputIter;
++sourceIter;
}
}
template <class TImageType>
void mitk::CLUtil::itkSqSumVoxelForLabel(TImageType* image, const mitk::Image::Pointer & source, typename TImageType::PixelType label, double & val )
{
itk::Image<double,3>::Pointer itk_source;
mitk::CastToItkImage(source,itk_source);
itk::ImageRegionConstIterator<TImageType> inputIter(image, image->GetLargestPossibleRegion());
itk::ImageRegionConstIterator< itk::Image<double,3> > sourceIter(itk_source, itk_source->GetLargestPossibleRegion());
while(!inputIter.IsAtEnd())
{
if(inputIter.Value() == label) val += sourceIter.Value() * sourceIter.Value();
++inputIter;
++sourceIter;
}
}
template<typename TStructuringElement>
void mitk::CLUtil::itkFitStructuringElement(TStructuringElement & se, MorphologicalDimensions d, int factor)
{
typename TStructuringElement::SizeType size;
size.Fill(factor);
switch(d)
{
case(All):
case(Axial):
size.SetElement(2,0);
break;
case(Sagittal):
size.SetElement(0,0);
break;
case(Coronal):
size.SetElement(1,0);
break;
}
se.SetRadius(size);
se.CreateStructuringElement();
}
template<typename TImageType>
void mitk::CLUtil::itkClosingBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d)
{
typedef itk::BinaryBallStructuringElement<typename TImageType::PixelType, 3> BallType;
typedef itk::BinaryMorphologicalClosingImageFilter<TImageType, TImageType, BallType> FilterType;
BallType strElem;
itkFitStructuringElement(strElem,d,factor);
typename FilterType::Pointer erodeFilter = FilterType::New();
erodeFilter->SetKernel(strElem);
erodeFilter->SetInput(sourceImage);
erodeFilter->SetForegroundValue(1);
erodeFilter->Update();
mitk::CastToMitkImage(erodeFilter->GetOutput(), resultImage);
}
template<typename TImageType>
void mitk::CLUtil::itkDilateBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d)
{
typedef itk::BinaryBallStructuringElement<typename TImageType::PixelType, 3> BallType;
typedef typename itk::BinaryDilateImageFilter<TImageType, TImageType, BallType> BallDilateFilterType;
BallType strElem;
itkFitStructuringElement(strElem,d,factor);
typename BallDilateFilterType::Pointer erodeFilter = BallDilateFilterType::New();
erodeFilter->SetKernel(strElem);
erodeFilter->SetInput(sourceImage);
erodeFilter->SetDilateValue(1);
erodeFilter->Update();
mitk::CastToMitkImage(erodeFilter->GetOutput(), resultImage);
}
template<typename TImageType>
void mitk::CLUtil::itkErodeBinary(TImageType * sourceImage, mitk::Image::Pointer& resultImage, int factor, MorphologicalDimensions d)
{
typedef itk::BinaryBallStructuringElement<typename TImageType::PixelType, 3> BallType;
typedef typename itk::BinaryErodeImageFilter<TImageType, TImageType, BallType> BallErodeFilterType;
BallType strElem;
itkFitStructuringElement(strElem,d,factor);
typename BallErodeFilterType::Pointer erodeFilter = BallErodeFilterType::New();
erodeFilter->SetKernel(strElem);
erodeFilter->SetInput(sourceImage);
erodeFilter->SetErodeValue(1);
// erodeFilter->UpdateLargestPossibleRegion();
erodeFilter->Update();
mitk::CastToMitkImage(erodeFilter->GetOutput(), resultImage);
}
///
/// \brief itkFillHolesBinary
/// \param sourceImage
/// \param resultImage
///
template<typename TPixel, unsigned int VDimension>
void mitk::CLUtil::itkFillHolesBinary(itk::Image<TPixel, VDimension>* sourceImage, mitk::Image::Pointer& resultImage)
{
typedef itk::Image<TPixel, VDimension> ImageType;
typedef typename itk::BinaryFillholeImageFilter<ImageType> FillHoleFilterType;
typename FillHoleFilterType::Pointer fillHoleFilter = FillHoleFilterType::New();
fillHoleFilter->SetInput(sourceImage);
fillHoleFilter->SetForegroundValue(1);
fillHoleFilter->Update();
mitk::CastToMitkImage(fillHoleFilter->GetOutput(), resultImage);
}
///
/// \brief itkLogicalAndImages
/// \param image1 keep the values of image 1
/// \param image2
///
template<typename TImageType>
void mitk::CLUtil::itkLogicalAndImages(const TImageType * image1, const mitk::Image::Pointer & image2, mitk::Image::Pointer & outimage)
{
typename TImageType::Pointer itk_outimage = TImageType::New();
itk_outimage->SetRegions(image1->GetLargestPossibleRegion());
itk_outimage->SetDirection(image1->GetDirection());
itk_outimage->SetOrigin(image1->GetOrigin());
itk_outimage->SetSpacing(image1->GetSpacing());
itk_outimage->Allocate();
itk_outimage->FillBuffer(0);
typename TImageType::Pointer itk_image2;
mitk::CastToItkImage(image2,itk_image2);
itk::ImageRegionConstIterator<TImageType> it1(image1, image1->GetLargestPossibleRegion());
itk::ImageRegionConstIterator<TImageType> it2(itk_image2, itk_image2->GetLargestPossibleRegion());
itk::ImageRegionIterator<TImageType> oit(itk_outimage,itk_outimage->GetLargestPossibleRegion());
while(!it1.IsAtEnd())
{
if(it1.Value() == 0 || it2.Value() == 0)
{
oit.Set(0);
}else
oit.Set(it1.Value());
++it1;
++it2;
++oit;
}
mitk::CastToMitkImage(itk_outimage, outimage);
}
///
/// \brief GaussianFilter
/// \param image
/// \param smoothed
/// \param sigma
///
template<class TImageType>
void mitk::CLUtil::itkGaussianFilter(TImageType * image, mitk::Image::Pointer & smoothed ,double sigma)
{
typedef itk::DiscreteGaussianImageFilter<TImageType,TImageType> FilterType;
typename FilterType::Pointer filter = FilterType::New();
filter->SetInput(image);
filter->SetVariance(sigma);
filter->Update();
mitk::CastToMitkImage(filter->GetOutput(),smoothed);
}
template<class TImageType>
void mitk::CLUtil::itkDifferenceOfGaussianFilter(TImageType * image, mitk::Image::Pointer & smoothed, double sigma1, double sigma2)
{
typedef itk::DiscreteGaussianImageFilter<TImageType, TImageType> FilterType;
typedef itk::SubtractImageFilter <TImageType, TImageType> SubtractFilterType;
typename FilterType::Pointer filter1 = FilterType::New();
typename FilterType::Pointer filter2 = FilterType::New();
typename SubtractFilterType::Pointer subFilter = SubtractFilterType::New();
filter1->SetInput(image);
filter1->SetVariance(sigma1);
filter1->Update();
filter2->SetInput(image);
filter2->SetVariance(sigma2);
filter2->Update();
subFilter->SetInput1(filter1->GetOutput());
subFilter->SetInput2(filter2->GetOutput());
subFilter->Update();
mitk::CastToMitkImage(subFilter->GetOutput(), smoothed);
}
template<typename TPixel, unsigned int VImageDimension>
void mitk::CLUtil::itkLaplacianOfGaussianFilter(itk::Image<TPixel, VImageDimension>* itkImage, double variance, mitk::Image::Pointer &output)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::DiscreteGaussianImageFilter< ImageType, ImageType > GaussFilterType;
typedef itk::LaplacianRecursiveGaussianImageFilter<ImageType, ImageType> LaplacianFilter;
typename GaussFilterType::Pointer gaussianFilter = GaussFilterType::New();
gaussianFilter->SetInput(itkImage);
gaussianFilter->SetVariance(variance);
gaussianFilter->Update();
typename LaplacianFilter::Pointer laplaceFilter = LaplacianFilter::New();
laplaceFilter->SetInput(gaussianFilter->GetOutput());
laplaceFilter->Update();
mitk::CastToMitkImage(laplaceFilter->GetOutput(), output);
}
namespace Functor
{
template <class TInput, class TOutput>
class MatrixFirstEigenvalue
{
public:
MatrixFirstEigenvalue() {}
virtual ~MatrixFirstEigenvalue() {}
int order;
inline TOutput operator ()(const TInput& input)
{
double a, b, c;
if (input[0] < 0.01 && input[1] < 0.01 &&input[2] < 0.01 &&input[3] < 0.01 &&input[4] < 0.01 &&input[5] < 0.01)
return 0;
vnl_symmetric_eigensystem_compute_eigenvals(input[0], input[1], input[2], input[3], input[4], input[5], a, b, c);
switch (order)
{
case 0: return a;
case 1: return b;
case 2: return c;
default: return a;
}
}
bool operator !=(const MatrixFirstEigenvalue) const
{
return false;
}
bool operator ==(const MatrixFirstEigenvalue& other) const
{
return !(*this != other);
}
};
}
template<typename TPixel, unsigned int VImageDimension>
void mitk::CLUtil::itkHessianOfGaussianFilter(itk::Image<TPixel, VImageDimension>* itkImage, double variance, std::vector<mitk::Image::Pointer> &out)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<double, VImageDimension> FloatImageType;
typedef itk::HessianRecursiveGaussianImageFilter <ImageType> HessianFilterType;
typedef typename HessianFilterType::OutputImageType VectorImageType;
typedef Functor::MatrixFirstEigenvalue<typename VectorImageType::PixelType, double> DeterminantFunctorType;
typedef itk::UnaryFunctorImageFilter<VectorImageType, FloatImageType, DeterminantFunctorType> DetFilterType;
typename HessianFilterType::Pointer hessianFilter = HessianFilterType::New();
hessianFilter->SetInput(itkImage);
hessianFilter->SetSigma(std::sqrt(variance));
for (unsigned int i = 0; i < VImageDimension; ++i)
{
mitk::Image::Pointer tmpImage = mitk::Image::New();
typename DetFilterType::Pointer detFilter = DetFilterType::New();
detFilter->SetInput(hessianFilter->GetOutput());
detFilter->GetFunctor().order = i;
detFilter->Update();
mitk::CastToMitkImage(detFilter->GetOutput(), tmpImage);
out.push_back(tmpImage);
}
}
template<typename TPixel, unsigned int VImageDimension>
void mitk::CLUtil::itkLocalHistograms(itk::Image<TPixel, VImageDimension>* itkImage, std::vector<mitk::Image::Pointer> &out, int size, int bins)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::MultiHistogramFilter <ImageType, ImageType> MultiHistogramType;
typename MultiHistogramType::Pointer filter = MultiHistogramType::New();
filter->SetInput(itkImage);
filter->SetUseImageIntensityRange(true);
filter->SetSize(size);
filter->SetBins(bins);
filter->Update();
for (int i = 0; i < bins; ++i)
{
mitk::Image::Pointer img = mitk::Image::New();
mitk::CastToMitkImage(filter->GetOutput(i), img);
out.push_back(img);
}
}
template<class TImageType>
void mitk::CLUtil::itkErodeGrayscale(TImageType * image, mitk::Image::Pointer & outimage , unsigned int radius, mitk::CLUtil::MorphologicalDimensions d)
{
typedef itk::BinaryBallStructuringElement<typename TImageType::PixelType, 3> StructureElementType;
typedef itk::GrayscaleErodeImageFilter<TImageType,TImageType,StructureElementType> FilterType;
StructureElementType ball;
itkFitStructuringElement(ball,d, radius);
typename FilterType::Pointer filter = FilterType::New();
filter->SetKernel(ball);
filter->SetInput(image);
filter->Update();
mitk::CastToMitkImage(filter->GetOutput(),outimage);
}
template<class TImageType>
void mitk::CLUtil::itkDilateGrayscale(TImageType * image, mitk::Image::Pointer & outimage , unsigned int radius, mitk::CLUtil::MorphologicalDimensions d)
{
typedef itk::BinaryBallStructuringElement<typename TImageType::PixelType, 3> StructureElementType;
typedef itk::GrayscaleDilateImageFilter<TImageType,TImageType,StructureElementType> FilterType;
StructureElementType ball;
itkFitStructuringElement(ball,d, radius);
typename FilterType::Pointer filter = FilterType::New();
filter->SetKernel(ball);
filter->SetInput(image);
filter->Update();
mitk::CastToMitkImage(filter->GetOutput(),outimage);
}
template<class TImageType>
void mitk::CLUtil::itkFillHoleGrayscale(TImageType * image, mitk::Image::Pointer & outimage)
{
typedef itk::GrayscaleFillholeImageFilter<TImageType,TImageType> FilterType;
typename FilterType::Pointer filter = FilterType::New();
filter->SetInput(image);
filter->Update();
mitk::CastToMitkImage(filter->GetOutput(),outimage);
}
#endif
diff --git a/Modules/CommandLine/include/mitkCommandLineParser.h b/Modules/CommandLine/include/mitkCommandLineParser.h
index f91fe987d1..a751acbb20 100644
--- a/Modules/CommandLine/include/mitkCommandLineParser.h
+++ b/Modules/CommandLine/include/mitkCommandLineParser.h
@@ -1,372 +1,372 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkCommandLineParser_h
#define mitkCommandLineParser_h
#include <map>
#include <usAny.h>
#include <MitkCommandLineExports.h>
#include <mitkVersion.h>
/**
*
* The MITK command line parser, based on the CTK command line parser.
*
* Use this class to add information about the command line arguments
* your program understands and to easily parse them from a given list
* of strings.
*
* This parser provides the following features:
*
* <ul>
* <li>Add arguments by supplying a long name and/or a short name.
* Arguments are validated using a regular expression. They can have
* a default value and a help string.</li>
* <li>Deprecated arguments.</li>
* <li>Custom regular expressions for argument validation.</li>
* <li>Set different argument name prefixes for native platform look and feel.</li>
* <li>Create a help text for the command line arguments with support for
* grouping arguments.</li>
* </ul>
*
* The main difference between the MITK command line parser and the CTK command line
* parser is that the former does not depend on Qt. Apart from that an image type was
* added and XML output improved for automatic GUI generation.
*
* std::out is used for output to keep dependencies to a minimum.
*/
class MITKCOMMANDLINE_EXPORT mitkCommandLineParser
{
public:
enum Type
{
String = 0,
Bool = 1,
StringList = 2,
Int = 3,
Float = 4,
Directory = 5,
File = 6,
Image = 7
};
enum Channel
{
None = 0,
Input = 1,
Output = 2
};
typedef std::vector<std::string> StringContainerType;
mitkCommandLineParser();
~mitkCommandLineParser();
/**
* Parse a given list of command line arguments.
*
* This method parses a list of string elements considering the known arguments
* added by calls to <code>addArgument()</code>. If any one of the argument
* values does not match the corresponding regular expression,
* <code>ok</code> is set to false and an empty map object is returned.
*
* The keys in the returned map object correspond to the long argument string,
* if it is not empty. Otherwise, the short argument string is used as key. The
* us::Any values can safely be converted to the type specified in the
* <code>addArgument()</code> method call.
*
* @param arguments A StringContainerType containing command line arguments.
* @param ok A pointer to a boolean variable. Will be set to <code>true</code>
* if all regular expressions matched, <code>false</code> otherwise.
* @return A map object mapping the long argument (if empty, the short one)
* to a us::Any containing the value.
*/
std::map<std::string, us::Any> parseArguments(const StringContainerType &arguments, bool *ok = nullptr);
/**
* Convenient method allowing to parse a given list of command line arguments.
* @see parseArguments(const StringContainerType &, bool*)
*/
std::map<std::string, us::Any> parseArguments(int argc, char **argv, bool *ok = nullptr);
/**
* Returns a detailed error description if a call to <code>parseArguments()</code>
* failed.
*
- * @return The error description, empty if no error occured.
+ * @return The error description, empty if no error occurred.
* @see parseArguments(const StringContainerType&, bool*)
*/
std::string errorString() const;
/**
* This method returns all unparsed arguments, i.e. all arguments
* for which no long or short name has been registered via a call
* to <code>addArgument()</code>.
*
* @see addArgument()
*
* @return A list containing unparsed arguments.
*/
const StringContainerType &unparsedArguments() const;
/**
* Checks if the given argument has been added via a call
* to <code>addArgument()</code>.
*
* @see addArgument()
*
* @param argument The argument to be checked.
* @return <code>true</code> if the argument was added, <code>false</code>
* otherwise.
*/
bool argumentAdded(const std::string &argument) const;
/**
* Checks if the given argument has been parsed successfully by a previous
* call to <code>parseArguments()</code>.
*
* @param argument The argument to be checked.
* @return <code>true</code> if the argument was parsed, <code>false</code>
* otherwise.
*/
bool argumentParsed(const std::string &argument) const;
/**
* Adds a command line argument. An argument can have a long name
* (like --long-argument-name), a short name (like -l), or both. The type
* of the argument can be specified by using the <code>type</code> parameter.
* The following types are supported:
*
* <table>
* <tr><td><b>Type</b></td><td><b># of parameters</b></td><td><b>Default regular expr</b></td>
* <td><b>Example</b></td></tr>
* <tr><td>us::Any::String</td><td>1</td><td>.*</td><td>--test-string StringParameter</td></tr>
* <tr><td>us::Any::Bool</td><td>0</td><td>does not apply</td><td>--enable-something</td></tr>
* <tr><td>us::Any::StringList</td><td>-1</td><td>.*</td><td>--test-list string1 string2</td></tr>
* <tr><td>us::Any::Int</td><td>1</td><td>-?[0-9]+</td><td>--test-int -5</td></tr>
* </table>
*
* The regular expressions are used to validate the parameters of command line
* arguments. You can restrict the valid set of parameters by calling
* <code>setExactMatchRegularExpression()</code> for your argument.
*
* Optionally, a help string and a default value can be provided for the argument. If
* the us::Any type of the default value does not match <code>type</code>, an
* exception is thrown. Arguments with default values are always returned by
* <code>parseArguments()</code>.
*
* You can also declare an argument deprecated, by setting <code>deprecated</code>
* to <code>true</code>. Alternatively you can add a deprecated argument by calling
* <code>addDeprecatedArgument()</code>.
*
* If the long or short argument has already been added, or if both are empty strings,
* the method call has no effect.
*
* @param longarg The long argument name.
* @param shortarg The short argument name.
* @param type The argument type (see the list above for supported types).
* @param argLabel The label of this argument, when auto generated interface is used.
* @param argHelp A help string describing the argument.
* @param defaultValue A default value for the argument.
* @param optional
* @param ignoreRest All arguments after the current one will be ignored.
* @param deprecated Declares the argument deprecated.
* @param channel
*
* @see setExactMatchRegularExpression()
* @see addDeprecatedArgument()
* @throws std::logic_error If the us::Any type of <code>defaultValue</code>
* does not match <code>type</code>, a <code>std::logic_error</code> is thrown.
*/
void addArgument(const std::string &longarg,
const std::string &shortarg,
Type type,
const std::string &argLabel,
const std::string &argHelp = std::string(),
const us::Any &defaultValue = us::Any(),
bool optional = true,
bool ignoreRest = false,
bool deprecated = false,
mitkCommandLineParser::Channel channel = mitkCommandLineParser::Channel::None);
/**
* Adds a deprecated command line argument. If a deprecated argument is provided
* on the command line, <code>argHelp</code> is displayed in the console and
* processing continues with the next argument.
*
* Deprecated arguments are grouped separately at the end of the help text
* returned by <code>helpText()</code>.
*
* @param longarg The long argument name.
* @param shortarg The short argument name.
* @param argLabel
* @param argHelp A help string describing alternatives to the deprecated argument.
*/
void addDeprecatedArgument(const std::string &longarg,
const std::string &shortarg,
const std::string &argLabel,
const std::string &argHelp);
/**
* Returns the vector of current Command line Parameter
*
*/
std::vector < std::map<std::string, us::Any> > getArgumentList();
/**
* Sets a custom regular expression for validating argument parameters. The method
* <code>errorString()</code> can be used the get the last error description.
*
* @param argument The previously added long or short argument name.
* @param expression A regular expression which the arugment parameters must match.
* @param exactMatchFailedMessage An error message explaining why the parameter did
* not match.
*
* @return <code>true</code> if the argument was found and the regular expression was set,
* <code>false</code> otherwise.
*
* @see errorString()
*/
bool setExactMatchRegularExpression(const std::string &argument,
const std::string &expression,
const std::string &exactMatchFailedMessage);
/**
* The field width for the argument names without the help text.
*
* @return The argument names field width in the help text.
*/
std::string::size_type fieldWidth() const;
/**
* Creates a help text containing properly formatted argument names and help strings
* provided by calls to <code>addArgument()</code>. The arguments can be grouped by
* using <code>beginGroup()</code> and <code>endGroup()</code>.
*
* @return The formatted help text.
*/
std::string helpText() const;
/**
* Sets the argument prefix for long and short argument names. This can be used
* to create native command line arguments without changing the calls to
* <code>addArgument()</code>. For example on Unix-based systems, long argument
* names start with "--" and short names with "-", while on Windows argument names
* always start with "/".
*
* Note that all methods in mitkCommandLineParser which take an argument name
* expect the name as it was supplied to <code>addArgument</code>.
*
* Example usage:
*
* \code
* ctkCommandLineParser parser;
* parser.setArgumentPrefix("--", "-");
* parser.addArgument("long-argument", "l", us::Any::String);
* StringContainerType args;
* args << "program name" << "--long-argument Hi";
* parser.parseArguments(args);
* \endcode
*
* @param longPrefix The prefix for long argument names.
* @param shortPrefix The prefix for short argument names.
*/
void setArgumentPrefix(const std::string &longPrefix, const std::string &shortPrefix);
/**
* Begins a new group for documenting arguments. All newly added arguments via
* <code>addArgument()</code> will be put in the new group. You can close the
* current group by calling <code>endGroup()</code> or be opening a new group.
*
* Note that groups cannot be nested and all arguments which do not belong to
* a group will be listed at the top of the text created by <code>helpText()</code>.
*
* @param description The description of the group
*/
void beginGroup(const std::string &description);
/**
* Ends the current group.
*
* @see beginGroup(const std::string&)
*/
void endGroup();
/**
* Can be used to teach the parser to stop parsing the arguments and return False when
* an unknown argument is encountered. By default <code>StrictMode</code> is disabled.
*
* @see parseArguments(const StringContainerType &, bool*)
*/
void setStrictModeEnabled(bool strictMode);
/**
* Is used to generate an XML output for any commandline program.
*/
void generateXmlOutput();
/**
* Is used to set the title of the auto generated interface.
*
* @param title The title of the app.
*/
void setTitle(std::string title);
/**
* Is used to set the contributor for the help view in the auto generated interface.
*
* @param contributor Contributor of the app.
*/
void setContributor(std::string contributor);
/**
* Is used to categorize the apps in the commandline module.
*
* @param category The category of the app.
*/
void setCategory(std::string category);
/**
* Is used as the help text in the auto generated interface.
*
* @param description A short description for the app.
*/
void setDescription(std::string description);
/**
* Is used to group several Parameters in one groupbox in the auto generated interface.
* Default name is "Parameters", with the tooltip: "Groupbox containing parameters."
*
* To change the group of several arguments, call this method before the arguments are added.
*
* @param name The name of the groupbox.
* @param tooltip The tooltip of the groupbox.
*/
void changeParameterGroup(std::string name, std::string tooltip);
protected:
class ctkInternal;
ctkInternal *Internal;
std::string Title;
std::string Contributor;
std::string Category;
std::string Description;
std::string ParameterGroupName;
std::string ParameterGroupDescription;
};
#endif
diff --git a/Modules/CommandLine/src/mitkCommandLineParser.cpp b/Modules/CommandLine/src/mitkCommandLineParser.cpp
index cf60f88d99..72215d1221 100644
--- a/Modules/CommandLine/src/mitkCommandLineParser.cpp
+++ b/Modules/CommandLine/src/mitkCommandLineParser.cpp
@@ -1,1056 +1,1056 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
/*=========================================================================
Library: CTK
Copyright (c) Kitware Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://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.
=========================================================================*/
// STL includes
#include <iostream>
#include <stdexcept>
// MITK includes
#include "mitkCommandLineParser.h"
using namespace std;
namespace
{
// --------------------------------------------------------------------------
class CommandLineParserArgumentDescription
{
public:
CommandLineParserArgumentDescription(const string &longArg,
const string &longArgPrefix,
const string &shortArg,
const string &shortArgPrefix,
mitkCommandLineParser::Type type,
const string &argHelp,
const string &argLabel,
const us::Any &defaultValue,
bool ignoreRest,
bool deprecated,
bool optional,
mitkCommandLineParser::Channel channel,
string &argGroup,
string &groupDescription)
: LongArg(longArg),
LongArgPrefix(longArgPrefix),
ShortArg(shortArg),
ShortArgPrefix(shortArgPrefix),
ArgHelp(argHelp),
ArgLabel(argLabel),
ArgGroup(argGroup),
ArgGroupDescription(groupDescription),
IgnoreRest(ignoreRest),
NumberOfParametersToProcess(0),
Deprecated(deprecated),
Optional(optional),
Channel(channel),
DefaultValue(defaultValue),
Value(type),
ValueType(type)
{
Value = defaultValue;
switch (type)
{
case mitkCommandLineParser::String:
{
NumberOfParametersToProcess = 1;
}
break;
case mitkCommandLineParser::Bool:
{
NumberOfParametersToProcess = 0;
}
break;
case mitkCommandLineParser::StringList:
{
NumberOfParametersToProcess = -1;
}
break;
case mitkCommandLineParser::Int:
{
NumberOfParametersToProcess = 1;
}
break;
case mitkCommandLineParser::Float:
{
NumberOfParametersToProcess = 1;
}
break;
case mitkCommandLineParser::Directory:
{
NumberOfParametersToProcess = 1;
}
break;
case mitkCommandLineParser::File:
{
NumberOfParametersToProcess = 1;
}
break;
case mitkCommandLineParser::Image:
{
NumberOfParametersToProcess = 1;
}
break;
default:
std::cout << "Type not supported: " << static_cast<int>(type);
}
}
~CommandLineParserArgumentDescription() {}
bool addParameter(const string &value);
string helpText();
string LongArg;
string LongArgPrefix;
string ShortArg;
string ShortArgPrefix;
string ArgHelp;
string ArgLabel;
string ArgGroup;
string ArgGroupDescription;
bool IgnoreRest;
int NumberOfParametersToProcess;
bool Deprecated;
bool Optional;
mitkCommandLineParser::Channel Channel;
us::Any DefaultValue;
us::Any Value;
mitkCommandLineParser::Type ValueType;
};
// --------------------------------------------------------------------------
bool CommandLineParserArgumentDescription::addParameter(const string &value)
{
switch (ValueType)
{
case mitkCommandLineParser::String:
{
Value = value;
}
break;
case mitkCommandLineParser::Bool:
{
if (value.compare("true") == 0)
Value = true;
else
Value = false;
}
break;
case mitkCommandLineParser::StringList:
{
try
{
mitkCommandLineParser::StringContainerType list =
us::any_cast<mitkCommandLineParser::StringContainerType>(Value);
list.push_back(value);
Value = list;
}
catch (...)
{
mitkCommandLineParser::StringContainerType list;
list.push_back(value);
Value = list;
}
}
break;
case mitkCommandLineParser::Int:
{
stringstream ss(value);
int i;
ss >> i;
Value = i;
}
break;
case mitkCommandLineParser::Float:
{
stringstream ss(value);
float f;
ss >> f;
Value = f;
}
break;
case mitkCommandLineParser::Directory:
case mitkCommandLineParser::Image:
case mitkCommandLineParser::File:
{
Value = value;
}
break;
default:
return false;
}
return true;
}
// --------------------------------------------------------------------------
string CommandLineParserArgumentDescription::helpText()
{
string text;
string shortAndLongArg;
if (!this->ShortArg.empty())
{
shortAndLongArg = " ";
shortAndLongArg += this->ShortArgPrefix;
shortAndLongArg += this->ShortArg;
}
if (!this->LongArg.empty())
{
if (this->ShortArg.empty())
shortAndLongArg.append(" ");
else
shortAndLongArg.append(", ");
shortAndLongArg += this->LongArgPrefix;
shortAndLongArg += this->LongArg;
}
text = text + shortAndLongArg + ", " + this->ArgHelp;
if (this->Optional)
text += " (optional)";
if (!this->DefaultValue.Empty())
{
if (this->ValueType == 1)
{
if (this->DefaultValue.ToString() == "0")
text = text + ", (default: false)";
else
text = text + ", (default: true)";
}
else
text = text + ", (default: " + this->DefaultValue.ToString() + ")";
}
string value_type = "Unknown";
switch (this->ValueType)
{
case 0:
{
value_type = "String";
break;
}
case 1:
{
value_type = "Bool";
break;
}
case 2:
{
value_type = "StringList";
break;
}
case 3:
{
value_type = "Int";
break;
}
case 4:
{
value_type = "Float";
break;
}
case 5:
{
value_type = "Directory";
break;
}
case 6:
{
value_type = "File";
break;
}
case 7:
{
value_type = "Image";
}
}
text = text + ", Type: " + value_type;
if (this->Channel == mitkCommandLineParser::Input)
text = text + ", Channel: input";
else if (this->Channel == mitkCommandLineParser::Output)
text = text + ", Channel: output";
text += "\n";
return text;
}
}
// --------------------------------------------------------------------------
// ctkCommandLineParser::ctkInternal class
// --------------------------------------------------------------------------
class mitkCommandLineParser::ctkInternal
{
public:
ctkInternal() : Debug(false), FieldWidth(0), StrictMode(false) {}
~ctkInternal() {}
CommandLineParserArgumentDescription *argumentDescription(const string &argument);
vector<CommandLineParserArgumentDescription *> ArgumentDescriptionList;
map<string, CommandLineParserArgumentDescription *> ArgNameToArgumentDescriptionMap;
map<string, vector<CommandLineParserArgumentDescription *>> GroupToArgumentDescriptionListMap;
StringContainerType UnparsedArguments;
StringContainerType ProcessedArguments;
string ErrorString;
bool Debug;
string::size_type FieldWidth;
string LongPrefix;
string ShortPrefix;
string CurrentGroup;
string DisableQSettingsLongArg;
string DisableQSettingsShortArg;
bool StrictMode;
};
// --------------------------------------------------------------------------
// ctkCommandLineParser::ctkInternal methods
// --------------------------------------------------------------------------
CommandLineParserArgumentDescription *mitkCommandLineParser::ctkInternal::argumentDescription(const string &argument)
{
string unprefixedArg = argument;
if (!LongPrefix.empty() && argument.compare(0, LongPrefix.size(), LongPrefix) == 0)
{
// Case when (ShortPrefix + UnPrefixedArgument) matches LongPrefix
if (argument == LongPrefix && !ShortPrefix.empty() && argument.compare(0, ShortPrefix.size(), ShortPrefix) == 0)
{
unprefixedArg = argument.substr(ShortPrefix.size(), argument.size());
}
else
{
unprefixedArg = argument.substr(LongPrefix.size(), argument.size());
}
}
else if (!ShortPrefix.empty() && argument.compare(0, ShortPrefix.size(), ShortPrefix) == 0)
{
unprefixedArg = argument.substr(ShortPrefix.size(), argument.size());
}
else if (!LongPrefix.empty() && !ShortPrefix.empty())
{
return nullptr;
}
if (ArgNameToArgumentDescriptionMap.count(unprefixedArg))
{
return this->ArgNameToArgumentDescriptionMap[unprefixedArg];
}
return nullptr;
}
// --------------------------------------------------------------------------
// ctkCommandLineParser methods
// --------------------------------------------------------------------------
mitkCommandLineParser::mitkCommandLineParser()
{
this->Internal = new ctkInternal();
this->Category = string();
this->Title = string();
this->Contributor = string();
this->Description = string();
this->ParameterGroupName = "Parameters";
this->ParameterGroupDescription = "Parameters";
}
// --------------------------------------------------------------------------
mitkCommandLineParser::~mitkCommandLineParser()
{
delete this->Internal;
}
// --------------------------------------------------------------------------
map<string, us::Any> mitkCommandLineParser::parseArguments(const StringContainerType &arguments, bool *ok)
{
// Reset
this->Internal->UnparsedArguments.clear();
this->Internal->ProcessedArguments.clear();
this->Internal->ErrorString.clear();
// foreach (CommandLineParserArgumentDescription* desc, this->Internal->ArgumentDescriptionList)
for (unsigned int i = 0; i < Internal->ArgumentDescriptionList.size(); i++)
{
CommandLineParserArgumentDescription *desc = Internal->ArgumentDescriptionList.at(i);
desc->Value = us::Any(desc->ValueType);
if (!desc->DefaultValue.Empty())
{
desc->Value = desc->DefaultValue;
}
}
bool error = false;
bool ignoreRest = false;
CommandLineParserArgumentDescription *currentArgDesc = nullptr;
vector<CommandLineParserArgumentDescription *> parsedArgDescriptions;
for (unsigned int i = 1; i < arguments.size(); ++i)
{
string argument = arguments.at(i);
if (this->Internal->Debug)
{
std::cout << "Processing" << argument;
}
if (!argument.compare("--version"))
{
std::cout << "Git commit hash: " << MITK_REVISION << std::endl;
std::cout << "Git branch name: " << MITK_REVISION_NAME << "\n" << std::endl;
}
if (!argument.compare("--xml") || !argument.compare("-xml") || !argument.compare("--XML") ||
!argument.compare("-XML"))
{
this->generateXmlOutput();
return map<string, us::Any>();
}
// should argument be ignored ?
if (ignoreRest)
{
if (this->Internal->Debug)
{
std::cout << " Skipping: IgnoreRest flag was been set";
}
this->Internal->UnparsedArguments.push_back(argument);
continue;
}
// Skip if the argument does not start with the defined prefix
if (!(argument.compare(0, Internal->LongPrefix.size(), Internal->LongPrefix) == 0 ||
argument.compare(0, Internal->ShortPrefix.size(), Internal->ShortPrefix) == 0))
{
if (this->Internal->StrictMode)
{
this->Internal->ErrorString = "Unknown argument ";
this->Internal->ErrorString += argument;
error = true;
break;
}
if (this->Internal->Debug)
{
std::cout << " Skipping: It does not start with the defined prefix";
}
this->Internal->UnparsedArguments.push_back(argument);
continue;
}
// Skip if argument has already been parsed ...
bool alreadyProcessed = false;
for (const auto &alreadyHandledArgument : Internal->ProcessedArguments)
if (argument.compare(alreadyHandledArgument) == 0)
{
alreadyProcessed = true;
break;
}
if (alreadyProcessed)
{
if (this->Internal->StrictMode)
{
this->Internal->ErrorString = "Argument ";
this->Internal->ErrorString += argument;
this->Internal->ErrorString += " already processed !";
error = true;
break;
}
if (this->Internal->Debug)
{
std::cout << " Skipping: Already processed !";
}
continue;
}
// Retrieve corresponding argument description
currentArgDesc = this->Internal->argumentDescription(argument);
// Is there a corresponding argument description ?
if (currentArgDesc)
{
// If the argument is deprecated, print the help text but continue processing
if (currentArgDesc->Deprecated)
{
std::cout << "Deprecated argument " << argument << ": " << currentArgDesc->ArgHelp;
}
else
{
parsedArgDescriptions.push_back(currentArgDesc);
}
this->Internal->ProcessedArguments.push_back(currentArgDesc->ShortArg);
this->Internal->ProcessedArguments.push_back(currentArgDesc->LongArg);
int numberOfParametersToProcess = currentArgDesc->NumberOfParametersToProcess;
ignoreRest = currentArgDesc->IgnoreRest;
if (this->Internal->Debug && ignoreRest)
{
std::cout << " IgnoreRest flag is True";
}
// Is the number of parameters associated with the argument being processed known ?
if (numberOfParametersToProcess == 0)
{
currentArgDesc->addParameter("true");
}
else if (numberOfParametersToProcess > 0)
{
- string missingParameterError = "Argument %1 has %2 value(s) associated whereas exacly %3 are expected.";
+ string missingParameterError = "Argument %1 has %2 value(s) associated whereas exactly %3 are expected.";
for (int j = 1; j <= numberOfParametersToProcess; ++j)
{
if (i + j >= arguments.size())
{
// this->Internal->ErrorString =
// missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
// if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; }
if (ok)
{
*ok = false;
}
return map<string, us::Any>();
}
string parameter = arguments.at(i + j);
if (this->Internal->Debug)
{
std::cout << " Processing parameter" << j << ", value:" << parameter;
}
if (this->argumentAdded(parameter))
{
// this->Internal->ErrorString =
// missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
// if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; }
if (ok)
{
*ok = false;
}
return map<string, us::Any>();
}
if (!currentArgDesc->addParameter(parameter))
{
// this->Internal->ErrorString = string(
// "Value(s) associated with argument %1 are incorrect. %2").
// arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);
// if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; }
if (ok)
{
*ok = false;
}
return map<string, us::Any>();
}
}
// Update main loop increment
i = i + numberOfParametersToProcess;
}
else if (numberOfParametersToProcess == -1)
{
if (this->Internal->Debug)
{
std::cout << " Proccessing StringList ...";
}
int j = 1;
while (j + i < arguments.size())
{
if (this->argumentAdded(arguments.at(j + i)))
{
if (this->Internal->Debug)
{
std::cout << " No more parameter for" << argument;
}
break;
}
string parameter = arguments.at(j + i);
if (parameter.compare(0, Internal->LongPrefix.size(), Internal->LongPrefix) == 0 ||
parameter.compare(0, Internal->ShortPrefix.size(), Internal->ShortPrefix) == 0)
{
j--;
break;
}
if (this->Internal->Debug)
{
std::cout << " Processing parameter" << j << ", value:" << parameter;
}
if (!currentArgDesc->addParameter(parameter))
{
// this->Internal->ErrorString = string(
// "Value(s) associated with argument %1 are incorrect. %2").
// arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);
// if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; }
if (ok)
{
*ok = false;
}
return map<string, us::Any>();
}
j++;
}
// Update main loop increment
i = i + j;
}
}
else
{
if (this->Internal->StrictMode)
{
this->Internal->ErrorString = "Unknown argument ";
this->Internal->ErrorString += argument;
error = true;
break;
}
if (this->Internal->Debug)
{
std::cout << " Skipping: Unknown argument";
}
this->Internal->UnparsedArguments.push_back(argument);
}
}
if (ok)
{
*ok = !error;
}
map<string, us::Any> parsedArguments;
int obligatoryArgs = 0;
vector<CommandLineParserArgumentDescription *>::iterator it;
for (it = Internal->ArgumentDescriptionList.begin(); it != Internal->ArgumentDescriptionList.end(); ++it)
{
CommandLineParserArgumentDescription *desc = *it;
if (!desc->Optional)
obligatoryArgs++;
}
int parsedObligatoryArgs = 0;
for (it = parsedArgDescriptions.begin(); it != parsedArgDescriptions.end(); ++it)
{
CommandLineParserArgumentDescription *desc = *it;
string key;
if (!desc->LongArg.empty())
{
key = desc->LongArg;
}
else
{
key = desc->ShortArg;
}
if (!desc->Optional)
parsedObligatoryArgs++;
std::pair<string, us::Any> elem;
elem.first = key;
elem.second = desc->Value;
parsedArguments.insert(elem);
}
if (obligatoryArgs > parsedObligatoryArgs)
{
parsedArguments.clear();
cout << helpText();
}
return parsedArguments;
}
// -------------------------------------------------------------------------
map<string, us::Any> mitkCommandLineParser::parseArguments(int argc, char **argv, bool *ok)
{
std::cout << "Running Command Line Utility *" << Title << "*" << std::endl;
StringContainerType arguments;
// Create a StringContainerType of arguments
for (int i = 0; i < argc; ++i)
arguments.push_back(argv[i]);
return this->parseArguments(arguments, ok);
}
// -------------------------------------------------------------------------
string mitkCommandLineParser::errorString() const
{
return this->Internal->ErrorString;
}
// -------------------------------------------------------------------------
const mitkCommandLineParser::StringContainerType &mitkCommandLineParser::unparsedArguments() const
{
return this->Internal->UnparsedArguments;
}
// --------------------------------------------------------------------------
void mitkCommandLineParser::addArgument(const string &longarg,
const string &shortarg,
Type type,
const string &argLabel,
const string &argHelp,
const us::Any &defaultValue,
bool optional,
bool ignoreRest,
bool deprecated,
mitkCommandLineParser::Channel channel)
{
if (longarg.empty() && shortarg.empty())
{
return;
}
/* Make sure it's not already added */
bool added = (this->Internal->ArgNameToArgumentDescriptionMap.count(longarg) != 0);
if (added)
{
return;
}
added = (this->Internal->ArgNameToArgumentDescriptionMap.count(shortarg) != 0);
if (added)
{
return;
}
auto argDesc = new CommandLineParserArgumentDescription(longarg,
this->Internal->LongPrefix,
shortarg,
this->Internal->ShortPrefix,
type,
argHelp,
argLabel,
defaultValue,
ignoreRest,
deprecated,
optional,
channel,
ParameterGroupName,
ParameterGroupDescription);
std::string::size_type argWidth = 0;
if (!longarg.empty())
{
this->Internal->ArgNameToArgumentDescriptionMap[longarg] = argDesc;
argWidth += longarg.size() + this->Internal->LongPrefix.size();
}
if (!shortarg.empty())
{
this->Internal->ArgNameToArgumentDescriptionMap[shortarg] = argDesc;
argWidth += shortarg.size() + this->Internal->ShortPrefix.size() + 2;
}
argWidth += 5;
// Set the field width for the arguments
if (argWidth > this->Internal->FieldWidth)
{
this->Internal->FieldWidth = argWidth;
}
this->Internal->ArgumentDescriptionList.push_back(argDesc);
this->Internal->GroupToArgumentDescriptionListMap[this->Internal->CurrentGroup].push_back(argDesc);
}
// --------------------------------------------------------------------------
void mitkCommandLineParser::addDeprecatedArgument(const string &longarg,
const string &shortarg,
const string &argLabel,
const string &argHelp)
{
addArgument(longarg, shortarg, StringList, argLabel, argHelp, us::Any(), false, true, false);
}
// --------------------------------------------------------------------------
std::vector < std::map<std::string, us::Any> > mitkCommandLineParser::getArgumentList()
{
std::vector < std::map<std::string, us::Any> > parameterList;
//for (CommandLineParserArgumentDescription* argument : this->Internal->ArgumentDescriptionList)
for (std::size_t i = 0; i< this->Internal->ArgumentDescriptionList.size(); ++i)
{
CommandLineParserArgumentDescription* argument = this->Internal->ArgumentDescriptionList[i];
std::map<std::string, us::Any> tmpMap;
//tmpMap["helptext"] = us::Any(argument->helpText);
tmpMap["longarg"] = us::Any(argument->LongArg);
tmpMap["longargprefix"] = us::Any(argument->LongArgPrefix);
tmpMap["shortarg"] = us::Any(argument->ShortArg);
tmpMap["shortargprefix"] = us::Any(argument->ShortArgPrefix);
tmpMap["arghelp"] = us::Any(argument->ArgHelp);
tmpMap["arglabel"] = us::Any(argument->ArgLabel);
tmpMap["arggroup"] = us::Any(argument->ArgGroup);
tmpMap["arggroupdescription"] = us::Any(argument->ArgGroupDescription);
tmpMap["ignorerest"] = us::Any(argument->IgnoreRest);
tmpMap["numberofparameterstoprocess"] = us::Any(argument->NumberOfParametersToProcess);
tmpMap["deprecated"] = us::Any(argument->Deprecated);
tmpMap["optional"] = us::Any(argument->Optional);
tmpMap["defaultvalue"] = argument->DefaultValue;
tmpMap["value"] = argument->Value;
tmpMap["valuetype"] = us::Any(argument->ValueType);
tmpMap["channel"] = us::Any(argument->Channel);
parameterList.push_back(tmpMap);
}
return parameterList;
}
// --------------------------------------------------------------------------
std::string::size_type mitkCommandLineParser::fieldWidth() const
{
return this->Internal->FieldWidth;
}
// --------------------------------------------------------------------------
void mitkCommandLineParser::beginGroup(const string &description)
{
this->Internal->CurrentGroup = description;
}
// --------------------------------------------------------------------------
void mitkCommandLineParser::endGroup()
{
this->Internal->CurrentGroup.clear();
}
// --------------------------------------------------------------------------
string mitkCommandLineParser::helpText() const
{
string text;
vector<CommandLineParserArgumentDescription *> deprecatedArgs;
text = "Command Line Utility *" + Title + "* in Category *" + Category + "*\n";
text += Description + "\n";
text += Contributor + "\n\n";
text += "Use --xml to generate an XML description parsable as a CTK Command Line Module Plugin.\n";
text += "Use --version to print MITK revision information.\n";
// Loop over grouped argument descriptions
map<string, vector<CommandLineParserArgumentDescription *>>::iterator it;
for (it = Internal->GroupToArgumentDescriptionListMap.begin();
it != Internal->GroupToArgumentDescriptionListMap.end();
++it)
{
if (!(*it).first.empty())
{
text = text + "\n" + (*it).first + "\n";
}
vector<CommandLineParserArgumentDescription *>::iterator it2;
for (it2 = (*it).second.begin(); it2 != (*it).second.end(); ++it2)
{
CommandLineParserArgumentDescription *argDesc = *it2;
if (argDesc->Deprecated)
{
deprecatedArgs.push_back(argDesc);
}
else
{
text += argDesc->helpText();
}
}
}
if (!deprecatedArgs.empty())
{
text += "\nDeprecated arguments:\n";
vector<CommandLineParserArgumentDescription *>::iterator it2;
for (it2 = deprecatedArgs.begin(); it2 != deprecatedArgs.end(); ++it2)
{
CommandLineParserArgumentDescription *argDesc = *it2;
text += argDesc->helpText();
}
}
return text;
}
// --------------------------------------------------------------------------
bool mitkCommandLineParser::argumentAdded(const string &argument) const
{
return (this->Internal->ArgNameToArgumentDescriptionMap.count(argument) != 0);
}
// --------------------------------------------------------------------------
bool mitkCommandLineParser::argumentParsed(const string &argument) const
{
for (unsigned int i = 0; i < Internal->ProcessedArguments.size(); i++)
if (argument.compare(Internal->ProcessedArguments.at(i)) == 0)
return true;
return false;
}
// --------------------------------------------------------------------------
void mitkCommandLineParser::setArgumentPrefix(const string &longPrefix, const string &shortPrefix)
{
this->Internal->LongPrefix = longPrefix;
this->Internal->ShortPrefix = shortPrefix;
}
// --------------------------------------------------------------------------
void mitkCommandLineParser::setStrictModeEnabled(bool strictMode)
{
this->Internal->StrictMode = strictMode;
}
void mitkCommandLineParser::generateXmlOutput()
{
std::stringstream xml;
xml << "<executable>" << endl;
xml << "<category>" << Category << "</category>" << endl;
xml << "<title>" << Title << "</title>" << endl;
xml << "<description>" << Description << "</description>" << endl;
xml << "<contributor>" << Contributor << "</contributor>" << endl;
xml << "<parameters>" << endl;
std::vector<CommandLineParserArgumentDescription *>::iterator it;
std::string lastParameterGroup = "";
for (it = this->Internal->ArgumentDescriptionList.begin(); it != this->Internal->ArgumentDescriptionList.end(); it++)
{
std::string type;
switch ((*it)->ValueType)
{
case mitkCommandLineParser::String:
type = "string";
break;
case mitkCommandLineParser::Bool:
type = "boolean";
break;
case mitkCommandLineParser::StringList:
type = "string-vector";
break;
case mitkCommandLineParser::Int:
type = "integer";
break;
case mitkCommandLineParser::Float:
type = "float";
break;
case mitkCommandLineParser::Directory:
type = "directory";
break;
case mitkCommandLineParser::Image:
type = "image";
break;
case mitkCommandLineParser::File:
type = "file";
break;
}
if (lastParameterGroup.compare((*it)->ArgGroup))
{
if (it != this->Internal->ArgumentDescriptionList.begin())
{
xml << "</parameters>" << endl;
xml << "<parameters>" << endl;
}
xml << "<label>" << (*it)->ArgGroup << "</label>" << endl;
xml << "<description>" << (*it)->ArgGroupDescription << "</description>" << endl;
lastParameterGroup = (*it)->ArgGroup;
}
// Skip help item, as it's no use in GUI
if ((*it)->ShortArg == "h")
continue;
auto name = (*it)->LongArg;
if (name.empty())
name = (*it)->ShortArg;
xml << "<" << type << ">" << endl;
xml << "<name>" << name << "</name>" << endl;
xml << "<description>" << (*it)->ArgHelp << "</description>" << endl;
xml << "<label>" << (*it)->ArgLabel << "</label>" << endl;
if (!(*it)->DefaultValue.Empty())
xml << "<default>" << (*it)->DefaultValue.ToString() << "</default>" << endl;
xml << "<longflag>" << (*it)->LongArg << "</longflag>" << endl;
xml << "<flag>" << (*it)->ShortArg << "</flag>" << endl;
if (((*it)->ValueType == mitkCommandLineParser::File ||
(*it)->ValueType == mitkCommandLineParser::Directory ||
(*it)->ValueType == mitkCommandLineParser::Image) &&
(*it)->Channel == mitkCommandLineParser::Channel::Input)
{
xml << "<channel>input</channel>" << endl;
}
else if (((*it)->ValueType == mitkCommandLineParser::File ||
(*it)->ValueType == mitkCommandLineParser::Directory ||
(*it)->ValueType == mitkCommandLineParser::Image) &&
(*it)->Channel == mitkCommandLineParser::Channel::Output)
{
xml << "<channel>output</channel>" << endl;
}
else if ((*it)->Channel == mitkCommandLineParser::Channel::Output ||
(*it)->Channel == mitkCommandLineParser::Channel::Input)
{
std::cout << "Only the types Directory, File or Image may be flagged as Input or Output! Ignoring flag for parameter " + name << std::endl;
}
xml << "</" << type << ">" << endl;
}
xml << "</parameters>" << endl;
xml << "</executable>" << endl;
cout << xml.str();
}
void mitkCommandLineParser::setTitle(string title)
{
Title = title;
}
void mitkCommandLineParser::setContributor(string contributor)
{
Contributor = contributor;
}
void mitkCommandLineParser::setCategory(string category)
{
Category = category;
}
void mitkCommandLineParser::setDescription(string description)
{
Description = description;
}
void mitkCommandLineParser::changeParameterGroup(string name, string tooltip)
{
ParameterGroupName = name;
ParameterGroupDescription = tooltip;
}
diff --git a/Modules/ContourModel/Algorithms/mitkContourModelSetSource.h b/Modules/ContourModel/Algorithms/mitkContourModelSetSource.h
index c76138b687..2ed42a9bf2 100644
--- a/Modules/ContourModel/Algorithms/mitkContourModelSetSource.h
+++ b/Modules/ContourModel/Algorithms/mitkContourModelSetSource.h
@@ -1,62 +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 mitkContourModelSetSource_h
#define mitkContourModelSetSource_h
#include "mitkBaseDataSource.h"
#include "mitkContourModelSet.h"
#include <MitkContourModelExports.h>
namespace mitk
{
/**
* @brief Superclass of all classes generating ContourModels.
* @ingroup MitkContourModelModule
*/
class MITKCONTOURMODEL_EXPORT ContourModelSetSource : public BaseDataSource
{
public:
mitkClassMacro(ContourModelSetSource, BaseDataSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef ContourModelSet OutputType;
typedef OutputType::Pointer OutputTypePointer;
mitkBaseDataSourceGetOutputDeclarations
/**
* 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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
protected:
ContourModelSetSource();
~ContourModelSetSource() override;
};
}
#endif
diff --git a/Modules/ContourModel/Algorithms/mitkContourModelSource.h b/Modules/ContourModel/Algorithms/mitkContourModelSource.h
index 89d1925adc..393b729e6d 100644
--- a/Modules/ContourModel/Algorithms/mitkContourModelSource.h
+++ b/Modules/ContourModel/Algorithms/mitkContourModelSource.h
@@ -1,62 +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 mitkContourModelSource_h
#define mitkContourModelSource_h
#include "mitkBaseDataSource.h"
#include "mitkContourModel.h"
#include <MitkContourModelExports.h>
namespace mitk
{
/**
* @brief Superclass of all classes generating ContourModels.
* @ingroup MitkContourModelModule
*/
class MITKCONTOURMODEL_EXPORT ContourModelSource : public BaseDataSource
{
public:
mitkClassMacro(ContourModelSource, BaseDataSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self)
typedef ContourModel OutputType;
typedef OutputType::Pointer OutputTypePointer;
mitkBaseDataSourceGetOutputDeclarations
/**
* 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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
protected:
ContourModelSource();
~ContourModelSource() override;
};
}
#endif
diff --git a/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.cpp b/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.cpp
index a7840e854f..89693bebc5 100644
--- a/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.cpp
+++ b/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.cpp
@@ -1,202 +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.
============================================================================*/
#include "mitkContourModelSubDivisionFilter.h"
#include <mitkInteractionConst.h>
#include <mitkPointOperation.h>
mitk::ContourModelSubDivisionFilter::ContourModelSubDivisionFilter()
{
OutputType::Pointer output = dynamic_cast<OutputType *>(this->MakeOutput(0).GetPointer());
this->SetNumberOfRequiredInputs(1);
this->SetNumberOfIndexedOutputs(1);
this->SetNthOutput(0, output.GetPointer());
this->m_InterpolationIterations = 4;
}
mitk::ContourModelSubDivisionFilter::~ContourModelSubDivisionFilter()
{
}
void mitk::ContourModelSubDivisionFilter::SetInput(const mitk::ContourModelSubDivisionFilter::InputType *input)
{
this->SetInput(0, input);
}
void mitk::ContourModelSubDivisionFilter::SetInput(unsigned int idx,
const mitk::ContourModelSubDivisionFilter::InputType *input)
{
if (idx + 1 > this->GetNumberOfInputs())
{
this->SetNumberOfRequiredInputs(idx + 1);
}
if (input != static_cast<InputType *>(this->ProcessObject::GetInput(idx)))
{
this->ProcessObject::SetNthInput(idx, const_cast<InputType *>(input));
this->Modified();
}
}
const mitk::ContourModelSubDivisionFilter::InputType *mitk::ContourModelSubDivisionFilter::GetInput(void)
{
if (this->GetNumberOfInputs() < 1)
return nullptr;
return static_cast<const mitk::ContourModelSubDivisionFilter::InputType *>(this->ProcessObject::GetInput(0));
}
const mitk::ContourModelSubDivisionFilter::InputType *mitk::ContourModelSubDivisionFilter::GetInput(unsigned int idx)
{
if (this->GetNumberOfInputs() < 1)
return nullptr;
return static_cast<const mitk::ContourModelSubDivisionFilter::InputType *>(this->ProcessObject::GetInput(idx));
}
void mitk::ContourModelSubDivisionFilter::GenerateData()
{
mitk::ContourModel::Pointer input = const_cast<mitk::ContourModel *>(this->GetInput(0));
// mitk::ContourModelSubDivisionFilter::OutputType::Pointer outputContour = this->GetOutput();
mitk::ContourModel::Pointer contour(input);
auto timestep = static_cast<TimeStepType>(input->GetTimeSteps());
for (decltype(timestep) currentTimestep = 0; currentTimestep < timestep; currentTimestep++)
{
if (input->GetNumberOfVertices(currentTimestep) >= 4)
{
for (int iterations = 0; iterations < this->m_InterpolationIterations; iterations++)
{
auto it = contour->IteratorBegin();
auto end = contour->IteratorEnd();
auto first = contour->IteratorBegin();
auto last = contour->IteratorEnd() - 1;
// tempory contour to store result of a subdivision iteration
mitk::ContourModel::Pointer tempContour = mitk::ContourModel::New();
// insert subpoints
while (it != end)
{
// add the current point to the temp contour
tempContour->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, currentTimestep);
// control points for interpolation
auto Ci = it;
InputType::VertexIterator CiPlus1;
InputType::VertexIterator CiPlus2;
InputType::VertexIterator CiMinus1;
// consider all possible cases
if (it == first)
{
if (input->IsClosed(currentTimestep))
{
CiPlus1 = it + 1;
CiPlus2 = it + 2;
CiMinus1 = last;
}
else
{
CiPlus1 = it + 1;
CiPlus2 = it + 2;
CiMinus1 = it;
}
}
else if (it == last)
{
if (input->IsClosed(currentTimestep))
{
CiPlus1 = first;
CiPlus2 = first + 1;
CiMinus1 = it - 1;
}
else
{
// don't add point after last
break;
}
}
else if (it == (last - 1))
{
if (input->IsClosed(currentTimestep))
{
CiPlus1 = it + 1;
CiPlus2 = first;
CiMinus1 = it - 1;
}
else
{
CiPlus1 = it + 1;
CiPlus2 = it + 1;
CiMinus1 = it - 1;
}
}
else
{
CiPlus1 = it + 1;
CiPlus2 = it + 2;
CiMinus1 = it - 1;
}
/* F2i = Ci
* F2i+1 = -1/16Ci-1 + 9/16Ci + 9/16Ci+1 - 1/16Ci+2
*/
mitk::Point3D subpoint;
mitk::Point3D a;
a[0] = (-1.0 / 16.0) * (*CiMinus1)->Coordinates[0];
a[1] = (-1.0 / 16.0) * (*CiMinus1)->Coordinates[1];
a[2] = (-1.0 / 16.0) * (*CiMinus1)->Coordinates[2];
mitk::Point3D b;
b[0] = (9.0 / 16.0) * (*Ci)->Coordinates[0];
b[1] = (9.0 / 16.0) * (*Ci)->Coordinates[1];
b[2] = (9.0 / 16.0) * (*Ci)->Coordinates[2];
mitk::Point3D c;
c[0] = (9.0 / 16.0) * (*CiPlus1)->Coordinates[0];
c[1] = (9.0 / 16.0) * (*CiPlus1)->Coordinates[1];
c[2] = (9.0 / 16.0) * (*CiPlus1)->Coordinates[2];
mitk::Point3D d;
d[0] = (-1.0 / 16.0) * (*CiPlus2)->Coordinates[0];
d[1] = (-1.0 / 16.0) * (*CiPlus2)->Coordinates[1];
d[2] = (-1.0 / 16.0) * (*CiPlus2)->Coordinates[2];
subpoint[0] = a[0] + b[0] + c[0] + d[0];
subpoint[1] = a[1] + b[1] + c[1] + d[1];
subpoint[2] = a[2] + b[2] + c[2] + d[2];
InputType::VertexType subdivisionPoint(subpoint, false);
// add the new subdivision point to our tempContour
tempContour->AddVertex(subdivisionPoint.Coordinates, currentTimestep);
it++;
}
// set the interpolated contour as the contour for the next iteration
contour = tempContour;
}
}
else
{
- // filter not executeable - set input to output
+ // filter not executable - set input to output
contour = input;
}
}
// somehow the isClosed property is not set via copy constructor
contour->SetClosed(input->IsClosed());
this->SetNthOutput(0, contour);
}
diff --git a/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.h b/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.h
index f52edbc290..0fd4f1a269 100644
--- a/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.h
+++ b/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.h
@@ -1,74 +1,74 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkContourModelSubDivisionFilter_h
#define mitkContourModelSubDivisionFilter_h
#include "mitkCommon.h"
#include "mitkContourModel.h"
#include "mitkContourModelSource.h"
#include <MitkContourModelExports.h>
namespace mitk
{
/**
*
* \brief This filter interpolates a subdivision curve between control points of the contour.
* For inserting subpoints Dyn-Levin-Gregory (DLG) interpolation scheme is used.
* Interpolating a cruve subdivision is done by:
* F2i = Ci
* F2i+1 = -1/16Ci-1 + 9/16Ci + 9/16Ci+1 - 1/16Ci+2
*
- * The number of interpolation iterations can be set via SetNumberOfIterations(int) which are 4 by dafault.
+ * The number of interpolation iterations can be set via SetNumberOfIterations(int) which are 4 by default.
*
* @ingroup MitkContourModelModule
*/
class MITKCONTOURMODEL_EXPORT ContourModelSubDivisionFilter : public ContourModelSource
{
public:
mitkClassMacro(ContourModelSubDivisionFilter, ContourModelSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef ContourModel OutputType;
typedef OutputType::Pointer OutputTypePointer;
typedef mitk::ContourModel InputType;
/**
* \brief Set the number of iterations for inserting new interpolated control points.
*
*/
void SetNumberOfIterations(int iterations) { this->m_InterpolationIterations = iterations; }
using Superclass::SetInput;
virtual void SetInput(const InputType *input);
virtual void SetInput(unsigned int idx, const InputType *input);
const InputType *GetInput(void);
const InputType *GetInput(unsigned int idx);
protected:
ContourModelSubDivisionFilter();
~ContourModelSubDivisionFilter() override;
void GenerateOutputInformation() override{};
void GenerateData() override;
int m_InterpolationIterations;
};
}
#endif
diff --git a/Modules/ContourModel/Algorithms/mitkContourModelToSurfaceFilter.cpp b/Modules/ContourModel/Algorithms/mitkContourModelToSurfaceFilter.cpp
index 93dde7008c..31897208cc 100644
--- a/Modules/ContourModel/Algorithms/mitkContourModelToSurfaceFilter.cpp
+++ b/Modules/ContourModel/Algorithms/mitkContourModelToSurfaceFilter.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 <mitkContourModelToSurfaceFilter.h>
#include <mitkSurface.h>
#include <vtkCellArray.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolygon.h>
#include <vtkSmartPointer.h>
mitk::ContourModelToSurfaceFilter::ContourModelToSurfaceFilter()
{
this->SetNthOutput(0, mitk::Surface::New().GetPointer());
}
mitk::ContourModelToSurfaceFilter::~ContourModelToSurfaceFilter()
{
}
void mitk::ContourModelToSurfaceFilter::GenerateOutputInformation()
{
}
void mitk::ContourModelToSurfaceFilter::SetInput(const mitk::ContourModelToSurfaceFilter::InputType *input)
{
this->SetInput(0, input);
}
void mitk::ContourModelToSurfaceFilter::SetInput(unsigned int idx,
const mitk::ContourModelToSurfaceFilter::InputType *input)
{
if (idx + 1 > this->GetNumberOfInputs())
{
this->SetNumberOfRequiredInputs(idx + 1);
}
if (input != static_cast<InputType *>(this->ProcessObject::GetInput(idx)))
{
this->ProcessObject::SetNthInput(idx, const_cast<InputType *>(input));
this->Modified();
}
}
const mitk::ContourModelToSurfaceFilter::InputType *mitk::ContourModelToSurfaceFilter::GetInput(void)
{
if (this->GetNumberOfInputs() < 1)
return nullptr;
return static_cast<const mitk::ContourModelToSurfaceFilter::InputType *>(this->ProcessObject::GetInput(0));
}
const mitk::ContourModelToSurfaceFilter::InputType *mitk::ContourModelToSurfaceFilter::GetInput(unsigned int idx)
{
if (this->GetNumberOfInputs() < 1)
return nullptr;
return static_cast<const mitk::ContourModelToSurfaceFilter::InputType *>(this->ProcessObject::GetInput(idx));
}
void mitk::ContourModelToSurfaceFilter::GenerateData()
{
mitk::Surface *surface = this->GetOutput();
auto *inputContour = (mitk::ContourModel *)GetInput();
unsigned int numberOfTimeSteps = inputContour->GetTimeSteps();
surface->Expand(numberOfTimeSteps);
for (unsigned int currentTimeStep = 0; currentTimeStep < numberOfTimeSteps; currentTimeStep++)
{
/* First of all convert the control points of the contourModel to vtk points
* and add lines in between them
*/
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); // the points to draw
vtkSmartPointer<vtkCellArray> polygons = vtkSmartPointer<vtkCellArray>::New();
vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New(); // the lines to connect the points
// if the contour has less than 3 points, set empty PolyData for current timestep
// polygon needs at least 3 points
if (inputContour->GetNumberOfVertices(currentTimeStep) <= 2)
{
vtkSmartPointer<vtkPolyData> emptyPolyData = vtkSmartPointer<vtkPolyData>::New();
surface->SetVtkPolyData(emptyPolyData, currentTimeStep);
continue;
}
// iterate over all control points
auto current = inputContour->IteratorBegin(currentTimeStep);
auto end = inputContour->IteratorEnd(currentTimeStep);
vtkSmartPointer<vtkPolygon> polygon = vtkSmartPointer<vtkPolygon>::New();
polygon->GetPointIds()->SetNumberOfIds(inputContour->GetNumberOfVertices(currentTimeStep));
int j(0);
while (current != end)
{
mitk::ContourModel::VertexType *currentPoint = *current;
vtkIdType id = points->InsertNextPoint(
currentPoint->Coordinates[0], currentPoint->Coordinates[1], currentPoint->Coordinates[2]);
polygon->GetPointIds()->SetId(j, id);
// create connections between the points
- // no previous point for first point available (ingnore id=0)
+ // no previous point for first point available (ignore id=0)
if (id > 0)
{
lines->InsertNextCell(2);
lines->InsertCellPoint(id - 1);
lines->InsertCellPoint(id);
}
current++;
j++;
}
/*
* If the contour is closed an additional line has to be created between the first point
* and the last point
*/
if (inputContour->IsClosed(currentTimeStep))
{
lines->InsertNextCell(2);
lines->InsertCellPoint(0);
lines->InsertCellPoint((inputContour->GetNumberOfVertices(currentTimeStep) - 1));
}
polygons->InsertNextCell(polygon);
// Create a polydata to store everything in
vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
// Add the points to the dataset
polyData->SetPoints(points);
polyData->SetPolys(polygons);
polyData->SetLines(lines);
polyData->BuildLinks();
surface->SetVtkPolyData(polyData, currentTimeStep);
}
}
diff --git a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h b/Modules/ContourModel/Algorithms/mitkContourModelUtils.h
index 769634ae1d..29ccd2919f 100644
--- a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h
+++ b/Modules/ContourModel/Algorithms/mitkContourModelUtils.h
@@ -1,155 +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.
============================================================================*/
#ifndef mitkContourModelUtils_h
#define mitkContourModelUtils_h
#include <mitkContourModel.h>
#include <mitkImage.h>
#include <vtkSmartPointer.h>
#include <MitkContourModelExports.h>
namespace mitk
{
/**
* \brief Helpful methods for working with contours and images
*
*
*/
class MITKCONTOURMODEL_EXPORT ContourModelUtils : public itk::Object
{
public:
mitkClassMacroItkParent(ContourModelUtils, itk::Object);
/**
\brief Projects a contour onto an image point by point. Converts from world to index coordinates.
\param slice
\param contourIn3D
*/
static ContourModel::Pointer ProjectContourTo2DSlice(const Image *slice,
const ContourModel *contourIn3D);
/**
\brief Projects a slice index coordinates of a contour back into world coordinates.
\param sliceGeometry
\param contourIn2D
*/
static ContourModel::Pointer BackProjectContourFrom2DSlice(const BaseGeometry *sliceGeometry,
const ContourModel *contourIn2D);
/**
\brief Fill a contour in a 2D slice with a specified pixel value.
This version always uses the contour of time step 0 and fills the image.
- \deprecated Ths function is deprecated. Use FillContourInSlice2() (in
+ \deprecated This function is deprecated. Use FillContourInSlice2() (in
conjunction e.g. with TransferLabelContent()) instead.
\pre sliceImage points to a valid instance
\pre projectedContour points to a valid instance
*/
//[[deprecated]]
DEPRECATED(static void FillContourInSlice(const ContourModel *projectedContour,
Image *sliceImage,
const Image* workingImage,
int paintingPixelValue = 1));
/**
\brief Fill a contour in a 2D slice with a specified pixel value.
This overloaded version uses the contour at the passed contourTimeStep
to fill the passed image slice.
- \deprecated Ths function is deprecated. Use FillContourInSlice2() (in
+ \deprecated This function is deprecated. Use FillContourInSlice2() (in
conjunction e.g. with TransferLabelContentAtTimeStep()) instead.
\pre sliceImage points to a valid instance
\pre projectedContour points to a valid instance
*/
//[[deprecated]]
DEPRECATED(static void FillContourInSlice(const ContourModel *projectedContour,
TimeStepType contourTimeStep,
Image *sliceImage,
const Image* workingImage,
int paintingPixelValue = 1));
/**
\brief Fill a contour in a 2D slice with a specified pixel value.
This version always uses the contour of time step 0 and fills the image.
\param projectedContour Pointer to the contour that should be projected.
\param sliceImage Pointer to the image which content should be altered by
adding the contour with the specified paintingPixelValue.
\param paintingPixelValue
\pre sliceImage points to a valid instance
\pre projectedContour points to a valid instance
*/
static void FillContourInSlice2(const ContourModel* projectedContour,
Image* sliceImage,
int paintingPixelValue = 1);
/**
\brief Fill a contour in a 2D slice with a specified pixel value.
This overloaded version uses the contour at the passed contourTimeStep
to fill the passed image slice.
\param projectedContour Pointer to the contour that should be projected.
\param contourTimeStep
\param sliceImage Pointer to the image which content should be altered by
\param paintingPixelValue
adding the contour with the specified paintingPixelValue.
\pre sliceImage points to a valid instance
\pre projectedContour points to a valid instance
*/
static void FillContourInSlice2(const ContourModel* projectedContour,
TimeStepType contourTimeStep,
Image* sliceImage,
int paintingPixelValue = 1);
/**
\brief Fills the paintingPixelValue into every pixel of resultImage as indicated by filledImage.
If a LableSet image is specified it also by incorporating the rules of LabelSet images when filling the content.
- \param filledImage Pointer to the image content that should be checked to decied of a pixel in resultImage should
+ \param filledImage Pointer to the image content that should be checked to decide if a pixel in resultImage should
be filled with paintingPixelValue or not.
\param resultImage Pointer to the image content that should be overwritten guided by the content of filledImage.
\param image Pointer to an mitk image that allows to define the LabelSet image which states steer the filling process.
If an LabelSet instance is passed its states (e.g. locked labels etc...) will be used. If nullptr or an normal image
is passed, then simply any pixel position indicated by filledImage will be overwritten.
\param paintingPixelValue the pixelvalue/label that should be used in the result image when filling.
\param fillForegroundThreshold The threshold value that decides if a pixel in the filled image counts
as foreground (>=fillForegroundThreshold) or not.
- \deprecated Ths function is deprecated. Use TransferLabelContent() instead.
+ \deprecated This function is deprecated. Use TransferLabelContent() instead.
*/
[[deprecated]]
static void FillSliceInSlice(vtkSmartPointer<vtkImageData> filledImage,
vtkSmartPointer<vtkImageData> resultImage,
const Image* image,
int paintingPixelValue,
double fillForegroundThreshold = 1.0);
/**
\brief Move the contour in time step 0 to to a new contour model at the given time step.
*/
static ContourModel::Pointer MoveZerothContourTimeStep(const ContourModel *contour, TimeStepType timeStep);
/**
\brief Retrieves the active pixel value of a (labelset) image.
If the image is basic image, the pixel value 1 (one) will be returned.
If the image is actually a labelset image, the pixel value of the active label of the active layer will be
returned.
\param workingImage The (labelset) image to retrieve the active pixel value of.
*/
static int GetActivePixelValue(const Image* workingImage);
protected:
ContourModelUtils();
~ContourModelUtils() override;
};
}
#endif
diff --git a/Modules/ContourModel/DataManagement/mitkContourElement.h b/Modules/ContourModel/DataManagement/mitkContourElement.h
index f0029e9f0a..5aab226c8c 100644
--- a/Modules/ContourModel/DataManagement/mitkContourElement.h
+++ b/Modules/ContourModel/DataManagement/mitkContourElement.h
@@ -1,308 +1,308 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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.
+ class the ownership of the vertex is transferred 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
+ ContourModel. Therefore it is advised 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;
/** Function that searches for the line segment of the contour that is closest to the passed point
and close enough (distance between point and line segment <= eps). If such an line segment exist,
the starting vertex and closing vertex of the found segment are passed back.
@return True indicates that a line segment was found. False indicates that no segment of the contour
is close enough to the passed point.
@remark previousVertex and nextVertex are only valid if return is true.*/
bool GetLineSegmentForPoint(const mitk::Point3D &point,
float eps,
mitk::ContourElement::VertexType *previousVertex,
mitk::ContourElement::VertexType *nextVertex) const;
/** Overloaded version that offers additional options when searching for the line segment.
In contrast to the other version it returns the index of the segment start and end as well as the point
on the line segment closest to the passed point. Further one can decide if the function should search for
the first segment that is close enough (see eps) or for the segment that is really the closest (findClosest==true).
@remark segmentStartIndex, segmentEndIndex and closestContourPoint are only valid if return is true.*/
bool GetLineSegmentForPoint(const mitk::Point3D& point,
float eps, VertexSizeType& segmentStartIndex, VertexSizeType& segmentEndIndex, mitk::Point3D& closestContourPoint, bool findClosest = true) 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
+ list directly point to the vertices stored internally. 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
diff --git a/Modules/ContourModel/DataManagement/mitkContourModel.h b/Modules/ContourModel/DataManagement/mitkContourModel.h
index e1b5125feb..2ebfaabffe 100644
--- a/Modules/ContourModel/DataManagement/mitkContourModel.h
+++ b/Modules/ContourModel/DataManagement/mitkContourModel.h
@@ -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.
============================================================================*/
#ifndef mitkContourModel_h
#define mitkContourModel_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 for each timestep.
The contour line segments are implicitly defined by the given linked vertices.
By default two control points are linked by a straight line. It is possible to add
vertices at the 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 as well 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 sourceModel must point to a valid instance
@pre sourceTimePoint must be valid
@note Updating 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;
const VertexType *GetVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) 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.
*/
bool IsNearContour(Point3D &point, float eps, TimeStepType timestep) const;
/** Function that searches for the line segment of the contour that is closest to the passed point
and close enough (distance between point and line segment <= eps). If such an line segment exist,
the starting vertex and closing vertex of the found segment are passed back.
@return True indicates that a line segment was found. False indicates that no segment of the contour
is close enough to the passed point.
@remark previousVertex and nextVertex are only valid if return is true.*/
bool GetLineSegmentForPoint(Point3D &point,
float eps,
TimeStepType timestep,
mitk::ContourElement::VertexType *previousVertex = nullptr,
mitk::ContourElement::VertexType *nextVertex = nullptr);
/**Overloaded version that returns additional information (start and end vertix of the line
closest to the passed point and the closest point on the contour).
@remark segmentStart, segmentStop and closestContourPoint are only valid if the function returns true.
*/
bool GetLineSegmentForPoint(const mitk::Point3D& point,
float eps, TimeStepType timestep, ContourElement::VertexSizeType& segmentStartIndex,
ContourElement::VertexSizeType& segmentEndIndex, mitk::Point3D& closestContourPoint,
bool findClosest = true) const;
/** \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(const 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(const 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
+ \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 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)
+ /** \brief Redistributes control 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;
};
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/Rendering/mitkContourModelGLMapper2D.h b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2D.h
index c27194620a..9ef7e1bd39 100644
--- a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2D.h
+++ b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2D.h
@@ -1,67 +1,67 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkContourModelGLMapper2D_h
#define mitkContourModelGLMapper2D_h
#include "mitkCommon.h"
#include "mitkContourModel.h"
#include "mitkContourModelGLMapper2DBase.h"
#include <MitkContourModelExports.h>
namespace mitk
{
class BaseRenderer;
class ContourModel;
/**
* @brief OpenGL-based mapper to display a mitk::Contour object in a 2D render window
*
*
* @ingroup MitkContourModelModule
*/
class MITKCONTOURMODEL_EXPORT ContourModelGLMapper2D : public ContourModelGLMapper2DBase
{
public:
mitkClassMacro(ContourModelGLMapper2D, ContourModelGLMapper2DBase);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* reimplemented from Baseclass
*/
void MitkRender(BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) override;
static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false);
LocalStorageHandler<BaseLocalStorage> m_LSH;
protected:
ContourModelGLMapper2D();
~ContourModelGLMapper2D() override;
mitk::ContourModel::Pointer m_SubdivisionContour;
bool m_InitSubdivisionCurve;
private:
/**
- * return a refernce of the rendered data object
+ * return a reference of the rendered data object
*/
ContourModel *GetInput(void);
};
} // namespace mitk
#endif
diff --git a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp
index 1dcf695ea1..dcc46349ee 100644
--- a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp
+++ b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp
@@ -1,386 +1,386 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkContourModelSetGLMapper2D.h"
#include "mitkColorProperty.h"
#include "mitkContourModelSet.h"
#include "mitkPlaneGeometry.h"
#include "mitkProperties.h"
#include <vtkContext2D.h>
#include <vtkLinearTransform.h>
#include <vtkOpenGLContextDevice2D.h>
#include <vtkPen.h>
#include "mitkManualPlacementAnnotationRenderer.h"
#include "mitkBaseRenderer.h"
#include "mitkContourModel.h"
#include "mitkTextAnnotation2D.h"
mitk::ContourModelGLMapper2DBase::ContourModelGLMapper2DBase()
{
m_PointNumbersAnnotation = mitk::TextAnnotation2D::New();
m_ControlPointNumbersAnnotation = mitk::TextAnnotation2D::New();
}
mitk::ContourModelGLMapper2DBase::~ContourModelGLMapper2DBase()
{
}
void mitk::ContourModelGLMapper2DBase::ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * /*actor*/)
{
auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer);
float rgba[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
// check for color prop and use it for rendering if it exists
GetDataNode()->GetColor(rgba, renderer, "color");
// check for opacity prop and use it for rendering if it exists
GetDataNode()->GetOpacity(rgba[3], renderer, "opacity");
if (localStorage->Context->GetPen() == nullptr)
{
return;
}
localStorage->Context->GetPen()->SetColorF((double)rgba[0], (double)rgba[1], (double)rgba[2], (double)rgba[3]);
}
void mitk::ContourModelGLMapper2DBase::DrawContour(mitk::ContourModel *renderingContour, mitk::BaseRenderer *renderer)
{
if (std::find(m_RendererList.begin(), m_RendererList.end(), renderer) == m_RendererList.end())
{
m_RendererList.push_back(renderer);
}
mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_PointNumbersAnnotation.GetPointer(), renderer);
m_PointNumbersAnnotation->SetVisibility(false);
mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_ControlPointNumbersAnnotation.GetPointer(), renderer);
m_ControlPointNumbersAnnotation->SetVisibility(false);
InternalDrawContour(renderingContour, renderer);
}
void mitk::ContourModelGLMapper2DBase::InternalDrawContour(mitk::ContourModel *renderingContour,
mitk::BaseRenderer *renderer)
{
if (!renderingContour)
return;
auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer);
localStorage->Device = vtkSmartPointer<vtkOpenGLContextDevice2D>::New();
localStorage->Context = vtkSmartPointer<vtkContext2D>::New();
localStorage->Device->Begin(renderer->GetVtkRenderer());
localStorage->Context->Begin(localStorage->Device);
mitk::DataNode *dataNode = this->GetDataNode();
renderingContour->UpdateOutputInformation();
const auto timestep = this->GetTimestep();
if (!renderingContour->IsEmptyTimeStep(timestep))
{
// apply color and opacity read from the PropertyList
ApplyColorAndOpacityProperties(renderer);
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(dataNode->GetProperty("contour.color", renderer));
float opacity = 0.5;
dataNode->GetFloatProperty("opacity", opacity, renderer);
if (colorprop)
{
// set the color of the contour
double red = colorprop->GetColor().GetRed();
double green = colorprop->GetColor().GetGreen();
double blue = colorprop->GetColor().GetBlue();
localStorage->Context->GetPen()->SetColorF(red, green, blue, opacity);
}
mitk::ColorProperty::Pointer selectedcolor =
dynamic_cast<mitk::ColorProperty *>(dataNode->GetProperty("contour.points.color", renderer));
if (!selectedcolor)
{
selectedcolor = mitk::ColorProperty::New(1.0, 0.0, 0.1);
}
vtkLinearTransform *transform = dataNode->GetVtkTransform();
// ContourModel::OutputType point;
mitk::Point3D point;
mitk::Point3D p;
float vtkp[3];
float lineWidth = 3.0;
bool drawit = false;
bool isHovering = false;
dataNode->GetBoolProperty("contour.hovering", isHovering);
if (isHovering)
dataNode->GetFloatProperty("contour.hovering.width", lineWidth);
else
dataNode->GetFloatProperty("contour.width", lineWidth);
bool showSegments = false;
dataNode->GetBoolProperty("contour.segments.show", showSegments);
bool showControlPoints = false;
dataNode->GetBoolProperty("contour.controlpoints.show", showControlPoints);
bool showPoints = false;
dataNode->GetBoolProperty("contour.points.show", showPoints);
bool showPointsNumbers = false;
dataNode->GetBoolProperty("contour.points.text", showPointsNumbers);
bool showControlPointsNumbers = false;
dataNode->GetBoolProperty("contour.controlpoints.text", showControlPointsNumbers);
bool projectmode = false;
dataNode->GetVisibility(projectmode, renderer, "contour.project-onto-plane");
auto pointsIt = renderingContour->IteratorBegin(timestep);
Point2D pt2d; // projected_p in display coordinates
Point2D lastPt2d;
int index = 0;
mitk::ScalarType maxDiff = 0.25;
while (pointsIt != renderingContour->IteratorEnd(timestep))
{
lastPt2d = pt2d;
point = (*pointsIt)->Coordinates;
itk2vtk(point, vtkp);
transform->TransformPoint(vtkp, vtkp);
vtk2itk(vtkp, p);
renderer->WorldToView(p, pt2d);
ScalarType scalardiff = fabs(renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(p));
// project to plane
if (projectmode)
{
drawit = true;
}
else if (scalardiff < maxDiff) // point is close enough to be drawn
{
drawit = true;
}
else
{
drawit = false;
}
// draw line
if (drawit)
{
if (showSegments)
{
// lastPt2d is not valid in first step
if (!(pointsIt == renderingContour->IteratorBegin(timestep)))
{
localStorage->Context->GetPen()->SetWidth(lineWidth);
localStorage->Context->DrawLine(pt2d[0], pt2d[1], lastPt2d[0], lastPt2d[1]);
localStorage->Context->GetPen()->SetWidth(1);
}
}
if (showControlPoints)
{
- // draw ontrol points
+ // draw control points
if ((*pointsIt)->IsControlPoint)
{
float pointsize = 4;
Point2D tmp;
Vector2D horz, vert;
horz[1] = 0;
vert[0] = 0;
horz[0] = pointsize;
vert[1] = pointsize;
localStorage->Context->GetPen()->SetColorF(selectedcolor->GetColor().GetRed(),
selectedcolor->GetColor().GetBlue(),
selectedcolor->GetColor().GetGreen());
localStorage->Context->GetPen()->SetWidth(1);
// a rectangle around the point with the selected color
auto* rectPts = new float[8];
tmp = pt2d - horz;
rectPts[0] = tmp[0];
rectPts[1] = tmp[1];
tmp = pt2d + vert;
rectPts[2] = tmp[0];
rectPts[3] = tmp[1];
tmp = pt2d + horz;
rectPts[4] = tmp[0];
rectPts[5] = tmp[1];
tmp = pt2d - vert;
rectPts[6] = tmp[0];
rectPts[7] = tmp[1];
localStorage->Context->DrawPolygon(rectPts,4);
// the actual point in the specified color to see the usual color of the point
localStorage->Context->GetPen()->SetColorF(
colorprop->GetColor().GetRed(), colorprop->GetColor().GetGreen(), colorprop->GetColor().GetBlue());
localStorage->Context->DrawPoint(pt2d[0], pt2d[1]);
}
}
if (showPoints)
{
float pointsize = 3;
Point2D tmp;
Vector2D horz, vert;
horz[1] = 0;
vert[0] = 0;
horz[0] = pointsize;
vert[1] = pointsize;
localStorage->Context->GetPen()->SetColorF(0.0, 0.0, 0.0);
localStorage->Context->GetPen()->SetWidth(1);
// a rectangle around the point with the selected color
auto* rectPts = new float[8];
tmp = pt2d - horz;
rectPts[0] = tmp[0];
rectPts[1] = tmp[1];
tmp = pt2d + vert;
rectPts[2] = tmp[0];
rectPts[3] = tmp[1];
tmp = pt2d + horz;
rectPts[4] = tmp[0];
rectPts[5] = tmp[1];
tmp = pt2d - vert;
rectPts[6] = tmp[0];
rectPts[7] = tmp[1];
localStorage->Context->DrawPolygon(rectPts, 4);
// the actual point in the specified color to see the usual color of the point
localStorage->Context->GetPen()->SetColorF(
colorprop->GetColor().GetRed(), colorprop->GetColor().GetGreen(), colorprop->GetColor().GetBlue());
localStorage->Context->DrawPoint(pt2d[0], pt2d[1]);
}
if (showPointsNumbers)
{
std::string l;
std::stringstream ss;
ss << index;
l.append(ss.str());
float rgb[3];
rgb[0] = 0.0;
rgb[1] = 0.0;
rgb[2] = 0.0;
WriteTextWithAnnotation(m_PointNumbersAnnotation, l.c_str(), rgb, pt2d, renderer);
}
if (showControlPointsNumbers && (*pointsIt)->IsControlPoint)
{
std::string l;
std::stringstream ss;
ss << index;
l.append(ss.str());
float rgb[3];
rgb[0] = 1.0;
rgb[1] = 1.0;
rgb[2] = 0.0;
WriteTextWithAnnotation(m_ControlPointNumbersAnnotation, l.c_str(), rgb, pt2d, renderer);
}
index++;
}
pointsIt++;
} // end while iterate over controlpoints
// close contour if necessary
if (renderingContour->IsClosed(timestep) && drawit && showSegments)
{
lastPt2d = pt2d;
point = renderingContour->GetVertexAt(0, timestep)->Coordinates;
itk2vtk(point, vtkp);
transform->TransformPoint(vtkp, vtkp);
vtk2itk(vtkp, p);
renderer->WorldToDisplay(p, pt2d);
localStorage->Context->GetPen()->SetWidth(lineWidth);
localStorage->Context->DrawLine(lastPt2d[0], lastPt2d[1], pt2d[0], pt2d[1]);
localStorage->Context->GetPen()->SetWidth(1);
}
// draw selected vertex if exists
if (renderingContour->GetSelectedVertex())
{
// transform selected vertex
point = renderingContour->GetSelectedVertex()->Coordinates;
itk2vtk(point, vtkp);
transform->TransformPoint(vtkp, vtkp);
vtk2itk(vtkp, p);
renderer->WorldToDisplay(p, pt2d);
ScalarType scalardiff = fabs(renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(p));
//----------------------------------
// draw point if close to plane
if (scalardiff < maxDiff)
{
float pointsize = 5;
Point2D tmp;
localStorage->Context->GetPen()->SetColorF(0.0, 1.0, 0.0);
localStorage->Context->GetPen()->SetWidth(1);
// a rectangle around the point with the selected color
auto* rectPts = new float[8];
// a diamond around the point
// begin from upper left corner and paint clockwise
rectPts[0] = pt2d[0] - pointsize;
rectPts[1] = pt2d[1] + pointsize;
rectPts[2] = pt2d[0] + pointsize;
rectPts[3] = pt2d[1] + pointsize;
rectPts[4] = pt2d[0] + pointsize;
rectPts[5] = pt2d[1] - pointsize;
rectPts[6] = pt2d[0] - pointsize;
rectPts[7] = pt2d[1] - pointsize;
localStorage->Context->DrawPolygon(rectPts, 4);
}
//------------------------------------
}
}
localStorage->Context = nullptr;
localStorage->Device = nullptr;
}
void mitk::ContourModelGLMapper2DBase::WriteTextWithAnnotation(TextAnnotationPointerType textAnnotation,
const char *text,
float rgb[3],
Point2D /*pt2d*/,
mitk::BaseRenderer * /*renderer*/)
{
textAnnotation->SetText(text);
textAnnotation->SetColor(rgb);
textAnnotation->SetOpacity(1);
textAnnotation->SetFontSize(16);
textAnnotation->SetBoolProperty("drawShadow", false);
textAnnotation->SetVisibility(true);
}
diff --git a/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp b/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp
index 4fdf5d812d..01e848e008 100644
--- a/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp
+++ b/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp
@@ -1,372 +1,372 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkContourModelMapper2D.h>
#include <mitkContourModelSubDivisionFilter.h>
#include <vtkAppendPolyData.h>
#include <vtkCellArray.h>
#include <vtkCutter.h>
#include <vtkPlane.h>
#include <vtkPoints.h>
#include <vtkProperty.h>
#include <vtkSphereSource.h>
#include <vtkStripper.h>
#include <vtkTubeFilter.h>
#include <mitkPlaneGeometry.h>
mitk::ContourModelMapper2D::ContourModelMapper2D()
{
}
mitk::ContourModelMapper2D::~ContourModelMapper2D()
{
}
const mitk::ContourModel *mitk::ContourModelMapper2D::GetInput(void)
{
- // convient way to get the data from the dataNode
+ // convenient way to get the data from the dataNode
return static_cast<const mitk::ContourModel *>(GetDataNode()->GetData());
}
vtkProp *mitk::ContourModelMapper2D::GetVtkProp(mitk::BaseRenderer *renderer)
{
// return the actor corresponding to the renderer
return m_LSH.GetLocalStorage(renderer)->m_Actor;
}
void mitk::ContourModelMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
/*++ convert the contour to vtkPolyData and set it as input for our mapper ++*/
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
auto *inputContour = static_cast<mitk::ContourModel *>(GetDataNode()->GetData());
const auto timestep = this->GetTimestep();
// if there's something to be rendered
if (inputContour->GetNumberOfVertices(timestep) > 0)
{
localStorage->m_OutlinePolyData = this->CreateVtkPolyDataFromContour(inputContour, renderer);
}
this->ApplyContourProperties(renderer);
localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData);
}
void mitk::ContourModelMapper2D::Update(mitk::BaseRenderer *renderer)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if (!visible)
return;
// check if there is something to be rendered
auto *data = static_cast<mitk::ContourModel *>(GetDataNode()->GetData());
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(renderer->GetTimeStep())))
{
// clear the rendered polydata
localStorage->m_Mapper->RemoveAllInputs(); // SetInput(vtkSmartPointer<vtkPolyData>::New());
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()) // 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();
}
vtkSmartPointer<vtkPolyData> mitk::ContourModelMapper2D::CreateVtkPolyDataFromContour(mitk::ContourModel *inputContour,
mitk::BaseRenderer *renderer)
{
const auto timestep = this->GetTimestep();
// Create a polydata to store everything in
vtkSmartPointer<vtkPolyData> resultingPolyData = vtkSmartPointer<vtkPolyData>::New();
// check for the worldgeometry from the current render window
const mitk::PlaneGeometry *currentWorldGeometry = renderer->GetCurrentWorldPlaneGeometry();
if (currentWorldGeometry)
{
// origin and normal of vtkPlane
mitk::Point3D origin = currentWorldGeometry->GetOrigin();
mitk::Vector3D normal = currentWorldGeometry->GetNormal();
// the implicit function to slice through the polyData
vtkSmartPointer<vtkPlane> plane = vtkSmartPointer<vtkPlane>::New();
plane->SetOrigin(origin[0], origin[1], origin[2]);
plane->SetNormal(normal[0], normal[1], normal[2]);
/* First of all convert the control points of the contourModel to vtk points
* and add lines in between them
*/
// the points to draw
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
// the lines to connect the points
vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New();
// Create a polydata to store everything in
vtkSmartPointer<vtkPolyData> polyDataIn3D = vtkSmartPointer<vtkPolyData>::New();
vtkSmartPointer<vtkAppendPolyData> appendPoly = vtkSmartPointer<vtkAppendPolyData>::New();
mitk::ContourModel::Pointer renderingContour = mitk::ContourModel::New();
renderingContour = inputContour;
bool subdivision = false;
this->GetDataNode()->GetBoolProperty("subdivision curve", subdivision, renderer);
if (subdivision)
{
mitk::ContourModel::Pointer subdivContour = mitk::ContourModel::New();
mitk::ContourModelSubDivisionFilter::Pointer subdivFilter = mitk::ContourModelSubDivisionFilter::New();
subdivFilter->SetInput(inputContour);
subdivFilter->Update();
subdivContour = subdivFilter->GetOutput();
if (subdivContour->GetNumberOfVertices() == 0)
{
subdivContour = inputContour;
}
renderingContour = subdivContour;
}
// iterate over all control points
auto current = renderingContour->IteratorBegin(timestep);
auto next = renderingContour->IteratorBegin(timestep);
if (next != renderingContour->IteratorEnd(timestep))
{
next++;
auto end = renderingContour->IteratorEnd(timestep);
while (next != end)
{
mitk::ContourModel::VertexType *currentControlPoint = *current;
mitk::ContourModel::VertexType *nextControlPoint = *next;
vtkIdType p1 = points->InsertNextPoint(currentControlPoint->Coordinates[0],
currentControlPoint->Coordinates[1],
currentControlPoint->Coordinates[2]);
vtkIdType p2 = points->InsertNextPoint(
nextControlPoint->Coordinates[0], nextControlPoint->Coordinates[1], nextControlPoint->Coordinates[2]);
// add the line between both contorlPoints
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
if (currentControlPoint->IsControlPoint)
{
double coordinates[3];
coordinates[0] = currentControlPoint->Coordinates[0];
coordinates[1] = currentControlPoint->Coordinates[1];
coordinates[2] = currentControlPoint->Coordinates[2];
double distance = plane->DistanceToPlane(coordinates);
if (distance < 0.1)
{
vtkSmartPointer<vtkSphereSource> sphere = vtkSmartPointer<vtkSphereSource>::New();
sphere->SetRadius(1.2);
sphere->SetCenter(coordinates[0], coordinates[1], coordinates[2]);
sphere->Update();
appendPoly->AddInputConnection(sphere->GetOutputPort());
}
}
current++;
next++;
} // end while (it!=end)
// check if last control point is enabled to draw it
if ((*current)->IsControlPoint)
{
double coordinates[3];
coordinates[0] = (*current)->Coordinates[0];
coordinates[1] = (*current)->Coordinates[1];
coordinates[2] = (*current)->Coordinates[2];
double distance = plane->DistanceToPlane(coordinates);
if (distance < 0.1)
{
vtkSmartPointer<vtkSphereSource> sphere = vtkSmartPointer<vtkSphereSource>::New();
sphere->SetRadius(1.2);
sphere->SetCenter(coordinates[0], coordinates[1], coordinates[2]);
sphere->Update();
appendPoly->AddInputConnection(sphere->GetOutputPort());
}
}
/* If the contour is closed an additional line has to be created between the very first point
* and the last point
*/
if (renderingContour->IsClosed(timestep))
{
// add a line from the last to the first control point
mitk::ContourModel::VertexType *firstControlPoint = *(renderingContour->IteratorBegin(timestep));
mitk::ContourModel::VertexType *lastControlPoint = *(--(renderingContour->IteratorEnd(timestep)));
vtkIdType p2 = points->InsertNextPoint(
lastControlPoint->Coordinates[0], lastControlPoint->Coordinates[1], lastControlPoint->Coordinates[2]);
vtkIdType p1 = points->InsertNextPoint(
firstControlPoint->Coordinates[0], firstControlPoint->Coordinates[1], firstControlPoint->Coordinates[2]);
// add the line between both contorlPoints
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
} // end if(isClosed)
// Add the points to the dataset
polyDataIn3D->SetPoints(points);
// Add the lines to the dataset
polyDataIn3D->SetLines(lines);
// cut through polyData
bool useCuttingPlane = false;
this->GetDataNode()->GetBoolProperty("use cutting plane", useCuttingPlane, renderer);
if (useCuttingPlane)
{
// slice through the data to get a 2D representation of the (possible) 3D contour
- // needed because currently there is no outher solution if the contour is within the plane
+ // needed because currently there is no other solution if the contour is within the plane
vtkSmartPointer<vtkTubeFilter> tubeFilter = vtkSmartPointer<vtkTubeFilter>::New();
tubeFilter->SetInputData(polyDataIn3D);
tubeFilter->SetRadius(0.05);
// cuts through vtkPolyData with a given implicit function. In our case a plane
vtkSmartPointer<vtkCutter> cutter = vtkSmartPointer<vtkCutter>::New();
cutter->SetCutFunction(plane);
cutter->SetInputConnection(tubeFilter->GetOutputPort());
// we want the scalars of the input - so turn off generating the scalars within vtkCutter
cutter->GenerateCutScalarsOff();
cutter->Update();
// set to 2D representation of the contour
resultingPolyData = cutter->GetOutput();
} // end if(project contour)
else
{
// set to 3D polyData
resultingPolyData = polyDataIn3D;
}
} // end if (it != end)
appendPoly->AddInputData(resultingPolyData);
appendPoly->Update();
// return contour with control points
return appendPoly->GetOutput();
}
else
{
// return empty polyData
return resultingPolyData;
}
}
void mitk::ContourModelMapper2D::ApplyContourProperties(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
float lineWidth(1.0);
if (this->GetDataNode()->GetFloatProperty("width", lineWidth, renderer))
{
localStorage->m_Actor->GetProperty()->SetLineWidth(lineWidth);
}
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("color", renderer));
if (colorprop)
{
// set the color of the contour
double red = colorprop->GetColor().GetRed();
double green = colorprop->GetColor().GetGreen();
double blue = colorprop->GetColor().GetBlue();
localStorage->m_Actor->GetProperty()->SetColor(red, green, blue);
}
// make sure that directional lighting isn't used for our contour
localStorage->m_Actor->GetProperty()->SetAmbient(1.0);
localStorage->m_Actor->GetProperty()->SetDiffuse(0.0);
localStorage->m_Actor->GetProperty()->SetSpecular(0.0);
}
/*+++++++++++++++++++ LocalStorage part +++++++++++++++++++++++++*/
mitk::ContourModelMapper2D::LocalStorage *mitk::ContourModelMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer)
{
return m_LSH.GetLocalStorage(renderer);
}
mitk::ContourModelMapper2D::LocalStorage::LocalStorage()
{
m_Mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_Actor = vtkSmartPointer<vtkActor>::New();
m_OutlinePolyData = vtkSmartPointer<vtkPolyData>::New();
// set the mapper for the actor
m_Actor->SetMapper(m_Mapper);
}
void mitk::ContourModelMapper2D::SetDefaultProperties(mitk::DataNode *node,
mitk::BaseRenderer *renderer,
bool overwrite)
{
node->AddProperty("color", ColorProperty::New(0.9, 1.0, 0.1), renderer, overwrite);
node->AddProperty("width", mitk::FloatProperty::New(1.0), renderer, overwrite);
node->AddProperty("use cutting plane", mitk::BoolProperty::New(true), renderer, overwrite);
node->AddProperty("subdivision curve", mitk::BoolProperty::New(false), renderer, overwrite);
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
diff --git a/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp b/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp
index e6b499da4b..74cd97e436 100644
--- a/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp
+++ b/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp
@@ -1,233 +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.
============================================================================*/
#include <mitkContourModelMapper3D.h>
#include <vtkCellArray.h>
#include <vtkPoints.h>
#include <vtkProperty.h>
mitk::ContourModelMapper3D::ContourModelMapper3D()
{
}
mitk::ContourModelMapper3D::~ContourModelMapper3D()
{
}
const mitk::ContourModel *mitk::ContourModelMapper3D::GetInput(void)
{
- // convient way to get the data from the dataNode
+ // convenient way to get the data from the dataNode
return static_cast<const mitk::ContourModel *>(GetDataNode()->GetData());
}
vtkProp *mitk::ContourModelMapper3D::GetVtkProp(mitk::BaseRenderer *renderer)
{
// return the actor corresponding to the renderer
return m_LSH.GetLocalStorage(renderer)->m_Actor;
}
void mitk::ContourModelMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
/* First convert the contourModel to vtkPolyData, then tube filter it and
* set it input for our mapper
*/
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
auto *inputContour = static_cast<mitk::ContourModel *>(GetDataNode()->GetData());
localStorage->m_OutlinePolyData = this->CreateVtkPolyDataFromContour(inputContour);
this->ApplyContourProperties(renderer);
// tube filter the polyData
localStorage->m_TubeFilter->SetInputData(localStorage->m_OutlinePolyData);
float lineWidth(1.0);
if (this->GetDataNode()->GetFloatProperty("contour.3D.width", lineWidth, renderer))
{
localStorage->m_TubeFilter->SetRadius(lineWidth);
}
else
{
localStorage->m_TubeFilter->SetRadius(0.5);
}
localStorage->m_TubeFilter->CappingOn();
localStorage->m_TubeFilter->SetNumberOfSides(10);
localStorage->m_TubeFilter->Update();
localStorage->m_Mapper->SetInputConnection(localStorage->m_TubeFilter->GetOutputPort());
}
void mitk::ContourModelMapper3D::Update(mitk::BaseRenderer *renderer)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
auto *data = static_cast<mitk::ContourModel *>(GetDataNode()->GetData());
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->IsValidTimePoint(renderer->GetTime())) || (this->GetTimestep() == TIMESTEP_INVALID))
{
// clear the rendered polydata
localStorage->m_Mapper->SetInputData(vtkSmartPointer<vtkPolyData>::New());
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()) // 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();
}
vtkSmartPointer<vtkPolyData> mitk::ContourModelMapper3D::CreateVtkPolyDataFromContour(mitk::ContourModel *inputContour)
{
const auto timestep = this->GetTimestep();
// the points to draw
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
// the lines to connect the points
vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New();
// Create a polydata to store everything in
vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
// iterate over the control points
auto current = inputContour->IteratorBegin(timestep);
auto next = inputContour->IteratorBegin(timestep);
if (next != inputContour->IteratorEnd(timestep))
{
next++;
auto end = inputContour->IteratorEnd(timestep);
while (next != end)
{
mitk::ContourModel::VertexType *currentControlPoint = *current;
mitk::ContourModel::VertexType *nextControlPoint = *next;
if (!(currentControlPoint->Coordinates[0] == nextControlPoint->Coordinates[0] &&
currentControlPoint->Coordinates[1] == nextControlPoint->Coordinates[1] &&
currentControlPoint->Coordinates[2] == nextControlPoint->Coordinates[2]))
{
vtkIdType p1 = points->InsertNextPoint(currentControlPoint->Coordinates[0],
currentControlPoint->Coordinates[1],
currentControlPoint->Coordinates[2]);
vtkIdType p2 = points->InsertNextPoint(
nextControlPoint->Coordinates[0], nextControlPoint->Coordinates[1], nextControlPoint->Coordinates[2]);
// add the line between both contorlPoints
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
current++;
next++;
}
if (inputContour->IsClosed(timestep))
{
// If the contour is closed add a line from the last to the first control point
mitk::ContourModel::VertexType *firstControlPoint = *(inputContour->IteratorBegin(timestep));
mitk::ContourModel::VertexType *lastControlPoint = *(--(inputContour->IteratorEnd(timestep)));
if (lastControlPoint->Coordinates[0] != firstControlPoint->Coordinates[0] ||
lastControlPoint->Coordinates[1] != firstControlPoint->Coordinates[1] ||
lastControlPoint->Coordinates[2] != firstControlPoint->Coordinates[2])
{
vtkIdType p2 = points->InsertNextPoint(
lastControlPoint->Coordinates[0], lastControlPoint->Coordinates[1], lastControlPoint->Coordinates[2]);
vtkIdType p1 = points->InsertNextPoint(
firstControlPoint->Coordinates[0], firstControlPoint->Coordinates[1], firstControlPoint->Coordinates[2]);
// add the line to the cellArray
lines->InsertNextCell(2);
lines->InsertCellPoint(p1);
lines->InsertCellPoint(p2);
}
}
// Add the points to the dataset
polyData->SetPoints(points);
// Add the lines to the dataset
polyData->SetLines(lines);
}
return polyData;
}
void mitk::ContourModelMapper3D::ApplyContourProperties(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
mitk::ColorProperty::Pointer colorprop =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("contour.color", renderer));
if (colorprop)
{
// set the color of the contour
double red = colorprop->GetColor().GetRed();
double green = colorprop->GetColor().GetGreen();
double blue = colorprop->GetColor().GetBlue();
localStorage->m_Actor->GetProperty()->SetColor(red, green, blue);
}
}
/*+++++++++++++++++++ LocalStorage part +++++++++++++++++++++++++*/
mitk::ContourModelMapper3D::LocalStorage *mitk::ContourModelMapper3D::GetLocalStorage(mitk::BaseRenderer *renderer)
{
return m_LSH.GetLocalStorage(renderer);
}
mitk::ContourModelMapper3D::LocalStorage::LocalStorage()
{
m_Mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_Actor = vtkSmartPointer<vtkActor>::New();
m_OutlinePolyData = vtkSmartPointer<vtkPolyData>::New();
m_TubeFilter = vtkSmartPointer<vtkTubeFilter>::New();
// set the mapper for the actor
m_Actor->SetMapper(m_Mapper);
}
void mitk::ContourModelMapper3D::SetDefaultProperties(mitk::DataNode *node,
mitk::BaseRenderer *renderer,
bool overwrite)
{
node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite);
node->AddProperty("contour.3D.width", mitk::FloatProperty::New(0.5), renderer, overwrite);
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
diff --git a/Modules/ContourModel/Testing/mitkContourModelSetTest.cpp b/Modules/ContourModel/Testing/mitkContourModelSetTest.cpp
index fc0e5c17f2..e0fdd370f5 100644
--- a/Modules/ContourModel/Testing/mitkContourModelSetTest.cpp
+++ b/Modules/ContourModel/Testing/mitkContourModelSetTest.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 <mitkContourModelSet.h>
#include <mitkTestingMacros.h>
static void TestAddVertex()
{
mitk::ContourModelSet::Pointer contourSet = mitk::ContourModelSet::New();
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
contourSet->AddContourModel(contour);
MITK_TEST_CONDITION(contourSet->GetSize() > 0, "Add a contour, size increased");
}
static void TestRemoveContourAtIndex()
{
mitk::ContourModelSet::Pointer contourSet = mitk::ContourModelSet::New();
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
contourSet->AddContourModel(contour);
contourSet->RemoveContourModelAt(0);
MITK_TEST_CONDITION(contourSet->GetSize() == 0, "removed contour by index");
contourSet->AddContourModel(contour);
contourSet->RemoveContourModel(contour);
MITK_TEST_CONDITION(contourSet->GetSize() == 0, "removed contour by object");
}
static void TestEmptyContour()
{
mitk::ContourModelSet::Pointer contourSet = mitk::ContourModelSet::New();
- MITK_TEST_CONDITION(contourSet->Begin() == contourSet->End(), "test iterator of emtpy contour");
+ MITK_TEST_CONDITION(contourSet->Begin() == contourSet->End(), "test iterator of empty contour");
MITK_TEST_CONDITION(contourSet->GetSize() == 0, "test numberof vertices of empty contour");
}
int mitkContourModelSetTest(int /*argc*/, char * /*argv*/ [])
{
MITK_TEST_BEGIN("mitkContourModelSetTest")
TestEmptyContour();
TestAddVertex();
TestRemoveContourAtIndex();
MITK_TEST_END()
}
diff --git a/Modules/ContourModel/Testing/mitkContourModelTest.cpp b/Modules/ContourModel/Testing/mitkContourModelTest.cpp
index 9464bdcb83..e0b029a155 100644
--- a/Modules/ContourModel/Testing/mitkContourModelTest.cpp
+++ b/Modules/ContourModel/Testing/mitkContourModelTest.cpp
@@ -1,483 +1,483 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkTestingMacros.h>
// Add a vertex to the contour and see if size changed
static void TestAddVertex()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 1, "Add a Vertex, size increased");
MITK_TEST_CONDITION(contour->GetVertexAt(0)->Coordinates == p, "Added vertex has the correct value.");
mitk::Point3D outOfTimeBoundPoint;
outOfTimeBoundPoint[0] = outOfTimeBoundPoint[1] = outOfTimeBoundPoint[2] = 1;
contour->AddVertex(outOfTimeBoundPoint, mitk::TimeStepType(1));
MITK_TEST_CONDITION(contour->GetTimeSteps() == 1, "Add a vertex to an unsupported time step has not changed geometry.");
MITK_TEST_CONDITION(contour->IsEmptyTimeStep(1), "Add a vertex to an unsupported time step has not added an contour element.");
MITK_TEST_CONDITION(contour->GetNumberOfVertices(1) == -1, "Add a vertex to an unsupported time step has not added an contour element.");
contour->Expand(3);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 2;
mitk::Point3D p3;
p3[0] = p3[1] = p3[2] = 3;
contour->AddVertex(p2, mitk::TimeStepType(1));
contour->AddVertex(mitk::ContourModel::VertexType(p3), mitk::TimeStepType(1));
MITK_TEST_CONDITION(!contour->IsEmptyTimeStep(1), "Add a vertex to an unsupported time step has not added an contour element.");
MITK_TEST_CONDITION(contour->GetVertexAt(0,1)->Coordinates == p2, "Add a vertex to the 2nd time step (as Point).");
MITK_TEST_CONDITION(contour->GetVertexAt(1,1)->Coordinates == p3, "Add a vertex to the 2nd time step via overload (as vertex type).");
MITK_TEST_CONDITION(contour->GetNumberOfVertices(1) == 2, "Add a vertex to an unsupported time step has not added an contour element.");
}
// Select a vertex by index. successful if the selected vertex member of the contour is no longer set to null
static void TestSelectVertexAtIndex()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
contour->SelectVertexAt(0);
MITK_TEST_CONDITION(contour->GetSelectedVertex() != nullptr, "Vertex was selected at index");
}
// Select a vertex by worldposition. successful if the selected vertex member of the contour is no longer set to null
static void TestSelectVertexAtWorldposition()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
// same point is used here so the epsilon can be chosen very small
contour->SelectVertexAt(p, 0.01);
MITK_TEST_CONDITION(contour->GetSelectedVertex() != nullptr, "Vertex was selected at position");
}
// Move a vertex by a translation vector
static void TestMoveSelectedVertex()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
// Same point is used here so the epsilon can be chosen very small
contour->SelectVertexAt(p, 0.01);
mitk::Vector3D v;
v[0] = 1;
v[1] = 3;
v[2] = -1;
contour->ShiftSelectedVertex(v);
const mitk::ContourModel::VertexType *vertex = contour->GetSelectedVertex();
bool correctlyMoved = false;
correctlyMoved =
(vertex->Coordinates)[0] == (v[0]) && (vertex->Coordinates)[1] == (v[1]) && (vertex->Coordinates)[2] == (v[2]);
MITK_TEST_CONDITION(correctlyMoved, "Vertex has been moved");
}
// Test to move the whole contour
/*
static void TestMoveContour()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 0;
contour->AddVertex(p2);
mitk::Vector3D v;
v[0] = 1;
v[1] = 3;
v[2] = -1;
contour->ShiftContour(v);
mitk::ContourModel::VertexIterator it = contour->IteratorBegin();
mitk::ContourModel::VertexIterator end = contour->IteratorEnd();
bool correctlyMoved = false;
while(it != end)
{
correctlyMoved &= (*it)->Coordinates[0] == (v[0]) &&
(*it)->Coordinates[1] == (v[1]) &&
(*it)->Coordinates[2] == (v[2]);
}
MITK_TEST_CONDITION(correctlyMoved, "Contour has been moved");
}
*/
// Remove a vertex by index
static void TestRemoveVertexAtIndex()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
contour->RemoveVertexAt(0);
MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 0, "removed vertex");
}
// Remove a vertex by position
static void TestRemoveVertexAtWorldPosition()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
contour->RemoveVertexAt(p, 0.01);
MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 0, "removed vertex");
}
// Check closeable contour
static void TestIsclosed()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 1;
contour->AddVertex(p2);
contour->Close();
MITK_TEST_CONDITION(contour->IsClosed(), "closed contour");
// no vertices should be added to a closed contour
int oldNumberOfVertices = contour->GetNumberOfVertices();
mitk::Point3D p3;
p3[0] = p3[1] = p3[2] = 4;
contour->AddVertex(p3);
int newNumberOfVertices = contour->GetNumberOfVertices();
MITK_TEST_CONDITION(oldNumberOfVertices != newNumberOfVertices, "vertices added to closed contour");
}
// Test concatenating two contours
static void TestConcatenate()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 1;
contour->AddVertex(p2);
mitk::ContourModel::Pointer contour2 = mitk::ContourModel::New();
mitk::Point3D p3;
p3[0] = -2;
p3[1] = 10;
p3[2] = 0;
contour2->AddVertex(p3);
mitk::Point3D p4;
p4[0] = -3;
p4[1] = 6;
p4[2] = -5;
contour2->AddVertex(p4);
contour->Concatenate(contour2);
MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 4, "two contours were concatenated");
}
// Try to select a vertex at position (within a epsilon of course) where no vertex is.
// So the selected verted member should be null.
static void TestSelectVertexAtWrongPosition()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
MITK_TEST_CONDITION_REQUIRED(contour->GetSelectedVertex() == nullptr, "selected vertex is nullptr");
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 2;
contour->SelectVertexAt(p2, 0.1);
MITK_TEST_CONDITION(contour->GetSelectedVertex() == nullptr, "Vertex was not selected");
}
static void TestInsertVertex()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 1;
contour->AddVertex(p2);
mitk::Point3D pointToInsert;
pointToInsert[0] = pointToInsert[1] = pointToInsert[2] = 10;
contour->InsertVertexAtIndex(pointToInsert, 1);
MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 3, "test insert vertex");
MITK_TEST_CONDITION(contour->GetVertexAt(1)->Coordinates == pointToInsert, "compare inserted vertex");
mitk::Point3D outOfTimeBoundPoint;
outOfTimeBoundPoint[0] = outOfTimeBoundPoint[1] = outOfTimeBoundPoint[2] = 1;
contour->InsertVertexAtIndex(outOfTimeBoundPoint, 4, false, mitk::TimeStepType(1));
MITK_TEST_CONDITION(contour->GetTimeSteps() == 1, "Insert a vertex to an unsupported time step has not changed geometry.");
MITK_TEST_CONDITION(contour->IsEmptyTimeStep(1), "Insert a vertex to an unsupported time step has not added an contour element.");
MITK_TEST_CONDITION(contour->GetNumberOfVertices(1) == -1, "Insert a vertex to an unsupported time step has not added an contour element.");
}
// try to access an invalid timestep
static void TestInvalidTimeStep()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D p2;
p2[0] = p2[1] = p2[2] = 1;
contour->AddVertex(p2);
mitk::TimeStepType invalidTimeStep = 42;
MITK_TEST_CONDITION_REQUIRED(contour->IsEmptyTimeStep(invalidTimeStep), "invalid timestep required");
MITK_TEST_FOR_EXCEPTION(std::exception, contour->IteratorBegin(-1));
contour->Close(invalidTimeStep);
MITK_TEST_CONDITION(contour->IsClosed() == false, "test close for timestep 0");
MITK_TEST_CONDITION(contour->IsClosed(invalidTimeStep) == false, "test close at invalid timestep");
contour->SetClosed(true, invalidTimeStep);
MITK_TEST_CONDITION(contour->GetNumberOfVertices(invalidTimeStep) == -1,
"test number of vertices at invalid timestep");
contour->AddVertex(p2, invalidTimeStep);
MITK_TEST_CONDITION(contour->GetNumberOfVertices(invalidTimeStep) == -1, "test add vertex at invalid timestep");
contour->InsertVertexAtIndex(p2, 0, false, invalidTimeStep);
MITK_TEST_CONDITION(contour->GetNumberOfVertices(invalidTimeStep) == -1, "test insert vertex at invalid timestep");
MITK_TEST_CONDITION(contour->SelectVertexAt(0, invalidTimeStep) == false, "test select vertex at invalid timestep");
MITK_TEST_CONDITION(contour->RemoveVertexAt(0, invalidTimeStep) == false, "test remove vertex at invalid timestep");
MITK_TEST_CONDITION(contour->GetVertexAt(0, 5) == nullptr, "Access a vertex on an invalid time step.");
MITK_TEST_CONDITION(contour->GetVertexAt(10, 0) == nullptr, "Access a vertex on an invalid index.");
}
static void TestEmptyContour()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
- MITK_TEST_CONDITION(contour->IteratorBegin() == contour->IteratorEnd(), "test iterator of emtpy contour");
+ MITK_TEST_CONDITION(contour->IteratorBegin() == contour->IteratorEnd(), "test iterator of empty contour");
MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 0, "test numberof vertices of empty contour");
}
static void TestSetVertices()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
mitk::Point3D newCoordinates;
newCoordinates[0] = newCoordinates[1] = newCoordinates[2] = 1;
contour->SetVertexAt(0, newCoordinates);
MITK_TEST_CONDITION(mitk::Equal(contour->GetVertexAt(0)->Coordinates, newCoordinates), "set coordinates");
mitk::ContourModel::Pointer contour2 = mitk::ContourModel::New();
mitk::Point3D p3;
p3[0] = -2;
p3[1] = 10;
p3[2] = 0;
contour2->AddVertex(p3);
mitk::Point3D p4;
p4[0] = -3;
p4[1] = 6;
p4[2] = -5;
contour2->AddVertex(p4);
contour->AddVertex(p);
contour->SetVertexAt(1, contour2->GetVertexAt(1), 0);
MITK_TEST_CONDITION(
mitk::Equal(contour->GetVertexAt(1)->Coordinates, contour2->GetVertexAt(1)->Coordinates), "Use setter and getter combination");
}
static void TestContourModelAPI()
{
mitk::ContourModel::Pointer contour1 = mitk::ContourModel::New();
mitk::Point3D p1;
p1[0] = -2;
p1[1] = 10;
p1[2] = 0;
contour1->AddVertex(p1);
// adding vertices should always copy the content and not store pointers or references.
MITK_TEST_CONDITION(&p1 != &(contour1->GetVertexAt(0)->Coordinates), "copied point");
mitk::Point3D p2;
p2[0] = -3;
p2[1] = 6;
p2[2] = -5;
contour1->AddVertex(p2);
// test use of setter and getter with const and non-const pointers
const mitk::ContourModel::VertexType *vertex = contour1->GetVertexAt(1);
MITK_TEST_CONDITION(contour1->GetIndex(vertex) == 1, "Get index");
auto *nonConstVertex = const_cast<mitk::ContourModel::VertexType *>(vertex);
MITK_TEST_CONDITION(contour1->GetIndex(nonConstVertex) == 1, "Get index non-const");
mitk::ContourModel::Pointer contour2 = mitk::ContourModel::New();
contour2->AddVertex(*(contour1->GetVertexAt(0)));
MITK_TEST_CONDITION(contour2->GetNumberOfVertices() == 1, "Add call with another contour");
}
static void TestClear()
{
mitk::ContourModel::Pointer contour = mitk::ContourModel::New();
contour->Expand(3);
contour->Expand(3);
mitk::Point3D p;
p[0] = p[1] = p[2] = 0;
contour->AddVertex(p);
p[0] = p[1] = p[2] = 1;
contour->AddVertex(p, mitk::TimeStepType(1));
p[0] = p[1] = p[2] = 2;
contour->AddVertex(p, mitk::TimeStepType(2));
contour->Clear(1);
MITK_TEST_CONDITION(contour->GetTimeSteps() == 3, "Check time step count stays 3.");
MITK_TEST_CONDITION(!contour->IsEmpty(0), "Check time step 0 is not empty.");
MITK_TEST_CONDITION(contour->IsEmpty(1), "Check time step 1 is empty.");
MITK_TEST_CONDITION(!contour->IsEmpty(2), "Check time step 2 is not empty.");
MITK_TEST_CONDITION(contour->GetVertexAt(0, 2)->Coordinates == p, "compare if vertex at t == 2 is still the same");
contour->Clear();
MITK_TEST_CONDITION(contour->GetTimeSteps() == 1, "Check time step count stays 1.");
MITK_TEST_CONDITION(contour->IsEmpty(0), "Check time step 0 is empty.");
}
int mitkContourModelTest(int /*argc*/, char * /*argv*/ [])
{
MITK_TEST_BEGIN("mitkContourModelTest")
TestAddVertex();
TestSelectVertexAtIndex();
TestSelectVertexAtWorldposition();
TestMoveSelectedVertex();
TestRemoveVertexAtIndex();
TestRemoveVertexAtWorldPosition();
TestIsclosed();
TestConcatenate();
TestInvalidTimeStep();
TestInsertVertex();
TestEmptyContour();
TestSetVertices();
TestSelectVertexAtWrongPosition();
TestContourModelAPI();
TestClear();
MITK_TEST_END()
}
diff --git a/Modules/Core/CMakeLists.txt b/Modules/Core/CMakeLists.txt
index 74cada7281..7bc61bab26 100644
--- a/Modules/Core/CMakeLists.txt
+++ b/Modules/Core/CMakeLists.txt
@@ -1,74 +1,83 @@
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(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1)
+ set(optional_public_target_depends stdc++fs)
+elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)
+ set(optional_public_target_depends c++fs)
+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
MitkLog
CppMicroServices
PACKAGE_DEPENDS
PUBLIC
Boost
nlohmann_json
ITK|IOImageBase+SpatialObjects+Statistics
#ITK|Statistics+Transform
VTK|FiltersTexture+FiltersParallel+ImagingStencil+ImagingMath+InteractionStyle+RenderingOpenGL2+RenderingVolumeOpenGL2+RenderingFreeType+RenderingLabel+InteractionWidgets+IOGeometry+IOImage+IOXML
PRIVATE
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}
+ TARGET_DEPENDS
+ PUBLIC
+ ${optional_public_target_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/include/mitkTestFixture.h b/Modules/Core/TestingHelper/include/mitkTestFixture.h
index 971b9f6248..a6e089f7e7 100644
--- a/Modules/Core/TestingHelper/include/mitkTestFixture.h
+++ b/Modules/Core/TestingHelper/include/mitkTestFixture.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 mitkTestFixture_h
#define mitkTestFixture_h
#include <cppunit/TestFixture.h>
#include <mitkTestingConfig.h>
#include <itksys/SystemTools.hxx>
#include <string>
#include <vector>
namespace mitk
{
/**
* \brief Test fixture for parameterized tests
*
* This class is a drop-in replacement for CppUnit::TextFixture and
* enables test methods to access individual parameters. You can also
* invoke one method multiple times with different parameters.
*
*
* The following simple example creates a single test without custom
* parameters:
*
* \code
* class MySimpleTestSuite : public mitk::TestFixture
* {
* CPPUNIT_TEST_SUITE(MySimpleTestSuite);
* MITK_TEST(FivePlusFiveTest);
* CPPUNIT_TEST_SUITE_END();
*
* public:
* void FivePlusFiveTest()
* {
* CPPUNIT_ASSERT(5+5 == 10);
* }
* };
* MITK_TEST_SUITE_REGISTRATION(MySimpleTestSuite)
* \endcode
*
*
* The following example creates a test class containing only
* one test method, but the associated test suite contains three tests,
* using different parameters for each call of the same method. Use
* the macro MITK_PARAMETERIZED_TEST_1 only if you know what you are
* doing. If you are not sure, use MITK_TEST instead.
*
* \code
* class MyTestSuite : public mitk::TestFixture
* {
* CPPUNIT_TEST_SUITE(MyTestSuite);
* MITK_PARAMETERIZED_TEST_1(TestSomething, "One");
* MITK_PARAMETERIZED_TEST_1(TestSomething, "Two");
* MITK_PARAMETERIZED_TEST_1(TestSomething, "Three");
* CPPUNIT_TEST_SUITE_END();
*
* public:
*
* void TestSomething()
* {
* std::vector<std::string> parameter = GetTestParameter();
* CPPUNIT_ASSERT(parameter.size() == 1);
* std::string testParam = parameter[0];
*
* MITK_INFO << "Parameter: " << testParam;
* }
* };
* MITK_TEST_SUITE_REGISTRATION(MyTestSuite)
* \endcode
*
* \sa MITK_PARAMETERIZED_TEST
* \sa MITK_PARAMETERIZED_TEST_1
*/
class TestFixture : public CppUnit::TestFixture
{
protected:
/**
* \brief Get parameters for this test fixture
*
* This method can be called in tests added via the MITK_PARAMETERIZED_TEST
* macro or one of its variants.
*
* \return The list of \c std::string parameters passed to previous calls
* of the MITK_PARAMETERIZED_TEST macro or one of its variants.
*
*/
std::vector<std::string> GetTestParameter() const { return m_Parameter; }
/**
* \brief Get the absolute path for test data.
*
- * \param testData The realative path in the MITK test data repository.
+ * \param testData The relative path in the MITK test data repository.
*
* \return The absolute path for the test data.
*/
static std::string GetTestDataFilePath(const std::string &testData)
{
if (itksys::SystemTools::FileIsFullPath(testData.c_str()))
return testData;
return std::string(MITK_DATA_DIR) + "/" + testData;
}
private:
template <class P>
friend class TestCaller;
std::vector<std::string> m_Parameter;
};
}
#endif
diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake
index e9f3daabdf..f1bd7b5ec4 100644
--- a/Modules/Core/files.cmake
+++ b/Modules/Core/files.cmake
@@ -1,329 +1,330 @@
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/mitkCrosshairManager.cpp
Controllers/mitkLimitedLinearUndo.cpp
Controllers/mitkOperationEvent.cpp
Controllers/mitkPlanePositionManager.cpp
Controllers/mitkProgressBar.cpp
Controllers/mitkRenderingManager.cpp
Controllers/mitkSliceNavigationController.cpp
Controllers/mitkSliceNavigationHelper.cpp
Controllers/mitkStatusBar.cpp
Controllers/mitkStepper.cpp
Controllers/mitkTestManager.cpp
Controllers/mitkTimeNavigationController.cpp
Controllers/mitkUndoController.cpp
Controllers/mitkVerboseLimitedLinearUndo.cpp
Controllers/mitkVtkLayerController.cpp
+ DataManagement/mitkAffineTransform3D.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/mitkCrosshairData.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/mitkIPropertyDeserialization.cpp
DataManagement/mitkIPropertyExtensions.cpp
DataManagement/mitkIPropertyFilters.cpp
DataManagement/mitkIPropertyOwner.cpp
DataManagement/mitkIPropertyPersistence.cpp
DataManagement/mitkIPropertyProvider.cpp
DataManagement/mitkITKEventObserverGuard.cpp
DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp
DataManagement/mitkLandmarkProjector.cpp
DataManagement/mitkLevelWindow.cpp
DataManagement/mitkLevelWindowManager.cpp
DataManagement/mitkLevelWindowPreset.cpp
DataManagement/mitkLevelWindowProperty.cpp
DataManagement/mitkLookupTable.cpp
DataManagement/mitkLookupTableProperty.cpp
DataManagement/mitkLookupTables.cpp
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/mitkNodePredicateFunction.cpp
DataManagement/mitkNodePredicateGeometry.cpp
DataManagement/mitkNodePredicateNot.cpp
DataManagement/mitkNodePredicateOr.cpp
DataManagement/mitkNodePredicateProperty.cpp
DataManagement/mitkNodePredicateDataProperty.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/mitkPropertyDeserialization.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/mitkVectorProperty.cpp
DataManagement/mitkVtkInterpolationProperty.cpp
DataManagement/mitkVtkRepresentationProperty.cpp
DataManagement/mitkVtkResliceInterpolationProperty.cpp
DataManagement/mitkVtkScalarModeProperty.cpp
DataManagement/mitkWeakPointerProperty.cpp
DataManagement/mitkIPropertyRelations.cpp
DataManagement/mitkPropertyRelations.cpp
Interactions/mitkAction.cpp
Interactions/mitkBindDispatcherInteractor.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/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/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/mitkLogBackend.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
IO/mitkIPreferences.cpp
IO/mitkPreferences.cpp
IO/mitkIPreferencesService.cpp
IO/mitkPreferencesService.cpp
IO/mitkIPreferencesStorage.cpp
IO/mitkXMLPreferencesStorage.cpp
Rendering/mitkAbstractAnnotationRenderer.cpp
Rendering/mitkAnnotationUtils.cpp
Rendering/mitkBaseRenderer.cpp
Rendering/mitkBaseRendererHelper.cpp
Rendering/mitkCrosshairVtkMapper2D.cpp
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/mitkSurfaceVtkMapper2D.cpp
Rendering/mitkSurfaceVtkMapper3D.cpp
Rendering/mitkVideoRecorder.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/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/DisplayConfigBlockLMB.xml
Interactions/PointSet.xml
Interactions/PointSetConfig.xml
mitkLevelWindowPresets.xml
mitkAnatomicalStructureColorPresets.xml
)
diff --git a/Modules/Core/include/mitkAffineTransform3D.h b/Modules/Core/include/mitkAffineTransform3D.h
index e6f535cd75..840fd8f6ac 100644
--- a/Modules/Core/include/mitkAffineTransform3D.h
+++ b/Modules/Core/include/mitkAffineTransform3D.h
@@ -1,24 +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 mitkAffineTransform3D_h
#define mitkAffineTransform3D_h
#include <mitkNumericConstants.h>
#include <itkScalableAffineTransform.h>
+#include <nlohmann/json.hpp>
+#include <MitkCoreExports.h>
namespace mitk
{
using AffineTransform3D = itk::ScalableAffineTransform<ScalarType, 3>;
+
+ /** \brief Write transform (4x4 matrix) as JSON array with 16 elements.
+ */
+ MITKCORE_EXPORT void ToJSON(nlohmann::json& j, AffineTransform3D::ConstPointer transform);
+
+ /** \brief Read transform from JSON array (16 elements, resp. 4x4 matrix).
+ */
+ MITKCORE_EXPORT void FromJSON(const nlohmann::json& j, AffineTransform3D::Pointer transform);
}
#endif
diff --git a/Modules/Core/include/mitkBaseRenderer.h b/Modules/Core/include/mitkBaseRenderer.h
index a6ad38fd83..08290199dc 100644
--- a/Modules/Core/include/mitkBaseRenderer.h
+++ b/Modules/Core/include/mitkBaseRenderer.h
@@ -1,506 +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.
============================================================================*/
#ifndef mitkBaseRenderer_h
#define mitkBaseRenderer_h
#include <mitkDataStorage.h>
#include <mitkPlaneGeometry.h>
#include <mitkPlaneGeometryData.h>
#include <mitkTimeGeometry.h>
#include <mitkCameraController.h>
#include <mitkCameraRotationController.h>
#include <mitkSliceNavigationController.h>
#include <mitkTimeNavigationController.h>
#include <mitkBindDispatcherInteractor.h>
#include <mitkDispatcher.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <map>
#include <set>
namespace mitk
{
class Mapper;
class BaseLocalStorageHandler;
#pragma GCC visibility push(default)
itkEventMacroDeclaration(RendererResetEvent, itk::AnyEvent);
#pragma GCC visibility pop
/*
* \brief Organizes the rendering process
*
* A BaseRenderer contains a reference to a given vtkRenderWindow
* and a corresponding vtkRenderer.
* The BaseRenderer defines which mapper should be used (2D / 3D)
* and which view direction should be rendered.
*
* All existing BaseRenderer are stored in a static variable
* that can be accessed / modified via the static functions.
* VtkPropRenderer is a concrete implementation of a BaseRenderer.
*/
class MITKCORE_EXPORT BaseRenderer : public itk::Object
{
public:
typedef std::map<vtkRenderWindow*, BaseRenderer*> BaseRendererMapType;
static BaseRendererMapType baseRendererMap;
/**
* \brief Defines which kind of mapper (e.g. 2D or 3D) should be used.
*/
enum StandardMapperSlot
{
Standard2D = 1,
Standard3D = 2
};
static BaseRenderer* GetInstance(vtkRenderWindow* renderWindow);
static void AddInstance(vtkRenderWindow* renderWindow, BaseRenderer* baseRenderer);
static void RemoveInstance(vtkRenderWindow* renderWindow);
static BaseRenderer* GetByName(const std::string& name);
static vtkRenderWindow* GetRenderWindowByName(const std::string& name);
/**
* \brief Get a map of specific RenderWindows
*/
static BaseRendererMapType GetSpecificRenderWindows(MapperSlotId mapper);
/**
* \brief Convenience function: Get a map of all 2D RenderWindows
*/
static BaseRendererMapType GetAll2DRenderWindows();
/**
* \brief Convenience function: Get a map of all 3D RenderWindows
*/
static BaseRendererMapType GetAll3DRenderWindows();
mitkClassMacroItkParent(BaseRenderer, itk::Object);
BaseRenderer(const char* name = nullptr, vtkRenderWindow* renderWindow = nullptr);
void RemoveAllLocalStorages();
void RegisterLocalStorageHandler(BaseLocalStorageHandler* lsh);
void UnregisterLocalStorageHandler(BaseLocalStorageHandler* lsh);
virtual void SetDataStorage(DataStorage* storage);
virtual DataStorage::Pointer GetDataStorage() const
{
return m_DataStorage.GetPointer();
}
vtkRenderWindow* GetRenderWindow() const
{
return m_RenderWindow;
}
vtkRenderer* GetVtkRenderer() const
{
return m_VtkRenderer;
}
/**
* \brief Get the dispatcher, which handles events for this base renderer.
*/
Dispatcher::Pointer GetDispatcher() const;
/**
* \brief Set a new size for the render window.
*/
virtual void Resize(int w, int h);
/**
* \brief Initialize the base renderer with a vtk render window.
* Set the new renderer for the camera controller.
*/
virtual void InitRenderer(vtkRenderWindow* renderwindow);
/**
* \brief Set the initial size for the render window.
*/
virtual void InitSize(int w, int h);
virtual void DrawOverlayMouse(Point2D&)
{
MITK_INFO << "BaseRenderer::DrawOverlayMouse() should be in concret implementation OpenGLRenderer." << std::endl;
}
/**
* \brief Set the world time geometry using the given TimeGeometry.
*
* Setting a new world time geometry updates the current world geometry and the
- * curent world plane geometry, using the currently selected slice and timestep.
+ * current world plane geometry, using the currently selected slice and timestep.
*/
virtual void SetWorldTimeGeometry(const TimeGeometry* geometry);
itkGetConstObjectMacro(WorldTimeGeometry, TimeGeometry);
/**
* \brief Set the interaction reference world time geometry using the given TimeGeometry.
*
* Setting a new interaction reference world time geometry also updates the
* alignment status of the reference geometry, which can be retrieved using
* 'GetReferenceGeometryAligned'.
* Using a nullptr as the interaction reference geomertry implies that
* no requirements on the geometry exist, thus in this case any check
* will result in 'ReferenceGeometryAligned' being true.
*
* \param geometry The reference geometry used for render window interaction.
*/
virtual void SetInteractionReferenceGeometry(const TimeGeometry* geometry);
/**
* \brief Get the current interaction reference geometry.
*/
itkGetConstObjectMacro(InteractionReferenceGeometry, TimeGeometry);
/**
* \brief Return if the reference geometry aligns with the base renderer's world geometry.
* If true, the interaction reference geometry aligns with the base renderer's
* current world geometry. False otherwise.
*/
itkGetMacro(ReferenceGeometryAligned, bool);
/**
* \brief Get the current time-extracted 3D-geometry.
*/
itkGetConstObjectMacro(CurrentWorldGeometry, BaseGeometry);
/**
* \brief Get the current slice-extracted 2D-geometry.
*/
itkGetConstObjectMacro(CurrentWorldPlaneGeometry, PlaneGeometry);
virtual bool SetWorldGeometryToDataStorageBounds()
{
return false;
}
/**
* \brief Set the slice that should be used for geometry extraction.
*
* The slice defines the current slice-extracted 2D-geometry (CurrentWorldPlaneGeometry).
* Setting a new slice will update the current world geometry and the
- * curent world plane geometry.
+ * current world plane geometry.
*/
virtual void SetSlice(unsigned int slice);
itkGetConstMacro(Slice, unsigned int);
/**
* \brief Set the timestep that should be used for geometry extraction.
*
* The timestep defines the current time-extracted 3D-geometry (CurrentWorldGeometry).
* Setting a new timestep will update the current world geometry and the
- * curent world plane geometry.
+ * current world plane geometry.
*/
virtual void SetTimeStep(unsigned int timeStep);
itkGetConstMacro(TimeStep, unsigned int);
/**
* \brief Get the timestep of a BaseData object which
* exists at the time of the currently displayed content.
*
* Returns -1 if there is no data at the current time.
*/
TimeStepType GetTimeStep(const BaseData* data) const;
/**
* \brief Get the time in ms of the currently display content (geometry).
*/
ScalarType GetTime() const;
/**
* \brief Set the world time geometry using the geometry of the given event.
*
* The function is triggered by a SliceNavigationController::GeometrySendEvent.
*/
virtual void SetGeometry(const itk::EventObject& geometrySliceEvent);
/**
* \brief Set the current world plane geometry using the existing current world geometry.
*
* The function is triggered by a SliceNavigationController::GeometryUpdateEvent.
*/
virtual void UpdateGeometry(const itk::EventObject& geometrySliceEvent);
/**
* \brief Set the current slice using "SetSlice" and update the current world geometry
* and the current world plane geometry.
*
* The function is triggered by a SliceNavigationController::GeometrySliceEvent.
*/
virtual void SetGeometrySlice(const itk::EventObject& geometrySliceEvent);
/**
* \brief Set the current time using "SetTimeStep" and update the current world geometry
* and the current world plane geometry.
*
* The function is triggered by a TimeNavigationController::TimeEvent.
*/
virtual void SetGeometryTime(const itk::EventObject& geometryTimeEvent);
itkGetObjectMacro(CurrentWorldPlaneGeometryNode, DataNode);
/**
* \brief Modify the update time of the current world plane geometry and force reslicing.
*/
void SendUpdateSlice();
/**
* \brief Get timestamp of the update time of the current world plane geometry.
*/
itkGetMacro(CurrentWorldPlaneGeometryUpdateTime, unsigned long);
/**
* \brief Get timestamp of the update time of the current timestep.
*/
itkGetMacro(TimeStepUpdateTime, unsigned long);
/**
* \brief Pick a world coordinate (x,y,z) given a display coordinate (x,y).
*
* \warning Not implemented; has to be overwritten in subclasses.
*/
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;
}
/**
* \brief Get the currently used mapperID.
*/
itkGetMacro(MapperID, MapperSlotId);
itkGetConstMacro(MapperID, MapperSlotId);
/**
* \brief Set the used mapperID.
*/
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);
/**
* \brief Getter/Setter for defining if the displayed region should be shifted
* or rescaled if the render window is resized.
*/
itkGetMacro(KeepDisplayedRegion, bool);
itkSetMacro(KeepDisplayedRegion, bool);
/**
* \brief Return the name of the base renderer
*/
const char* GetName() const
{
return m_Name.c_str();
}
/**
* \brief Return the size in x-direction of the base renderer.
*/
int GetSizeX() const
{
return this->GetSize()[0];
}
/**
* \brief Return the size in y-direction of the base renderer.
*/
int GetSizeY() const
{
return this->GetSize()[1];
}
/**
* \brief Return the bounds of the bounding box of the
* current world geometry (time-extracted 3D-geometry).
*
* If the geometry is empty, the bounds are set to zero.
*/
const double* GetBounds() const;
void RequestUpdate();
void ForceImmediateUpdate();
/**
* \brief Return the number of mappers which are visible and have
* level-of-detail rendering enabled.
*/
unsigned int GetNumberOfVisibleLODEnabledMappers() const;
/**
* \brief Convert a display point to the 3D world index
* using the geometry of the renderWindow.
*/
void DisplayToWorld(const Point2D& displayPoint, Point3D& worldIndex) const;
/**
* \brief Convert 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;
/**
* \brief Convert a 3D world index to the display point
* using the geometry of the renderWindow.
*/
void WorldToDisplay(const Point3D& worldIndex, Point2D& displayPoint) const;
/**
* \brief Convert a 3D world index to the point on the viewport
* using the geometry of the renderWindow.
*/
void WorldToView(const Point3D& worldIndex, Point2D& viewPoint) const;
/**
* \brief Convert a 2D plane coordinate to the display point
* using the geometry of the renderWindow.
*/
void PlaneToDisplay(const Point2D& planePointInMM, Point2D& displayPoint) const;
/**
* \brief Convert 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);
protected:
~BaseRenderer() override;
virtual void Update() = 0;
vtkRenderWindow* m_RenderWindow;
vtkRenderer* m_VtkRenderer;
MapperSlotId m_MapperID;
DataStorage::Pointer m_DataStorage;
unsigned long m_LastUpdateTime;
CameraController::Pointer m_CameraController;
CameraRotationController::Pointer m_CameraRotationController;
SliceNavigationController::Pointer m_SliceNavigationController;
void UpdateCurrentGeometries();
virtual void SetCurrentWorldPlaneGeometry(const PlaneGeometry* geometry2d);
virtual void SetCurrentWorldGeometry(const BaseGeometry *geometry);
private:
/**
* \brief Pointer to the current TimeGeometry.
*
* This WorldTimeGeometry is used to extract a SlicedGeometry3D,
* using the current timestep (set via SetTimeStep).
* The time-extracted 3D-geometry is used as the "CurrentWorldGeometry".
* A PlaneGeometry can further be extracted using the current slice (set via SetSlice).
* The slice-extracted 2D-geometry is used as the "CurrentWorldPlaneGeometry".
*/
TimeGeometry::ConstPointer m_WorldTimeGeometry;
/**
* \brief Pointer to the interaction reference geometry used for interaction.
*
* This InteractionReferenceGeometry is used to decide if a base renderer /
* render window is able to correctly handle display interaction, e.g. drawing.
* It will be set using the "SetInteractionReferenceGeometry"-function.
*/
TimeGeometry::ConstPointer m_InteractionReferenceGeometry;
/**
* \brief Pointer to the current time-extracted 3D-geometry.
*
* This CurrentWorldGeometry is used to define the bounds for this
* BaseRenderer.
* It will be set using the "SetCurrentWorldGeometry"-function.
*/
BaseGeometry::ConstPointer m_CurrentWorldGeometry;
/**
* \brief Pointer to the current slice-extracted 2D-geometry.
*
* This CurrentWorldPlaneGeometry is used to define the maximal
* area (2D manifold) to be rendered in case we are doing 2D-rendering.
* It will be set using the "SetCurrentWorldPlaneGeometry"-function.
*/
PlaneGeometry::Pointer m_CurrentWorldPlaneGeometry;
unsigned int m_Slice;
unsigned int m_TimeStep;
itk::TimeStamp m_CurrentWorldPlaneGeometryUpdateTime;
itk::TimeStamp m_TimeStepUpdateTime;
BindDispatcherInteractor* m_BindDispatcherInteractor;
bool m_KeepDisplayedRegion;
bool m_ReferenceGeometryAligned;
protected:
void PrintSelf(std::ostream& os, itk::Indent indent) const override;
PlaneGeometryData::Pointer m_CurrentWorldPlaneGeometryData;
DataNode::Pointer m_CurrentWorldPlaneGeometryNode;
unsigned long m_CurrentWorldPlaneGeometryTransformTime;
std::string m_Name;
double m_Bounds[6];
bool m_EmptyWorldGeometry;
typedef std::set<Mapper*> LODEnabledMappersType;
unsigned int m_NumberOfVisibleLODEnabledMappers;
std::list<BaseLocalStorageHandler*> m_RegisteredLocalStorageHandlers;
bool m_ConstrainZoomingAndPanning;
};
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkException.h b/Modules/Core/include/mitkException.h
index e4ac635604..0f300fc6b7 100644
--- a/Modules/Core/include/mitkException.h
+++ b/Modules/Core/include/mitkException.h
@@ -1,115 +1,115 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkException_h
#define mitkException_h
#include <MitkCoreExports.h>
#include <itkMacro.h>
#include <vector>
namespace mitk
{
/**Documentation
* \brief An object of this class represents an exception of MITK.
* Please don't instantiate exceptions manually, but use the
* exception macros (file mitkExceptionMacro.h) instead.
* Simple use in your code is:
*
* mitkThrow() << "optional exception message";
*
* You can also define specialized exceptions which must inherit
* from this class. Please always use the mitkExceptionClassMacro
* when implementing specialized exceptions. A simple implementation
* can look like:
*
* class MyException : public mitk::Exception
* {
* public:
* mitkExceptionClassMacro(MyException,mitk::Exception);
* };
*
* You can then throw your specialized exceptions by using the macro
*
* mitkThrowException(MyException) << "optional exception message";
*/
class MITKCORE_EXPORT Exception : public itk::ExceptionObject
{
public:
Exception(const char *file, unsigned int lineNumber = 0, const char *desc = "None", const char *loc = "Unknown")
: itk::ExceptionObject(file, lineNumber, desc, loc)
{
}
~Exception() throw() override {}
- itkTypeMacro(ClassName, SuperClassName);
+ itkTypeMacro(Exception, itk::ExceptionObject);
/** \brief Adds rethrow data to this exception. */
void AddRethrowData(const char *file, unsigned int lineNumber, const char *message);
/** \return Returns how often the exception was rethrown. */
int GetNumberOfRethrows();
/** Returns the rethrow data of the specified rethrow number. Returns empty data, if the rethrowNumber
* doesn't
* exist.
* @param rethrowNumber The internal number of the rethrow.
* @param file (returnvalue) This variable will be filled with the file of the specified rethrow.
* @param line (returnvalue) This variable will be filled with the line of the specified rethrow.
* @param message (returnvalue) This variable will be filled with the message of the specified rethrow.
*/
void GetRethrowData(int rethrowNumber, std::string &file, int &line, std::string &message);
/** \brief Definition of the bit shift operator for this class.*/
template <class T>
inline Exception &operator<<(const T &data)
{
std::stringstream ss;
ss << this->GetDescription() << data;
this->SetDescription(ss.str());
return *this;
}
/** \brief Definition of the bit shift operator for this class (for non const data).*/
template <class T>
inline Exception &operator<<(T &data)
{
std::stringstream ss;
ss << this->GetDescription() << data;
this->SetDescription(ss.str());
return *this;
}
/** \brief Definition of the bit shift operator for this class (for functions).*/
inline Exception &operator<<(std::ostream &(*func)(std::ostream &))
{
std::stringstream ss;
ss << this->GetDescription() << func;
this->SetDescription(ss.str());
return *this;
}
protected:
struct ReThrowData
{
std::string RethrowClassname;
unsigned int RethrowLine;
std::string RethrowMessage;
};
std::vector<ReThrowData> m_RethrowData;
};
MITKCORE_EXPORT std::ostream &operator<<(std::ostream &os, const mitk::Exception &e);
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkAffineTransform3D.h b/Modules/Core/include/mitkFileSystem.h
similarity index 55%
copy from Modules/Core/include/mitkAffineTransform3D.h
copy to Modules/Core/include/mitkFileSystem.h
index e6f535cd75..e7c4df187f 100644
--- a/Modules/Core/include/mitkAffineTransform3D.h
+++ b/Modules/Core/include/mitkFileSystem.h
@@ -1,24 +1,25 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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
-
-#include <mitkNumericConstants.h>
-#include <itkScalableAffineTransform.h>
-
-namespace mitk
-{
- using AffineTransform3D = itk::ScalableAffineTransform<ScalarType, 3>;
-}
+#ifndef mitkFileSystem_h
+#define mitkFileSystem_h
+
+#if __has_include(<filesystem>)
+ #define MITK_HAS_FILESYSTEM
+ #include <filesystem>
+ namespace fs = std::filesystem;
+#elif __has_include(<experimental/filesystem>)
+ #include <experimental/filesystem>
+ namespace fs = std::experimental::filesystem;
+#endif
#endif
diff --git a/Modules/Core/include/mitkIOMetaInformationPropertyConstants.h b/Modules/Core/include/mitkIOMetaInformationPropertyConstants.h
index 729fac961c..854d6cd673 100644
--- a/Modules/Core/include/mitkIOMetaInformationPropertyConstants.h
+++ b/Modules/Core/include/mitkIOMetaInformationPropertyConstants.h
@@ -1,44 +1,44 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkIOMetaInformationPropertyConstants_h
#define mitkIOMetaInformationPropertyConstants_h
#include <MitkCoreExports.h>
#include "mitkPropertyKeyPath.h"
namespace mitk
{
/**
* @ingroup IO
* @brief The IOMetaInformationPropertyConstants struct
*/
struct MITKCORE_EXPORT IOMetaInformationPropertyConstants
{
//Path to the property containing the name of the reader used
static PropertyKeyPath READER_DESCRIPTION();
//Path to the property containing the version of mitk used to read the data
static PropertyKeyPath READER_VERSION();
//Path to the property containing the mine name detected used to read the data
static PropertyKeyPath READER_MIME_NAME();
//Path to the property containing the mime category detected to read the data
static PropertyKeyPath READER_MIME_CATEGORY();
//Path to the property containing the input location if loaded by file used to read the data
static PropertyKeyPath READER_INPUTLOCATION();
- //Path to the properties containing the reader optins used to read the data
+ //Path to the properties containing the reader options used to read the data
static PropertyKeyPath READER_OPTION_ROOT();
static PropertyKeyPath READER_OPTIONS_ANY();
};
}
#endif
diff --git a/Modules/Core/include/mitkIOUtil.h b/Modules/Core/include/mitkIOUtil.h
index 0c07208d68..cff4b3d829 100644
--- a/Modules/Core/include/mitkIOUtil.h
+++ b/Modules/Core/include/mitkIOUtil.h
@@ -1,444 +1,444 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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>
#if !defined(MITK_WINDOWS_NO_UNDEF) && defined(GetTempPath)
#undef GetTempPath
#endif
namespace us
{
class ModuleResource;
}
namespace mitk
{
class PropertyList;
/**
* \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 contains 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;
const PropertyList* m_Properties;
};
/**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 contains 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 contains 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 consecutive '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 consecutive '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.
+ * represents 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());
}
static BaseData::Pointer Load(const std::string& path, const PropertyList* properties);
/**
* @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);
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
diff --git a/Modules/Core/include/mitkIPreferencesService.h b/Modules/Core/include/mitkIPreferencesService.h
index 3597af1aff..e4434df42c 100644
--- a/Modules/Core/include/mitkIPreferencesService.h
+++ b/Modules/Core/include/mitkIPreferencesService.h
@@ -1,84 +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.
============================================================================*/
#ifndef mitkIPreferencesService_h
#define mitkIPreferencesService_h
#include <mitkServiceInterface.h>
#include <MitkCoreExports.h>
-#include <filesystem>
+#include <mitkFileSystem.h>
namespace mitk
{
class IPreferences;
/**
* \brief A service for persistent application preferences.
*
* \sa CoreServices::GetPreferencesService()
* \sa IPreferences
*
* \ingroup MicroServices_Interfaces
*/
class MITKCORE_EXPORT IPreferencesService
{
public:
/**
* \brief If initialized, ask the preferences backend to flush preferences, i.e. write them to disk.
*/
virtual ~IPreferencesService();
/**
* \brief Initialize the preferences backend.
*
* Load preferences from the specified file. If the file does not yet exist,
* create an empty preferences storage.
*
* This method must be called once at the start of an application before
* accessing any preferences via GetSystemPreferences().
*
* \throw Exception The method is called more than once.
*
* \sa IPreferencesStorage
*/
- virtual void InitializeStorage(const std::filesystem::path& filename) = 0;
+ virtual void InitializeStorage(const fs::path& filename) = 0;
/**
* \brief For internal use only.
*
* This method is only used for testing purposes.
* Do not use in production code.
*/
virtual void UninitializeStorage(bool removeFile = true) = 0;
/**
* \brief Access preferences.
*
* The system preferences should be considered as resource and as such their
* lifetime is coupled to this service. It is recommended to access the
* preferences always through this method as needed instead of keeping
* a pointer permanently.
*
* \note The term "system preferences" is kept for historical reasons to stay as
* API-compatible as possible to the previous preferences service. A more
* precise term would be "application preferences".
*
* \return The root node of the preferences tree.
*/
virtual IPreferences* GetSystemPreferences() = 0;
};
}
MITK_DECLARE_SERVICE_INTERFACE(mitk::IPreferencesService, "org.mitk.IPreferencesService")
#endif
diff --git a/Modules/Core/include/mitkIPreferencesStorage.h b/Modules/Core/include/mitkIPreferencesStorage.h
index 82c1342711..7ca8c3db64 100644
--- a/Modules/Core/include/mitkIPreferencesStorage.h
+++ b/Modules/Core/include/mitkIPreferencesStorage.h
@@ -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.
============================================================================*/
#ifndef mitkIPreferencesStorage_h
#define mitkIPreferencesStorage_h
#include <mitkIPreferences.h>
#include <MitkCoreExports.h>
-#include <filesystem>
+#include <mitkFileSystem.h>
#include <memory>
namespace mitk
{
/**
* \brief The backend for persistent preferences.
*
* This interface and its implementation is internally used by the IPreferencesService
* to hold the preferences root node and to store and restore the preferences from disk.
*/
class MITKCORE_EXPORT IPreferencesStorage
{
public:
/**
* \brief Constructor. Load preferences from the specified file.
*
* If the file does not yet exist, create an empty preferences storage.
*/
- explicit IPreferencesStorage(const std::filesystem::path& filename);
+ explicit IPreferencesStorage(const fs::path& filename);
/**
* \brief Destructor. Write preferences to disk for the last time.
*/
virtual ~IPreferencesStorage();
/**
* \brief Get the preferences root node.
*
* The preferences root node is owned by the preferences storage.
*/
virtual IPreferences* GetRoot();
/**
* \sa GetRoot()
*/
virtual const IPreferences* GetRoot() const;
/**
* \brief Get the filename of the preferences storage.
*/
- virtual std::filesystem::path GetFilename() const;
+ virtual fs::path GetFilename() const;
/**
* \brief Write the in-memory preferences to disk.
*
* Usually called by clients indirectly through IPreferences::Flush().
*/
virtual void Flush() = 0;
protected:
- std::filesystem::path m_Filename;
+ fs::path m_Filename;
std::unique_ptr<IPreferences> m_Root;
};
}
#endif
diff --git a/Modules/Core/include/mitkImageAccessorBase.h b/Modules/Core/include/mitkImageAccessorBase.h
index 0b79494dee..851189f2c7 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 <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. */
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 */
+ /** \brief System dependent thread method, to prevent recursive mutex access */
ThreadIDType CurrentThreadHandle();
- /** \brief System dependend thread method, to prevent recursive mutex access */
+ /** \brief System dependent 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/mitkItkImageIO.h b/Modules/Core/include/mitkItkImageIO.h
index f64356551f..d9bfe6c73a 100644
--- a/Modules/Core/include/mitkItkImageIO.h
+++ b/Modules/Core/include/mitkItkImageIO.h
@@ -1,102 +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 mitkItkImageIO_h
#define mitkItkImageIO_h
#include "mitkAbstractFileIO.h"
#include <mitkImage.h>
#include <itkImageIOBase.h>
namespace mitk
{
/**
* This class wraps ITK image IO objects as mitk::IFileReader and
* mitk::IFileWriter objects.
*
* Instantiating this class with a given itk::ImageIOBase instance
* will register corresponding MITK reader/writer services for that
* ITK ImageIO object.
* For all ITK ImageIOs that support the serialization of MetaData
* (e.g. nrrd or mhd) the ItkImageIO ensures the serialization
* of Identification UID.
*/
class MITKCORE_EXPORT ItkImageIO : public AbstractFileIO
{
public:
ItkImageIO(itk::ImageIOBase::Pointer imageIO);
ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank);
// -------------- AbstractFileReader -------------
using AbstractFileReader::Read;
ConfidenceLevel GetReaderConfidenceLevel() const override;
// -------------- AbstractFileWriter -------------
void Write() override;
ConfidenceLevel GetWriterConfidenceLevel() const override;
/**Helper function that can be used to convert a MetaDataDictionary into a PropertyList for a certain mimeType.
The function uses the Property serialization service for that.
@param mimeTypeName Mime type that should be assumed for the meta data deserialization.
@param defaultMetaDataKeys Vector of keys that should be assumed as defaults. For defaults no PropertyInfo will be registered
at the PropertyPersistence service, as they are assumed to be handled anyways. For all other keys an info will be registered
- to ensure that they will be serialized again, even if unkown.
+ to ensure that they will be serialized again, even if unknown.
@param dictionary Reference to the meta data dictionary that contains the information that should be extracted.*/
static PropertyList::Pointer ExtractMetaDataAsPropertyList(const itk::MetaDataDictionary& dictionary, const std::string& mimeTypeName, const std::vector<std::string>& defaultMetaDataKeys);
/** Helper function that van be used to extract a raw mitk image for the passed path using the also passed ImageIOBase instance.
Raw means, that only the pixel data and geometry information is loaded. But e.g. no properties etc...*/
static Image::Pointer LoadRawMitkImageFromImageIO(itk::ImageIOBase* imageIO, const std::string& path);
/** Helper function that van be used to extract a raw mitk image for the passed path using the also passed ImageIOBase instance.
Raw means, that only the pixel data and geometry information is loaded. But e.g. no properties etc...*/
static void PreparImageIOToWriteImage(itk::ImageIOBase* imageIO, const Image* image);
static void SavePropertyListAsMetaData(itk::MetaDataDictionary& dictionary, const PropertyList* properties, const std::string& mimeTypeName);
protected:
virtual std::vector<std::string> FixUpImageIOExtensions(const std::string &imageIOName);
virtual void FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType);
// Fills the m_DefaultMetaDataKeys vector with default values
virtual void InitializeDefaultMetaDataKeys();
// -------------- AbstractFileReader -------------
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
private:
ItkImageIO(const ItkImageIO &other);
ItkImageIO *IOClone() const override;
itk::ImageIOBase::Pointer m_ImageIO;
std::vector<std::string> m_DefaultMetaDataKeys;
};
/**Helper function that converts the content of a meta data into a time point vector.
* If MetaData is not valid or cannot be converted an empty vector is returned.*/
MITKCORE_EXPORT std::vector<TimePointType> ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data);
/**Helper function that converts the time points of a passed time geometry to a time point list
and stores it in a itk::MetaDataObject. Use ConvertMetaDataObjectToTimePointList() to convert it back
to a time point list.*/
MITKCORE_EXPORT itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry);
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkMimeType.h b/Modules/Core/include/mitkMimeType.h
index 5135f92c35..8889240191 100644
--- a/Modules/Core/include/mitkMimeType.h
+++ b/Modules/Core/include/mitkMimeType.h
@@ -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.
============================================================================*/
#ifndef mitkMimeType_h
#define mitkMimeType_h
#include <MitkCoreExports.h>
#include <usSharedData.h>
#include <vector>
namespace mitk
{
class CustomMimeType;
/**
* @ingroup IO
*
- * @brief The MimeType class represens a registered mime-type. It is an immutable wrapper for mitk::CustomMimeType
+ * @brief The MimeType class represents a registered mime-type. It is an immutable wrapper for mitk::CustomMimeType
* that makes memory handling easier by providing a stack-object for the user.
*
* If you want to register a new MimeType, use the CustomMimeType class instead. Wrapping will be performed for you
* automatically.
* In all other cases you should use mitk::MimeType when working with mime-types.
*/
class MITKCORE_EXPORT MimeType
{
public:
MimeType();
MimeType(const MimeType &other);
MimeType(const CustomMimeType &x, int rank, long id);
~MimeType();
MimeType &operator=(const MimeType &other);
bool operator==(const MimeType &other) const;
bool operator<(const MimeType &other) const;
/** @see mitk::CustomMimeType::GetName()*/
std::string GetName() const;
/** @see mitk::CustomMimeType::GetCategory()*/
std::string GetCategory() const;
/** @see mitk::CustomMimeType::GetExtensions()*/
std::vector<std::string> GetExtensions() const;
/** @see mitk::CustomMimeType::GetComment()*/
std::string GetComment() const;
/** @see mitk::CustomMimeType::GetFileNameWithoutExtension()*/
std::string GetFilenameWithoutExtension(const std::string &path) const;
/** @see mitk::CustomMimeType::AppliesTo()*/
bool AppliesTo(const std::string &path) const;
/** @see mitk::CustomMimeType::MatchesExtension()*/
bool MatchesExtension(const std::string &path) const;
/** @see mitk::CustomMimeType::IsValid()*/
bool IsValid() const;
/** @see mitk::CustomMimeType::Swap()*/
void Swap(MimeType &m);
private:
struct Impl;
// Use C++11 shared_ptr instead
us::SharedDataPointer<const Impl> m_Data;
};
MITKCORE_EXPORT void swap(MimeType &m1, MimeType &m2);
MITKCORE_EXPORT std::ostream &operator<<(std::ostream &os, const MimeType &mimeType);
}
#endif
diff --git a/Modules/Core/include/mitkOperationEvent.h b/Modules/Core/include/mitkOperationEvent.h
index 0dc70737fe..d7b4d6cd17 100644
--- a/Modules/Core/include/mitkOperationEvent.h
+++ b/Modules/Core/include/mitkOperationEvent.h
@@ -1,201 +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.
============================================================================*/
#ifndef mitkOperationEvent_h
#define mitkOperationEvent_h
#include "mitkOperation.h"
#include "mitkOperationActor.h"
#include "mitkUndoModel.h"
#include <MitkCoreExports.h>
#include <list>
#include <string>
namespace mitk
{
//##Documentation
//## @brief Represents an entry of the undo or redo stack.
//##
//## This basic entry includes a textual description of the item and a pair of IDs. Static
//## member functions handle creation and incrementing of these IDs.
//##
//## The GroupEventID is intended for logical grouping of several related Operations.
//## Currently this is used only by PointSetDataInteractor. How this is done and when to use
//## GroupEventIDs is still undocumented.
//## @ingroup Undo
class MITKCORE_EXPORT UndoStackItem
{
public:
UndoStackItem(std::string description = "");
virtual ~UndoStackItem();
//##Documentation
//## @brief For combining operations in groups
//##
//## This ID is used in the undo mechanism.
//## For separation of the separate operations
//## If the GroupEventId of two OperationEvents is equal,
//## then they share one group and will be undone in case of Undo(fine==false)
static int GetCurrGroupEventId();
//##Documentation
//## @brief For combining operations in Objects
//##
//## This ID is used in the Undo-Mechanism.
//## For separation of the separate operations
//## If the ObjectEventId of two OperationEvents is equal,
//## then they share one Object and will be undone in all cases of Undo(true and false).
- //## they shal not be separated, because they were produced to realize one object-change.
+ //## they shall not be separated, because they were produced to realize one object-change.
//## for example: OE_statechange and OE_addlastpoint
static int GetCurrObjectEventId();
//##Documentation
//## @brief Returns the GroupEventId for this object
int GetGroupEventId();
//##Documentation
//## @brief Returns the ObjectEventId for this object
int GetObjectEventId();
//##Documentation
//## @brief Returns the textual description of this object
std::string GetDescription();
virtual void ReverseOperations();
virtual void ReverseAndExecute();
//##Documentation
//## @brief Increases the current ObjectEventId
//## For example if a button click generates operations the ObjectEventId has to be incremented to be able to undo
//the
// operations.
//## Difference between ObjectEventId and GroupEventId: The ObjectEventId capsulates all operations caused by one
// event.
//## A GroupEventId capsulates several ObjectEventIds so that several operations caused by several events can be
// undone with one Undo call.
static void IncCurrObjectEventId();
//##Documentation
//## @brief Increases the current GroupEventId
//## For example if a button click generates operations the GroupEventId has to be incremented to be able to undo
//the
// operations.
//## Difference between ObjectEventId and GroupEventId: The ObjectEventId capsulates all operations caused by one
// event.
//## A GroupEventId capsulates several ObjectEventIds so that several operations caused by several events can be
// undone with one Undo call.
static void IncCurrGroupEventId();
protected:
//##Documentation
//## @brief true, if operation and undooperation have been swapped/changed
bool m_Reversed;
private:
static int m_CurrObjectEventId;
static int m_CurrGroupEventId;
int m_ObjectEventId;
int m_GroupEventId;
std::string m_Description;
UndoStackItem(UndoStackItem &); // hide copy constructor
void operator=(const UndoStackItem &); // hide operator=
};
//##Documentation
//## @brief Represents a pair of operations: undo and the according redo.
//##
//## Additionally to the base class UndoStackItem, which only provides a description of an
//## item, OperationEvent does the actual accounting of the undo/redo stack. This class
//## holds two Operation objects (operation and its inverse operation) and the corresponding
//## OperationActor. The operations may be swapped by the
//## undo models, when an OperationEvent is moved from their undo to their redo
//## stack or vice versa.
//##
//## Note, that memory management of operation and undooperation is done by this class.
//## Memory of both objects is freed in the destructor. For this, the method IsValid() is needed which holds
//## information of the state of m_Destination. In case the object referenced by m_Destination is already deleted,
//## isValid() returns false.
//## In more detail if the destination happens to be an itk::Object (often the case), OperationEvent is informed as
//soon
//## as the object is deleted - from this moment on the OperationEvent gets invalid. You should
//## check this flag before you call anything on destination
//##
//## @ingroup Undo
class MITKCORE_EXPORT OperationEvent : public UndoStackItem
{
public:
//## @brief default constructor
OperationEvent(OperationActor *destination,
Operation *operation,
Operation *undoOperation,
std::string description = "");
//## @brief default destructor
//##
//## removes observers if destination is valid
//## and frees memory referenced by m_Operation and m_UndoOperation
~OperationEvent() override;
//## @brief Returns the operation
Operation *GetOperation();
//## @brief Returns the destination of the operations
OperationActor *GetDestination();
friend class UndoModel;
//## @brief Swaps the two operations and sets a flag,
//## that it has been swapped and doOp is undoOp and undoOp is doOp
void ReverseOperations() override;
//##reverses and executes both operations (used, when moved from undo to redo stack)
void ReverseAndExecute() override;
//## @brief returns true if the destination still is present
//## and false if it already has been deleted
virtual bool IsValid();
protected:
void OnObjectDeleted();
private:
// Has to be observed for itk::DeleteEvents.
// When destination is deleted, this stack item is invalid!
OperationActor *m_Destination;
//## reference to the operation
Operation *m_Operation;
//## reference to the undo operation
Operation *m_UndoOperation;
//## hide copy constructor
OperationEvent(OperationEvent &);
//## hide operator=
void operator=(const OperationEvent &);
// observertag used to listen to m_Destination
unsigned long m_DeleteTag;
//## stores if destination is valid or already has been freed
bool m_Invalid;
};
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkPlaneGeometry.h b/Modules/Core/include/mitkPlaneGeometry.h
index 81a0c3c0e1..680c65ea1b 100644
--- a/Modules/Core/include/mitkPlaneGeometry.h
+++ b/Modules/Core/include/mitkPlaneGeometry.h
@@ -1,606 +1,606 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkPlaneGeometry_h
#define mitkPlaneGeometry_h
#include <MitkCoreExports.h>
#include <mitkAnatomicalPlanes.h>
#include <mitkBaseGeometry.h>
#include <mitkRestorePlanePositionOperation.h>
#include <vnl/vnl_cross.h>
namespace mitk
{
template <class TCoordRep, unsigned int NPointDimension>
class Line;
typedef Line<ScalarType, 3> Line3D;
/**
* \brief Describes the geometry of a plane object
*
* Describes a two-dimensional manifold, i.e., to put it simply,
* an object that can be described using a 2D coordinate-system.
*
* PlaneGeometry can map points between 3D world coordinates
* (in mm) and the described 2D coordinate-system (in mm) by first projecting
* the 3D point onto the 2D manifold and then calculating the 2D-coordinates
* (in mm). These 2D-mm-coordinates can be further converted into
* 2D-unit-coordinates (e.g., pixels), giving a parameter representation of
* the object with parameter values inside a rectangle
* (e.g., [0,0]..[width, height]), which is the bounding box (bounding range
* in z-direction always [0]..[1]).
*
* A PlaneGeometry describes the 2D representation within a 3D object (derived from BaseGeometry). For example,
* a single CT-image (slice) is 2D in the sense that you can access the
* pixels using 2D-coordinates, but is also 3D, as the pixels are really
* voxels, thus have an extension (thickness) in the 3rd dimension.
*
*
* Optionally, a reference BaseGeometry can be specified, which usually would
* be the geometry associated with the underlying dataset. This is currently
* used for calculating the intersection of inclined / rotated planes
* (represented as PlaneGeometry) with the bounding box of the associated
* BaseGeometry.
*
* \warning The PlaneGeometry are not necessarily up-to-date and not even
* initialized. As described in the previous paragraph, one of the
* Generate-/Copy-/UpdateOutputInformation methods have to initialize it.
* mitk::BaseData::GetPlaneGeometry() makes sure, that the PlaneGeometry is
* up-to-date before returning it (by setting the update extent appropriately
* and calling UpdateOutputInformation).
*
* Rule: everything is in mm (or ms for temporal information) if not
* stated otherwise.
* \ingroup Geometry
*/
class PlaneGeometry;
/** \deprecatedSince{2014_10} This class is deprecated. Please use PlaneGeometry instead. */
DEPRECATED(typedef PlaneGeometry Geometry2D);
/**
* \brief Describes a two-dimensional, rectangular plane
*
* \ingroup Geometry
*/
class MITKCORE_EXPORT PlaneGeometry : public BaseGeometry
{
public:
mitkClassMacro(PlaneGeometry, BaseGeometry);
/** Method for creation through the object factory. */
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
virtual void IndexToWorld(const Point2D &pt_units, Point2D &pt_mm) const;
virtual void WorldToIndex(const Point2D &pt_mm, Point2D &pt_units) const;
//##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
virtual void IndexToWorld(const mitk::Point2D &atPt2d_untis,
const mitk::Vector2D &vec_units,
mitk::Vector2D &vec_mm) const;
//##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
virtual void IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const;
//##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
virtual void WorldToIndex(const mitk::Point2D &atPt2d_mm,
const mitk::Vector2D &vec_mm,
mitk::Vector2D &vec_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
virtual void WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const;
/**
* \brief Initialize a plane with orientation \a AnatomicalPlane
* (default: axial) with respect to \a BaseGeometry (default: identity).
* Spacing also taken from \a BaseGeometry.
*
* \warning A former version of this method created a geometry with unit
* spacing. For unit spacing use
*
* \code
* // for in-plane unit spacing:
* thisgeometry->SetSizeInUnits(thisgeometry->GetExtentInMM(0),
* thisgeometry->GetExtentInMM(1));
* // additionally, for unit spacing in normal direction (former version
* // did not do this):
* thisgeometry->SetExtentInMM(2, 1.0);
* \endcode
*/
virtual void InitializeStandardPlane(const BaseGeometry *geometry3D,
AnatomicalPlane planeorientation = AnatomicalPlane::Axial,
ScalarType zPosition = 0,
bool frontside = true,
bool rotated = false,
bool top = true);
/**
* \brief Initialize a plane with orientation \a AnatomicalPlane
* (default: axial) with respect to \a BaseGeometry (default: identity).
* Spacing also taken from \a BaseGeometry.
*
* \param geometry3D
* \param top if \a true, create plane at top, otherwise at bottom
* (for AnatomicalPlane Axial, for other plane locations respectively)
* \param planeorientation
* \param frontside
* \param rotated
*/
virtual void InitializeStandardPlane(const BaseGeometry *geometry3D,
bool top,
AnatomicalPlane planeorientation = AnatomicalPlane::Axial,
bool frontside = true,
bool rotated = false);
/**
* \brief Initialize a plane with orientation \a AnatomicalPlane
* (default: axial) with respect to \a transform (default: identity)
* given width and height in units.
*
* \a Rotated means rotated by 180 degrees (1/2 rotation) within the plane.
* Rotation by 90 degrees (1/4 rotation) is not implemented as of now.
*
* \a Frontside/Backside:
* Viewed from below = frontside in the axial case;
* (radiologist's view versus neuro-surgeon's view, see:
* http://www.itk.org/Wiki/images/e/ed/DICOM-OrientationDiagram-Radiologist-vs-NeuroSurgeon.png )
* Viewed from front = frontside in the coronal case;
* Viewed from left = frontside in the sagittal case.
*
* \a Cave/Caution: Currently only RPI, LAI, LPS and RAS in the three standard planes are covered,
* i.e. 12 cases of 144: 3 standard planes * 48 coordinate orientations = 144 cases.
*/
virtual void InitializeStandardPlane(ScalarType width,
ScalarType height,
const AffineTransform3D *transform = nullptr,
AnatomicalPlane planeorientation = AnatomicalPlane::Axial,
ScalarType zPosition = 0,
bool frontside = true,
bool rotated = false,
bool top = true);
/**
* \brief Initialize plane with orientation \a AnatomicalPlane
* (default: axial) given width, height and spacing.
*
*/
virtual void InitializeStandardPlane(ScalarType width,
ScalarType height,
const Vector3D &spacing,
AnatomicalPlane planeorientation = AnatomicalPlane::Axial,
ScalarType zPosition = 0,
bool frontside = true,
bool rotated = false,
bool top = true);
/**
* \brief Initialize plane by width and height in pixels, right-/down-vector
* (itk) to describe orientation in world-space (vectors will be normalized)
* and spacing (default: 1.0 mm in all directions).
*
* The vectors are normalized and multiplied by the respective spacing before
* they are set in the matrix.
*
* This overloaded version of InitializeStandardPlane() creates only righthanded
* coordinate orientations, unless spacing contains 1 or 3 negative entries.
*
*/
virtual void InitializeStandardPlane(ScalarType width,
ScalarType height,
const Vector3D &rightVector,
const Vector3D &downVector,
const Vector3D *spacing = nullptr);
/**
* \brief Initialize plane by width and height in pixels,
* right-/down-vector (vnl) to describe orientation in world-space (vectors
* will be normalized) and spacing (default: 1.0 mm in all directions).
*
* The vectors are normalized and multiplied by the respective spacing
* before they are set in the matrix.
*
* This overloaded version of InitializeStandardPlane() creates only righthanded
* coordinate orientations, unless spacing contains 1 or 3 negative entries.
*
*/
virtual void InitializeStandardPlane(ScalarType width,
ScalarType height,
const VnlVector &rightVector,
const VnlVector &downVector,
const Vector3D *spacing = nullptr);
/**
* \brief Initialize plane by right-/down-vector (itk) and spacing
* (default: 1.0 mm in all directions).
*
* The length of the right-/-down-vector is used as width/height in units,
* respectively. Then, the vectors are normalized and multiplied by the
* respective spacing before they are set in the matrix.
*/
virtual void InitializeStandardPlane(const Vector3D &rightVector,
const Vector3D &downVector,
const Vector3D *spacing = nullptr);
/**
* \brief Initialize plane by right-/down-vector (vnl) and spacing
* (default: 1.0 mm in all directions).
*
* The length of the right-/-down-vector is used as width/height in units,
* respectively. Then, the vectors are normalized and multiplied by the
* respective spacing before they are set in the matrix.
*/
virtual void InitializeStandardPlane(const VnlVector &rightVector,
const VnlVector &downVector,
const Vector3D *spacing = nullptr);
/**
* \brief Initialize plane by origin and normal (size is 1.0 mm in
* all directions, direction of right-/down-vector valid but
* undefined).
* \warning This function can only produce righthanded coordinate orientation, not lefthanded.
*/
virtual void InitializePlane(const Point3D &origin, const Vector3D &normal);
/**
* \brief Initialize plane by right-/down-vector.
*
* \warning The vectors are set into the matrix as they are,
* \em without normalization!
* This function creates a righthanded IndexToWorldTransform,
* only a negative thickness could still make it lefthanded.
*/
void SetMatrixByVectors(const VnlVector &rightVector, const VnlVector &downVector, ScalarType thickness = 1.0);
/**
* \brief Check if matrix is a rotation matrix:
* - determinant is 1?
* - R*R^T is ID?
* Output warning otherwise.
*/
static bool CheckRotationMatrix(AffineTransform3D *transform, double epsilon=1e-6);
/**
* \brief Normal of the plane
*
*/
Vector3D GetNormal() const;
/**
* \brief Normal of the plane as VnlVector
*
*/
VnlVector GetNormalVnl() const;
virtual ScalarType SignedDistance(const Point3D &pt3d_mm) const;
/**
* \brief Calculates, whether a point is below or above the plane. There are two different
*calculation methods, with or without consideration of the bounding box.
*/
virtual bool IsAbove(const Point3D &pt3d_mm, bool considerBoundingBox = false) const;
/**
* \brief Distance of the point from the plane
* (bounding-box \em not considered)
*
*/
ScalarType DistanceFromPlane(const Point3D &pt3d_mm) const;
/**
* \brief Signed distance of the point from the plane
* (bounding-box \em not considered)
*
* > 0 : point is in the direction of the direction vector.
*/
inline ScalarType SignedDistanceFromPlane(const Point3D &pt3d_mm) const
{
ScalarType len = GetNormalVnl().two_norm();
if (len == 0)
return 0;
return (pt3d_mm - GetOrigin()) * GetNormal() / len;
}
/**
* \brief Distance of the plane from another plane
* (bounding-box \em not considered)
*
* Result is 0 if planes are not parallel.
*/
ScalarType DistanceFromPlane(const PlaneGeometry *plane) const { return fabs(SignedDistanceFromPlane(plane)); }
/**
* \brief Signed distance of the plane from another plane
* (bounding-box \em not considered)
*
* Result is 0 if planes are not parallel.
*/
inline ScalarType SignedDistanceFromPlane(const PlaneGeometry *plane) const
{
if (IsParallel(plane))
{
return SignedDistance(plane->GetOrigin());
}
return 0;
}
/**
* \brief Calculate the intersecting line of two planes
*
* \return \a true planes are intersecting
* \return \a false planes do not intersect
*/
bool IntersectionLine(const PlaneGeometry *plane, Line3D &crossline) const;
/**
* \brief Calculate two points where another plane intersects the border of this plane
*
- * \return number of intersection points (0..2). First interection point (if existing)
+ * \return number of intersection points (0..2). First intersection point (if existing)
* is returned in \a lineFrom, second in \a lineTo.
*/
unsigned int IntersectWithPlane2D(const PlaneGeometry *plane, Point2D &lineFrom, Point2D &lineTo) const;
/**
* \brief Calculate the angle between two planes
*
* \return angle in radiants
*/
double Angle(const PlaneGeometry *plane) const;
/**
* \brief Calculate the angle between the plane and a line
*
* \return angle in radiants
*/
double Angle(const Line3D &line) const;
/**
* \brief Calculate intersection point between the plane and a line
*
* \param line
* \param intersectionPoint intersection point
* \return \a true if \em unique intersection exists, i.e., if line
* is \em not on or parallel to the plane
*/
bool IntersectionPoint(const Line3D &line, Point3D &intersectionPoint) const;
/**
* \brief Calculate line parameter of intersection point between the
* plane and a line
*
* \param line
* \param t parameter of line: intersection point is
* line.GetPoint()+t*line.GetDirection()
* \return \a true if \em unique intersection exists, i.e., if line
* is \em not on or parallel to the plane
*/
bool IntersectionPointParam(const Line3D &line, double &t) const;
/**
* \brief Returns whether the plane is parallel to another plane
*
* @return true iff the normal vectors both point to the same or exactly oposit direction
*/
bool IsParallel(const PlaneGeometry *plane) const;
/**
* \brief Returns whether the point is on the plane
* (bounding-box \em not considered)
*/
bool IsOnPlane(const Point3D &point) const;
/**
* \brief Returns whether the line is on the plane
* (bounding-box \em not considered)
*/
bool IsOnPlane(const Line3D &line) const;
/**
* \brief Returns whether the plane is on the plane
* (bounding-box \em not considered)
*
* @return true if the normal vector of the planes point to the same or the exactly oposit direction and
* the distance of the planes is < eps
*
*/
bool IsOnPlane(const PlaneGeometry *plane) const;
/**
* \brief Returns the lot from the point to the plane
*/
Point3D ProjectPointOntoPlane(const Point3D &pt) const;
itk::LightObject::Pointer InternalClone() const override;
/** Implements operation to re-orient the plane */
void ExecuteOperation(Operation *operation) override;
/**
* \brief Project a 3D point given in mm (\a pt3d_mm) onto the 2D
* geometry. The result is a 2D point in mm (\a pt2d_mm).
*
* The result is a 2D point in mm (\a pt2d_mm) relative to the upper-left
* corner of the geometry. To convert this point into units (e.g., pixels
* in case of an image), use WorldToIndex.
* \return true projection was possible
* \sa Project(const mitk::Point3D &pt3d_mm, mitk::Point3D
* &projectedPt3d_mm)
*/
virtual bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const;
/**
* \brief Converts a 2D point given in mm (\a pt2d_mm) relative to the
* upper-left corner of the geometry into the corresponding
* world-coordinate (a 3D point in mm, \a pt3d_mm).
*
* To convert a 2D point given in units (e.g., pixels in case of an
* image) into a 2D point given in mm (as required by this method), use
* IndexToWorld.
*/
virtual void Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const;
/**
* \brief Set the width and height of this 2D-geometry in units by calling
* SetBounds. This does \a not change the extent in mm!
*
* For an image, this is the number of pixels in x-/y-direction.
* \note In contrast to calling SetBounds directly, this does \a not change
* the extent in mm!
*/
virtual void SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height);
/**
* \brief Project a 3D point given in mm (\a pt3d_mm) onto the 2D
* geometry. The result is a 3D point in mm (\a projectedPt3d_mm).
*
* \return true projection was possible
*/
virtual bool Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const;
/**
* \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D
* geometry. The result is a 2D vector in mm (\a vec2d_mm).
*
* The result is a 2D vector in mm (\a vec2d_mm) relative to the
* upper-left
* corner of the geometry. To convert this point into units (e.g., pixels
* in case of an image), use WorldToIndex.
* \return true projection was possible
* \sa Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D
* &projectedVec3d_mm)
*/
virtual bool Map(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const;
/**
* \brief Converts a 2D vector given in mm (\a vec2d_mm) relative to the
* upper-left corner of the geometry into the corresponding
* world-coordinate (a 3D vector in mm, \a vec3d_mm).
*
* To convert a 2D vector given in units (e.g., pixels in case of an
* image) into a 2D vector given in mm (as required by this method), use
* IndexToWorld.
*/
virtual void Map(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const;
/**
* \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D
* geometry. The result is a 3D vector in mm (\a projectedVec3d_mm).
*
* DEPRECATED. Use Project(vector,vector) instead
*
* \return true projection was possible
*/
virtual bool Project(const mitk::Point3D &atPt3d_mm,
const mitk::Vector3D &vec3d_mm,
mitk::Vector3D &projectedVec3d_mm) const;
/**
* \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D
* geometry. The result is a 3D vector in mm (\a projectedVec3d_mm).
*
* \return true projection was possible
*/
virtual bool Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const;
/**
* \brief Distance of the point from the geometry
* (bounding-box \em not considered)
*
*/
inline ScalarType Distance(const Point3D &pt3d_mm) const { return fabs(SignedDistance(pt3d_mm)); }
/**
* \brief Set the geometrical frame of reference in which this PlaneGeometry
* is placed.
*
* This would usually be the BaseGeometry of the underlying dataset, but
* setting it is optional.
*/
void SetReferenceGeometry(const mitk::BaseGeometry *geometry);
/**
* \brief Get the geometrical frame of reference for this PlaneGeometry.
*/
const BaseGeometry *GetReferenceGeometry() const;
bool HasReferenceGeometry() const;
protected:
PlaneGeometry();
PlaneGeometry(const PlaneGeometry &other);
~PlaneGeometry() override;
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
const mitk::BaseGeometry *m_ReferenceGeometry;
//##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); };
//##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!)
void CheckBounds(const BoundsArrayType &bounds) override;
//##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);.
void CheckIndexToWorldTransform(mitk::AffineTransform3D *transform) override;
private:
/**
* \brief Compares plane with another plane: \a true if IsOnPlane
* (bounding-box \em not considered)
*/
virtual bool operator==(const PlaneGeometry *) const { return false; };
/**
* \brief Compares plane with another plane: \a false if IsOnPlane
* (bounding-box \em not considered)
*/
virtual bool operator!=(const PlaneGeometry *) const { return false; };
};
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkPlanePositionManager.h b/Modules/Core/include/mitkPlanePositionManager.h
index 235688a5a8..4c4f46a52e 100644
--- a/Modules/Core/include/mitkPlanePositionManager.h
+++ b/Modules/Core/include/mitkPlanePositionManager.h
@@ -1,90 +1,90 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkPlanePositionManager_h
#define mitkPlanePositionManager_h
#include "mitkCommon.h"
#include "mitkDataStorage.h"
#include "mitkRestorePlanePositionOperation.h"
#include <mitkPlaneGeometry.h>
#include <mitkServiceInterface.h>
class MitkCoreActivator;
namespace mitk
{
/**
The mitk::PlanePositionManagerService holds and manages a list of certain planepositions.
To store a new position you need to specify the first slice of your slicestack and the
slicenumber you want to restore in the mitk::PlanePositionManager::AddNewPlanePosition() function.
To restore a position call mitk::PlanePositionManagerService::GetPlanePosition(ID) where ID is the position
- in the plane positionlist (returned by AddNewPlanePostion). This will give a mitk::RestorePlanePositionOperation
+ in the plane positionlist (returned by AddNewPlanePosition). This will give a mitk::RestorePlanePositionOperation
which can be executed by the SliceNavigationController of the slicestack.
\sa QmitkSegmentationView.cpp
*/
class MITKCORE_EXPORT PlanePositionManagerService
{
public:
PlanePositionManagerService();
~PlanePositionManagerService();
/**
\brief Adds a new plane position to the list. If this geometry is identical to one of the list nothing will be
added
\a plane THE FIRST! slice of the slice stack
\a sliceIndex the slice number of the selected slice
\return returns the ID i.e. the position in the positionlist. If the PlaneGeometry which is to be added already
exists the existing
ID will be returned.
*/
unsigned int AddNewPlanePosition(const mitk::PlaneGeometry *plane, unsigned int sliceIndex = 0);
/**
\brief Removes the plane at the position \a ID from the list.
\a ID the plane ID which should be removed, i.e. its position in the list
\return true if the plane was removed successfully and false if it is an invalid ID
*/
bool RemovePlanePosition(unsigned int ID);
/// \brief Clears the complete positionlist
void RemoveAllPlanePositions();
/**
\brief Getter for a specific plane position with a given ID
\a ID the ID of the plane position
\return Returns a RestorePlanePositionOperation which can be executed by th SliceNavigationController or nullptr for
an
invalid ID
*/
mitk::RestorePlanePositionOperation *GetPlanePosition(unsigned int ID);
/// \brief Getting the number of all stored planes
unsigned int GetNumberOfPlanePositions();
private:
// Disable copy constructor and assignment operator.
PlanePositionManagerService(const PlanePositionManagerService &);
PlanePositionManagerService &operator=(const PlanePositionManagerService &);
std::vector<mitk::RestorePlanePositionOperation *> m_PositionList;
};
}
MITK_DECLARE_SERVICE_INTERFACE(mitk::PlanePositionManagerService, "org.mitk.PlanePositionManagerService")
#endif
diff --git a/Modules/Core/include/mitkPointSetVtkMapper2D.h b/Modules/Core/include/mitkPointSetVtkMapper2D.h
index c4300428b6..3bc9e54ea7 100644
--- a/Modules/Core/include/mitkPointSetVtkMapper2D.h
+++ b/Modules/Core/include/mitkPointSetVtkMapper2D.h
@@ -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.
============================================================================*/
#ifndef mitkPointSetVtkMapper2D_h
#define mitkPointSetVtkMapper2D_h
#include "mitkBaseRenderer.h"
#include "mitkLocalStorageHandler.h"
#include "mitkVtkMapper.h"
#include <MitkCoreExports.h>
#include <mitkPointSetShapeProperty.h>
// VTK
#include <vtkSmartPointer.h>
class vtkActor;
class vtkPropAssembly;
class vtkPolyData;
class vtkPolyDataMapper;
class vtkGlyphSource2D;
class vtkGlyph3D;
class vtkFloatArray;
class vtkCellArray;
namespace mitk
{
class PointSet;
/**
* @brief Vtk-based 2D mapper for PointSet
*
* Due to the need of different colors for selected
* and unselected points and the facts, that we also have a contour and
* labels for the points, the vtk structure is build up the following way:
*
* We have three PolyData, one selected, and one unselected and one
* for a contour between the points. Each one is connected to an own
* PolyDataMapper and an Actor. The different color for the unselected and
* selected state and for the contour is read from properties.
*
* This mapper has several additional functionalities, such as rendering
* a contour between points, calculating and displaying distances or angles
* between points.
*
* @section mitkPointSetVtkMapper2D_point_rep Point Representation
*
* The points are displayed as small glyphs of configurable shape
* (see property "PointSet.2D.shape"). The size of these glyphs
* is given in world units. That means, the size or shape of those
* glyphs is independent of the BaseGeometry object that you assign
* to the PointSet. As for all other objects, _positions_ of points
* will be transformed into the world via the Geometry's index-to-world
* transform.
*
* Then the three Actors are combined inside a vtkPropAssembly and this
* object is returned in GetProp() and so hooked up into the rendering
* pipeline.
*
* @section mitkPointSetVtkMapper2D_propertires Applicable Properties
*
* Properties that can be set for point sets and influence the PointSetVTKMapper2D are:
*
* - \b "line width": (IntProperty 2) // line width of the line from one point to another
* - \b "point line width": (IntProperty 1) // line width of the cross marking a point
* - \b "point 2D size": (FloatProperty 6) // size of the glyph marking a point (diameter, in world
* units!)
* - \b "show contour": (BoolProperty false) // enable contour rendering between points (lines)
* - \b "close contour": (BoolProperty false) // if enabled, the open strip is closed (first point
* connected with last point)
* - \b "show points": (BoolProperty true) // show or hide points
* - \b "show distances": (BoolProperty false) // show or hide distance measure
* - \b "distance decimal digits": (IntProperty 2) // set the number of decimal digits to be shown when
* rendering the distance information
* - \b "show angles": (BoolProperty false) // show or hide angle measurement
* - \b "show distant lines": (BoolProperty false) // show the line between to points from a distant view
* (equals "always on top" option)
* - \b "layer": (IntProperty 1) // default is drawing pointset above images (they have a
* default layer of 0)
* - \b "PointSet.2D.shape" (EnumerationProperty Cross) // provides different shapes marking a point
* 0 = "None", 1 = "Vertex", 2 = "Dash", 3 = "Cross", 4 = "ThickCross", 5 = "Triangle", 6 = "Square", 7 =
* "Circle",
* 8 = "Diamond", 9 = "Arrow", 10 = "ThickArrow", 11 = "HookedArrow", 12 = "Cross"
* - \b "PointSet.2D.fill shape": (BoolProperty false) // fill or do not fill the glyph shape
* - \b "Pointset.2D.distance to plane": (FloatProperty 4.0) //In the 2D render window, points are rendered which lie
* within a certain distance
* to the current plane. They are projected on the current
* plane and scaled according to their distance.
* Point markers appear smaller as the plane moves away
* from
* their true location.
* The distance threshold can be adjusted by this float
* property, which ables the user to delineate the points
* that lie exactly on the plane. (+/- rounding error)
*
* Other Properties used here but not defined in this class:
*
* - \b "selectedcolor": (ColorProperty (1.0f, 0.0f, 0.0f)) // default color of the selected pointset e.g. the
* current
* point is red
* - \b "contourcolor" : (ColorProperty (1.0f, 0.0f, 0.0f)) // default color for the contour is red
* - \b "color": (ColorProperty (1.0f, 1.0f, 0.0f)) // default color of the (unselected) pointset is yellow
* - \b "opacity": (FloatProperty 1.0) // opacity of point set, contours
* - \b "label": (StringProperty nullptr) // a label can be defined for each point, which is rendered in proximity
* to
* the point
*
* @ingroup Mapper
*/
class MITKCORE_EXPORT PointSetVtkMapper2D : public VtkMapper
{
public:
mitkClassMacro(PointSetVtkMapper2D, VtkMapper);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
virtual const mitk::PointSet *GetInput() const;
/** \brief returns the a prop assembly */
vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override;
/** \brief set the default properties for this mapper */
static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false);
/** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */
class LocalStorage : public mitk::Mapper::BaseLocalStorage
{
public:
/* constructor */
LocalStorage();
/* destructor */
~LocalStorage() override;
// points
vtkSmartPointer<vtkPoints> m_UnselectedPoints;
vtkSmartPointer<vtkPoints> m_SelectedPoints;
vtkSmartPointer<vtkPoints> m_ContourPoints;
// scales
vtkSmartPointer<vtkFloatArray> m_UnselectedScales;
vtkSmartPointer<vtkFloatArray> m_SelectedScales;
// distances
vtkSmartPointer<vtkFloatArray> m_DistancesBetweenPoints;
// lines
vtkSmartPointer<vtkCellArray> m_ContourLines;
// glyph source (provides different shapes for the points)
vtkSmartPointer<vtkGlyphSource2D> m_UnselectedGlyphSource2D;
vtkSmartPointer<vtkGlyphSource2D> m_SelectedGlyphSource2D;
// glyph
vtkSmartPointer<vtkGlyph3D> m_UnselectedGlyph3D;
vtkSmartPointer<vtkGlyph3D> m_SelectedGlyph3D;
// polydata
vtkSmartPointer<vtkPolyData> m_VtkUnselectedPointListPolyData;
vtkSmartPointer<vtkPolyData> m_VtkSelectedPointListPolyData;
vtkSmartPointer<vtkPolyData> m_VtkContourPolyData;
// actor
vtkSmartPointer<vtkActor> m_UnselectedActor;
vtkSmartPointer<vtkActor> m_SelectedActor;
vtkSmartPointer<vtkActor> m_ContourActor;
vtkSmartPointer<vtkTextActor> m_VtkTextActor;
std::vector<vtkSmartPointer<vtkTextActor>> m_VtkTextLabelActors;
std::vector<vtkSmartPointer<vtkTextActor>> m_VtkTextDistanceActors;
std::vector<vtkSmartPointer<vtkTextActor>> m_VtkTextAngleActors;
// mappers
vtkSmartPointer<vtkPolyDataMapper> m_VtkUnselectedPolyDataMapper;
vtkSmartPointer<vtkPolyDataMapper> m_VtkSelectedPolyDataMapper;
vtkSmartPointer<vtkPolyDataMapper> m_VtkContourPolyDataMapper;
// propassembly
vtkSmartPointer<vtkPropAssembly> m_PropAssembly;
};
/** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */
mitk::LocalStorageHandler<LocalStorage> m_LSH;
protected:
/* constructor */
PointSetVtkMapper2D();
/* destructor */
~PointSetVtkMapper2D() override;
/* \brief Applies the color and opacity properties and calls CreateVTKRenderObjects */
void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override;
/* \brief Called in mitk::Mapper::Update
* If TimeGeometry or time step is not valid of point set: reset mapper so that nothing is
* displayed e.g. toggle visibility of the propassembly */
void ResetMapper(BaseRenderer *renderer) override;
/* \brief Fills the vtk objects, thus it is only called when the point set has been changed.
* This function iterates over the input point set and determines the glyphs which lie in a specific
* range around the current slice. Those glyphs are rendered using a specific shape defined in vtk glyph source
* to mark each point. The shape can be changed in MITK using the property "PointSet.2D.shape".
*
* There were issues when rendering vtk glyphs in the 2D-render windows. By default, the glyphs are
* rendered within the x-y plane in each 2D-render window, so you would only see them from the
* side in the sagittal and coronal 2D-render window. The solution to this is to rotate the glyphs in order
- * to be ortogonal to the current view vector. To achieve this, the rotation (vtktransform) of the current
+ * to be orthogonal to the current view vector. To achieve this, the rotation (vtktransform) of the current
* PlaneGeometry is applied to the orientation of the glyphs. */
virtual void CreateVTKRenderObjects(mitk::BaseRenderer *renderer);
// member variables holding the current value of the properties used in this mapper
bool m_ShowContour; // "show contour" property
bool m_CloseContour; // "close contour" property
bool m_ShowPoints; // "show points" property
bool m_ShowDistances; // "show distances" property
int m_DistancesDecimalDigits; // "distance decimal digits" property
bool m_ShowAngles; // "show angles" property
bool m_ShowDistantLines; // "show distant lines" property
int m_LineWidth; // "line width" property
int m_PointLineWidth; // "point line width" property
float m_Point2DSize; // "point 2D size" property
int m_IDShapeProperty; // ID for mitkPointSetShape Enumeration Property "Pointset.2D.shape"
bool m_FillShape; // "Pointset.2D.fill shape" property
float m_DistanceToPlane; // "Pointset.2D.distance to plane" property
bool m_FixedSizeOnScreen; // "Pointset.2D.fixed size on screen" property
};
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkProgressBarImplementation.h b/Modules/Core/include/mitkProgressBarImplementation.h
index 458335612e..9c39cf1da7 100644
--- a/Modules/Core/include/mitkProgressBarImplementation.h
+++ b/Modules/Core/include/mitkProgressBarImplementation.h
@@ -1,52 +1,52 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkProgressBarImplementation_h
#define mitkProgressBarImplementation_h
#include <MitkCoreExports.h>
namespace mitk
{
//##Documentation
- //## @brief GUI independent Interface for all Gui depentent implementations of a ProgressBar.
+ //## @brief GUI independent Interface for all Gui dependent implementations of a ProgressBar.
class MITKCORE_EXPORT ProgressBarImplementation
{
public:
//##Documentation
//## @brief Constructor
ProgressBarImplementation(){};
//##Documentation
//## @brief Destructor
virtual ~ProgressBarImplementation(){};
//##Documentation
//## @brief Sets whether the current progress value is displayed.
virtual void SetPercentageVisible(bool visible) = 0;
//##Documentation
//## @brief Explicitly reset progress bar.
virtual void Reset() = 0;
//##Documentation
//## @brief Adds steps to totalSteps.
virtual void AddStepsToDo(unsigned int steps) = 0;
//##Documentation
//## @brief Sets the current amount of progress to current progress + steps.
//## @param steps the number of steps done since last Progress(int steps) call.
virtual void Progress(unsigned int steps) = 0;
};
} // end namespace mitk
#endif
diff --git a/Modules/Core/include/mitkPropertyKeyPath.h b/Modules/Core/include/mitkPropertyKeyPath.h
index bf0c37101e..ad0334725a 100644
--- a/Modules/Core/include/mitkPropertyKeyPath.h
+++ b/Modules/Core/include/mitkPropertyKeyPath.h
@@ -1,216 +1,216 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkPropertyKeyPath_h
#define mitkPropertyKeyPath_h
#include <string>
#include <vector>
#include <mitkExceptionMacro.h>
#include <MitkCoreExports.h>
namespace mitk
{
/** @brief Class that can be used to specify nested or wild carded property keys. E.g.
* for the use in context of the property persistence service or the property relation service.\n
* Following assumptions are made /preconditions are defined:
* - A property key is partitioned by "." into nodes (c.f. visualization of property keys in the PropertyView).
* - A node can either be an element or a selection.
* - An element has a name (alphanumric, - and space; "A-Za-z0-9- ") or is wildcarded ("*")
* - A selection is either an index (e.g. "[1]") or a wildcard ("[*]").
*
- * Selections are used to indicate that the preceding element has multiple occurrences and which occurence is meant.
+ * Selections are used to indicate that the preceding element has multiple occurrences and which occurrence is meant.
* Example property keys would be:
* - prop : A simple property key
* - prop.subprop1 : A property key consisting of two nodes
* - prop.* : Any property key that starts with a node "prop"
* - prop.sub.[2] : A property key that starts with a node "prop" and a has a second node that is selection and has
* the index 2.
* - prop.sub.[*] : Any property key that starts with a node "prop" and a has a second node that is selection (with
* any index).
*
* To build a path one may use the Add* method to build up the PropertyKeyPath element by element.\n
* "first.*.third.[3]" would be equivalent to
* propKeyPath.AddElement("first");
* propKeyPath.AddAnyElement();
* propKeyPath.AddSelection("third",3);\n
* or the inline version
* propKeyPath.AddElement("first").AddAnyElement().AddSelection("third",3);
*/
class MITKCORE_EXPORT PropertyKeyPath final
{
public:
using ItemSelectionIndex = std::size_t;
using ElementNameType = std::string;
struct MITKCORE_EXPORT NodeInfo
{
enum class NodeType
{
Invalid = 0, //*< Node does not exist or is invalid.
Element, //*< Selects an specific element given the node name.
ElementSelection, //*< Selects an specific item in a sequence of items and has a item selector ("[n]").
AnySelection, //*< Selects all items of a specific element ("[*]").
AnyElement //*< Selects any element/item. Node name is wildcarded ("*"); item selection as well implictily.
};
NodeType type;
ElementNameType name;
ItemSelectionIndex selection;
NodeInfo();
NodeInfo(const ElementNameType &name, NodeType type = NodeType::Element, ItemSelectionIndex index = 0);
bool Matches(const NodeInfo &right) const;
bool operator==(const NodeInfo &right) const;
};
using NodeInfoVectorType = std::vector<NodeInfo>;
using PathIndexType = NodeInfoVectorType::size_type;
/** Returns if the PropertyKeyPath is empty.*/
bool IsEmpty() const;
/** Returns if the path is explicit (has no wildcards).*/
bool IsExplicit() const;
/** Returns if the path has any nodes with item selection wild cards ([*]).*/
bool HasItemSelectionWildcardsOnly() const;
/** Number of path nodes the PropertyKeyPath contains.*/
PathIndexType GetSize() const;
/** Adds a new node to the end of the path.
\param [in] newNode Reference to the node that should be added.
\return Returns the index of the newly added node.*/
PathIndexType AddNode(const NodeInfo &newNode);
/** Function returns the node info of a path node specified by the index
* within the PropertyKeyPath.
* \pre Passed index must not be out of bounds.
* \param [in] index Index of the node whose info should be retrieved.
* \return Info of the specified path node. If the index is out of bound an InvalidPathNode exception will be
* thrown.*/
const NodeInfo &GetNode(const PathIndexType &index) const;
/** Function returns the node info of a path node specified by the index
* within the PropertyKeyPath.
* \pre Passed index must not be out of bounds.
* \param [in] index Index of the node whose info should be retrieved.
* \return Info of the specified path node. If the index is out of bound an InvalidPathNode exception will be
* thrown.*/
NodeInfo &GetNode(const PathIndexType &index);
/** Function returns the node info of the first path node within the PropertyKeyPath.
* \pre PropertyKeyPath must not be empty.
* \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/
NodeInfo &GetFirstNode();
/** Function returns the node info of the first path node within the PropertyKeyPath.
* \pre PropertyKeyPath must not be empty.
* \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/
const NodeInfo &GetFirstNode() const;
/** Function returns the node info of the last path node within the PropertyKeyPath.
* \pre PropertyKeyPath must not be empty.
* \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/
NodeInfo &GetLastNode();
/** Function returns the node info of the last path node within the PropertyKeyPath.
* \pre PropertyKeyPath must not be empty.
* \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/
const NodeInfo &GetLastNode() const;
const NodeInfoVectorType &GetNodes() const;
/**Compares two PropertyKeyPaths for real equality. So it is a string comparison of their string conversion.*/
bool operator==(const PropertyKeyPath &path) const;
/**Operation equals like comparing the ToStr() results with operator <.*/
bool operator<(const PropertyKeyPath &right) const;
/**Operation equals like comparing the ToStr() results with operator <=.*/
bool operator<=(const PropertyKeyPath &right) const;
/**Operation equals like comparing the ToStr() results with operator >=.*/
bool operator>=(const PropertyKeyPath &right) const;
/**Operation equals like comparing the ToStr() results with operator >.*/
bool operator>(const PropertyKeyPath &right) const;
/**Checks if two PropertyKeyPaths specify the same node. Hence all wildcards will be processed.\n
* E.G.: "item1.child1.grandChild2" == "item1.*.grandChild2" is true.
* \remark If you want to check if two paths are "truly" equal and not only equal in terms of
* pointing to the same node, use the member function operator ==().*/
bool Equals(const PropertyKeyPath &path) const;
PropertyKeyPath &operator=(const PropertyKeyPath &path);
/** Appends an "any element" to the path instance.*/
PropertyKeyPath &AddAnyElement();
/** Appends an element with the passed name to the path instance.*/
PropertyKeyPath &AddElement(const ElementNameType &name);
/** Appends an element with the passed name and any selection to the path instance.*/
PropertyKeyPath &AddAnySelection(const ElementNameType &name);
/** Appends an element with the passed name and selection index to the path instance.*/
PropertyKeyPath &AddSelection(const ElementNameType &name, ItemSelectionIndex index);
PropertyKeyPath();
PropertyKeyPath(const PropertyKeyPath &path);
/** overload constructor that supports simple key paths consisting only of elements.*/
PropertyKeyPath(const std::initializer_list< ElementNameType >& list);
~PropertyKeyPath();
void Reset();
protected:
NodeInfoVectorType m_NodeInfos;
static bool PropertyKeyPathsMatch(const PropertyKeyPath &left, const PropertyKeyPath &right);
};
class MITKCORE_EXPORT InvalidPathNodeException : public mitk::Exception
{
public:
mitkExceptionClassMacro(InvalidPathNodeException, mitk::Exception);
};
MITKCORE_EXPORT std::ostream &operator<<(std::ostream &os, const PropertyKeyPath &path);
/**Helper function that converts a path PropertyKeyPath into a regex string that can be used
to search for property keys (using std::regex) that are matched by the PropertyKeyPath.
This function is used in context of the property persistence service.*/
MITKCORE_EXPORT std::string PropertyKeyPathToPropertyRegEx(const PropertyKeyPath &tagPath);
/**Helper function that converts a path PropertyKeyPath into a regex string that can be used
to search for property persistence keys (using std::regex) that are matched by the PropertyKeyPath.
This function is used in context of the property persistence service.*/
MITKCORE_EXPORT std::string PropertyKeyPathToPersistenceKeyRegEx(const PropertyKeyPath &tagPath);
/**Helper function that converts a path PropertyKeyPath into a regex that can be used as key template
in a PropertyPersistanceInfo.
This function is used in context of the property persistence service.*/
MITKCORE_EXPORT std::string PropertyKeyPathToPersistenceKeyTemplate(const PropertyKeyPath &tagPath);
/**Helper function that converts a path PropertyKeyPath into a regex that can be used as name template
in a PropertyPersistanceInfo.
This function is used in context of the property persistence service.*/
MITKCORE_EXPORT std::string PropertyKeyPathToPersistenceNameTemplate(const PropertyKeyPath &tagPath);
/** Converts the passed property name into a tag path. If the property name cannot be converted
into a valid path, the returned path is empty.*/
MITKCORE_EXPORT PropertyKeyPath PropertyNameToPropertyKeyPath(const std::string &propertyName);
/** returns the correct property name for a given PropertyKeyPath instance. */
MITKCORE_EXPORT std::string PropertyKeyPathToPropertyName(const PropertyKeyPath &tagPath);
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkPropertyRelationRuleBase.h b/Modules/Core/include/mitkPropertyRelationRuleBase.h
index 1a77b47f64..e2c43b9018 100644
--- a/Modules/Core/include/mitkPropertyRelationRuleBase.h
+++ b/Modules/Core/include/mitkPropertyRelationRuleBase.h
@@ -1,403 +1,403 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkPropertyRelationRuleBase_h
#define mitkPropertyRelationRuleBase_h
#include "mitkIPropertyOwner.h"
#include "mitkIdentifiable.h"
#include "mitkException.h"
#include "mitkNodePredicateBase.h"
#include "mitkPropertyKeyPath.h"
#include <MitkCoreExports.h>
#include <string>
namespace mitk
{
/**Base class to standardize/abstract/encapsulate rules and business logic to detect and define
(property/data based) relations in MITK.
Following important definitions must be regarded when using/implementing/specifying rule classes:
- Relations represented by rules are directed relations that point from a source IPropertyOwner (Source)
to a destination IPropertyOwner (Destination).
- Rule can be abstract (indicated by IsAbstract()) or concrete. Abstract rules cannot be used to connect relations.
Abstract rules can only be used to detect/indicate or disconnect relations. Therefore, in contrast to concrete rules,
abstract rules can be used to indicate several relations that are established be "derived" rules. See e.g. GenericIDRelationRule:
in its abstract state it cannot connect but be used to detect any type of generic ID relation.
- A concrete rule ID (rule ID of a concrete rule) always "implements" a concrete relation type. E.g. In DICOM the
way to express the source image relation to an input image and to a mask would be nearly the same
and only differs by the encoded purpose. One may implement an interim or joined class that manages the mutual
stuff, but the registered instances must be one concrete rule for "DICOM source input image" and one concrete rule for
"DICOM source mask" and both rules must have distinct rule IDs.
- Source may have several relations of a rule to different Destinations.
Destination may have several relations of a rule from different Sources. But a specific source destination
pair may have only one relation of a specific rule id (concrete rule). A specific source destination
pair may however have multiple relations for an abstract rule.
- The deletion of a Destination in the storage does not remove the relation implicitly. It becomes a "zombie" relation
but it should still be documented, even if the destination is unknown. One has to explicitly
disconnect a zombie relation to get rid of it.
- Each relation has its own UID (relationUID) that can be used to address it.
The basic concept of the rule design is that we have two layers of relation identification: Layer 1 is the ID-layer
which uses the IIdentifiable interface and UIDs if available to encode "hard" relations. Layer 2 is the Data-layer
which uses the properties of Source and Destination to deduce if there is a relation of the rule type.
The ID-layer is completely implemented by this base class. The base class falls back to the Data-layer
(implemented by the concrete rule class) if the ID-layer is not sufficient or it is explicitly stated to (only)
look at the data layer.
Reasons for the introduction of the ID-layer are: 1st, data-defined relations may be weak (several Destinations are
possible; e.g. DICOM source images may point to several loaded mitk images). But if explicitly a relation was
connected it should be deduceable. 2nd, checks on a UID are faster then unnecessary data deduction.
Rules use relation instance identifying (RII) properties in order to manage their relations that are stored in the
Source. The RII-properties follow the following naming schema:
"MITK.Relations.\<InstanceID\>.[relationUID|destinationUID|ruleID|\<data-layer-specific\>]"
- \<InstanceID\>: The unique index of the relation for the Source. Used to assign/group the properties to
their relation. In the default implementation of this class the instance id is an positive integer (i>0).
- relationUID: The UID of the relation. Set by the ID-layer (so by this class)
- destinationUID: The UID of the Destination. Set by the ID-layer (so by this class) if Destination implements
IIdentifiable.
- ruleID: The identifier of the concrete rule that sets the property. Is specified by the derived class and set
automatically be this base class.
- <data-layer-specific>: Information needed by the Data-layer (so derived classes) to find the relationUID
*/
class MITKCORE_EXPORT PropertyRelationRuleBase : public itk::Object
{
public:
mitkClassMacroItkParent(PropertyRelationRuleBase, itk::Object);
itkCloneMacro(Self);
itkCreateAnotherMacro(Self);
using RuleIDType = std::string;
using RelationUIDType = Identifiable::UIDType;
using RelationUIDVectorType = std::vector<RelationUIDType>;
/** Enum class for different types of relations. */
enum class RelationType
{
None = 0, /**< Two IPropertyOwner have no relation under the rule.*/
Data = 1, /**< Two IPropertyOwner have a relation, but it is "only" deduced from the Data-layer (so a bit
"weaker" as ID). Reasons for the missing ID connection could be that Destintination has not
IIdentifiable implemented.*/
ID = 2, /**< Two IPropertyOwner have a relation and are explicitly connected via the ID of IIdentifiable of the Destination.*/
Complete = 3 /**< Two IPropertyOwner have a relation and are fully explicitly connected (via data layer and ID layer).*/
};
using RelationVectorType = std::vector<RelationType>;
/** Returns the generic root path for relation rules ("MITK.Relations").*/
static PropertyKeyPath GetRootKeyPath();
using InstanceIDType = std::string;
/** Returns the property key path for a RII property.
* @param propName If not empty a PropertyPath element will added (with the passed value) after the \<InstanceID\> element.
* @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty,
* it is wildcarded and will match RIIs property of any instance.*/
static PropertyKeyPath GetRIIPropertyKeyPath(const std::string propName, const InstanceIDType& instanceID);
/** Returns the property key path for RII RelationUID properties.
* @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty,
* it is wildcarded and will match RII RelationUIDs property of any instance.*/
static PropertyKeyPath GetRIIRelationUIDPropertyKeyPath(const InstanceIDType& instanceID = "");
/** Returns the property key path for RII RuleID properties.
* @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty,
* it is wildcarded and will match RII RuleIDs property of any instance.*/
static PropertyKeyPath GetRIIRuleIDPropertyKeyPath(const InstanceIDType& instanceID = "");
/** Returns the property key path for RII DestinationUID properties.
* @param instanceID If not empty, the PropertyKeyPath is only for a specific instance. If empty,
* it is wildcarded and will match RII DestinationUIDs property of any instance.*/
static PropertyKeyPath GetRIIDestinationUIDPropertyKeyPath(const InstanceIDType& instanceID = "");
/** Returns an ID string that identifies the rule class.
@post The returned rule ID must met the preconditions of a PropertyKeyPath element name
(see mitk::PropertyKeyPath*/
virtual RuleIDType GetRuleID() const = 0;
/** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/
virtual std::string GetDisplayName() const = 0;
/** Returns a human readable string that can be used to describe the role of a source in context of the rule
* instance.*/
virtual std::string GetSourceRoleName() const = 0;
/** Returns a human readable string that can be used to describe the role of a destination in context of the rule
* instance.*/
virtual std::string GetDestinationRoleName() const = 0;
/** Returns if the instance is a abstract rule (true). Default implementation is true. Overwrite and reimplement
if another behavior is needed.*/
virtual bool IsAbstract() const;
/** This method checks if owner is eligible to be a Source for the rule. The default implementation returns a
True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if
they have requirements on potential Sources).*/
virtual bool IsSourceCandidate(const IPropertyProvider *owner) const;
/** This method checks if owner is eligible to be a Destination for the rule. The default implementation returns a
True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if
they have requirements on potential Sources).*/
virtual bool IsDestinationCandidate(const IPropertyProvider *owner) const;
/** Returns true if the passed owner is a Source of a relation defined by the rule.
@pre owner must be a pointer to a valid IPropertyProvider instance.*/
bool IsSource(const IPropertyProvider *owner) const;
/** Returns all relation types of the passed IPropertyOwner instances.
@remark Abstract rules may have several relationtypes between the instances (from different supported concrete rules), that cover
both ID and Data relations; thus it returns a vector of RelationTypes.
@result Vector of all relation types that exist between the given instances. Empty vector equals none relation at all.
@pre source must be a pointer to a valid IPropertyProvider instance.
@pre destination must be a pointer to a valid IPropertyProvider instance.
*/
RelationVectorType GetRelationTypes(const IPropertyProvider* source, const IPropertyProvider* destination) const;
/** Indicates if passed IPropertyOwner instances have a relation of a certain type.
@remark Abstract rules may also indicate RelationType::Complete if there
are multiple relations (from different supported concrete rules), that cover
both ID and Data relations.
@param source
@param destination
@param requiredRelation Defines the type of relation that should be present. None: does not matter which one, as long as at least one is present.
Data: Only data layer exclusive connections, ID: Only ID layer exclusive connections. Complete: Only relations that are connected on both layers.
@pre source must be a pointer to a valid IPropertyProvider instance.
@pre destination must be a pointer to a valid IPropertyProvider instance.
*/
bool HasRelation(const IPropertyProvider *source, const IPropertyProvider *destination, RelationType requiredRelation = RelationType::None) const;
/** Returns a vector of relation UIDs for all relations of this rule instance that are defined for
the passed source.
@pre source must be a pointer to a valid IPropertyOwner instance.
@param source
@param layer Defines the layer the relations must be reflected. None: does not matter which one, as long as at least one is present.
Data: Only data layer exclusive connections, ID: Only ID layer exclusive connections. Complete: Only relations that are connected on both layers.*/
RelationUIDVectorType GetExistingRelations(const IPropertyProvider *source, RelationType layer = RelationType::None) const;
/** Returns the relation UID(s) for the passed source and destination of this rule instance.
If the rule is abstract multiple relation UIDs might be returned. In case of concrete rule only
one relation UID.
@pre source must be a pointer to a valid IPropertyOwner instance.
@pre destination must be a pointer to a valid IPropertyOwner instance.*/
RelationUIDVectorType GetRelationUIDs(const IPropertyProvider *source, const IPropertyProvider *destination) const;
/** Returns the relation UID for the passed source and destination of this rule instance.
If the passed instances have no relation, no ID can be deduced and an exception will be thrown.
If more than one relation is found, also an exception will be thrown. Thus only use this convenience method,
if you are sure that one(!) relation UID can exist.
@pre source must be a pointer to a valid IPropertyOwner instance.
@pre destination must be a pointer to a valid IPropertyOwner instance.
@pre Source and destination have one relation; otherwise
if no relation exists a NoPropertyRelationException is thrown; if more than one relation exists
a default MITK exception is thrown.*/
RelationUIDType GetRelationUID(const IPropertyProvider *source, const IPropertyProvider *destination) const;
/**Predicate that can be used to find nodes that qualify as source for that rule (but must not be a source yet).
Thus all nodes where IsSourceCandidate() returns true. */
NodePredicateBase::ConstPointer GetSourceCandidateIndicator() const;
/**Predicate that can be used to find nodes that qualify as destination for that rule (but must not be a destination
yet). Thus all nodes where IsDestinationCandidate() returns true. */
NodePredicateBase::ConstPointer GetDestinationCandidateIndicator() const;
/**Predicate that can be used to find nodes that are Sources of that rule and connected.
Thus all nodes where IsSource() returns true.*/
NodePredicateBase::ConstPointer GetConnectedSourcesDetector() const;
/**Predicate that can be used to find nodes that are as source related to the passed Destination under the rule
@param destination Pointer to the Destination instance that should be used for detection.
@param exclusiveRelation Defines if only special types of relations should detected. None: All relations (default);
Data: must be a data relation (so Data or Complete); ID: must be an ID relation (so ID or Complete); Complete: only complete relations.
@pre Destination must be a valid instance.*/
NodePredicateBase::ConstPointer GetSourcesDetector(
const IPropertyProvider *destination, RelationType exclusiveRelation = RelationType::None) const;
/**Predicate that can be used to find nodes that are as Destination related to the passed Source under the rule
@param source Pointer to the Source instance that should be used for detection.
@param exclusiveRelation Defines if only special types of relations should detected. None: All relations (default);
Data: must be a data relation (so Data or Complete); ID: must be an ID relation (so ID or Complete); Complete: only complete relations.
@pre Source must be a valid instance.*/
NodePredicateBase::ConstPointer GetDestinationsDetector(
const IPropertyProvider *source, RelationType exclusiveRelation = RelationType::None) const;
/**Returns a predicate that can be used to find the Destination of the passed Source for a given relationUID.
@param source Pointer to the Source instance that should be used for detection.
@param relationUID
@pre source must be a valid instance.
@pre relationUID must identify a relation of the passed source and rule. (This must be in the return of
this->GetExistingRelations(source). */
NodePredicateBase::ConstPointer GetDestinationDetector(const IPropertyProvider *source,
RelationUIDType relationUID) const;
/**Disconnect the passed instances by modifying source. One can specify which layer should be disconnected
via the argument "layer". Default is the complete disconnection.
All RII-properties or properties that define the connection on the data layer in the source
for the passed destination will be removed.
@pre source must be a valid instance.
@pre destination must be a valid instance.
@param source
@param destination
@param layer Defines the way of disconnection. Data: Only the remove the connection on the data layer. ID: Only remove the connection
on the ID layer. Complete: Remove the connection on all layers. If a connection does not exist on a selected layer, it is silently ignored.*/
void Disconnect(IPropertyOwner *source, const IPropertyProvider *destination, RelationType layer = RelationType::Complete) const;
/**Disconnect the source from the passed relationUID (useful for "zombie relations").
One can specify which layer should be disconnected
via the argument "layer". Default is the complete disconnection.
All RII-properties or properties that define the connection on the data layer in the source
for the passed destination will be removed.
If the relationUID is not part of the source. Nothing will be changed.
@pre source must be a valid instance.
@param source
@param relationUID
@param layer Defines the way of disconnection. Data: Only the remove the connection on the data layer. ID: Only remove the connection
on the ID layer. Complete: Remove the connection on all layers. If a connection does not exist on a selected layer, it is silently ignored.*/
void Disconnect(IPropertyOwner *source, RelationUIDType relationUID, RelationType layer = RelationType::Complete) const;
/**Returns the list of PropertyKeyPaths of all properties that are relevant for a given relation.
@param source Pointer to the Source instance that contains the potential properties.
@param relationUID UID of the relation that is relevant for the requested properties.
@param layer Indicates which layer is requested. ID: returns all RII properties that belong to the relation. Data: returns all properties that are relevant/belong to the data layer of the relation. Complete: returns all properties (ID+Data)
@pre source must be a valid instance.
@pre relationUID must identify a relation of the passed source and rule. (This must be in the return of
this->GetExistingRelations(source). */
std::vector<PropertyKeyPath> GetRelationPropertyPaths(const IPropertyProvider* source,
RelationUIDType relationUID, RelationType layer = RelationType::Data) const;
protected:
PropertyRelationRuleBase() = default;
~PropertyRelationRuleBase() override = default;
using InstanceIDVectorType = std::vector<InstanceIDType>;
static InstanceIDType NULL_INSTANCE_ID();
/** Returns the instance IDs for the passed source and destination for this rule instance.
If the passed source and destination instances has no explicit relation on the ID layer (Connected_ID),
an empty vector will be returned.
@remark Per definition of property relation rules only 0 or 1 instance should be found for one provider
pair and concrete rule. But there might be more then one instanceID because either 1) the rule is abstract and
supports multiple rule IDs or 2) the data layer may be ambiguous and therefore multiple relation instances of the rule instance
could match. The implementation of this function should report all relation instances. The calling function
will take care.
@pre source must be a pointer to a valid IPropertyProvider instance.
@pre destination must be a pointer to a valid IPropertyProvider instance.*/
InstanceIDVectorType GetInstanceID_IDLayer(const IPropertyProvider *source,
const IPropertyProvider *destination) const;
using DataRelationUIDVectorType = std::vector< std::pair<RelationUIDType, RuleIDType> >;
/** Returns the RelationUIDs of all relations that are defined by the data layer of source for
this rule instance and, if defined, destination.
If the passed source (and destination) instance has no relation on the data layer,
an empty vector will be returned.
@remark Per definition for property relation rules only 0 or 1 instance should be found for one provider
pair and concrete rule. But there might be more then one instance because either 1) the rule is abstract and
supports multiple rule IDs or 2) the data layer may be ambiguous (e.g. because the destination was not specified)
and therefore multiple relation instances of the rule instance could match.
The implementation of this function should report all relation instances. The calling function
will take care.
@param source
@param destination Destination the find relations should point to. If destination is NULL any relation
on the data layer for this rule and source are wanted.
@param instances_IDLayer List of relation instances that are already defined by the ID layer. The implementation of this
function should only cover relations that are not already resembled in the passed relarions_IDLayer.
@pre source must be a pointer to a valid IPropertyProvider instance.*/
virtual DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider * source,
const IPropertyProvider * destination, const InstanceIDVectorType& instances_IDLayer) const = 0;
/**Helper function that deduces the relation UID of the given relation instance.
If it cannot be deduced an NoPropertyRelationException is thrown.*/
RelationUIDType GetRelationUIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const;
/**Helper function that deduces the relation instance ID given the relation UID.
If it cannot be deduced an NoPropertyRelationException is thrown.*/
InstanceIDType GetInstanceIDByRelationUID(const IPropertyProvider *source,
const RelationUIDType &relationUID) const;
/**Explicitly connects the passed instances. Afterwards they have a relation of Data (if data layer is supported), ID (if a
destination implements IIdentifiable) or Complete (if Data and ID could be connected). If the passed instance are already
connected the old connection will be overwritten (and raised to the highest possible connection level).
@remark This method has protected visibility in the base implementation, because it is a design decision of derived rule classes
which interface they want to offer for connecting. It may just be made public (e.g. GenericIDRelationRule) or used by own implementations.
@pre source must be a valid instance.
@pre destination must be a valid instance.
@pre the rule instance must not be abstract.
@return Return the relation uid of the relation connected by this method call*/
RelationUIDType Connect(IPropertyOwner *source, const IPropertyProvider *destination) const;
/**Is called by Connect() to ensure that source has correctly set properties to resemble
the relation on the data layer. This means that the method should set the properties that describe
and encode the relation on the data layer (data-layer-specific relation properties).
If the passed instance are already connected, the old settings should be
overwritten. Connect() will ensure that source and destination are valid pointers.
@param source
@param destination
@param instanceID is the ID for the relation instance that should be connected. Existence of the relation instance
is ensured.
@pre source must be a valid instance.
@pre destination must be a valid instance.*/
virtual void Connect_datalayer(IPropertyOwner *source,
const IPropertyProvider *destination,
const InstanceIDType &instanceID) const = 0;
/**This method is called by Disconnect() to remove all properties of the relation from the source that
are set by Connect_datalayer().
@remark This method should remove all properties that are set for a specific relation by Connect_datalayer(...).
If the relationUID is not part of the source, nothing will be changed. Disconnect() ensures that source is a valid
pointer if called.
- @remark Disconnect() ensures that sourece is valid and only invokes if instance exists.*/
+ @remark Disconnect() ensures that source is valid and only invokes if instance exists.*/
virtual void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType & relationUID) const = 0;
/** Returns if the passed rule ID is supported/relevant for the rule. Either because it is the very ID of the rule (default implementation) or because
it is an abstract rule which also supports the rule ID.
@return true: If the rule ID can handle the rule ID. false: the rule does not support the rule ID.*/
virtual bool IsSupportedRuleID(const RuleIDType& ruleID) const;
/** Helper function that generates a reg ex that can be used to find a specific RII property for the rule instance.
* @param propName If not empty a PropertyPath element will be added (with the passed value) after the \<InstanceID\> element.
* @param instanceID If not empty only for the reg ex will only valid for the passed instanceID. Otherwise for all.*/
std::string GetRIIPropertyRegEx(const std::string propName = "", const InstanceIDType &instanceID = "") const;
/**Helper function that deduces the instance ID out of a property name.
If it cannot be deduced an MITK exception is thrown.*/
static InstanceIDType GetInstanceIDByPropertyName(const std::string propName);
/**Helper function that retrieves the rule ID of a relation instance of a passed source.
@pre source must be valid.
@pre source must have a relation instance with this ID*/
RuleIDType GetRuleIDByInstanceID(const IPropertyProvider *source,
const InstanceIDType &instanceID) const;
/**Helper function that retrieves the destination UID of a relation instance of a passed
source. If the relation has no destination UID, an empty string will be returned.
@pre source must be valid.*/
std::string GetDestinationUIDByInstanceID(const IPropertyProvider * source,
const InstanceIDType & instanceID) const;
itk::LightObject::Pointer InternalClone() const override;
/** helper method that serves as a workaround until T24729 is done.
Please remove if T24728 is done then could directly use owner->GetPropertyKeys() again.*/
static std::vector<std::string> GetPropertyKeys(const IPropertyProvider *owner);
/** Helper method that tries to cast the provider to the Identifiable interface.*/
const Identifiable* CastProviderAsIdentifiable(const mitk::IPropertyProvider* provider) const;
private:
/** Creates a relation UID*/
static RelationUIDType CreateRelationUID();
/**Prepares a new relation instance. Therefore an unused and valid instance ID for the passed source will be generated
and a relationUID property with the relationUID will be set to block the instance ID. The
instance ID will be returned.
@remark The method is guarded by a class wide mutex to avoid racing conditions in a scenario where rules are used
concurrently.*/
InstanceIDType CreateNewRelationInstance(IPropertyOwner *source, const RelationUIDType &relationUID) const;
};
/**Exception that is used by PropertyRelationRuleBase based classes to indicate that two objects have no relation.*/
class NoPropertyRelationException : public Exception
{
public:
mitkExceptionClassMacro(NoPropertyRelationException, Exception)
};
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkRenderingManager.h b/Modules/Core/include/mitkRenderingManager.h
index 5dab86b41a..2e5c3a4775 100644
--- a/Modules/Core/include/mitkRenderingManager.h
+++ b/Modules/Core/include/mitkRenderingManager.h
@@ -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.
============================================================================*/
#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 TimeNavigationController;
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).
*
* Convenience methods for updating all RenderWindows which have been
* registered with the RenderingManager exist. If these 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 issuing 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 given render window by the aggregated geometry of all objects that are held in
* the data storage.
*
- * @param renderWindow The specifid render window to update
+ * @param renderWindow The specified render window to update
* @param dataStorage The data storage from which the bounding object can be retrieved
* @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 void InitializeViewByBoundingObjects(vtkRenderWindow* renderWindow,
const DataStorage* dataStorage,
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 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 specific render window to update
* @param geometry The geometry to be used to initialize / update the
* render window's time- and slice navigation controller
* @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 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 specific render window to update
* @param geometry The geometry to be used to initialize / update the
* render window's time- and slice navigation controller
* @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 resetCamera = true);
/**
* @brief Initialize the specified render window to the default viewing direction
* without updating the geometry information.
*
* @param renderWindow The specific render window to update
*/
virtual bool InitializeView(vtkRenderWindow *renderWindow);
/** Gets the (global) TimeNavigationController responsible for
* time-slicing. */
const TimeNavigationController* GetTimeNavigationController() const;
/** Gets the (global) TimeNavigationController responsible for
* time-slicing. */
TimeNavigationController* 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<TimeNavigationController> 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 specific 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)
itkEventMacroDeclaration(RenderingManagerEvent, itk::AnyEvent);
itkEventMacroDeclaration(RenderingManagerViewsInitializedEvent, RenderingManagerEvent);
#pragma GCC visibility pop
itkEventMacroDeclaration(FocusChangedEvent, itk::AnyEvent);
/**
* Generic RenderingManager implementation for "non-rendering-platform",
* 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
diff --git a/Modules/Core/include/mitkStatusBarImplementation.h b/Modules/Core/include/mitkStatusBarImplementation.h
index 5324621fcb..6aa47bfdf1 100644
--- a/Modules/Core/include/mitkStatusBarImplementation.h
+++ b/Modules/Core/include/mitkStatusBarImplementation.h
@@ -1,60 +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 mitkStatusBarImplementation_h
#define mitkStatusBarImplementation_h
#include <MitkCoreExports.h>
#include <mitkCommon.h>
namespace mitk
{
//##Documentation
- //## @brief GUI independent Interface for all Gui depentent implementations of a StatusBar.
+ //## @brief GUI independent Interface for all Gui dependent implementations of a StatusBar.
class MITKCORE_EXPORT StatusBarImplementation
{
public:
mitkClassMacroNoParent(StatusBarImplementation)
//##Documentation
//## @brief Constructor
StatusBarImplementation(){};
//##Documentation
//## @brief Destructor
virtual ~StatusBarImplementation(){};
//##Documentation
//## @brief Send a string to the applications StatusBar
virtual void DisplayText(const char *t) = 0;
//##Documentation
//## @brief Send a string with a time delay to the applications StatusBar
virtual void DisplayText(const char *t, int ms) = 0;
virtual void DisplayErrorText(const char *t) = 0;
virtual void DisplayWarningText(const char *t) = 0;
virtual void DisplayWarningText(const char *t, int ms) = 0;
virtual void DisplayGenericOutputText(const char *t) = 0;
virtual void DisplayDebugText(const char *t) = 0;
virtual void DisplayGreyValueText(const char *t) = 0;
//##Documentation
//## @brief removes any temporary message being shown.
virtual void Clear() = 0;
//##Documentation
//## @brief Set the SizeGrip of the window
//## (the triangle in the lower right Windowcorner for changing the size)
//## to enabled or disabled
virtual void SetSizeGripEnabled(bool enable) = 0;
};
} // end namespace mitk
#endif
diff --git a/Modules/Core/include/mitkTimeNavigationController.h b/Modules/Core/include/mitkTimeNavigationController.h
index 16128e2106..4ded3fcc28 100644
--- a/Modules/Core/include/mitkTimeNavigationController.h
+++ b/Modules/Core/include/mitkTimeNavigationController.h
@@ -1,172 +1,172 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 TimeNavigationController_h
#define TimeNavigationController_h
#include <MitkCoreExports.h>
#include <mitkBaseController.h>
#include <mitkTimeGeometry.h>
#include <itkCommand.h>
namespace mitk
{
/**
* \brief Controls the time-related properties of the time stepper, according to the
- * given input world time geomtry.
+ * given input world time geometry.
*
* A TimeNavigationController takes a TimeGeometry as input world time geometry
* and sets the properties of the associated stepper (BaseController).
* The time-related properties are:
* - steps: the number of input time steps
* - pos: the current time step / position of the stepper
* - range: the time bounds for the slider (minimum, maximum)
*
* The timestep controls the selection of a specific time point from the TimeGeometry.
* The TimeNavigationController generates ITK events to tell observers,
* like a BaseRenderer, when the selected timestep changes.
*
* Example:
* \code
* // Initialize the time navigation controller.
* timeCtrl = mitk::TimeNavigationController::New();
*
- * // Set the required input world time geomtry (a TimeGeometry::ConstPointer).
+ * // Set the required input world time geometry (a TimeGeometry::ConstPointer).
* timeCtrl->SetInputWorldTimeGeometry(geometry.GetPointer());
*
* // Set the time-related properties and send the information to the connected renderer(s).
* timeCtrl->Update();
* \endcode
*
* Vvisible navigation widgets can be connected to a TimeNavigationController, e.g., a
* QmitkSliceNavigationWidget (for Qt):
*
* \code
* // Create the visible navigation widget (a slider with a spin-box).
* QmitkSliceNavigationWidget* navigationWidget = new QmitkSliceNavigationWidget(parent);
*
* // Connect the navigation widget to the time-stepper of the
* // TimeNavigationController. For initialization (timestep, minimal and
* // maximal values) the values of the TimeNavigationController are used.
* // Thus, accessing methods of a navigation widget is normally not necessary,
* // since everything can be set via the (Qt-independent) TimeNavigationController.
* // The QmitkStepperAdapter converts the Qt-signals to Qt-independent
* // itk-events.
* new QmitkStepperAdapter(navigationWidget, timeCtrl->GetStepper());
* \endcode
*/
class MITKCORE_EXPORT TimeNavigationController : public BaseController
{
public:
mitkClassMacro(TimeNavigationController, BaseController);
itkNewMacro(Self);
/**
* \brief Set the input time geometry out of which the
* time-related properties will be generated.
*
* Any previous set input geometry (3D or Time) will
* be ignored in the future.
*/
void SetInputWorldTimeGeometry(const TimeGeometry* geometry);
itkGetConstObjectMacro(InputWorldTimeGeometry, mitk::TimeGeometry);
/**
* \brief Do the actual time-related properties extraction
- and send the currently selected time step to the connecte
+ and send the currently selected time step to the connected
observers (renderers).
*/
virtual void Update();
/**
* \brief Send the currently selected time step to the connected
* observers (renderers).
*
* Called by Update().
*/
virtual void SendTime();
class MITKCORE_EXPORT TimeEvent : public itk::AnyEvent
{
public:
typedef TimeEvent Self;
typedef itk::AnyEvent Superclass;
TimeEvent(TimeStepType timeStep) : m_TimeStep(timeStep) {}
~TimeEvent() override {}
const char *GetEventName() const override { return "TimeEvent"; }
bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast<const Self *>(e); }
::itk::EventObject *MakeObject() const override { return new Self(m_TimeStep); }
TimeStepType GetTimeStep() const { return m_TimeStep; }
private:
TimeStepType m_TimeStep;
void operator=(const Self &);
};
template <typename T>
void ConnectTimeEvent(T* receiver)
{
typedef typename itk::ReceptorMemberCommand<T>::Pointer ReceptorMemberCommandPointer;
ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand<T>::New();
eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometryTime);
unsigned long tag = AddObserver(TimeEvent(0), eventReceptorCommand);
m_ReceiverToObserverTagsMap[static_cast<void *>(receiver)].push_back(tag);
}
// 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);
}
/**
* \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 controller.
* If the time geometry is not yet set, this function will always return 0.0.
*/
TimePointType GetSelectedTimePoint() const;
protected:
TimeNavigationController();
~TimeNavigationController() override;
TimeGeometry::ConstPointer m_InputWorldTimeGeometry;
bool m_BlockUpdate;
typedef std::map<void*, std::list<unsigned long>> ObserverTagsMapType;
ObserverTagsMapType m_ReceiverToObserverTagsMap;
};
} // namespace mitk
#endif
diff --git a/Modules/Core/include/mitkVideoRecorder.h b/Modules/Core/include/mitkVideoRecorder.h
index ee93447afb..2d7f4a4a80 100644
--- a/Modules/Core/include/mitkVideoRecorder.h
+++ b/Modules/Core/include/mitkVideoRecorder.h
@@ -1,86 +1,86 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkVideoRecorder_h
#define mitkVideoRecorder_h
-#include <filesystem>
+#include <mitkFileSystem.h>
#include <memory>
#include <string>
#include <MitkCoreExports.h>
namespace mitk
{
/** \brief Record the contents of a render window as video using FFmpeg as external command-line application.
*
* Before recording, set the render window, the path to FFmpeg, the path to the video output file,
* its format/codec, and frame rate.
*
* Most settings have decent defaults, e.g., the royalty-free and open VP9 video codec in a WebM container as
* output format and a frame rate of 30 frames per second.
*
* If not set explicitly, the FFmpeg path and output format are queried from the preferences, if available.
*
* Call StartRecording() to begin a recording session, record each frame with RecordFrame(), and end the recording
* session with a call to StopRecording(). StopRecording() is a blocking call that may take a long time to return
* since it calls FFmpeg to encode the recorded frames into a video. Consider calling it from a separate thread.
*
* The VideoRecorder throws an Exception on any error. It is advised to use it within a try/catch block.
*/
class MITKCORE_EXPORT VideoRecorder
{
public:
enum class OutputFormat
{
WebM_VP9,
MP4_H264
};
/** \brief Get the file extension corresponding to the specified video output format.
*
* \return A file extension string like ".webm" or ".mp4".
*/
static std::string GetFileExtension(OutputFormat format);
VideoRecorder();
~VideoRecorder();
VideoRecorder(const VideoRecorder&) = delete;
VideoRecorder& operator=(const VideoRecorder&) = delete;
- std::filesystem::path GetFFmpegPath() const;
- void SetFFmpegPath(const std::filesystem::path& path);
+ fs::path GetFFmpegPath() const;
+ void SetFFmpegPath(const fs::path& path);
- std::filesystem::path GetOutputPath() const;
- void SetOutputPath(const std::filesystem::path& path);
+ fs::path GetOutputPath() const;
+ void SetOutputPath(const fs::path& path);
OutputFormat GetOutputFormat() const;
void SetOutputFormat(OutputFormat format);
std::string GetRenderWindowName() const;
void SetRenderWindowName(const std::string& renderWindowName);
int GetFrameRate() const;
void SetFrameRate(unsigned int fps);
void StartRecording();
void RecordFrame() const;
int StopRecording();
private:
class Impl;
std::unique_ptr<Impl> m_Impl;
};
}
#endif
diff --git a/Modules/Core/src/DataManagement/mitkAffineTransform3D.cpp b/Modules/Core/src/DataManagement/mitkAffineTransform3D.cpp
new file mode 100644
index 0000000000..24677b60d7
--- /dev/null
+++ b/Modules/Core/src/DataManagement/mitkAffineTransform3D.cpp
@@ -0,0 +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 <mitkAffineTransform3D.h>
+
+void mitk::ToJSON(nlohmann::json& j, AffineTransform3D::ConstPointer transform)
+{
+ const auto& matrix = transform->GetMatrix().GetVnlMatrix();
+
+ for (int row = 0; row < 3; ++row)
+ {
+ for (int column = 0; column < 3; ++column)
+ {
+ j.push_back(matrix[row][column]);
+ }
+
+ j.push_back(0);
+ }
+
+ const auto& offset = transform->GetOffset();
+
+ for (int column = 0; column < 3; ++column)
+ j.push_back(offset[column]);
+
+ j.push_back(1);
+}
+
+void mitk::FromJSON(const nlohmann::json& j, AffineTransform3D::Pointer transform)
+{
+ size_t i = 0;
+ itk::Matrix<ScalarType, 3, 3> matrix;
+
+ for (int row = 0; row < 3; ++row)
+ {
+ for (int column = 0; column < 3; ++column)
+ j.at(i++).get_to(matrix[row][column]);
+
+ ++i;
+ }
+
+ transform->SetMatrix(matrix);
+
+ itk::Vector<ScalarType, 3> offset;
+
+ for (int column = 0; column < 3; ++column)
+ j.at(i++).get_to(offset[column]);
+
+ transform->SetOffset(offset);
+}
diff --git a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp
index be8a030d6a..c269481332 100644
--- a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp
+++ b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp
@@ -1,548 +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 "mitkLevelWindow.h"
#include "mitkImage.h"
#include "mitkImageSliceSelector.h"
#include "mitkImageStatisticsHolder.h"
#include <algorithm>
#include <nlohmann/json.hpp>
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;
upperBound = std::clamp(upperBound, -1e300, 1e300);
lowerBound = std::clamp(lowerBound, -1e300, 1e300);
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
extremes 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::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::IOPixelEnum::SCALAR &&
image->GetPixelType().GetComponentType() == itk::IOComponentEnum::INT && image->GetPixelType().GetBpe() >= 8)
{
// the windows compiler complains about ambiguous '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
+ // now we can assume more than three distinct 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;
}
}
namespace mitk
{
void to_json(nlohmann::json& j, const LevelWindow& lw)
{
j = nlohmann::json{
{"Fixed", lw.IsFixed()},
{"IsFloatingImage", lw.IsFloatingValues()},
{"CurrentSettings", {
{"Level", lw.GetLevel()},
{"Window", lw.GetWindow()}}},
{"DefaultSettings", {
{"Level", lw.GetDefaultLevel()},
{"Window", lw.GetDefaultWindow()}}},
{"CurrentRange", {
{"Min", lw.GetRangeMin()},
{"Max", lw.GetRangeMax()}}}};
}
void from_json(const nlohmann::json& j, LevelWindow& lw)
{
lw.SetRangeMinMax(
j["CurrentRange"]["Min"].get<ScalarType>(),
j["CurrentRange"]["Max"].get<ScalarType>());
lw.SetDefaultLevelWindow(
j["DefaultSettings"]["Level"].get<ScalarType>(),
j["DefaultSettings"]["Window"].get<ScalarType>());
lw.SetLevelWindow(
j["CurrentSettings"]["Level"].get<ScalarType>(),
j["CurrentSettings"]["Window"].get<ScalarType>());
lw.SetFixed(j["Fixed"].get<bool>());
lw.SetFloatingValues(j["IsFloatingImage"].get<bool>());
}
}
diff --git a/Modules/Core/src/IO/mitkIPreferencesStorage.cpp b/Modules/Core/src/IO/mitkIPreferencesStorage.cpp
index 9d9141ee6d..4fb4b5586f 100644
--- a/Modules/Core/src/IO/mitkIPreferencesStorage.cpp
+++ b/Modules/Core/src/IO/mitkIPreferencesStorage.cpp
@@ -1,37 +1,37 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkIPreferencesStorage.h>
-mitk::IPreferencesStorage::IPreferencesStorage(const std::filesystem::path& filename)
+mitk::IPreferencesStorage::IPreferencesStorage(const fs::path& filename)
: m_Filename(filename)
{
}
mitk::IPreferencesStorage::~IPreferencesStorage()
{
}
mitk::IPreferences* mitk::IPreferencesStorage::GetRoot()
{
return m_Root.get();
}
const mitk::IPreferences* mitk::IPreferencesStorage::GetRoot() const
{
return m_Root.get();
}
-std::filesystem::path mitk::IPreferencesStorage::GetFilename() const
+fs::path mitk::IPreferencesStorage::GetFilename() const
{
return m_Filename;
}
diff --git a/Modules/Core/src/IO/mitkPreferencesService.cpp b/Modules/Core/src/IO/mitkPreferencesService.cpp
index 731ed4da39..5957e54161 100644
--- a/Modules/Core/src/IO/mitkPreferencesService.cpp
+++ b/Modules/Core/src/IO/mitkPreferencesService.cpp
@@ -1,49 +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 "mitkPreferencesService.h"
#include "mitkXMLPreferencesStorage.h"
#include <mitkExceptionMacro.h>
mitk::PreferencesService::PreferencesService()
{
}
mitk::PreferencesService::~PreferencesService()
{
if (m_Storage)
m_Storage->Flush();
}
-void mitk::PreferencesService::InitializeStorage(const std::filesystem::path& filename)
+void mitk::PreferencesService::InitializeStorage(const fs::path& filename)
{
if (m_Storage)
mitkThrow() << "The preferences service must be initialized only once to guarantee valid preferences pointers during its lifetime.";
m_Storage = std::make_unique<XMLPreferencesStorage>(filename);
}
void mitk::PreferencesService::UninitializeStorage(bool removeFile)
{
if (m_Storage && removeFile)
- std::filesystem::remove(m_Storage->GetFilename());
+ fs::remove(m_Storage->GetFilename());
m_Storage.reset(nullptr);
}
mitk::IPreferences* mitk::PreferencesService::GetSystemPreferences()
{
return m_Storage
? m_Storage->GetRoot()
: nullptr;
}
diff --git a/Modules/Core/src/IO/mitkPreferencesService.h b/Modules/Core/src/IO/mitkPreferencesService.h
index 43bf80dc9c..71a0e87506 100644
--- a/Modules/Core/src/IO/mitkPreferencesService.h
+++ b/Modules/Core/src/IO/mitkPreferencesService.h
@@ -1,46 +1,46 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkPreferencesService_h
#define mitkPreferencesService_h
#include <mitkIPreferencesService.h>
#include <memory>
namespace mitk
{
class IPreferencesStorage;
/**
* \brief Implementation of the IPreferencesService interface.
*
* Only used through the IPreferencesService interface.
*
* \sa IPreferencesService
*/
class PreferencesService : public IPreferencesService
{
public:
PreferencesService();
~PreferencesService() override;
- void InitializeStorage(const std::filesystem::path& filename) override;
+ void InitializeStorage(const fs::path& filename) override;
void UninitializeStorage(bool removeFile) override;
IPreferences* GetSystemPreferences() override;
private:
std::unique_ptr<IPreferencesStorage> m_Storage;
};
}
#endif
diff --git a/Modules/Core/src/IO/mitkXMLPreferencesStorage.cpp b/Modules/Core/src/IO/mitkXMLPreferencesStorage.cpp
index 3e56b2de4a..1dbfc2f5bd 100644
--- a/Modules/Core/src/IO/mitkXMLPreferencesStorage.cpp
+++ b/Modules/Core/src/IO/mitkXMLPreferencesStorage.cpp
@@ -1,125 +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.
============================================================================*/
#include "mitkXMLPreferencesStorage.h"
#include "mitkPreferences.h"
#include <mitkLog.h>
#include <tinyxml2.h>
namespace
{
std::string GetStringAttribute(const tinyxml2::XMLElement* xmlElement, const char* name)
{
const auto* attribute = xmlElement->Attribute(name);
return attribute != nullptr
? attribute
: "";
}
mitk::Preferences* Deserialize(const tinyxml2::XMLElement* xmlPreferenceElement, mitk::Preferences* parentPreferences, mitk::IPreferencesStorage* storage)
{
const std::string name = GetStringAttribute(xmlPreferenceElement, "name");
const auto* xmlPropertyElement = xmlPreferenceElement->FirstChildElement("property");
mitk::Preferences::Properties properties;
while (xmlPropertyElement != nullptr)
{
properties[GetStringAttribute(xmlPropertyElement, "name")] = GetStringAttribute(xmlPropertyElement, "value");
xmlPropertyElement = xmlPropertyElement->NextSiblingElement("property");
}
auto* preferences = new mitk::Preferences(properties, name, parentPreferences, storage);
const auto* xmlPreferenceChildElement = xmlPreferenceElement->FirstChildElement("preferences");
while (xmlPreferenceChildElement != nullptr)
{
Deserialize(xmlPreferenceChildElement, preferences, storage);
xmlPreferenceChildElement = xmlPreferenceChildElement->NextSiblingElement("preferences");
}
return preferences;
}
void Serialize(const mitk::Preferences* preferences, tinyxml2::XMLNode* xmlParentNode)
{
auto* xmlDocument = xmlParentNode->GetDocument();
auto* xmlPreferenceElement = xmlDocument->NewElement("preferences");
xmlPreferenceElement->SetAttribute("name", preferences->Name().c_str());
for (const auto& [name, value] : preferences->GetProperties())
{
auto* xmlPropertyElement = xmlDocument->NewElement("property");
xmlPropertyElement->SetAttribute("name", name.c_str());
xmlPropertyElement->SetAttribute("value", value.c_str());
xmlPreferenceElement->InsertEndChild(xmlPropertyElement);
}
xmlParentNode->InsertEndChild(xmlPreferenceElement);
for (const auto& child : preferences->GetChildren())
Serialize(child.get(), xmlPreferenceElement);
}
}
-mitk::XMLPreferencesStorage::XMLPreferencesStorage(const std::filesystem::path& filename)
+mitk::XMLPreferencesStorage::XMLPreferencesStorage(const fs::path& filename)
: IPreferencesStorage(filename)
{
- if (std::filesystem::exists(filename))
+ if (fs::exists(filename))
{
tinyxml2::XMLDocument xmlDocument;
if (xmlDocument.LoadFile(filename.string().c_str()) == tinyxml2::XML_SUCCESS)
{
m_Root.reset(Deserialize(xmlDocument.RootElement(), nullptr, this));
return;
}
else
{
MITK_ERROR << "Could not load preferences from \"" << filename.string() << "\"!";
}
}
else
{
try
{
- std::filesystem::create_directories(filename.parent_path());
+ fs::create_directories(filename.parent_path());
}
catch (const std::exception& e)
{
MITK_ERROR << "Could not create directories for \"" << filename.string() << "\": " << e.what();
}
}
m_Root = std::make_unique<Preferences>(Preferences::Properties(), "", nullptr, this);
}
mitk::XMLPreferencesStorage::~XMLPreferencesStorage()
{
}
void mitk::XMLPreferencesStorage::Flush()
{
tinyxml2::XMLDocument xmlDocument;
xmlDocument.InsertEndChild(xmlDocument.NewDeclaration());
Serialize(static_cast<const mitk::Preferences*>(m_Root.get()), &xmlDocument);
if (xmlDocument.SaveFile(m_Filename.string().c_str()) != tinyxml2::XML_SUCCESS)
MITK_ERROR << "Could not save preferences to \"" << m_Filename.string() << "\"!";
}
diff --git a/Modules/Core/src/IO/mitkXMLPreferencesStorage.h b/Modules/Core/src/IO/mitkXMLPreferencesStorage.h
index b982ee62a1..eb57d5720e 100644
--- a/Modules/Core/src/IO/mitkXMLPreferencesStorage.h
+++ b/Modules/Core/src/IO/mitkXMLPreferencesStorage.h
@@ -1,33 +1,33 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkXMLPreferencesStorage_h
#define mitkXMLPreferencesStorage_h
#include <mitkIPreferencesStorage.h>
namespace mitk
{
/**
* \brief See IPreferencesStorage.
*/
class XMLPreferencesStorage : public IPreferencesStorage
{
public:
- explicit XMLPreferencesStorage(const std::filesystem::path& filename);
+ explicit XMLPreferencesStorage(const fs::path& filename);
~XMLPreferencesStorage() override;
void Flush() override;
};
}
#endif
diff --git a/Modules/Core/src/Interactions/mitkStateMachineState.cpp b/Modules/Core/src/Interactions/mitkStateMachineState.cpp
index a3aa4ddfdb..81b60052a3 100755
--- a/Modules/Core/src/Interactions/mitkStateMachineState.cpp
+++ b/Modules/Core/src/Interactions/mitkStateMachineState.cpp
@@ -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.
============================================================================*/
#include "mitkStateMachineState.h"
mitk::StateMachineState::StateMachineState(const std::string &stateName, const std::string &stateMode)
: m_Name(stateName), m_StateMode(stateMode)
{
}
std::string mitk::StateMachineState::GetMode() const
{
return m_StateMode;
}
mitk::StateMachineState::~StateMachineState()
{
m_Transitions.clear();
}
bool mitk::StateMachineState::AddTransition(StateMachineTransition::Pointer transition)
{
for (auto it = m_Transitions.begin(); it != m_Transitions.end(); ++it)
{
if (transition.GetPointer() == (*it).GetPointer())
return false;
}
m_Transitions.push_back(transition);
return true;
}
mitk::StateMachineTransition::Pointer mitk::StateMachineState::GetTransition(const std::string &eventClass,
const std::string &eventVariant)
{
TransitionVector transitions = this->GetTransitionList(eventClass, eventVariant);
if (transitions.size() > 1)
{
MITK_WARN << "Multiple transitions have been found for event. Use non-deprecated method "
"StateMachineState::GetTransitionList() instead!";
}
if (transitions.empty())
{
return nullptr;
}
else
{
return transitions.at(0);
}
}
mitk::StateMachineState::TransitionVector mitk::StateMachineState::GetTransitionList(const std::string &eventClass,
const std::string &eventVariant)
{
TransitionVector transitions;
mitk::StateMachineTransition::Pointer t = mitk::StateMachineTransition::New("", eventClass, eventVariant);
for (auto it = m_Transitions.begin(); it != m_Transitions.end(); ++it)
{
if (**it == *t) // do not switch it and t, order matters, see mitk::StateMachineTransition == operator
transitions.push_back(*it);
}
return transitions;
}
std::string mitk::StateMachineState::GetName() const
{
return m_Name;
}
//##Documentation
-//## Post-processing step, when builing StateMachine from XML.
+//## Post-processing step, when building StateMachine from XML.
//## Parse all transitions and find the State that matches the String-Name.
bool mitk::StateMachineState::ConnectTransitions(StateMap *allStates)
{
for (auto transIt = m_Transitions.begin(); transIt != m_Transitions.end(); ++transIt)
{
bool found = false;
for (auto stateIt = allStates->begin(); stateIt != allStates->end(); ++stateIt)
{
if ((*stateIt)->GetName() == (*transIt)->GetNextStateName())
{
(*transIt)->SetNextState(*stateIt);
found = true;
break;
}
}
if (!found)
{
MITK_WARN << "Target State not found in StateMachine.";
return false; // only reached if no state matching the string is found
}
}
return true;
}
diff --git a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp
index 3d6da2125f..e565ad34ab 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 sagittal) 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 conversion
// 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 before. 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
+ // Bounds information for reslicing (only required 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
auto componentType = image->GetPixelType().GetComponentType();
switch (componentType)
{
case itk::IOComponentEnum::UCHAR:
// generate contours/outlines
localStorage->m_OutlinePolyData = CreateOutlinePolyData<unsigned char>(renderer);
break;
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 iteration 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::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::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 sagittal
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 sagittal)
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/Rendering/mitkPointSetVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp
index 55d87b3364..9f8a5db819 100644
--- a/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp
+++ b/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp
@@ -1,781 +1,781 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkPointSetVtkMapper2D.h"
// mitk includes
#include "mitkVtkPropRenderer.h"
#include <mitkDataNode.h>
#include <mitkPlaneGeometry.h>
#include <mitkPointSet.h>
#include <mitkProperties.h>
// vtk includes
#include <vtkActor.h>
#include <vtkCellArray.h>
#include <vtkFloatArray.h>
#include <vtkGlyph3D.h>
#include <vtkGlyphSource2D.h>
#include <vtkLine.h>
#include <vtkPointData.h>
#include <vtkPolyDataMapper.h>
#include <vtkPropAssembly.h>
#include <vtkTextActor.h>
#include <vtkTextProperty.h>
#include <vtkTransform.h>
#include <vtkTransformFilter.h>
#include <cstdlib>
namespace
{
double GetScreenResolution(const mitk::BaseRenderer* renderer)
{
if (nullptr == renderer)
return 1.0;
mitk::Point2D pD1, pD2;
pD1[0] = 0.0;
pD1[1] = 0.0;
pD2[0] = 0.0;
pD2[1] = 1.0;
// Calculate world coordinates of in-plane screen pixels (0, 0) and (0, 1).
mitk::Point3D pW1, pW2;
renderer->DisplayToWorld(pD1, pW1);
renderer->DisplayToWorld(pD2, pW2);
// For 2D renderers, the distance between these points is the screen resolution.
return pW1.EuclideanDistanceTo(pW2);
}
}
// constructor LocalStorage
mitk::PointSetVtkMapper2D::LocalStorage::LocalStorage()
{
// points
m_UnselectedPoints = vtkSmartPointer<vtkPoints>::New();
m_SelectedPoints = vtkSmartPointer<vtkPoints>::New();
m_ContourPoints = vtkSmartPointer<vtkPoints>::New();
// scales
m_UnselectedScales = vtkSmartPointer<vtkFloatArray>::New();
m_SelectedScales = vtkSmartPointer<vtkFloatArray>::New();
// distances
m_DistancesBetweenPoints = vtkSmartPointer<vtkFloatArray>::New();
// lines
m_ContourLines = vtkSmartPointer<vtkCellArray>::New();
// glyph source (provides the different shapes)
m_UnselectedGlyphSource2D = vtkSmartPointer<vtkGlyphSource2D>::New();
m_SelectedGlyphSource2D = vtkSmartPointer<vtkGlyphSource2D>::New();
// glyphs
m_UnselectedGlyph3D = vtkSmartPointer<vtkGlyph3D>::New();
m_SelectedGlyph3D = vtkSmartPointer<vtkGlyph3D>::New();
// polydata
m_VtkUnselectedPointListPolyData = vtkSmartPointer<vtkPolyData>::New();
m_VtkSelectedPointListPolyData = vtkSmartPointer<vtkPolyData>::New();
m_VtkContourPolyData = vtkSmartPointer<vtkPolyData>::New();
// actors
m_UnselectedActor = vtkSmartPointer<vtkActor>::New();
m_SelectedActor = vtkSmartPointer<vtkActor>::New();
m_ContourActor = vtkSmartPointer<vtkActor>::New();
// mappers
m_VtkUnselectedPolyDataMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_VtkSelectedPolyDataMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_VtkContourPolyDataMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
// propassembly
m_PropAssembly = vtkSmartPointer<vtkPropAssembly>::New();
}
// destructor LocalStorage
mitk::PointSetVtkMapper2D::LocalStorage::~LocalStorage()
{
}
// input for this mapper ( = point set)
const mitk::PointSet *mitk::PointSetVtkMapper2D::GetInput() const
{
return static_cast<const mitk::PointSet *>(GetDataNode()->GetData());
}
// constructor PointSetVtkMapper2D
mitk::PointSetVtkMapper2D::PointSetVtkMapper2D()
: m_ShowContour(false),
m_CloseContour(false),
m_ShowPoints(true),
m_ShowDistances(false),
m_DistancesDecimalDigits(1),
m_ShowAngles(false),
m_ShowDistantLines(false),
m_LineWidth(1),
m_PointLineWidth(1),
m_Point2DSize(6),
m_IDShapeProperty(mitk::PointSetShapeProperty::CROSS),
m_FillShape(false),
m_DistanceToPlane(4.0f),
m_FixedSizeOnScreen(false)
{
}
// destructor
mitk::PointSetVtkMapper2D::~PointSetVtkMapper2D()
{
}
// reset mapper so that nothing is displayed e.g. toggle visibility of the propassembly
void mitk::PointSetVtkMapper2D::ResetMapper(BaseRenderer *renderer)
{
LocalStorage *ls = m_LSH.GetLocalStorage(renderer);
ls->m_PropAssembly->VisibilityOff();
}
// returns propassembly
vtkProp *mitk::PointSetVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer)
{
LocalStorage *ls = m_LSH.GetLocalStorage(renderer);
return ls->m_PropAssembly;
}
static bool makePerpendicularVector2D(const mitk::Vector2D &in, mitk::Vector2D &out)
{
// The dot product of orthogonal vectors is zero.
// In two dimensions the slopes of perpendicular lines are negative reciprocals.
if ((fabs(in[0]) > 0) && ((fabs(in[0]) > fabs(in[1])) || (in[1] == 0)))
{
// negative reciprocal
out[0] = -in[1] / in[0];
out[1] = 1;
out.Normalize();
return true;
}
else if (fabs(in[1]) > 0)
{
out[0] = 1;
// negative reciprocal
out[1] = -in[0] / in[1];
out.Normalize();
return true;
}
else
return false;
}
void mitk::PointSetVtkMapper2D::CreateVTKRenderObjects(mitk::BaseRenderer *renderer)
{
LocalStorage *ls = m_LSH.GetLocalStorage(renderer);
unsigned i = 0;
// The vtk text actors need to be removed manually from the propassembly
// since the same vtk text actors are not overwritten within this function,
// but new actors are added to the propassembly each time this function is executed.
// Thus, the actors from the last call must be removed in the beginning.
for (i = 0; i < ls->m_VtkTextLabelActors.size(); i++)
{
if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextLabelActors.at(i)))
ls->m_PropAssembly->RemovePart(ls->m_VtkTextLabelActors.at(i));
}
for (i = 0; i < ls->m_VtkTextDistanceActors.size(); i++)
{
if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextDistanceActors.at(i)))
ls->m_PropAssembly->RemovePart(ls->m_VtkTextDistanceActors.at(i));
}
for (i = 0; i < ls->m_VtkTextAngleActors.size(); i++)
{
if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextAngleActors.at(i)))
ls->m_PropAssembly->RemovePart(ls->m_VtkTextAngleActors.at(i));
}
// initialize polydata here, otherwise we have update problems when
// executing this function again
ls->m_VtkUnselectedPointListPolyData = vtkSmartPointer<vtkPolyData>::New();
ls->m_VtkSelectedPointListPolyData = vtkSmartPointer<vtkPolyData>::New();
ls->m_VtkContourPolyData = vtkSmartPointer<vtkPolyData>::New();
// get input point set and update the PointSet
mitk::PointSet::Pointer input = const_cast<mitk::PointSet *>(this->GetInput());
// only update the input data, if the property tells us to
bool update = true;
this->GetDataNode()->GetBoolProperty("updateDataOnRender", update);
if (update == true)
input->Update();
int timestep = this->GetTimestep();
mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet(timestep);
if (itkPointSet.GetPointer() == nullptr)
{
ls->m_PropAssembly->VisibilityOff();
return;
}
// iterator for point set
mitk::PointSet::PointsContainer::Iterator pointsIter = itkPointSet->GetPoints()->Begin();
// PointDataContainer has additional information to each point, e.g. whether
// it is selected or not
mitk::PointSet::PointDataContainer::Iterator pointDataIter;
pointDataIter = itkPointSet->GetPointData()->Begin();
// check if the list for the PointDataContainer is the same size as the PointsContainer.
// If not, then the points were inserted manually and can not be visualized according to the PointData
// (selected/unselected)
bool pointDataBroken = (itkPointSet->GetPointData()->Size() != itkPointSet->GetPoints()->Size());
if (itkPointSet->GetPointData()->size() == 0 || pointDataBroken)
{
ls->m_PropAssembly->VisibilityOff();
return;
}
ls->m_PropAssembly->VisibilityOn();
// empty point sets, cellarrays, scalars
ls->m_UnselectedPoints->Reset();
ls->m_SelectedPoints->Reset();
ls->m_ContourPoints->Reset();
ls->m_ContourLines->Reset();
ls->m_UnselectedScales->Reset();
ls->m_SelectedScales->Reset();
ls->m_DistancesBetweenPoints->Reset();
ls->m_VtkTextLabelActors.clear();
ls->m_VtkTextDistanceActors.clear();
ls->m_VtkTextAngleActors.clear();
ls->m_UnselectedScales->SetNumberOfComponents(3);
ls->m_SelectedScales->SetNumberOfComponents(3);
int NumberContourPoints = 0;
bool pointsOnSameSideOfPlane = false;
const int text2dDistance = 10;
// initialize points with a random start value
// current point in point set
itk::Point<ScalarType> point = pointsIter->Value();
mitk::Point3D p = point; // currently visited point
mitk::Point3D lastP = point; // last visited point (predecessor in point set of "point")
mitk::Vector3D vec; // p - lastP
mitk::Vector3D lastVec; // lastP - point before lastP
vec.Fill(0.0);
lastVec.Fill(0.0);
mitk::Point2D pt2d;
pt2d[0] = point[0]; // projected_p in display coordinates
pt2d[1] = point[1];
mitk::Point2D lastPt2d = pt2d; // last projected_p in display coordinates (predecessor in point set of "pt2d")
mitk::Point2D preLastPt2d = pt2d; // projected_p in display coordinates before lastPt2
const mitk::PlaneGeometry *geo2D = renderer->GetCurrentWorldPlaneGeometry();
double resolution = GetScreenResolution(renderer);
vtkLinearTransform *dataNodeTransform = input->GetGeometry()->GetVtkTransform();
int count = 0;
for (pointsIter = itkPointSet->GetPoints()->Begin(); pointsIter != itkPointSet->GetPoints()->End(); pointsIter++)
{
lastP = p; // valid for number of points count > 0
preLastPt2d = lastPt2d; // valid only for count > 1
lastPt2d = pt2d; // valid for number of points count > 0
lastVec = vec; // valid only for counter > 1
// get current point in point set
point = pointsIter->Value();
// transform point
{
float vtkp[3];
itk2vtk(point, vtkp);
dataNodeTransform->TransformPoint(vtkp, vtkp);
vtk2itk(vtkp, point);
}
p[0] = point[0];
p[1] = point[1];
p[2] = point[2];
renderer->WorldToDisplay(p, pt2d);
vec = p - lastP; // valid only for counter > 0
// compute distance to current plane
float dist = geo2D->Distance(point);
// measure distance in screen pixel units if requested
if (m_FixedSizeOnScreen)
{
dist /= resolution;
}
// draw markers on slices a certain distance away from the points
// location according to the tolerance threshold (m_DistanceToPlane)
if (dist < m_DistanceToPlane)
{
// is point selected or not?
if (pointDataIter->Value().selected)
{
ls->m_SelectedPoints->InsertNextPoint(point[0], point[1], point[2]);
// point is scaled according to its distance to the plane
ls->m_SelectedScales->InsertNextTuple3(
std::max(0.0f, m_Point2DSize - (2 * dist)), 0, 0);
}
else
{
ls->m_UnselectedPoints->InsertNextPoint(point[0], point[1], point[2]);
// point is scaled according to its distance to the plane
ls->m_UnselectedScales->InsertNextTuple3(
std::max(0.0f, m_Point2DSize - (2 * dist)), 0, 0);
}
//---- LABEL -----//
// paint label for each point if available
if (dynamic_cast<mitk::StringProperty *>(this->GetDataNode()->GetProperty("label")) != nullptr)
{
const char *pointLabel =
dynamic_cast<mitk::StringProperty *>(this->GetDataNode()->GetProperty("label"))->GetValue();
std::string l = pointLabel;
if (input->GetSize() > 1)
{
std::stringstream ss;
ss << pointsIter->Index();
l.append(ss.str());
}
ls->m_VtkTextActor = vtkSmartPointer<vtkTextActor>::New();
ls->m_VtkTextActor->SetDisplayPosition(pt2d[0] + text2dDistance, pt2d[1] + text2dDistance);
ls->m_VtkTextActor->SetInput(l.c_str());
ls->m_VtkTextActor->GetTextProperty()->SetOpacity(100);
float unselectedColor[4] = {1.0, 1.0, 0.0, 1.0};
// check if there is a color property
GetDataNode()->GetColor(unselectedColor);
ls->m_VtkTextActor->GetTextProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]);
ls->m_VtkTextLabelActors.push_back(ls->m_VtkTextActor);
}
}
// draw contour, distance text and angle text in render window
// lines between points, which intersect the current plane, are drawn
if (m_ShowContour && count > 0)
{
ScalarType distance = renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(point);
ScalarType lastDistance = renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(lastP);
pointsOnSameSideOfPlane = (distance * lastDistance) > 0.5;
// Points must be on different side of plane in order to draw a contour.
// If "show distant lines" is enabled this condition is disregarded.
if (!pointsOnSameSideOfPlane || m_ShowDistantLines)
{
vtkSmartPointer<vtkLine> line = vtkSmartPointer<vtkLine>::New();
ls->m_ContourPoints->InsertNextPoint(lastP[0], lastP[1], lastP[2]);
line->GetPointIds()->SetId(0, NumberContourPoints);
NumberContourPoints++;
ls->m_ContourPoints->InsertNextPoint(point[0], point[1], point[2]);
line->GetPointIds()->SetId(1, NumberContourPoints);
NumberContourPoints++;
ls->m_ContourLines->InsertNextCell(line);
if (m_ShowDistances) // calculate and print distance between adjacent points
{
float distancePoints = point.EuclideanDistanceTo(lastP);
std::stringstream buffer;
buffer << std::fixed << std::setprecision(m_DistancesDecimalDigits) << distancePoints << " mm";
// compute desired display position of text
Vector2D vec2d = pt2d - lastPt2d;
makePerpendicularVector2D(vec2d,
vec2d); // text is rendered within text2dDistance perpendicular to current line
Vector2D pos2d = (lastPt2d.GetVectorFromOrigin() + pt2d.GetVectorFromOrigin()) * 0.5 + vec2d * text2dDistance;
ls->m_VtkTextActor = vtkSmartPointer<vtkTextActor>::New();
ls->m_VtkTextActor->SetDisplayPosition(pos2d[0], pos2d[1]);
ls->m_VtkTextActor->SetInput(buffer.str().c_str());
ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0);
ls->m_VtkTextDistanceActors.push_back(ls->m_VtkTextActor);
}
if (m_ShowAngles && count > 1) // calculate and print angle between connected lines
{
std::stringstream buffer;
buffer << angle(vec.GetVnlVector(), -lastVec.GetVnlVector()) * 180 / vnl_math::pi << "°";
// compute desired display position of text
Vector2D vec2d = pt2d - lastPt2d; // first arm enclosing the angle
vec2d.Normalize();
Vector2D lastVec2d = lastPt2d - preLastPt2d; // second arm enclosing the angle
lastVec2d.Normalize();
vec2d = vec2d - lastVec2d; // vector connecting both arms
vec2d.Normalize();
// middle between two vectors that enclose the angle
Vector2D pos2d = lastPt2d.GetVectorFromOrigin() + vec2d * text2dDistance * text2dDistance;
ls->m_VtkTextActor = vtkSmartPointer<vtkTextActor>::New();
ls->m_VtkTextActor->SetDisplayPosition(pos2d[0], pos2d[1]);
ls->m_VtkTextActor->SetInput(buffer.str().c_str());
ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0);
ls->m_VtkTextAngleActors.push_back(ls->m_VtkTextActor);
}
}
}
if (pointDataIter != itkPointSet->GetPointData()->End())
{
pointDataIter++;
count++;
}
}
// add each single text actor to the assembly
for (i = 0; i < ls->m_VtkTextLabelActors.size(); i++)
{
ls->m_PropAssembly->AddPart(ls->m_VtkTextLabelActors.at(i));
}
for (i = 0; i < ls->m_VtkTextDistanceActors.size(); i++)
{
ls->m_PropAssembly->AddPart(ls->m_VtkTextDistanceActors.at(i));
}
for (i = 0; i < ls->m_VtkTextAngleActors.size(); i++)
{
ls->m_PropAssembly->AddPart(ls->m_VtkTextAngleActors.at(i));
}
//---- CONTOUR -----//
// create lines between the points which intersect the plane
if (m_ShowContour)
{
// draw line between first and last point which is rendered
if (m_CloseContour && NumberContourPoints > 1)
{
vtkSmartPointer<vtkLine> closingLine = vtkSmartPointer<vtkLine>::New();
closingLine->GetPointIds()->SetId(0, 0); // index of first point
closingLine->GetPointIds()->SetId(1, NumberContourPoints - 1); // index of last point
ls->m_ContourLines->InsertNextCell(closingLine);
}
ls->m_VtkContourPolyData->SetPoints(ls->m_ContourPoints);
ls->m_VtkContourPolyData->SetLines(ls->m_ContourLines);
ls->m_VtkContourPolyDataMapper->SetInputData(ls->m_VtkContourPolyData);
ls->m_ContourActor->SetMapper(ls->m_VtkContourPolyDataMapper);
ls->m_ContourActor->GetProperty()->SetLineWidth(m_LineWidth);
ls->m_PropAssembly->AddPart(ls->m_ContourActor);
}
// the point set must be transformed in order to obtain the appropriate glyph orientation
// according to the current view
vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
vtkSmartPointer<vtkMatrix4x4> a, b = vtkSmartPointer<vtkMatrix4x4>::New();
a = geo2D->GetVtkTransform()->GetMatrix();
b->DeepCopy(a);
// delete transformation from matrix, only take orientation
b->SetElement(3, 3, 1);
b->SetElement(2, 3, 0);
b->SetElement(1, 3, 0);
b->SetElement(0, 3, 0);
b->SetElement(3, 2, 0);
b->SetElement(3, 1, 0);
b->SetElement(3, 0, 0);
Vector3D spacing = geo2D->GetSpacing();
- // If you find a way to simplyfy the following, feel free to change!
+ // If you find a way to simplify the following, feel free to change!
b->SetElement(0, 0, b->GetElement(0, 0) / spacing[0]);
b->SetElement(1, 0, b->GetElement(1, 0) / spacing[0]);
b->SetElement(2, 0, b->GetElement(2, 0) / spacing[0]);
b->SetElement(1, 1, b->GetElement(1, 1) / spacing[1]);
b->SetElement(2, 1, b->GetElement(2, 1) / spacing[1]);
b->SetElement(0, 2, b->GetElement(0, 2) / spacing[2]);
b->SetElement(1, 2, b->GetElement(1, 2) / spacing[2]);
b->SetElement(2, 2, b->GetElement(2, 2) / spacing[2]);
transform->SetMatrix(b);
//---- UNSELECTED POINTS -----//
// apply properties to glyph
ls->m_UnselectedGlyphSource2D->SetGlyphType(m_IDShapeProperty);
if (m_FillShape)
ls->m_UnselectedGlyphSource2D->FilledOn();
else
ls->m_UnselectedGlyphSource2D->FilledOff();
// apply transform
vtkSmartPointer<vtkTransformFilter> transformFilterU = vtkSmartPointer<vtkTransformFilter>::New();
transformFilterU->SetInputConnection(ls->m_UnselectedGlyphSource2D->GetOutputPort());
transformFilterU->SetTransform(transform);
ls->m_VtkUnselectedPointListPolyData->SetPoints(ls->m_UnselectedPoints);
ls->m_VtkUnselectedPointListPolyData->GetPointData()->SetVectors(ls->m_UnselectedScales);
// apply transform of current plane to glyphs
ls->m_UnselectedGlyph3D->SetSourceConnection(transformFilterU->GetOutputPort());
ls->m_UnselectedGlyph3D->SetInputData(ls->m_VtkUnselectedPointListPolyData);
ls->m_UnselectedGlyph3D->SetScaleFactor(m_FixedSizeOnScreen ? resolution : 1.0);
ls->m_UnselectedGlyph3D->SetScaleModeToScaleByVector();
ls->m_UnselectedGlyph3D->SetVectorModeToUseVector();
ls->m_VtkUnselectedPolyDataMapper->SetInputConnection(ls->m_UnselectedGlyph3D->GetOutputPort());
ls->m_UnselectedActor->SetMapper(ls->m_VtkUnselectedPolyDataMapper);
ls->m_UnselectedActor->GetProperty()->SetLineWidth(m_PointLineWidth);
ls->m_PropAssembly->AddPart(ls->m_UnselectedActor);
//---- SELECTED POINTS -----//
ls->m_SelectedGlyphSource2D->SetGlyphTypeToDiamond();
ls->m_SelectedGlyphSource2D->CrossOn();
ls->m_SelectedGlyphSource2D->FilledOff();
// apply transform
vtkSmartPointer<vtkTransformFilter> transformFilterS = vtkSmartPointer<vtkTransformFilter>::New();
transformFilterS->SetInputConnection(ls->m_SelectedGlyphSource2D->GetOutputPort());
transformFilterS->SetTransform(transform);
ls->m_VtkSelectedPointListPolyData->SetPoints(ls->m_SelectedPoints);
ls->m_VtkSelectedPointListPolyData->GetPointData()->SetVectors(ls->m_SelectedScales);
// apply transform of current plane to glyphs
ls->m_SelectedGlyph3D->SetSourceConnection(transformFilterS->GetOutputPort());
ls->m_SelectedGlyph3D->SetInputData(ls->m_VtkSelectedPointListPolyData);
ls->m_SelectedGlyph3D->SetScaleFactor(m_FixedSizeOnScreen ? resolution : 1.0);
ls->m_SelectedGlyph3D->SetScaleModeToScaleByVector();
ls->m_SelectedGlyph3D->SetVectorModeToUseVector();
ls->m_VtkSelectedPolyDataMapper->SetInputConnection(ls->m_SelectedGlyph3D->GetOutputPort());
ls->m_SelectedActor->SetMapper(ls->m_VtkSelectedPolyDataMapper);
ls->m_SelectedActor->GetProperty()->SetLineWidth(m_PointLineWidth);
ls->m_PropAssembly->AddPart(ls->m_SelectedActor);
}
void mitk::PointSetVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
const mitk::DataNode *node = GetDataNode();
if (node == nullptr)
return;
LocalStorage *ls = m_LSH.GetLocalStorage(renderer);
// check whether the input data has been changed
bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode());
// toggle visibility
bool visible = true;
node->GetVisibility(visible, renderer, "visible");
if (!visible)
{
ls->m_UnselectedActor->VisibilityOff();
ls->m_SelectedActor->VisibilityOff();
ls->m_ContourActor->VisibilityOff();
ls->m_PropAssembly->VisibilityOff();
return;
}
else
{
ls->m_PropAssembly->VisibilityOn();
}
node->GetBoolProperty("show contour", m_ShowContour, renderer);
node->GetBoolProperty("close contour", m_CloseContour, renderer);
node->GetBoolProperty("show points", m_ShowPoints, renderer);
node->GetBoolProperty("show distances", m_ShowDistances, renderer);
node->GetIntProperty("distance decimal digits", m_DistancesDecimalDigits, renderer);
node->GetBoolProperty("show angles", m_ShowAngles, renderer);
node->GetBoolProperty("show distant lines", m_ShowDistantLines, renderer);
node->GetIntProperty("line width", m_LineWidth, renderer);
node->GetIntProperty("point line width", m_PointLineWidth, renderer);
if (!node->GetFloatProperty(
"point 2D size", m_Point2DSize, renderer)) // re-defined to float 2015-08-13, keep a fallback
{
int oldPointSize = m_Point2DSize;
if (node->GetIntProperty("point 2D size", oldPointSize, renderer))
{
m_Point2DSize = oldPointSize;
}
}
node->GetBoolProperty("Pointset.2D.fill shape", m_FillShape, renderer);
node->GetFloatProperty("Pointset.2D.distance to plane", m_DistanceToPlane, renderer);
node->GetBoolProperty("Pointset.2D.fixed size on screen", m_FixedSizeOnScreen, renderer);
mitk::PointSetShapeProperty::Pointer shape =
dynamic_cast<mitk::PointSetShapeProperty *>(this->GetDataNode()->GetProperty("Pointset.2D.shape", renderer));
if (shape.IsNotNull())
{
m_IDShapeProperty = shape->GetPointSetShape();
}
// check for color props and use it for rendering of selected/unselected points and contour
// due to different params in VTK (double/float) we have to convert
float opacity = 1.0;
GetDataNode()->GetOpacity(opacity, renderer);
// apply color and opacity
if (m_ShowPoints)
{
float unselectedColor[4];
double selectedColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red
ls->m_UnselectedActor->VisibilityOn();
ls->m_SelectedActor->VisibilityOn();
// check if there is a color property
GetDataNode()->GetColor(unselectedColor);
// get selected color property
if (dynamic_cast<mitk::ColorProperty *>(
this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) != nullptr)
{
mitk::Color tmpColor = dynamic_cast<mitk::ColorProperty *>(
this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor"))
->GetValue();
selectedColor[0] = tmpColor[0];
selectedColor[1] = tmpColor[1];
selectedColor[2] = tmpColor[2];
selectedColor[3] = 1.0f; // alpha value
}
else if (dynamic_cast<mitk::ColorProperty *>(
this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("selectedcolor")) != nullptr)
{
mitk::Color tmpColor =
dynamic_cast<mitk::ColorProperty *>(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("selectedcolor"))
->GetValue();
selectedColor[0] = tmpColor[0];
selectedColor[1] = tmpColor[1];
selectedColor[2] = tmpColor[2];
selectedColor[3] = 1.0f; // alpha value
}
ls->m_SelectedActor->GetProperty()->SetColor(selectedColor);
ls->m_SelectedActor->GetProperty()->SetOpacity(opacity);
ls->m_UnselectedActor->GetProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]);
ls->m_UnselectedActor->GetProperty()->SetOpacity(opacity);
}
else
{
ls->m_UnselectedActor->VisibilityOff();
ls->m_SelectedActor->VisibilityOff();
}
if (m_ShowContour)
{
double contourColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red
ls->m_ContourActor->VisibilityOn();
// get contour color property
if (dynamic_cast<mitk::ColorProperty *>(
this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) != nullptr)
{
mitk::Color tmpColor =
dynamic_cast<mitk::ColorProperty *>(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor"))
->GetValue();
contourColor[0] = tmpColor[0];
contourColor[1] = tmpColor[1];
contourColor[2] = tmpColor[2];
contourColor[3] = 1.0f;
}
else if (dynamic_cast<mitk::ColorProperty *>(
this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("contourcolor")) != nullptr)
{
mitk::Color tmpColor =
dynamic_cast<mitk::ColorProperty *>(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("contourcolor"))
->GetValue();
contourColor[0] = tmpColor[0];
contourColor[1] = tmpColor[1];
contourColor[2] = tmpColor[2];
contourColor[3] = 1.0f;
}
ls->m_ContourActor->GetProperty()->SetColor(contourColor);
ls->m_ContourActor->GetProperty()->SetOpacity(opacity);
}
else
{
ls->m_ContourActor->VisibilityOff();
}
if (needGenerateData)
{
// create new vtk render objects (e.g. a circle for a point)
this->CreateVTKRenderObjects(renderer);
}
}
void mitk::PointSetVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite)
{
node->AddProperty("line width", mitk::IntProperty::New(2), renderer, overwrite);
node->AddProperty("point line width", mitk::IntProperty::New(1), renderer, overwrite);
node->AddProperty("point 2D size", mitk::FloatProperty::New(6), renderer, overwrite);
node->AddProperty("show contour", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("close contour", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("show points", mitk::BoolProperty::New(true), renderer, overwrite);
node->AddProperty("show distances", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("distance decimal digits", mitk::IntProperty::New(2), renderer, overwrite);
node->AddProperty("show angles", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("show distant lines", mitk::BoolProperty::New(false), renderer, overwrite);
node->AddProperty("layer", mitk::IntProperty::New(1), renderer, overwrite);
node->AddProperty("Pointset.2D.fill shape",
mitk::BoolProperty::New(false),
renderer,
overwrite); // fill or do not fill the glyph shape
mitk::PointSetShapeProperty::Pointer pointsetShapeProperty = mitk::PointSetShapeProperty::New();
node->AddProperty("Pointset.2D.shape", pointsetShapeProperty, renderer, overwrite);
node->AddProperty("Pointset.2D.distance to plane",
mitk::FloatProperty::New(4.0f),
renderer,
overwrite); // show the point at a certain distance above/below the 2D imaging plane.
node->AddProperty("Pointset.2D.fixed size on screen", mitk::BoolProperty::New(false), renderer, overwrite);
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
diff --git a/Modules/Core/src/Rendering/mitkVideoRecorder.cpp b/Modules/Core/src/Rendering/mitkVideoRecorder.cpp
index 1de3fcfb9a..feb313cc55 100644
--- a/Modules/Core/src/Rendering/mitkVideoRecorder.cpp
+++ b/Modules/Core/src/Rendering/mitkVideoRecorder.cpp
@@ -1,409 +1,409 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkVideoRecorder.h>
#include <mitkBaseRenderer.h>
#include <mitkCoreServices.h>
#include <mitkExceptionMacro.h>
#include <mitkIOUtil.h>
#include <mitkIPreferences.h>
#include <mitkIPreferencesService.h>
#include <mitkLog.h>
#include <vtkImageResize.h>
#include <vtkNew.h>
#include <vtkPNGWriter.h>
#include <vtkRenderWindow.h>
#include <vtkWindowToImageFilter.h>
#include <itksys/Process.h>
#include <iomanip>
#include <optional>
#include <sstream>
std::string mitk::VideoRecorder::GetFileExtension(OutputFormat format)
{
switch (format)
{
case OutputFormat::WebM_VP9:
return ".webm";
case OutputFormat::MP4_H264:
return ".mp4";
default:
break;
}
mitkThrow() << "Unknown output format for video recording.";
}
namespace
{
mitk::IPreferences* GetPreferences()
{
auto* preferencesService = mitk::CoreServices::GetPreferencesService();
return preferencesService->GetSystemPreferences()->Node("org.mitk.views.moviemaker");
}
class RecordingSession
{
public:
RecordingSession(vtkRenderWindow* renderWindow, mitk::VideoRecorder::OutputFormat format)
: m_FrameDir(mitk::IOUtil::CreateTemporaryDirectory("MITK_RecordingSession_XXXXXX")),
m_NumberOfFrames(0)
{
m_WindowToImageFilter->SetInput(renderWindow);
if (mitk::VideoRecorder::OutputFormat::MP4_H264 == format)
{
// H.264 only supports image dimensions that are a multiple of 2. Resize if necessary.
auto* size = renderWindow->GetActualSize();
if (size[0] & 1 || size[1] & 1)
{
m_ImageResize->SetInputConnection(m_WindowToImageFilter->GetOutputPort());
m_ImageResize->SetOutputDimensions(size[0] & ~1, size[1] & ~1, -1);
m_ImageResize->SetInterpolate(0);
m_ImageResize->BorderOn();
m_ImageWriter->SetInputConnection(m_ImageResize->GetOutputPort());
return;
}
}
m_ImageWriter->SetInputConnection(m_WindowToImageFilter->GetOutputPort());
}
~RecordingSession()
{
std::error_code errorCode;
- std::filesystem::remove_all(m_FrameDir, errorCode);
+ fs::remove_all(m_FrameDir, errorCode);
}
RecordingSession(const RecordingSession&) = delete;
RecordingSession& operator=(const RecordingSession&) = delete;
- std::filesystem::path GetFrameDir() const
+ fs::path GetFrameDir() const
{
return m_FrameDir;
}
void RecordFrame()
{
m_WindowToImageFilter->Modified();
std::stringstream frameFilename;
frameFilename << std::setw(6) << std::setfill('0') << m_NumberOfFrames << ".png";
const auto framePath = m_FrameDir / frameFilename.str();
m_ImageWriter->SetFileName(framePath.string().c_str());
m_ImageWriter->Write();
++m_NumberOfFrames;
}
private:
- std::filesystem::path m_FrameDir;
+ fs::path m_FrameDir;
unsigned int m_NumberOfFrames;
vtkNew<vtkWindowToImageFilter> m_WindowToImageFilter;
vtkNew<vtkImageResize> m_ImageResize;
vtkNew<vtkPNGWriter> m_ImageWriter;
};
}
namespace mitk
{
class VideoRecorder::Impl
{
public:
Impl()
: m_FrameRate(30)
{
}
~Impl() = default;
Impl(const Impl&) = delete;
Impl& operator=(const Impl&) = delete;
- std::filesystem::path GetFFmpegPath() const
+ fs::path GetFFmpegPath() const
{
if (m_FFmpegPath)
return m_FFmpegPath.value();
auto* preferences = GetPreferences();
if (nullptr != preferences)
{
auto ffmpegPath = preferences->Get("ffmpeg", "");
if (!ffmpegPath.empty())
return ffmpegPath;
}
- return std::filesystem::path();
+ return fs::path();
}
- void SetFFmpegPath(const std::filesystem::path& path)
+ void SetFFmpegPath(const fs::path& path)
{
m_FFmpegPath = path;
}
- std::filesystem::path GetOutputPath() const
+ fs::path GetOutputPath() const
{
return m_OutputPath;
}
- void SetOutputPath(const std::filesystem::path& path)
+ void SetOutputPath(const fs::path& path)
{
m_OutputPath = path;
}
mitk::VideoRecorder::OutputFormat GetOutputFormat() const
{
if (m_OutputFormat)
return m_OutputFormat.value();
auto* preferences = GetPreferences();
if (nullptr != preferences)
return static_cast<OutputFormat>(preferences->GetInt("format", 0));
return OutputFormat::WebM_VP9;
}
void SetOutputFormat(OutputFormat format)
{
m_OutputFormat = format;
}
std::string GetRenderWindowName() const
{
return m_RenderWindowName;
}
void SetRenderWindowName(const std::string& renderWindowName)
{
m_RenderWindowName = renderWindowName;
}
int GetFrameRate() const
{
return m_FrameRate;
}
void SetFrameRate(unsigned int fps)
{
m_FrameRate = fps;
}
bool OnAir() const
{
return nullptr != m_RecordingSession.get();
}
void StartRecording()
{
if (this->OnAir())
mitkThrow() << "Recording session already running.";
auto renderWindowName = this->GetRenderWindowName();
if (renderWindowName.empty())
mitkThrow() << "No render window specified for recording.";
auto* renderWindow = BaseRenderer::GetRenderWindowByName(renderWindowName);
if (nullptr == renderWindow)
mitkThrow() << "\"" << renderWindowName << "\" references unknown render window for recording.";
m_RecordingSession = std::make_unique<RecordingSession>(renderWindow, this->GetOutputFormat());
}
void RecordFrame()
{
if (!this->OnAir())
mitkThrow() << "Cannot record frame. No recording session running.";
m_RecordingSession->RecordFrame();
}
std::string GetFFmpegCommandLine() const
{
bool vp9 = OutputFormat::WebM_VP9 == this->GetOutputFormat();
std::stringstream stream;
stream << this->GetFFmpegPath() << ' '
<< "-y" << ' '
<< "-r " << std::to_string(this->GetFrameRate()) << ' '
<< "-i %6d.png" << ' '
<< "-c:v " << (vp9 ? "libvpx-vp9" : "libx264") << ' '
<< "-crf " << (vp9 ? "31" : "23") << ' '
<< "-pix_fmt yuv420p" << ' '
<< "-b:v 0" << ' '
<< this->GetOutputPath();
return stream.str();
}
int ExecuteFFmpeg() const
{
auto commandLine = this->GetFFmpegCommandLine();
auto commandLineCStr = commandLine.c_str();
auto workingDirectory = m_RecordingSession->GetFrameDir().string();
auto* ffmpeg = itksysProcess_New();
itksysProcess_SetOption(ffmpeg, itksysProcess_Option_Verbatim, 1);
itksysProcess_SetCommand(ffmpeg, &commandLineCStr);
itksysProcess_SetWorkingDirectory(ffmpeg, workingDirectory.c_str());
itksysProcess_Execute(ffmpeg);
itksysProcess_WaitForExit(ffmpeg, nullptr);
auto state = itksysProcess_GetState(ffmpeg);
if (itksysProcess_State_Exited != state)
{
std::stringstream message;
message << "FFmpeg process did not exit as expected: ";
if (itksysProcess_State_Error == state)
{
message << itksysProcess_GetErrorString(ffmpeg);
}
else if (itksysProcess_State_Exception == state)
{
message << itksysProcess_GetExceptionString(ffmpeg);
}
message << "\n Command: " << commandLineCStr;
message << "\n Working directory: " << workingDirectory.c_str();
itksysProcess_Delete(ffmpeg);
mitkThrow() << message.str();
}
auto exitCode = itksysProcess_GetExitValue(ffmpeg);
itksysProcess_Delete(ffmpeg);
return exitCode;
}
int StopRecording()
{
if (!this->OnAir())
mitkThrow() << "No recording session running.";
if (this->GetFFmpegPath().empty())
mitkThrow() << "Path to FFmpeg not set.";
if (this->GetOutputPath().empty())
mitkThrow() << "Path to output video file not set.";
auto exitCode = this->ExecuteFFmpeg();
m_RecordingSession = nullptr;
return exitCode;
}
private:
- std::optional<std::filesystem::path> m_FFmpegPath;
- std::filesystem::path m_OutputPath;
+ std::optional<fs::path> m_FFmpegPath;
+ fs::path m_OutputPath;
std::optional<OutputFormat> m_OutputFormat;
std::string m_RenderWindowName;
unsigned int m_FrameRate;
std::unique_ptr<RecordingSession> m_RecordingSession;
};
}
mitk::VideoRecorder::VideoRecorder()
: m_Impl(std::make_unique<Impl>())
{
}
mitk::VideoRecorder::~VideoRecorder()
{
}
-std::filesystem::path mitk::VideoRecorder::GetFFmpegPath() const
+fs::path mitk::VideoRecorder::GetFFmpegPath() const
{
return m_Impl->GetFFmpegPath();
}
-void mitk::VideoRecorder::SetFFmpegPath(const std::filesystem::path& path)
+void mitk::VideoRecorder::SetFFmpegPath(const fs::path& path)
{
m_Impl->SetFFmpegPath(path);
}
-std::filesystem::path mitk::VideoRecorder::GetOutputPath() const
+fs::path mitk::VideoRecorder::GetOutputPath() const
{
return m_Impl->GetOutputPath();
}
-void mitk::VideoRecorder::SetOutputPath(const std::filesystem::path& path)
+void mitk::VideoRecorder::SetOutputPath(const fs::path& path)
{
m_Impl->SetOutputPath(path);
}
mitk::VideoRecorder::OutputFormat mitk::VideoRecorder::GetOutputFormat() const
{
return m_Impl->GetOutputFormat();
}
void mitk::VideoRecorder::SetOutputFormat(OutputFormat format)
{
m_Impl->SetOutputFormat(format);
}
std::string mitk::VideoRecorder::GetRenderWindowName() const
{
return m_Impl->GetRenderWindowName();
}
void mitk::VideoRecorder::SetRenderWindowName(const std::string& renderWindowName)
{
m_Impl->SetRenderWindowName(renderWindowName);
}
int mitk::VideoRecorder::GetFrameRate() const
{
return m_Impl->GetFrameRate();
}
void mitk::VideoRecorder::SetFrameRate(unsigned int fps)
{
m_Impl->SetFrameRate(fps);
}
void mitk::VideoRecorder::StartRecording()
{
m_Impl->StartRecording();
}
void mitk::VideoRecorder::RecordFrame() const
{
m_Impl->RecordFrame();
}
int mitk::VideoRecorder::StopRecording()
{
return m_Impl->StopRecording();
}
diff --git a/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp b/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp
index 65f234967d..ae7dd51fa9 100644
--- a/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp
+++ b/Modules/Core/test/mitkClippedSurfaceBoundsCalculatorTest.cpp
@@ -1,795 +1,795 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <iostream>
#include "mitkClippedSurfaceBoundsCalculator.h"
#include "mitkGeometry3D.h"
#include "mitkNumericTypes.h"
#include "mitkPlaneGeometry.h"
static void CheckPlanesInsideBoundingBoxOnlyOnOneSlice(mitk::BaseGeometry::Pointer geometry3D)
{
// Check planes which are inside the bounding box
mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator();
mitk::Image::Pointer image = mitk::Image::New();
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(geometry3D.GetPointer()));
// Check planes which are only on one slice:
// Slice 0
mitk::Point3D origin;
origin[0] = 511;
origin[1] = 0;
origin[2] = 0;
mitk::Vector3D normal;
mitk::FillVector3D(normal, 0, 0, 1);
mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New();
planeOnSliceZero->InitializePlane(origin, normal);
calculator->SetInput(planeOnSliceZero, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0");
// Slice 3
origin[2] = 3;
mitk::PlaneGeometry::Pointer planeOnSliceThree = mitk::PlaneGeometry::New();
planeOnSliceThree->InitializePlane(origin, normal);
planeOnSliceThree->SetImageGeometry(false);
calculator->SetInput(planeOnSliceThree, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 3 && minMax.second == 3, "Check if plane is on slice 3");
// Slice 17
origin[2] = 17;
mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New();
planeOnSliceSeventeen->InitializePlane(origin, normal);
calculator->SetInput(planeOnSliceSeventeen, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 17 && minMax.second == 17, "Check if plane is on slice 17");
// Slice 20
origin[2] = 19;
mitk::PlaneGeometry::Pointer planeOnSliceTwenty = mitk::PlaneGeometry::New();
planeOnSliceTwenty->InitializePlane(origin, normal);
calculator->SetInput(planeOnSliceTwenty, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 19 && minMax.second == 19, "Check if plane is on slice 19");
delete calculator;
}
static void CheckPlanesInsideBoundingBox(mitk::BaseGeometry::Pointer geometry3D)
{
// Check planes which are inside the bounding box
mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator();
mitk::Image::Pointer image = mitk::Image::New();
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(geometry3D.GetPointer()));
// Check planes which are only on one slice:
// Slice 0
mitk::Point3D origin;
origin[0] = 510; // Set to 511.9 so that the intersection point is inside the bounding box
origin[1] = 0;
origin[2] = 0;
mitk::Vector3D normal;
mitk::FillVector3D(normal, 1, 0, 0);
mitk::PlaneGeometry::Pointer planeSagittalOne = mitk::PlaneGeometry::New();
planeSagittalOne->InitializePlane(origin, normal);
calculator->SetInput(planeSagittalOne, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19");
// Slice 3
origin[0] = 256;
MITK_INFO << "Case1 origin: " << origin;
mitk::PlaneGeometry::Pointer planeSagittalTwo = mitk::PlaneGeometry::New();
planeSagittalTwo->InitializePlane(origin, normal);
MITK_INFO << "PlaneNormal: " << planeSagittalTwo->GetNormal();
MITK_INFO << "PlaneOrigin: " << planeSagittalTwo->GetOrigin();
calculator->SetInput(planeSagittalTwo, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19");
// Slice 17
origin[0] = 0; // Set to 0.1 so that the intersection point is inside the bounding box
mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New();
planeOnSliceSeventeen->InitializePlane(origin, normal);
calculator->SetInput(planeOnSliceSeventeen, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19");
// Crooked planes:
origin[0] = 0;
origin[1] = 507;
origin[2] = 0;
normal[0] = 1;
normal[1] = -1;
normal[2] = 1;
mitk::PlaneGeometry::Pointer planeCrookedOne = mitk::PlaneGeometry::New();
planeCrookedOne->InitializePlane(origin, normal);
calculator->SetInput(planeCrookedOne, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 5,
"Check if plane is from slice 0 to slice 5 with inclined plane");
origin[0] = 512;
origin[1] = 0;
origin[2] = 16;
mitk::PlaneGeometry::Pointer planeCrookedTwo = mitk::PlaneGeometry::New();
planeCrookedTwo->InitializePlane(origin, normal);
calculator->SetInput(planeCrookedTwo, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
MITK_TEST_CONDITION(minMax.first == 16 && minMax.second == 19,
"Check if plane is from slice 16 to slice 19 with inclined plane");
origin[0] = 510;
origin[1] = 0;
origin[2] = 0;
normal[1] = 0;
normal[2] = 0.04;
mitk::PlaneGeometry::Pointer planeCrookedThree = mitk::PlaneGeometry::New();
planeCrookedThree->InitializePlane(origin, normal);
calculator->SetInput(planeCrookedThree, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19,
"Check if plane is from slice 0 to slice 19 with inclined plane");
delete calculator;
}
static void CheckPlanesOutsideOfBoundingBox(mitk::BaseGeometry::Pointer geometry3D)
{
// Check planes which are outside of the bounding box
mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator();
mitk::Image::Pointer image = mitk::Image::New();
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(geometry3D.GetPointer()));
// In front of the bounding box
mitk::Point3D origin;
origin[0] = 510;
origin[1] = 0;
origin[2] = -5;
mitk::Vector3D normal;
mitk::FillVector3D(normal, 0, 0, 1);
mitk::PlaneGeometry::Pointer planeInFront = mitk::PlaneGeometry::New();
planeInFront->InitializePlane(origin, normal);
calculator->SetInput(planeInFront, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == std::numeric_limits<int>::max(), "Check if min value hasn't been set");
MITK_TEST_CONDITION(minMax.second == std::numeric_limits<int>::min(), "Check if max value hasn't been set");
// Behind the bounding box
origin[2] = 515;
mitk::PlaneGeometry::Pointer planeBehind = mitk::PlaneGeometry::New();
planeBehind->InitializePlane(origin, normal);
calculator->SetInput(planeBehind, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == std::numeric_limits<int>::max(), "Check if min value hasn't been set");
MITK_TEST_CONDITION(minMax.second == std::numeric_limits<int>::min(), "Check if max value hasn't been set");
// Above
origin[1] = 515;
mitk::FillVector3D(normal, 0, 1, 0);
mitk::PlaneGeometry::Pointer planeAbove = mitk::PlaneGeometry::New();
planeAbove->InitializePlane(origin, normal);
calculator->SetInput(planeAbove, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == std::numeric_limits<int>::max(), "Check if min value hasn't been set");
MITK_TEST_CONDITION(minMax.second == std::numeric_limits<int>::min(), "Check if max value hasn't been set");
// Below
origin[1] = -5;
mitk::PlaneGeometry::Pointer planeBelow = mitk::PlaneGeometry::New();
planeBelow->InitializePlane(origin, normal);
calculator->SetInput(planeBelow, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == std::numeric_limits<int>::max(), "Check if min value hasn't been set");
MITK_TEST_CONDITION(minMax.second == std::numeric_limits<int>::min(), "Check if max value hasn't been set");
// Left side
origin[0] = -5;
mitk::FillVector3D(normal, 1, 0, 0);
mitk::PlaneGeometry::Pointer planeLeftSide = mitk::PlaneGeometry::New();
planeLeftSide->InitializePlane(origin, normal);
calculator->SetInput(planeLeftSide, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == std::numeric_limits<int>::max(), "Check if min value hasn't been set");
MITK_TEST_CONDITION(minMax.second == std::numeric_limits<int>::min(), "Check if max value hasn't been set");
// Right side
origin[1] = 515;
mitk::PlaneGeometry::Pointer planeRightSide = mitk::PlaneGeometry::New();
planeRightSide->InitializePlane(origin, normal);
calculator->SetInput(planeRightSide, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == std::numeric_limits<int>::max(), "Check if min value hasn't been set");
MITK_TEST_CONDITION(minMax.second == std::numeric_limits<int>::min(), "Check if max value hasn't been set");
delete calculator;
}
static void CheckIntersectionPointsOfTwoGeometry3D(mitk::BaseGeometry::Pointer firstGeometry3D,
mitk::BaseGeometry::Pointer secondGeometry3D)
{
mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator();
mitk::Image::Pointer firstImage = mitk::Image::New();
firstImage->Initialize(mitk::MakePixelType<int, int, 1>(), *(firstGeometry3D.GetPointer()));
calculator->SetInput(secondGeometry3D, firstImage);
calculator->Update();
auto minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19");
delete calculator;
}
static void CheckIntersectionWithPointCloud(mitk::BaseGeometry::Pointer geometry3D)
{
// Check planes which are inside the bounding box
mitk::Image::Pointer image = mitk::Image::New();
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(geometry3D.GetPointer()));
{
mitk::Point3D pnt1, pnt2;
pnt1[0] = 3;
pnt1[1] = 5;
pnt1[2] = 3;
pnt2[0] = 8;
pnt2[1] = 3;
pnt2[2] = 8;
mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist;
pointlist.push_back(pnt1);
pointlist.push_back(pnt2);
mitk::ClippedSurfaceBoundsCalculator calculator;
calculator.SetInput(pointlist, image);
calculator.Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMaxZ.first == 3 && minMaxZ.second == 8,
"Check if points span from slice 3 to slice 8 in axial");
mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX();
MITK_TEST_CONDITION(minMaxX.first == 3 && minMaxX.second == 5,
"Check if points span from slice 3 to slice 5 in sagittal");
}
{
mitk::Point3D pnt1, pnt2;
pnt1.Fill(-3);
pnt2.Fill(600);
mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist;
pointlist.push_back(pnt1);
pointlist.push_back(pnt2);
mitk::ClippedSurfaceBoundsCalculator calculator;
calculator.SetInput(pointlist, image);
calculator.Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMaxZ.first == 0 && minMaxZ.second == 19,
"Check if points are correctly clipped to slice 0 and slice 19 in axial");
mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX();
MITK_TEST_CONDITION(minMaxX.first == 0 && minMaxX.second == 511,
"Check if points are correctly clipped to slice 0 and slice 511 in sagittal");
}
}
static void CheckIntersectionWithRotatedGeometry()
{
// Define origin for second Geometry3D;
mitk::Point3D origin3;
origin3[0] = -45.25;
origin3[1] = -113.22;
origin3[2] = 0.62;
// Define normal:
mitk::Vector3D normal3;
normal3[0] = -0.02;
normal3[1] = -0.18;
normal3[2] = 2.99;
mitk::Vector3D spacing;
spacing[0] = 1.43;
spacing[1] = 1.43;
spacing[2] = 3.0;
// Initialize PlaneGeometry:
mitk::PlaneGeometry::Pointer planeGeometry3 = mitk::PlaneGeometry::New();
planeGeometry3->InitializePlane(origin3, normal3);
planeGeometry3->SetSpacing(spacing);
mitk::BoundingBox::BoundsArrayType moreBounds = planeGeometry3->GetBounds();
moreBounds[0] = 0;
moreBounds[1] = 64;
moreBounds[2] = 0;
moreBounds[3] = 140;
moreBounds[4] = 0;
moreBounds[5] = 1;
planeGeometry3->SetBounds(moreBounds);
// Initialize SlicedGeometry3D:
mitk::SlicedGeometry3D::Pointer rotatedSlicedGeometry3D = mitk::SlicedGeometry3D::New();
rotatedSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast<mitk::PlaneGeometry *>(planeGeometry3.GetPointer()), 25);
rotatedSlicedGeometry3D->SetImageGeometry(true);
mitk::AffineTransform3D *geom = rotatedSlicedGeometry3D->GetIndexToWorldTransform();
mitk::AffineTransform3D::MatrixType matrix;
matrix[0][0] = 1.43;
matrix[0][1] = -0.01;
matrix[0][2] = -0.02;
matrix[0][0] = 0.01;
matrix[1][1] = 1.43;
matrix[2][2] = -0.18;
matrix[0][0] = 0.01;
matrix[1][1] = 0.08;
matrix[2][2] = 2.99;
geom->SetMatrix(matrix);
mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator();
mitk::Image::Pointer image = mitk::Image::New();
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(rotatedSlicedGeometry3D.GetPointer()));
// Check planes which are only on one slice:
{
// last Slice
mitk::Point3D origin;
origin[0] = -47.79;
origin[1] = 81.41;
origin[2] = 84.34;
mitk::Vector3D normal;
mitk::FillVector3D(normal, 0.02, 0.18, -2.99);
mitk::Vector3D spacing;
spacing[0] = 1.43;
spacing[1] = 1.43;
spacing[2] = 3.0;
mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New();
planeOnSliceZero->InitializePlane(origin, normal);
planeOnSliceZero->SetSpacing(spacing);
calculator->SetInput(planeOnSliceZero, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 24 && minMax.second == 24, "Check if plane is on slice 24");
}
{
// Slice 0-24
mitk::Point3D origin;
origin[0] = -45;
origin[1] = -105;
origin[2] = 35;
mitk::Vector3D normal;
mitk::FillVector3D(normal, 1.43, 0.01, 0.01);
mitk::Vector3D spacing;
spacing[0] = 1.43;
spacing[1] = 3.0;
spacing[2] = 1.43;
mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New();
planeOnSliceZero->InitializePlane(origin, normal);
planeOnSliceZero->SetSpacing(spacing);
calculator->SetInput(planeOnSliceZero, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ();
MITK_TEST_CONDITION(minMax.first != minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 24, "Check if plane is on slices 0-24");
}
delete calculator;
}
static void CheckIntersectionWithRotatedGeometry90()
{
// Define origin for second Geometry3D;
mitk::Point3D origin3;
origin3[0] = 0.0;
origin3[1] = 0.0;
origin3[2] = 0.0;
// Define normal:
mitk::Vector3D normal3;
normal3[0] = 0;
normal3[1] = 0;
normal3[2] = 1;
mitk::Vector3D spacing;
spacing[0] = 1.0;
spacing[1] = 2.0;
spacing[2] = 1.0;
// Initialize PlaneGeometry:
mitk::PlaneGeometry::Pointer planeGeometry3 = mitk::PlaneGeometry::New();
planeGeometry3->InitializePlane(origin3, normal3);
planeGeometry3->SetSpacing(spacing);
mitk::BoundingBox::BoundsArrayType moreBounds = planeGeometry3->GetBounds();
moreBounds[0] = 0;
moreBounds[1] = 50;
moreBounds[2] = 0;
moreBounds[3] = 50;
moreBounds[4] = 0;
moreBounds[5] = 1;
planeGeometry3->SetBounds(moreBounds);
// Initialize SlicedGeometry3D:
mitk::SlicedGeometry3D::Pointer rotatedSlicedGeometry3D = mitk::SlicedGeometry3D::New();
rotatedSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast<mitk::PlaneGeometry *>(planeGeometry3.GetPointer()), 50);
rotatedSlicedGeometry3D->SetImageGeometry(true);
// Set the rotation to zero (identity times spacing matrix) because the default makes a rotation
mitk::AffineTransform3D *geom = rotatedSlicedGeometry3D->GetIndexToWorldTransform();
mitk::AffineTransform3D::MatrixType matrix;
matrix[0][0] = spacing[0];
matrix[0][1] = 0.0;
matrix[0][2] = 0.0;
matrix[1][0] = 0.0;
matrix[1][1] = spacing[1];
matrix[1][2] = 0.0;
matrix[2][0] = 0.0;
matrix[2][1] = 0.0;
matrix[2][2] = spacing[2];
geom->SetMatrix(matrix);
mitk::ClippedSurfaceBoundsCalculator *calculator = new mitk::ClippedSurfaceBoundsCalculator();
mitk::Image::Pointer image = mitk::Image::New();
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(rotatedSlicedGeometry3D.GetPointer()));
// construct the planes in y direction
{
// first Slice in Y direction
mitk::Point3D originFirstSlice;
originFirstSlice[0] = 0.0;
originFirstSlice[1] = -1.0;
originFirstSlice[2] = 0.0;
mitk::Vector3D normalFitrstSlice;
mitk::FillVector3D(normalFitrstSlice, 0.0, 1.0, 0.0);
mitk::Vector3D spacingFirstSlice;
spacingFirstSlice[0] = 1.0;
spacingFirstSlice[1] = 1.0;
spacingFirstSlice[2] = 1.0;
mitk::PlaneGeometry::Pointer planeOnFirstSlice = mitk::PlaneGeometry::New();
planeOnFirstSlice->InitializePlane(originFirstSlice, normalFitrstSlice);
planeOnFirstSlice->SetSpacing(spacingFirstSlice);
// last Slice in Y Direction
mitk::Point3D originLastSlice;
originLastSlice[0] = 0.0;
originLastSlice[1] = 99.0;
originLastSlice[2] = 0.0;
mitk::Vector3D normalLastSlice;
mitk::FillVector3D(normalLastSlice, 0.0, 1.0, 0.0);
mitk::Vector3D spacingLastSlice;
spacingLastSlice[0] = 1.0;
spacingLastSlice[1] = 1.0;
spacingLastSlice[2] = 1.0;
mitk::PlaneGeometry::Pointer planeOnLastSlice = mitk::PlaneGeometry::New();
planeOnLastSlice->InitializePlane(originLastSlice, normalLastSlice);
planeOnLastSlice->SetSpacing(spacingLastSlice);
// calculate the intersection on the last slice
calculator->SetInput(planeOnLastSlice, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionY();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 49 && minMax.second == 49, "Check if plane is on slice 49");
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
// calculate the intersection on the first slice
calculator->SetInput(planeOnFirstSlice, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionY();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0");
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
}
// now we make a rotation of the Image about 270 degrees around Z Axis to get an alignment on the positive x axis
double angleInDegrees = 270 * itk::Math::pi / 180;
mitk::AffineTransform3D::MatrixType matrix2;
matrix2[0][0] = cos(angleInDegrees);
matrix2[0][1] = -sin(angleInDegrees);
matrix2[0][2] = 0.0;
matrix2[1][0] = sin(angleInDegrees);
matrix2[1][1] = cos(angleInDegrees);
matrix2[1][2] = 0.0;
matrix2[2][0] = 0.0;
matrix2[2][1] = 0.0;
matrix2[2][2] = 1.0;
// multiplie the identity with the transformation matrix
mitk::AffineTransform3D::MatrixType TransformationMatrix = matrix2 * matrix;
// initialize the image with the new rotation Matrix
geom->SetMatrix(TransformationMatrix);
image->Initialize(mitk::MakePixelType<int, int, 1>(), *(rotatedSlicedGeometry3D.GetPointer()));
{
- // first Slice in X Diection
+ // first Slice in X Direction
mitk::Point3D originFirstSlice;
originFirstSlice[0] = -1.0;
originFirstSlice[1] = 0.0;
originFirstSlice[2] = 0.0;
mitk::Vector3D normalFitrstSlice;
mitk::FillVector3D(normalFitrstSlice, 1.0, 0.0, 0.0);
mitk::Vector3D spacingFirstSlice;
spacingFirstSlice[0] = 1.0;
spacingFirstSlice[1] = 1.0;
spacingFirstSlice[2] = 1.0;
mitk::PlaneGeometry::Pointer planeOnFirstSlice = mitk::PlaneGeometry::New();
planeOnFirstSlice->InitializePlane(originFirstSlice, normalFitrstSlice);
planeOnFirstSlice->SetSpacing(spacingFirstSlice);
// last Slice in X Direction
mitk::Point3D originLastSlice;
originLastSlice[0] = 99.0;
originLastSlice[1] = 0.0;
originLastSlice[2] = 0.0;
mitk::Vector3D normalLastSlice;
mitk::FillVector3D(normalLastSlice, 1.0, 0.0, 0.0);
mitk::Vector3D spacingLastSlice;
spacingLastSlice[0] = 1.0;
spacingLastSlice[1] = 1.0;
spacingLastSlice[2] = 1.0;
mitk::PlaneGeometry::Pointer planeOnLastSlice = mitk::PlaneGeometry::New();
planeOnLastSlice->InitializePlane(originLastSlice, normalLastSlice);
planeOnLastSlice->SetSpacing(spacingLastSlice);
calculator->SetInput(planeOnLastSlice, image);
calculator->Update();
mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionY();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 49 && minMax.second == 49, "Check if plane is on slice 49");
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
calculator->SetInput(planeOnFirstSlice, image);
calculator->Update();
minMax = calculator->GetMinMaxSpatialDirectionY();
MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice");
MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0");
MITK_INFO << "min: " << minMax.first << " max: " << minMax.second;
}
delete calculator;
}
int mitkClippedSurfaceBoundsCalculatorTest(int, char *[])
{
// always start with this!
MITK_TEST_BEGIN("ClippedSurfaceBoundsCalculator");
/** The class mitkClippedSurfaceBoundsCalculator calculates the intersection points of a PlaneGeometry and a
* Geometry3D.
* This unit test checks if the correct min and max values for the three spatial directions (x, y, z)
* are calculated. To test this we define artificial PlaneGeometries and Geometry3Ds and test different
* scenarios:
*
* 1. planes which are inside the bounding box of a 3D geometry but only on one slice
* 2. planes which are outside of the bounding box
* 3. planes which are inside the bounding box but over more than one slice
*
* Note: Currently rotated geometries are not tested!
*/
/********************* Define Geometry3D ***********************/
// Define origin:
mitk::Point3D origin;
origin[0] = 511;
origin[1] = 0;
origin[2] = 0;
// Define normal:
mitk::Vector3D normal;
mitk::FillVector3D(normal, 0, 0, 1);
// Initialize PlaneGeometry:
mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New();
planeGeometry->InitializePlane(origin, normal);
// Set Bounds:
mitk::BoundingBox::BoundsArrayType bounds = planeGeometry->GetBounds();
bounds[0] = 0;
bounds[1] = 512;
bounds[2] = 0;
bounds[3] = 512;
bounds[4] = 0;
bounds[5] = 1;
planeGeometry->SetBounds(bounds);
// Initialize SlicedGeometry3D:
mitk::SlicedGeometry3D::Pointer slicedGeometry3D = mitk::SlicedGeometry3D::New();
slicedGeometry3D->InitializeEvenlySpaced(dynamic_cast<mitk::PlaneGeometry *>(planeGeometry.GetPointer()), 20);
mitk::BaseGeometry::Pointer geometry3D = dynamic_cast<mitk::BaseGeometry *>(slicedGeometry3D.GetPointer());
geometry3D->SetImageGeometry(true);
// Define origin for second Geometry3D;
mitk::Point3D origin2;
origin2[0] = 511;
origin2[1] = 60;
origin2[2] = 0;
// Define normal:
mitk::Vector3D normal2;
mitk::FillVector3D(normal2, 0, 1, 0);
// Initialize PlaneGeometry:
mitk::PlaneGeometry::Pointer planeGeometry2 = mitk::PlaneGeometry::New();
planeGeometry2->InitializePlane(origin2, normal2);
// Initialize SlicedGeometry3D:
mitk::SlicedGeometry3D::Pointer secondSlicedGeometry3D = mitk::SlicedGeometry3D::New();
secondSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast<mitk::PlaneGeometry *>(planeGeometry2.GetPointer()), 20);
mitk::BaseGeometry::Pointer secondGeometry3D =
dynamic_cast<mitk::BaseGeometry *>(secondSlicedGeometry3D.GetPointer());
secondGeometry3D->SetImageGeometry(true);
/***************************************************************/
CheckPlanesInsideBoundingBoxOnlyOnOneSlice(geometry3D);
CheckPlanesOutsideOfBoundingBox(geometry3D);
CheckPlanesInsideBoundingBox(geometry3D);
CheckIntersectionPointsOfTwoGeometry3D(geometry3D, secondGeometry3D);
CheckIntersectionWithPointCloud(geometry3D);
CheckIntersectionWithRotatedGeometry();
CheckIntersectionWithRotatedGeometry90();
/** ToDo:
* test also rotated 3D geometry!
*/
MITK_TEST_END();
}
diff --git a/Modules/Core/test/mitkDataStorageTest.cpp b/Modules/Core/test/mitkDataStorageTest.cpp
index 81a5e0588e..393f05ac75 100644
--- a/Modules/Core/test/mitkDataStorageTest.cpp
+++ b/Modules/Core/test/mitkDataStorageTest.cpp
@@ -1,873 +1,873 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <fstream>
#include "mitkColorProperty.h"
#include "mitkDataNode.h"
#include "mitkGroupTagProperty.h"
#include "mitkImage.h"
#include "mitkReferenceCountWatcher.h"
#include "mitkStringProperty.h"
#include "mitkSurface.h"
#include "mitkDataStorage.h"
#include "mitkIOUtil.h"
#include "mitkMessage.h"
#include "mitkNodePredicateAnd.h"
#include "mitkNodePredicateData.h"
#include "mitkNodePredicateDataType.h"
#include "mitkNodePredicateDimension.h"
#include "mitkNodePredicateNot.h"
#include "mitkNodePredicateOr.h"
#include "mitkNodePredicateProperty.h"
#include "mitkStandaloneDataStorage.h"
//#include "mitkPicFileReader.h"
#include "mitkTestingMacros.h"
void TestDataStorage(mitk::DataStorage *ds, std::string filename);
namespace mitk
{
class TestStandaloneDataStorage : public StandaloneDataStorage
{
public:
mitkClassMacro(TestStandaloneDataStorage, mitk::DataStorage);
itkFactorylessNewMacro(Self)
itkCloneMacro(Self) std::map<const mitk::DataNode *, unsigned long> GetModifiedObserverTags() const
{
return m_NodeModifiedObserverTags;
}
std::map<const mitk::DataNode *, unsigned long> GetDeletedObserverTags() const { return m_NodeDeleteObserverTags; }
protected:
TestStandaloneDataStorage() {}
};
}
class DSEventReceiver // Helper class for event testing
{
public:
const mitk::DataNode *m_NodeAdded;
const mitk::DataNode *m_NodeRemoved;
DSEventReceiver() : m_NodeAdded(nullptr), m_NodeRemoved(nullptr) {}
void OnAdd(const mitk::DataNode *node) { m_NodeAdded = node; }
void OnRemove(const mitk::DataNode *node) { m_NodeRemoved = node; }
};
///
/// \brief a class for checking if the datastorage is really thread safe
///
/// Therefore it listens to a node contained in the datastorage. when this node
/// gets removed and deleted, this class gets informed by calling OnObjectDelete().
/// in OnObjectDelete() an empty node gets added. this must not cause a deadlock
///
struct ItkDeleteEventListener
{
ItkDeleteEventListener(mitk::DataStorage *ds) : m_Node(nullptr), m_DataStorage(ds), m_DeleteObserverTag(0) {}
void SetNode(mitk::DataNode *_Node)
{
if (m_Node)
return;
m_Node = _Node;
itk::MemberCommand<ItkDeleteEventListener>::Pointer onObjectDelete =
itk::MemberCommand<ItkDeleteEventListener>::New();
onObjectDelete->SetCallbackFunction(this, &ItkDeleteEventListener::OnObjectDelete);
m_DeleteObserverTag = m_Node->AddObserver(itk::DeleteEvent(), onObjectDelete);
}
void OnObjectDelete(const itk::Object * /*caller*/, const itk::EventObject &)
{
mitk::DataNode::Pointer node = mitk::DataNode::New();
m_DataStorage->Add(node); // SHOULD NOT CAUSE A DEADLOCK!
m_DataStorage->Remove(node); // tidy up: remove the empty node again
m_Node = nullptr;
}
protected:
mitk::DataNode *m_Node;
mitk::DataStorage::Pointer m_DataStorage;
unsigned int m_DeleteObserverTag;
};
//## Documentation
//## main testing method
//## NOTE: the current Singleton implementation of DataTreeStorage will lead to crashes if a testcase fails
//## and therefore mitk::DataStorage::ShutdownSingleton() is not called.
int mitkDataStorageTest(int argc, char *argv[])
{
MITK_TEST_BEGIN("DataStorageTest");
// muellerm: test observer tag remove
mitk::TestStandaloneDataStorage::Pointer testDS = mitk::TestStandaloneDataStorage::New();
mitk::DataNode::Pointer n1 = mitk::DataNode::New();
testDS->Add(n1);
MITK_TEST_CONDITION_REQUIRED(testDS->GetModifiedObserverTags().size() == 1,
"Testing if modified"
" observer was added.");
MITK_TEST_CONDITION_REQUIRED(testDS->GetDeletedObserverTags().size() == 1,
"Testing if delete"
" observer was added.");
testDS->Remove(n1);
MITK_TEST_CONDITION_REQUIRED(testDS->GetModifiedObserverTags().size() == 0,
"Testing if modified"
" observer was removed.");
MITK_TEST_CONDITION_REQUIRED(testDS->GetDeletedObserverTags().size() == 0,
"Testing if delete"
" observer was removed.");
/* Create StandaloneDataStorage */
MITK_TEST_OUTPUT(<< "Create StandaloneDataStorage : ");
mitk::StandaloneDataStorage::Pointer sds;
try
{
sds = mitk::StandaloneDataStorage::New();
- MITK_TEST_CONDITION_REQUIRED(sds.IsNotNull(), "Testing Instatiation");
+ MITK_TEST_CONDITION_REQUIRED(sds.IsNotNull(), "Testing Instantiation");
}
catch (...)
{
MITK_TEST_FAILED_MSG(<< "Exception during creation of StandaloneDataStorage");
}
MITK_TEST_OUTPUT(<< "Testing StandaloneDataStorage: ");
MITK_TEST_CONDITION_REQUIRED(argc > 1, "Testing correct test invocation");
TestDataStorage(sds, argv[1]);
// TODO: Add specific StandaloneDataStorage Tests here
sds = nullptr;
MITK_TEST_END();
}
//##Documentation
//## @brief Test for the DataStorage class and its associated classes (e.g. the predicate classes)
//## This method will be called once for each subclass of DataStorage
void TestDataStorage(mitk::DataStorage *ds, std::string filename)
{
/* DataStorage valid? */
MITK_TEST_CONDITION_REQUIRED(ds != nullptr, "DataStorage valid?");
// Take the ItkImageFile Reader for the .nrrd data format.
// (was previously pic which is now deprecated format)
mitk::Image::Pointer image = mitk::IOUtil::Load<mitk::Image>(filename);
// create some DataNodes to fill the ds
mitk::DataNode::Pointer n1 = mitk::DataNode::New(); // node with image and name property
// mitk::Image::Pointer image = mitk::Image::New();
// unsigned int imageDimensions[] = { 10, 10, 10, 10 };
// mitk::PixelType pt(typeid(int));
// image->Initialize( pt, 4, imageDimensions );
n1->SetData(image);
n1->SetProperty("name", mitk::StringProperty::New("Node 1 - Image Node"));
mitk::DataStorage::SetOfObjects::Pointer parents1 = mitk::DataStorage::SetOfObjects::New();
mitk::DataNode::Pointer n2 = mitk::DataNode::New(); // node with surface and name and color properties
mitk::Surface::Pointer surface = mitk::Surface::New();
n2->SetData(surface);
n2->SetProperty("name", mitk::StringProperty::New("Node 2 - Surface Node"));
mitk::Color color;
color.Set(1.0f, 1.0f, 0.0f);
n2->SetColor(color);
n2->SetProperty("Resection Proposal 1", mitk::GroupTagProperty::New());
mitk::DataStorage::SetOfObjects::Pointer parents2 = mitk::DataStorage::SetOfObjects::New();
parents2->InsertElement(0, n1); // n1 (image node) is source of n2 (surface node)
mitk::DataNode::Pointer n3 = mitk::DataNode::New(); // node without data but with name property
n3->SetProperty("name", mitk::StringProperty::New("Node 3 - Empty Node"));
n3->SetProperty("Resection Proposal 1", mitk::GroupTagProperty::New());
n3->SetProperty("Resection Proposal 2", mitk::GroupTagProperty::New());
mitk::DataStorage::SetOfObjects::Pointer parents3 = mitk::DataStorage::SetOfObjects::New();
parents3->InsertElement(0, n2); // n2 is source of n3
mitk::DataNode::Pointer n4 = mitk::DataNode::New(); // node without data but with color property
n4->SetColor(color);
n4->SetProperty("Resection Proposal 2", mitk::GroupTagProperty::New());
mitk::DataStorage::SetOfObjects::Pointer parents4 = mitk::DataStorage::SetOfObjects::New();
parents4->InsertElement(0, n2);
parents4->InsertElement(1, n3); // n2 and n3 are sources of n4
mitk::DataNode::Pointer n5 = mitk::DataNode::New(); // extra node
n5->SetProperty("name", mitk::StringProperty::New("Node 5"));
try /* adding objects */
{
/* Add an object */
ds->Add(n1, parents1);
MITK_TEST_CONDITION_REQUIRED((ds->GetAll()->Size() == 1) && (ds->GetAll()->GetElement(0) == n1),
"Testing Adding a new object");
/* Check exception on adding the same object again */
MITK_TEST_OUTPUT(<< "Check exception on adding the same object again: ");
MITK_TEST_FOR_EXCEPTION(std::exception, ds->Add(n1, parents1));
MITK_TEST_CONDITION(ds->GetAll()->Size() == 1, "Test if object count is correct after exception");
/* Add an object that has a source object */
ds->Add(n2, parents2);
MITK_TEST_CONDITION_REQUIRED(ds->GetAll()->Size() == 2, "Testing Adding an object that has a source object");
/* Add some more objects needed for further tests */
ds->Add(n3, parents3); // n3 object that has name property and one parent
ds->Add(n4, parents4); // n4 object that has color property
ds->Add(n5); // n5 has no parents
MITK_TEST_CONDITION_REQUIRED(ds->GetAll()->Size() == 5, "Adding some more objects needed for further tests");
}
catch (...)
{
MITK_TEST_FAILED_MSG(<< "Exception during object creation");
}
try /* object retrieval methods */
{
/* Requesting all Objects */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetAll();
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((stlAll.size() == 5) // check if all tree nodes are in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n5) != stlAll.end()),
"Testing GetAll()");
}
/* Requesting a named object */
{
mitk::NodePredicateProperty::Pointer predicate(
mitk::NodePredicateProperty::New("name", mitk::StringProperty::New("Node 2 - Surface Node")));
mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n2), "Requesting a named object");
}
/* Requesting objects of specific data type */
{
mitk::NodePredicateDataType::Pointer predicate(mitk::NodePredicateDataType::New("Image"));
mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1), "Requesting objects of specific data type")
}
/* Requesting objects of specific dimension */
{
mitk::NodePredicateDimension::Pointer predicate(mitk::NodePredicateDimension::New(4));
mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1), "Requesting objects of specific dimension")
}
/* Requesting objects with specific data object */
{
mitk::NodePredicateData::Pointer predicate(mitk::NodePredicateData::New(image));
mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1),
"Requesting objects with specific data object")
}
/* Requesting objects with nullptr data */
{
mitk::NodePredicateData::Pointer predicate(mitk::NodePredicateData::New(nullptr));
mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 3) && (std::find(all->begin(), all->end(), n3) != all->end()) &&
(std::find(all->begin(), all->end(), n4) != all->end()) &&
(std::find(all->begin(), all->end(), n5) != all->end()),
"Requesting objects with nullptr data");
}
/* Requesting objects that meet a conjunction criteria */
{
mitk::NodePredicateDataType::Pointer p1 = mitk::NodePredicateDataType::New("Surface");
mitk::NodePredicateProperty::Pointer p2 =
mitk::NodePredicateProperty::New("color", mitk::ColorProperty::New(color));
mitk::NodePredicateAnd::Pointer predicate = mitk::NodePredicateAnd::New();
predicate->AddPredicate(p1);
predicate->AddPredicate(p2); // objects must be of datatype "Surface" and have red color (= n2)
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n2),
"Requesting objects that meet a conjunction criteria");
}
/* Requesting objects that meet a disjunction criteria */
{
mitk::NodePredicateDataType::Pointer p1(mitk::NodePredicateDataType::New("Image"));
mitk::NodePredicateProperty::Pointer p2(
mitk::NodePredicateProperty::New("color", mitk::ColorProperty::New(color)));
mitk::NodePredicateOr::Pointer predicate = mitk::NodePredicateOr::New();
predicate->AddPredicate(p1);
predicate->AddPredicate(p2); // objects must be of datatype "Surface" or have red color (= n1, n2, n4)
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
MITK_TEST_CONDITION((all->Size() == 3) && (std::find(all->begin(), all->end(), n1) != all->end()) &&
(std::find(all->begin(), all->end(), n2) != all->end()) &&
(std::find(all->begin(), all->end(), n4) != all->end()),
"Requesting objects that meet a disjunction criteria");
}
/* Requesting objects that do not meet a criteria */
{
mitk::ColorProperty::Pointer cp = mitk::ColorProperty::New(color);
mitk::NodePredicateProperty::Pointer proppred(mitk::NodePredicateProperty::New("color", cp));
mitk::NodePredicateNot::Pointer predicate(mitk::NodePredicateNot::New(proppred));
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 3) // check if correct objects are in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n5) != stlAll.end()),
"Requesting objects that do not meet a criteria");
}
/* Requesting *direct* source objects */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all =
ds->GetSources(n3, nullptr, true); // Get direct parents of n3 (=n2)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 1) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()),
"Requesting *direct* source objects");
}
/* Requesting *all* source objects */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all =
ds->GetSources(n3, nullptr, false); // Get all parents of n3 (= n1 + n2)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 2) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()),
"Requesting *all* source objects"); // check if n1 and n2 are the resultset
}
/* Requesting *all* sources of object with multiple parents */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all =
ds->GetSources(n4, nullptr, false); // Get all parents of n4 (= n1 + n2 + n3)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION(
(all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n1 and n2 and n3 are the resultset
,
"Requesting *all* sources of object with multiple parents");
}
/* Requesting *direct* derived objects */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all =
ds->GetDerivations(n1, nullptr, true); // Get direct childs of n1 (=n2)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 1) && (std::find(stlAll.begin(), stlAll.end(), n2) !=
stlAll.end()) // check if n1 is the resultset
,
"Requesting *direct* derived objects");
}
///* Requesting *direct* derived objects with multiple parents/derivations */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all =
ds->GetDerivations(n2, nullptr, true); // Get direct childs of n2 (=n3 + n4)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION(
(all->Size() == 2) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n3 is the resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()) // check if n4 is the resultset
,
"Requesting *direct* derived objects with multiple parents/derivations");
}
//* Requesting *all* derived objects */
{
const mitk::DataStorage::SetOfObjects::ConstPointer all =
ds->GetDerivations(n1, nullptr, false); // Get all childs of n1 (=n2, n3, n4)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()),
"Requesting *all* derived objects");
}
/* Checking for circular source relationships */
{
parents1->InsertElement(0, n4); // make n1 derived from n4 (which is derived from n2, which is derived from n1)
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(
n4,
nullptr,
false); // Get all parents of n4 (= n1 + n2 + n3, not n4 itself and not multiple versions of the nodes!)
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION(
(all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n1 and n2 and n3 are the resultset
,
"Checking for circular source relationships");
}
///* Checking for circular derivation relationships can not be performed, because the internal derivations
/// datastructure
// can not be accessed from the outside. (Therefore it should not be possible to create these circular relations
// */
//* Checking GroupTagProperty */
{
mitk::GroupTagProperty::Pointer tp = mitk::GroupTagProperty::New();
mitk::NodePredicateProperty::Pointer pred(mitk::NodePredicateProperty::New("Resection Proposal 1", tp));
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(pred);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 2) // check if n2 and n3 are in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()),
"Checking GroupTagProperty");
}
/* Checking GroupTagProperty 2 */
{
mitk::GroupTagProperty::Pointer tp = mitk::GroupTagProperty::New();
mitk::NodePredicateProperty::Pointer pred(mitk::NodePredicateProperty::New("Resection Proposal 2", tp));
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(pred);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 2) // check if n3 and n4 are in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()),
"Checking GroupTagProperty 2");
}
/* Checking direct sources with condition */
{
mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("Surface");
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, true);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 1) // check if n2 is in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()),
"checking direct sources with condition");
}
/* Checking all sources with condition */
{
mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("Image");
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, false);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 1) // check if n1 is in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()),
"Checking all sources with condition");
}
/* Checking all sources with condition with empty resultset */
{
mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("VesselTree");
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, false);
MITK_TEST_CONDITION(all->Size() == 0,
"Checking all sources with condition with empty resultset"); // check if resultset is empty
}
/* Checking direct derivations with condition */
{
mitk::NodePredicateProperty::Pointer pred = mitk::NodePredicateProperty::New("color");
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, pred, true);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 1) // check if n2 is in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()),
"Checking direct derivations with condition");
}
/* Checking all derivations with condition */
{
mitk::NodePredicateProperty::Pointer pred = mitk::NodePredicateProperty::New("color");
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, pred, false);
std::vector<mitk::DataNode::Pointer> stlAll = all->CastToSTLConstContainer();
MITK_TEST_CONDITION((all->Size() == 2) // check if n2 and n4 are in resultset
&&
(std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) &&
(std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()),
"Checking direct derivations with condition");
}
/* Checking named node method */
MITK_TEST_CONDITION(ds->GetNamedNode("Node 2 - Surface Node") == n2, "Checking named node method");
MITK_TEST_CONDITION(ds->GetNamedNode(std::string("Node 2 - Surface Node")) == n2,
"Checking named node(std::string) method");
/* Checking named node method with wrong name */
MITK_TEST_CONDITION(ds->GetNamedNode("This name does not exist") == nullptr,
"Checking named node method with wrong name");
/* Checking named object method */
MITK_TEST_CONDITION(ds->GetNamedObject<mitk::Image>("Node 1 - Image Node") == image,
"Checking named object method");
MITK_TEST_CONDITION(ds->GetNamedObject<mitk::Image>(std::string("Node 1 - Image Node")) == image,
"Checking named object(std::string) method");
/* Checking named object method with wrong DataType */
MITK_TEST_CONDITION(ds->GetNamedObject<mitk::Surface>("Node 1 - Image Node") == nullptr,
"Checking named object method with wrong DataType");
/* Checking named object method with wrong name */
MITK_TEST_CONDITION(ds->GetNamedObject<mitk::Image>("This name does not exist") == nullptr,
"Checking named object method with wrong name");
/* Checking GetNamedDerivedNode with valid name and direct derivation only */
MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 2 - Surface Node", n1, true) == n2,
"Checking GetNamedDerivedNode with valid name & direct derivation only");
/* Checking GetNamedDerivedNode with invalid Name and direct derivation only */
MITK_TEST_CONDITION(ds->GetNamedDerivedNode("wrong name", n1, true) == nullptr,
"Checking GetNamedDerivedNode with invalid name & direct derivation only");
/* Checking GetNamedDerivedNode with invalid Name and direct derivation only */
MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 3 - Empty Node", n1, false) == n3,
"Checking GetNamedDerivedNode with invalid name & direct derivation only");
/* Checking GetNamedDerivedNode with valid Name but direct derivation only */
MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 3 - Empty Node", n1, true) == nullptr,
"Checking GetNamedDerivedNode with valid Name but direct derivation only");
/* Checking GetNode with valid predicate */
{
mitk::NodePredicateDataType::Pointer p(mitk::NodePredicateDataType::New("Image"));
MITK_TEST_CONDITION(ds->GetNode(p) == n1, "Checking GetNode with valid predicate");
}
/* Checking GetNode with invalid predicate */
{
mitk::NodePredicateDataType::Pointer p(mitk::NodePredicateDataType::New("PointSet"));
MITK_TEST_CONDITION(ds->GetNode(p) == nullptr, "Checking GetNode with invalid predicate");
}
} // object retrieval methods
catch (...)
{
MITK_TEST_FAILED_MSG(<< "Exception during object retrieval (GetXXX() Methods)");
}
try /* object removal methods */
{
/* Checking removal of a node without relations */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
int refCountbeforeDS = watcher->GetReferenceCount();
ds->Add(extra);
MITK_TEST_CONDITION(ds->GetNamedNode("extra") == extra, "Adding extra node");
ds->Remove(extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr) && (refCountbeforeDS == watcher->GetReferenceCount()),
"Checking removal of a node without relations");
extra = nullptr;
}
/* Checking removal of a node with a parent */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
int refCountbeforeDS = watcher->GetReferenceCount();
ds->Add(extra, n1); // n1 is parent of extra
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) &&
(ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1
,
"Adding extra node");
ds->Remove(extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr) && (refCountbeforeDS == watcher->GetReferenceCount()) &&
(ds->GetDerivations(n1)->Size() == 1),
"Checking removal of a node with a parent");
extra = nullptr;
}
/* Checking removal of a node with two parents */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
int refCountbeforeDS = watcher->GetReferenceCount();
mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New();
p->push_back(n1);
p->push_back(n2);
ds->Add(extra, p); // n1 and n2 are parents of extra
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) &&
(ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1
&&
(ds->GetDerivations(n2)->Size() == 3),
"add extra node");
ds->Remove(extra);
MITK_TEST_CONDITION(
(ds->GetNamedNode("extra") == nullptr) && (refCountbeforeDS == watcher->GetReferenceCount()) &&
(ds->GetDerivations(n1)->Size() == 1) // after remove, only n2 should be derived from n1
&&
(ds->GetDerivations(n2)->Size() == 2) // after remove, only n3 and n4 should be derived from n2
,
"Checking removal of a node with two parents");
extra = nullptr;
}
/* Checking removal of a node with two derived nodes */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
int refCountbeforeDS = watcher->GetReferenceCount();
ds->Add(extra);
mitk::DataNode::Pointer d1 = mitk::DataNode::New();
d1->SetProperty("name", mitk::StringProperty::New("d1"));
ds->Add(d1, extra);
mitk::DataNode::Pointer d2 = mitk::DataNode::New();
d2->SetProperty("name", mitk::StringProperty::New("d2"));
ds->Add(d2, extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1") == d1) &&
(ds->GetNamedNode("d2") == d2) &&
(ds->GetSources(d1)->Size() == 1) // extra should be source of d1
&&
(ds->GetSources(d2)->Size() == 1) // extra should be source of d2
&&
(ds->GetDerivations(extra)->Size() == 2) // d1 and d2 should be derived from extra
,
"add extra node");
ds->Remove(extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr) && (ds->GetNamedNode("d1") == d1) &&
(ds->GetNamedNode("d2") == d2) && (refCountbeforeDS == watcher->GetReferenceCount()) &&
(ds->GetSources(d1)->Size() == 0) // after remove, d1 should not have a source anymore
&&
(ds->GetSources(d2)->Size() == 0) // after remove, d2 should not have a source anymore
,
"Checking removal of a node with two derived nodes");
extra = nullptr;
}
/* Checking removal of a node with two parents and two derived nodes */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
mitk::ReferenceCountWatcher::Pointer n1watcher = new mitk::ReferenceCountWatcher(n1);
int refCountbeforeDS = watcher->GetReferenceCount();
mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New();
p->push_back(n1);
p->push_back(n2);
ds->Add(extra, p); // n1 and n2 are parents of extra
mitk::DataNode::Pointer d1 = mitk::DataNode::New();
d1->SetProperty("name", mitk::StringProperty::New("d1x"));
ds->Add(d1, extra);
mitk::DataNode::Pointer d2 = mitk::DataNode::New();
d2->SetProperty("name", mitk::StringProperty::New("d2x"));
ds->Add(d2, extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1x") == d1) &&
(ds->GetNamedNode("d2x") == d2) &&
(ds->GetSources(d1)->Size() == 1) // extra should be source of d1
&&
(ds->GetSources(d2)->Size() == 1) // extra should be source of d2
&&
(ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1
&&
(ds->GetDerivations(n2)->Size() == 3) // n3, n4 and extra should be derived from n2
&&
(ds->GetDerivations(extra)->Size() == 2) // d1 and d2 should be derived from extra
,
"add extra node");
ds->Remove(extra);
MITK_TEST_CONDITION(
(ds->GetNamedNode("extra") == nullptr) && (ds->GetNamedNode("d1x") == d1) && (ds->GetNamedNode("d2x") == d2) &&
(refCountbeforeDS == watcher->GetReferenceCount()) &&
(ds->GetDerivations(n1)->Size() == 1) // after remove, only n2 should be derived from n1
&&
(ds->GetDerivations(n2)->Size() == 2) // after remove, only n3 and n4 should be derived from n2
&&
(ds->GetSources(d1)->Size() == 0) // after remove, d1 should not have a source anymore
&&
(ds->GetSources(d2)->Size() == 0) // after remove, d2 should not have a source anymore
,
"Checking removal of a node with two parents and two derived nodes");
extra = nullptr;
}
}
catch (...)
{
MITK_TEST_FAILED_MSG(<< "Exception during object removal methods");
}
/* Checking for node is it's own parent exception */
{
MITK_TEST_FOR_EXCEPTION_BEGIN(std::exception);
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New();
p->push_back(n1);
p->push_back(extra); // extra is parent of extra!!!
ds->Add(extra, p);
MITK_TEST_FOR_EXCEPTION_END(std::exception);
}
/* Checking reference count of node after add and remove */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
extra->SetProperty("name", mitk::StringProperty::New("extra"));
mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New();
p->push_back(n1);
p->push_back(n3);
ds->Add(extra, p);
extra = nullptr;
ds->Remove(ds->GetNamedNode("extra"));
MITK_TEST_CONDITION(watcher->GetReferenceCount() == 0, "Checking reference count of node after add and remove");
}
/* Checking removal of a node with two derived nodes [ dataStorage->GetDerivations( rootNode )] see bug #3426 */
{
mitk::DataNode::Pointer extra = mitk::DataNode::New();
extra->SetProperty("name", mitk::StringProperty::New("extra"));
ds->Add(extra);
mitk::DataNode::Pointer d1y = mitk::DataNode::New();
d1y->SetProperty("name", mitk::StringProperty::New("d1y"));
mitk::ReferenceCountWatcher::Pointer watcherD1y = new mitk::ReferenceCountWatcher(d1y);
int refCountbeforeDS = watcherD1y->GetReferenceCount();
ds->Add(d1y, extra);
mitk::DataNode::Pointer d2y = mitk::DataNode::New();
d2y->SetProperty("name", mitk::StringProperty::New("d2y"));
ds->Add(d2y, extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1y") == d1y) &&
(ds->GetNamedNode("d2y") == d2y) &&
(ds->GetSources(d1y)->Size() == 1) // extra should be source of d1y
&&
(ds->GetSources(d2y)->Size() == 1) // extra should be source of d2y
&&
(ds->GetDerivations(extra)->Size() == 2) // d1y and d2y should be derived from extra
,
"add extra node");
ds->Remove(ds->GetDerivations(extra));
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) &&
(ds->GetNamedNode("d1y") == nullptr) // d1y should be nullptr now
&&
(ds->GetNamedNode("d2y") == nullptr) // d2y should be nullptr now
&&
(refCountbeforeDS == watcherD1y->GetReferenceCount()),
"Checking removal of subset of two derived nodes from one parent node");
ds->Remove(extra);
MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr), "Checking removal of a parent node");
extra = nullptr;
}
/* Checking GetGrouptags() */
{
const std::set<std::string> groupTags = ds->GetGroupTags();
MITK_TEST_CONDITION((groupTags.size() == 2) &&
(std::find(groupTags.begin(), groupTags.end(), "Resection Proposal 1") != groupTags.end()) &&
(std::find(groupTags.begin(), groupTags.end(), "Resection Proposal 2") != groupTags.end()),
"Checking GetGrouptags()");
}
/* Checking Event handling */
DSEventReceiver listener;
try
{
ds->AddNodeEvent +=
mitk::MessageDelegate1<DSEventReceiver, const mitk::DataNode *>(&listener, &DSEventReceiver::OnAdd);
ds->RemoveNodeEvent +=
mitk::MessageDelegate1<DSEventReceiver, const mitk::DataNode *>(&listener, &DSEventReceiver::OnRemove);
mitk::DataNode::Pointer extra = mitk::DataNode::New();
mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra);
ds->Add(extra);
MITK_TEST_CONDITION(listener.m_NodeAdded == extra.GetPointer(), "Checking AddEvent");
ds->Remove(extra);
MITK_TEST_CONDITION(listener.m_NodeRemoved == extra.GetPointer(), "Checking RemoveEvent");
/* RemoveListener */
ds->AddNodeEvent -=
mitk::MessageDelegate1<DSEventReceiver, const mitk::DataNode *>(&listener, &DSEventReceiver::OnAdd);
ds->RemoveNodeEvent -=
mitk::MessageDelegate1<DSEventReceiver, const mitk::DataNode *>(&listener, &DSEventReceiver::OnRemove);
listener.m_NodeAdded = nullptr;
listener.m_NodeRemoved = nullptr;
ds->Add(extra);
ds->Remove(extra);
MITK_TEST_CONDITION((listener.m_NodeRemoved == nullptr) && (listener.m_NodeAdded == nullptr), "Checking RemoveListener");
std::cout << "Pointer handling after event handling: " << std::flush;
extra = nullptr; // delete reference to the node. its memory should be freed now
MITK_TEST_CONDITION(watcher->GetReferenceCount() == 0, "Pointer handling after event handling");
}
catch (...)
{
/* cleanup */
ds->AddNodeEvent -=
mitk::MessageDelegate1<DSEventReceiver, const mitk::DataNode *>(&listener, &DSEventReceiver::OnAdd);
ds->RemoveNodeEvent -=
mitk::MessageDelegate1<DSEventReceiver, const mitk::DataNode *>(&listener, &DSEventReceiver::OnRemove);
MITK_TEST_FAILED_MSG(<< "Exception during object removal methods");
}
// Checking ComputeBoundingGeometry3D method*/
const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetAll();
auto geometry = ds->ComputeBoundingGeometry3D();
MITK_TEST_CONDITION(geometry->CountTimeSteps() == 4, "Test for number or time steps with ComputeBoundingGeometry()");
mitk::TimeBounds timebounds = geometry->GetTimeBounds();
MITK_TEST_CONDITION((timebounds[0] == 0) && (timebounds[1] == 4),
"Test for timebounds with ComputeBoundingGeometry()");
for (unsigned int i = 0; i < geometry->CountTimeSteps(); i++)
{
mitk::BaseGeometry::Pointer subGeometry = geometry->GetGeometryForTimeStep(i);
mitk::TimeBounds bounds = geometry->GetTimeBounds(i);
MITK_TEST_CONDITION((bounds[0] == i) && (bounds[1] == i + 1),
"Test for timebounds of geometry at different time steps with ComputeBoundingGeometry()");
}
geometry = ds->ComputeBoundingGeometry3D(all);
MITK_TEST_CONDITION(geometry->CountTimeSteps() == 4,
"Test for number or time steps with ComputeBoundingGeometry(allNodes)");
timebounds = geometry->GetTimeBounds();
MITK_TEST_CONDITION((timebounds[0] == 0) && (timebounds[1] == 4),
"Test for timebounds with ComputeBoundingGeometry(allNodes)");
for (unsigned int i = 0; i < geometry->CountTimeSteps(); i++)
{
mitk::BaseGeometry::Pointer subGeometry = geometry->GetGeometryForTimeStep(i);
mitk::TimeBounds bounds = geometry->GetTimeBounds(i);
MITK_TEST_CONDITION((bounds[0] == i) && (bounds[1] == i + 1),
"Test for timebounds of geometry at different time steps with ComputeBoundingGeometry()");
}
// test for thread safety of DataStorage
try
{
mitk::StandaloneDataStorage::Pointer standaloneDataStorage = mitk::StandaloneDataStorage::New();
ItkDeleteEventListener listener(standaloneDataStorage);
{
mitk::DataNode::Pointer emptyNode = mitk::DataNode::New();
mitk::DataNode *pEmptyNode = emptyNode;
listener.SetNode(emptyNode);
standaloneDataStorage->Add(emptyNode);
emptyNode = nullptr; // emptyNode is still alive because standaloneDataStorage
// owns it
standaloneDataStorage->Remove(pEmptyNode); // this should not freeze the whole thing
}
}
catch (...)
{
MITK_TEST_FAILED_MSG(<< "Exception during testing DataStorage thread safe");
}
/* Clear DataStorage */
ds->Remove(ds->GetAll());
MITK_TEST_CONDITION(ds->GetAll()->Size() == 0, "Checking Clear DataStorage");
}
diff --git a/Modules/Core/test/mitkExtractSliceFilterTest.cpp b/Modules/Core/test/mitkExtractSliceFilterTest.cpp
index afe25f5d1f..d371bdbf31 100644
--- a/Modules/Core/test/mitkExtractSliceFilterTest.cpp
+++ b/Modules/Core/test/mitkExtractSliceFilterTest.cpp
@@ -1,1069 +1,1069 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkExtractSliceFilter.h>
#include <mitkIOUtil.h>
#include <mitkITKImageImport.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <mitkImagePixelReadAccessor.h>
#include <mitkInteractionConst.h>
#include <mitkNumericTypes.h>
#include <mitkRotationOperation.h>
#include <mitkStandardFileLocations.h>
#include <mitkTestingMacros.h>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <mitkGeometry3D.h>
#include <vtkActor.h>
#include <vtkImageActor.h>
#include <vtkImageData.h>
#include <vtkImageMapToColors.h>
#include <vtkImageMapper.h>
#include <vtkImageReslice.h>
#include <vtkInteractorStyleImage.h>
#include <vtkLookupTable.h>
#include <vtkPlaneSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkTexture.h>
// use this to create the test volume on the fly
#define CREATE_VOLUME
// use this to save the created volume
//#define SAVE_VOLUME
// use this to calculate the error from the sphere mathematical model to our pixel based one
//#define CALC_TESTFAILURE_DEVIATION
// use this to render an oblique slice through a specified image
//#define SHOW_SLICE_IN_RENDER_WINDOW
// use this to have infos printed in mitkLog
//#define EXTRACTOR_DEBUG
/*these are the deviations calculated by the function CalcTestFailureDeviation (see for details)*/
#define Testfailure_Deviation_Mean_128 0.853842
#define Testfailure_Deviation_Volume_128 0.145184
#define Testfailure_Deviation_Diameter_128 1.5625
#define Testfailure_Deviation_Mean_256 0.397693
#define Testfailure_Deviation_Volume_256 0.0141357
#define Testfailure_Deviation_Diameter_256 0.78125
#define Testfailure_Deviation_Mean_512 0.205277
#define Testfailure_Deviation_Volume_512 0.01993
#define Testfailure_Deviation_Diameter_512 0.390625
class mitkExtractSliceFilterTestClass
{
public:
static void TestSlice(mitk::PlaneGeometry *planeGeometry, std::string testname)
{
TestPlane = planeGeometry;
TestName = testname;
mitk::ScalarType centerCoordValue = TestvolumeSize / 2.0;
mitk::ScalarType center[3] = {centerCoordValue, centerCoordValue, centerCoordValue};
mitk::Point3D centerIndex(center);
double radius = TestvolumeSize / 4.0;
if (TestPlane->Distance(centerIndex) >= radius)
return; // outside sphere
// feed ExtractSliceFilter
mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New();
slicer->SetInput(TestVolume);
slicer->SetWorldGeometry(TestPlane);
slicer->Update();
MITK_TEST_CONDITION_REQUIRED(slicer->GetOutput() != nullptr, "Extractor returned a slice");
mitk::Image::Pointer reslicedImage = slicer->GetOutput();
AccessFixedDimensionByItk(reslicedImage, TestSphereRadiusByItk, 2);
AccessFixedDimensionByItk(reslicedImage, TestSphereAreaByItk, 2);
/*
double devArea, devDiameter;
if(TestvolumeSize == 128.0){ devArea = Testfailure_Deviation_Volume_128; devDiameter =
Testfailure_Deviation_Diameter_128; }
else if(TestvolumeSize == 256.0){devArea = Testfailure_Deviation_Volume_256; devDiameter =
Testfailure_Deviation_Diameter_256;}
else if (TestvolumeSize == 512.0){devArea = Testfailure_Deviation_Volume_512; devDiameter =
Testfailure_Deviation_Diameter_512;}
else{devArea = Testfailure_Deviation_Volume_128; devDiameter = Testfailure_Deviation_Diameter_128;}
*/
std::string areatestName = TestName.append(" area");
std::string diametertestName = TestName.append(" testing diameter");
// TODO think about the deviation, 1% makes no sense at all
MITK_TEST_CONDITION(std::abs(100 - testResults.percentageAreaCalcToPixel) < 1, areatestName);
MITK_TEST_CONDITION(std::abs(100 - testResults.percentageRadiusToPixel) < 1, diametertestName);
#ifdef EXTRACTOR_DEBUG
MITK_INFO << TestName << " >>> "
<< "planeDistanceToSphereCenter: " << testResults.planeDistanceToSphereCenter;
MITK_INFO << "area in pixels: " << testResults.areaInPixel << " <-> area in mm: " << testResults.areaCalculated
<< " = " << testResults.percentageAreaCalcToPixel << "%";
MITK_INFO << "calculated diameter: " << testResults.diameterCalculated
<< " <-> diameter in mm: " << testResults.diameterInMM
<< " <-> diameter in pixel: " << testResults.diameterInPixel << " = "
<< testResults.percentageRadiusToPixel << "%";
#endif
}
/*
* get the radius of the slice of a sphere based on pixel distance from edge to edge of the circle.
*/
template <typename TPixel, unsigned int VImageDimension>
static void TestSphereRadiusByItk(itk::Image<TPixel, VImageDimension> *inputImage)
{
typedef itk::Image<TPixel, VImageDimension> InputImageType;
// set the index to the middle of the image's edge at x and y axis
typename InputImageType::IndexType currentIndexX;
currentIndexX[0] = (int)(TestvolumeSize / 2.0);
currentIndexX[1] = 0;
typename InputImageType::IndexType currentIndexY;
currentIndexY[0] = 0;
currentIndexY[1] = (int)(TestvolumeSize / 2.0);
// remember the last pixel value
double lastValueX = inputImage->GetPixel(currentIndexX);
double lastValueY = inputImage->GetPixel(currentIndexY);
// storage for the index marks
std::vector<typename InputImageType::IndexType> indicesX;
std::vector<typename InputImageType::IndexType> indicesY;
/*Get four indices on the edge of the circle*/
while (currentIndexX[1] < TestvolumeSize && currentIndexX[0] < TestvolumeSize)
{
// move x direction
currentIndexX[1] += 1;
// move y direction
currentIndexY[0] += 1;
if (inputImage->GetPixel(currentIndexX) > lastValueX)
{
// mark the current index
typename InputImageType::IndexType markIndex;
markIndex[0] = currentIndexX[0];
markIndex[1] = currentIndexX[1];
indicesX.push_back(markIndex);
}
else if (inputImage->GetPixel(currentIndexX) < lastValueX)
{
// mark the current index
typename InputImageType::IndexType markIndex;
markIndex[0] = currentIndexX[0];
markIndex[1] = currentIndexX[1] - 1; // value inside the sphere
indicesX.push_back(markIndex);
}
if (inputImage->GetPixel(currentIndexY) > lastValueY)
{
// mark the current index
typename InputImageType::IndexType markIndex;
markIndex[0] = currentIndexY[0];
markIndex[1] = currentIndexY[1];
indicesY.push_back(markIndex);
}
else if (inputImage->GetPixel(currentIndexY) < lastValueY)
{
// mark the current index
typename InputImageType::IndexType markIndex;
markIndex[0] = currentIndexY[0];
markIndex[1] = currentIndexY[1] - 1; // value inside the sphere
indicesY.push_back(markIndex);
}
// found both marks?
if (indicesX.size() == 2 && indicesY.size() == 2)
break;
// the new 'last' values
lastValueX = inputImage->GetPixel(currentIndexX);
lastValueY = inputImage->GetPixel(currentIndexY);
}
/*
*If we are here we found the four marks on the edge of the circle.
*For the case our plane is rotated and shifted, we have to calculate the center of the circle,
*else the center is the intersection of both straight lines between the marks.
*When we have the center, the diameter of the circle will be checked to the reference value(math!).
*/
// each distance from the first mark of each direction to the center of the straight line between the marks
double distanceToCenterX = std::abs(indicesX[0][1] - indicesX[1][1]) / 2.0;
// double distanceToCenterY = std::abs(indicesY[0][0] - indicesY[1][0]) / 2.0;
// the center of the straight lines
typename InputImageType::IndexType centerX;
// typename InputImageType::IndexType centerY;
centerX[0] = indicesX[0][0];
centerX[1] = indicesX[0][1] + distanceToCenterX;
// TODO think about implicit cast to int. this is not the real center of the image, which could be between two
// pixels
// centerY[0] = indicesY[0][0] + distanceToCenterY;
// centerY[1] = inidcesY[0][1];
typename InputImageType::IndexType currentIndex(centerX);
lastValueX = inputImage->GetPixel(currentIndex);
long sumpixels = 0;
std::vector<typename InputImageType::IndexType> diameterIndices;
// move up
while (currentIndex[1] < TestvolumeSize)
{
currentIndex[1] += 1;
if (inputImage->GetPixel(currentIndex) != lastValueX)
{
typename InputImageType::IndexType markIndex;
markIndex[0] = currentIndex[0];
markIndex[1] = currentIndex[1] - 1;
diameterIndices.push_back(markIndex);
break;
}
sumpixels++;
lastValueX = inputImage->GetPixel(currentIndex);
}
currentIndex[1] -= sumpixels; // move back to center to go in the other direction
lastValueX = inputImage->GetPixel(currentIndex);
// move down
while (currentIndex[1] >= 0)
{
currentIndex[1] -= 1;
if (inputImage->GetPixel(currentIndex) != lastValueX)
{
typename InputImageType::IndexType markIndex;
markIndex[0] = currentIndex[0];
markIndex[1] = currentIndex[1]; // outside sphere because we want to calculate the distance from edge to edge
diameterIndices.push_back(markIndex);
break;
}
sumpixels++;
lastValueX = inputImage->GetPixel(currentIndex);
}
/*
*Now sumpixels should be the apromximate diameter of the circle. This is checked with the calculated diameter from
*the plane transformation(math).
*/
mitk::Point3D volumeCenter;
volumeCenter[0] = volumeCenter[1] = volumeCenter[2] = TestvolumeSize / 2.0;
double planeDistanceToSphereCenter = TestPlane->Distance(volumeCenter);
double sphereRadius = TestvolumeSize / 4.0;
// calculate the radius of the circle cut from the sphere by the plane
double diameter = 2.0 * std::sqrt(std::pow(sphereRadius, 2) - std::pow(planeDistanceToSphereCenter, 2));
double percentageRadiusToPixel = 100 / diameter * sumpixels;
/*
*calculate the radius in mm by the both marks of the center line by using the world coordinates
*/
// get the points as 3D coordinates
mitk::Vector3D diameterPointRight, diameterPointLeft;
diameterPointRight[2] = diameterPointLeft[2] = 0.0;
diameterPointLeft[0] = diameterIndices[0][0];
diameterPointLeft[1] = diameterIndices[0][1];
diameterPointRight[0] = diameterIndices[1][0];
diameterPointRight[1] = diameterIndices[1][1];
// transform to worldcoordinates
TestVolume->GetGeometry()->IndexToWorld(diameterPointLeft, diameterPointLeft);
TestVolume->GetGeometry()->IndexToWorld(diameterPointRight, diameterPointRight);
// euklidian distance
double diameterInMM = ((diameterPointLeft * -1.0) + diameterPointRight).GetNorm();
testResults.diameterInMM = diameterInMM;
testResults.diameterCalculated = diameter;
testResults.diameterInPixel = sumpixels;
testResults.percentageRadiusToPixel = percentageRadiusToPixel;
testResults.planeDistanceToSphereCenter = planeDistanceToSphereCenter;
}
/*brute force the area pixel by pixel*/
template <typename TPixel, unsigned int VImageDimension>
static void TestSphereAreaByItk(itk::Image<TPixel, VImageDimension> *inputImage)
{
typedef itk::Image<TPixel, VImageDimension> InputImageType;
typedef itk::ImageRegionConstIterator<InputImageType> ImageIterator;
ImageIterator imageIterator(inputImage, inputImage->GetLargestPossibleRegion());
imageIterator.GoToBegin();
int sumPixelsInArea = 0;
while (!imageIterator.IsAtEnd())
{
if (inputImage->GetPixel(imageIterator.GetIndex()) == pixelValueSet)
sumPixelsInArea++;
++imageIterator;
}
mitk::Point3D volumeCenter;
volumeCenter[0] = volumeCenter[1] = volumeCenter[2] = TestvolumeSize / 2.0;
double planeDistanceToSphereCenter = TestPlane->Distance(volumeCenter);
double sphereRadius = TestvolumeSize / 4.0;
// calculate the radius of the circle cut from the sphere by the plane
double radius = std::sqrt(std::pow(sphereRadius, 2) - std::pow(planeDistanceToSphereCenter, 2));
double areaInMM = 3.14159265358979 * std::pow(radius, 2);
testResults.areaCalculated = areaInMM;
testResults.areaInPixel = sumPixelsInArea;
testResults.percentageAreaCalcToPixel = 100 / areaInMM * sumPixelsInArea;
}
/*
- * random a voxel. define plane through this voxel. reslice at the plane. compare the pixel vaues of the voxel
+ * random a voxel. define plane through this voxel. reslice at the plane. compare the pixel values of the voxel
* in the volume with the pixel value in the resliced image.
* there are some indice shifting problems which causes the test to fail for oblique planes. seems like the chosen
* worldcoordinate is not corresponding to the index in the 2D image. and so the pixel values are not the same as
* expected.
*/
static void PixelvalueBasedTest()
{
/* setup itk image */
typedef itk::Image<unsigned short, 3> ImageType;
typedef itk::ImageRegionConstIterator<ImageType> ImageIterator;
ImageType::Pointer image = ImageType::New();
ImageType::IndexType start;
start[0] = start[1] = start[2] = 0;
ImageType::SizeType size;
size[0] = size[1] = size[2] = 32;
ImageType::RegionType imgRegion;
imgRegion.SetSize(size);
imgRegion.SetIndex(start);
image->SetRegions(imgRegion);
image->SetSpacing(mitk::Vector(1.0));
image->Allocate();
ImageIterator imageIterator(image, image->GetLargestPossibleRegion());
imageIterator.GoToBegin();
unsigned short pixelValue = 0;
// fill the image with distinct values
while (!imageIterator.IsAtEnd())
{
image->SetPixel(imageIterator.GetIndex(), pixelValue);
++imageIterator;
++pixelValue;
}
/* end setup itk image */
mitk::Image::Pointer imageInMitk;
CastToMitkImage(image, imageInMitk);
/*mitk::ImageWriter::Pointer writer = mitk::ImageWriter::New();
writer->SetInput(imageInMitk);
std::string file = "C:\\Users\\schroedt\\Desktop\\cube.nrrd";
writer->SetFileName(file);
writer->Update();*/
PixelvalueBasedTestByPlane(imageInMitk, mitk::AnatomicalPlane::Coronal);
PixelvalueBasedTestByPlane(imageInMitk, mitk::AnatomicalPlane::Sagittal);
PixelvalueBasedTestByPlane(imageInMitk, mitk::AnatomicalPlane::Axial);
}
static void PixelvalueBasedTestByPlane(mitk::Image *imageInMitk, mitk::AnatomicalPlane orientation)
{
typedef itk::Image<unsigned short, 3> ImageType;
// set the seed of the rand function
srand((unsigned)time(nullptr));
/* setup a random orthogonal plane */
int sliceindex = 17; // rand() % 32;
bool isFrontside = true;
bool isRotated = false;
if (orientation == mitk::AnatomicalPlane::Axial)
{
/*isFrontside = false;
isRotated = true;*/
}
mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New();
plane->InitializeStandardPlane(imageInMitk->GetGeometry(), orientation, sliceindex, isFrontside, isRotated);
mitk::Point3D origin = plane->GetOrigin();
mitk::Vector3D normal;
normal = plane->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
plane->SetOrigin(origin);
// we dont need this any more, because we are only testing orthogonal planes
/*mitk::Vector3D rotationVector;
rotationVector[0] = randFloat();
rotationVector[1] = randFloat();
rotationVector[2] = randFloat();
float degree = randFloat() * 180.0;
mitk::RotationOperation* op = new mitk::RotationOperation(mitk::OpROTATE, plane->GetCenter(), rotationVector,
degree);
plane->ExecuteOperation(op);
delete op;*/
/* end setup plane */
/* define a point in the 3D volume.
* add the two axis vectors of the plane (each multiplied with a
* random number) to the origin. now the two random numbers
* become our index coordinates in the 2D image, because the
* length of the axis vectors is 1.
*/
mitk::Point3D planeOrigin = plane->GetOrigin();
mitk::Vector3D axis0, axis1;
axis0 = plane->GetAxisVector(0);
axis1 = plane->GetAxisVector(1);
axis0.Normalize();
axis1.Normalize();
unsigned char n1 = 7; // rand() % 32;
unsigned char n2 = 13; // rand() % 32;
mitk::Point3D testPoint3DInWorld;
testPoint3DInWorld = planeOrigin + (axis0 * n1) + (axis1 * n2);
// get the index of the point in the 3D volume
ImageType::IndexType testPoint3DInIndex;
imageInMitk->GetGeometry()->WorldToIndex(testPoint3DInWorld, testPoint3DInIndex);
itk::Index<3> testPoint2DInIndex;
/* end define a point in the 3D volume.*/
// do reslicing at the plane
mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New();
slicer->SetInput(imageInMitk);
slicer->SetWorldGeometry(plane);
slicer->Update();
mitk::Image::Pointer slice = slicer->GetOutput();
// Get TestPoiont3D as Index in Slice
slice->GetGeometry()->WorldToIndex(testPoint3DInWorld, testPoint2DInIndex);
mitk::Point3D p, sliceIndexToWorld, imageIndexToWorld;
p[0] = testPoint2DInIndex[0];
p[1] = testPoint2DInIndex[1];
p[2] = testPoint2DInIndex[2];
slice->GetGeometry()->IndexToWorld(p, sliceIndexToWorld);
p[0] = testPoint3DInIndex[0];
p[1] = testPoint3DInIndex[1];
p[2] = testPoint3DInIndex[2];
imageInMitk->GetGeometry()->IndexToWorld(p, imageIndexToWorld);
itk::Index<2> testPoint2DIn2DIndex;
testPoint2DIn2DIndex[0] = testPoint2DInIndex[0];
testPoint2DIn2DIndex[1] = testPoint2DInIndex[1];
typedef mitk::ImagePixelReadAccessor<unsigned short, 3> VolumeReadAccessorType;
typedef mitk::ImagePixelReadAccessor<unsigned short, 2> SliceReadAccessorType;
VolumeReadAccessorType VolumeReadAccessor(imageInMitk);
SliceReadAccessorType SliceReadAccessor(slice);
// compare the pixelvalues of the defined point in the 3D volume with the value of the resliced image
unsigned short valueAt3DVolume = VolumeReadAccessor.GetPixelByIndex(testPoint3DInIndex);
unsigned short valueAtSlice = SliceReadAccessor.GetPixelByIndex(testPoint2DIn2DIndex);
// valueAt3DVolume == valueAtSlice is not always working. because of rounding errors
// indices are shifted
MITK_TEST_CONDITION(valueAt3DVolume == valueAtSlice, "comparing pixelvalues for orthogonal plane");
vtkSmartPointer<vtkImageData> imageInVtk = imageInMitk->GetVtkImageData();
vtkSmartPointer<vtkImageData> sliceInVtk = slice->GetVtkImageData();
double PixelvalueByMitkOutput = sliceInVtk->GetScalarComponentAsDouble(n1, n2, 0, 0);
// double valueVTKinImage = imageInVtk->GetScalarComponentAsDouble(testPoint3DInIndex[0], testPoint3DInIndex[1],
// testPoint3DInIndex[2], 0);
/* Test that everything is working equally if vtkoutput is used instead of the default output
* from mitk ImageToImageFilter
*/
mitk::ExtractSliceFilter::Pointer slicerWithVtkOutput = mitk::ExtractSliceFilter::New();
slicerWithVtkOutput->SetInput(imageInMitk);
slicerWithVtkOutput->SetWorldGeometry(plane);
slicerWithVtkOutput->SetVtkOutputRequest(true);
slicerWithVtkOutput->Update();
vtkSmartPointer<vtkImageData> vtkImageByVtkOutput = slicerWithVtkOutput->GetVtkOutput();
double PixelvalueByVtkOutput = vtkImageByVtkOutput->GetScalarComponentAsDouble(n1, n2, 0, 0);
MITK_TEST_CONDITION(PixelvalueByMitkOutput == PixelvalueByVtkOutput,
"testing conversion of image output vtk->mitk by reslicer");
/*================ log outputs ===========================*/
#ifdef EXTRACTOR_DEBUG
MITK_INFO << "\n"
<< "TESTINFO index: " << sliceindex << " orientation: " << orientation << " frontside: " << isFrontside
<< " rotated: " << isRotated;
MITK_INFO << "\n"
<< "slice index to world: " << sliceIndexToWorld;
MITK_INFO << "\n"
<< "image index to world: " << imageIndexToWorld;
MITK_INFO << "\n"
<< "vtk: slice: " << PixelvalueByMitkOutput << ", image: " << valueVTKinImage;
MITK_INFO << "\n"
<< "testPoint3D InWorld" << testPoint3DInWorld << " is " << testPoint2DInIndex << " in 2D";
MITK_INFO << "\n"
<< "randoms: " << ((int)n1) << ", " << ((int)n2);
MITK_INFO << "\n"
<< "point is inside plane: " << plane->IsInside(testPoint3DInWorld)
<< " and volume: " << imageInMitk->GetGeometry()->IsInside(testPoint3DInWorld);
MITK_INFO << "\n"
<< "volume idx: " << testPoint3DInIndex << " = " << valueAt3DVolume;
MITK_INFO << "\n"
<< "volume world: " << testPoint3DInWorld << " = " << valueAt3DVolumeByWorld;
MITK_INFO << "\n"
<< "slice idx: " << testPoint2DInIndex << " = " << valueAtSlice;
itk::Index<3> curr;
curr[0] = curr[1] = curr[2] = 0;
for (int i = 0; i < 32; ++i)
{
for (int j = 0; j < 32; ++j)
{
++curr[1];
if (SliceReadAccessor.GetPixelByIndex(curr) == valueAt3DVolume)
{
MITK_INFO << "\n" << valueAt3DVolume << " MATCHED mitk " << curr;
}
}
curr[1] = 0;
++curr[0];
}
typedef itk::Image<unsigned short, 2> Image2DType;
Image2DType::Pointer img = Image2DType::New();
CastToItkImage(slice, img);
typedef itk::ImageRegionConstIterator<Image2DType> Iterator2D;
Iterator2D iter(img, img->GetLargestPossibleRegion());
iter.GoToBegin();
while (!iter.IsAtEnd())
{
if (img->GetPixel(iter.GetIndex()) == valueAt3DVolume)
MITK_INFO << "\n" << valueAt3DVolume << " MATCHED itk " << iter.GetIndex();
++iter;
}
#endif // EXTRACTOR_DEBUG
}
/* random a float value */
static float randFloat()
{
return (((float)rand() + 1.0) / ((float)RAND_MAX + 1.0)) +
(((float)rand() + 1.0) / ((float)RAND_MAX + 1.0)) / ((float)RAND_MAX + 1.0);
}
/* create a sphere with the size of the given testVolumeSize*/
static void InitializeTestVolume()
{
#ifdef CREATE_VOLUME
// do sphere creation
ItkVolumeGeneration();
#ifdef SAVE_VOLUME
// save in file
mitk::ImageWriter::Pointer writer = mitk::ImageWriter::New();
writer->SetInput(TestVolume);
std::string file;
std::ostringstream filename;
filename << "C:\\home\\schroedt\\MITK\\Modules\\ImageExtraction\\Testing\\Data\\sphere_";
filename << TestvolumeSize;
filename << ".nrrd";
file = filename.str();
writer->SetFileName(file);
writer->Update();
#endif // SAVE_VOLUME
#endif
#ifndef CREATE_VOLUME // read from file
mitk::StandardFileLocations::Pointer locator = mitk::StandardFileLocations::GetInstance();
std::string filename = locator->FindFile("sphere_512.nrrd.mhd", "Modules/ImageExtraction/Testing/Data");
TestVolume = mitk::IOUtil::Load<mitk::Image>(filename);
#endif
#ifdef CALC_TESTFAILURE_DEVIATION
// get the TestFailureDeviation in %
AccessFixedDimensionByItk(TestVolume, CalcTestFailureDeviation, 3);
#endif
}
// the test result of the sphere reslice
struct SliceProperties
{
double planeDistanceToSphereCenter;
double diameterInMM;
double diameterInPixel;
double diameterCalculated;
double percentageRadiusToPixel;
double areaCalculated;
double areaInPixel;
double percentageAreaCalcToPixel;
};
static mitk::Image::Pointer TestVolume;
static double TestvolumeSize;
static mitk::PlaneGeometry::Pointer TestPlane;
static std::string TestName;
static unsigned char pixelValueSet;
static SliceProperties testResults;
static double TestFailureDeviation;
private:
/*
* Generate a sphere with a radius of TestvolumeSize / 4.0
*/
static void ItkVolumeGeneration()
{
typedef itk::Image<unsigned char, 3> TestVolumeType;
typedef itk::ImageRegionConstIterator<TestVolumeType> ImageIterator;
TestVolumeType::Pointer sphereImage = TestVolumeType::New();
TestVolumeType::IndexType start;
start[0] = start[1] = start[2] = 0;
TestVolumeType::SizeType size;
size[0] = size[1] = size[2] = TestvolumeSize;
TestVolumeType::RegionType imgRegion;
imgRegion.SetSize(size);
imgRegion.SetIndex(start);
sphereImage->SetRegions(imgRegion);
sphereImage->SetSpacing(mitk::Vector(1.0));
sphereImage->Allocate();
sphereImage->FillBuffer(0);
mitk::Vector3D center;
center[0] = center[1] = center[2] = TestvolumeSize / 2.0;
double radius = TestvolumeSize / 4.0;
double pixelValue = pixelValueSet;
ImageIterator imageIterator(sphereImage, sphereImage->GetLargestPossibleRegion());
imageIterator.GoToBegin();
mitk::Vector3D currentVoxelInIndex;
while (!imageIterator.IsAtEnd())
{
currentVoxelInIndex[0] = imageIterator.GetIndex()[0];
currentVoxelInIndex[1] = imageIterator.GetIndex()[1];
currentVoxelInIndex[2] = imageIterator.GetIndex()[2];
double distanceToCenter = (center + (currentVoxelInIndex * -1.0)).GetNorm();
// if distance to center is smaller then the radius of the sphere
if (distanceToCenter < radius)
{
sphereImage->SetPixel(imageIterator.GetIndex(), pixelValue);
}
++imageIterator;
}
CastToMitkImage(sphereImage, TestVolume);
}
/* calculate the deviation of the voxel object to the mathematical sphere object.
* this is use to make a statement about the accuracy of the resliced image, eg. the circle's diameter or area.
*/
template <typename TPixel, unsigned int VImageDimension>
static void CalcTestFailureDeviation(itk::Image<TPixel, VImageDimension> *inputImage)
{
typedef itk::Image<TPixel, VImageDimension> InputImageType;
typedef itk::ImageRegionConstIterator<InputImageType> ImageIterator;
ImageIterator iterator(inputImage, inputImage->GetLargestPossibleRegion());
iterator.GoToBegin();
int volumeInPixel = 0;
while (!iterator.IsAtEnd())
{
if (inputImage->GetPixel(iterator.GetIndex()) == pixelValueSet)
volumeInPixel++;
++iterator;
}
double diameter = TestvolumeSize / 2.0;
double volumeCalculated = (1.0 / 6.0) * 3.14159265358979 * std::pow(diameter, 3);
double volumeDeviation = std::abs(100 - (100 / volumeCalculated * volumeInPixel));
typename InputImageType::IndexType index;
index[0] = index[1] = TestvolumeSize / 2.0;
index[2] = 0;
int sumpixels = 0;
while (index[2] < TestvolumeSize)
{
if (inputImage->GetPixel(index) == pixelValueSet)
sumpixels++;
index[2] += 1;
}
double diameterDeviation = std::abs(100 - (100 / diameter * sumpixels));
#ifdef DEBUG
MITK_INFO << "volume deviation: " << volumeDeviation << " diameter deviation:" << diameterDeviation;
#endif
mitkExtractSliceFilterTestClass::TestFailureDeviation = (volumeDeviation + diameterDeviation) / 2.0;
}
};
/*================ #END class ================*/
/*================#BEGIN Instantiation of members ================*/
mitk::Image::Pointer mitkExtractSliceFilterTestClass::TestVolume = mitk::Image::New();
double mitkExtractSliceFilterTestClass::TestvolumeSize = 256.0;
mitk::PlaneGeometry::Pointer mitkExtractSliceFilterTestClass::TestPlane = mitk::PlaneGeometry::New();
std::string mitkExtractSliceFilterTestClass::TestName = "";
unsigned char mitkExtractSliceFilterTestClass::pixelValueSet = 255;
mitkExtractSliceFilterTestClass::SliceProperties mitkExtractSliceFilterTestClass::testResults = {
-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0};
double mitkExtractSliceFilterTestClass::TestFailureDeviation = 0.0;
/*================ #END Instantiation of members ================*/
/*================ #BEGIN test main ================*/
int mitkExtractSliceFilterTest(int /*argc*/, char * /*argv*/ [])
{
MITK_TEST_BEGIN("mitkExtractSliceFilterTest")
// pixelvalue based testing
mitkExtractSliceFilterTestClass::PixelvalueBasedTest();
// initialize sphere test volume
mitkExtractSliceFilterTestClass::InitializeTestVolume();
mitk::Vector3D spacing = mitkExtractSliceFilterTestClass::TestVolume->GetGeometry()->GetSpacing();
// the center of the sphere = center of image
double sphereCenter = mitkExtractSliceFilterTestClass::TestvolumeSize / 2.0;
double planeSize = mitkExtractSliceFilterTestClass::TestvolumeSize;
/* axial plane */
mitk::PlaneGeometry::Pointer geometryAxial = mitk::PlaneGeometry::New();
geometryAxial->InitializeStandardPlane(
planeSize, planeSize, spacing, mitk::AnatomicalPlane::Axial, sphereCenter, false, true);
geometryAxial->ChangeImageGeometryConsideringOriginOffset(true);
mitk::Point3D origin = geometryAxial->GetOrigin();
mitk::Vector3D normal;
normal = geometryAxial->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
// geometryAxial->SetOrigin(origin);
mitkExtractSliceFilterTestClass::TestSlice(geometryAxial, "Testing axial plane");
/* end axial plane */
/* sagittal plane */
mitk::PlaneGeometry::Pointer geometrySagittal = mitk::PlaneGeometry::New();
geometrySagittal->InitializeStandardPlane(
planeSize, planeSize, spacing, mitk::AnatomicalPlane::Sagittal, sphereCenter, true, false);
geometrySagittal->ChangeImageGeometryConsideringOriginOffset(true);
origin = geometrySagittal->GetOrigin();
normal = geometrySagittal->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
// geometrySagittal->SetOrigin(origin);
mitkExtractSliceFilterTestClass::TestSlice(geometrySagittal, "Testing sagittal plane");
/* sagittal plane */
/* sagittal shifted plane */
mitk::PlaneGeometry::Pointer geometrySagittalShifted = mitk::PlaneGeometry::New();
geometrySagittalShifted->InitializeStandardPlane(
planeSize, planeSize, spacing, mitk::AnatomicalPlane::Sagittal, (sphereCenter - 14), true, false);
geometrySagittalShifted->ChangeImageGeometryConsideringOriginOffset(true);
origin = geometrySagittalShifted->GetOrigin();
normal = geometrySagittalShifted->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
// geometrySagittalShifted->SetOrigin(origin);
mitkExtractSliceFilterTestClass::TestSlice(geometrySagittalShifted, "Testing sagittal plane shifted");
/* end sagittal shifted plane */
/* coronal plane */
mitk::PlaneGeometry::Pointer geometryCoronal = mitk::PlaneGeometry::New();
geometryCoronal->InitializeStandardPlane(
planeSize, planeSize, spacing, mitk::AnatomicalPlane::Coronal, sphereCenter, true, false);
geometryCoronal->ChangeImageGeometryConsideringOriginOffset(true);
origin = geometryCoronal->GetOrigin();
normal = geometryCoronal->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
// geometryCoronal->SetOrigin(origin);
mitkExtractSliceFilterTestClass::TestSlice(geometryCoronal, "Testing coronal plane");
/* end coronal plane */
/* oblique plane */
mitk::PlaneGeometry::Pointer obliquePlane = mitk::PlaneGeometry::New();
obliquePlane->InitializeStandardPlane(
planeSize, planeSize, spacing, mitk::AnatomicalPlane::Sagittal, sphereCenter, true, false);
obliquePlane->ChangeImageGeometryConsideringOriginOffset(true);
origin = obliquePlane->GetOrigin();
normal = obliquePlane->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
// obliquePlane->SetOrigin(origin);
mitk::Vector3D rotationVector;
rotationVector[0] = 0.2;
rotationVector[1] = 0.4;
rotationVector[2] = 0.62;
float degree = 37.0;
mitk::RotationOperation *op =
new mitk::RotationOperation(mitk::OpROTATE, obliquePlane->GetCenter(), rotationVector, degree);
obliquePlane->ExecuteOperation(op);
delete op;
mitkExtractSliceFilterTestClass::TestSlice(obliquePlane, "Testing oblique plane");
/* end oblique plane */
#ifdef SHOW_SLICE_IN_RENDER_WINDOW
/*================ #BEGIN vtk render code ================*/
// set reslicer for renderwindow
mitk::Image::Pointer pic = mitk::IOUtil::Load<mitk::Image>(filename);
vtkSmartPointer<vtkImageReslice> slicer = vtkSmartPointer<vtkImageReslice>::New();
slicer->SetInput(pic->GetVtkImageData());
mitk::PlaneGeometry::Pointer obliquePl = mitk::PlaneGeometry::New();
obliquePl->InitializeStandardPlane(
pic->GetGeometry(), mitk::AnatomicalPlane::Sagittal, pic->GetGeometry()->GetCenter()[0], true, false);
obliquePl->ChangeImageGeometryConsideringOriginOffset(true);
mitk::Point3D origin2 = obliquePl->GetOrigin();
mitk::Vector3D n;
n = obliquePl->GetNormal();
n.Normalize();
origin2 += n * 0.5; // pixelspacing is 1, so half the spacing is 0.5
obliquePl->SetOrigin(origin2);
mitk::Vector3D rotation;
rotation[0] = 0.534307;
rotation[1] = 0.000439605;
rotation[2] = 0.423017;
MITK_INFO << rotation;
mitk::RotationOperation *operation =
new mitk::RotationOperation(mitk::OpROTATE, obliquePl->GetCenter(), rotationVector, degree);
obliquePl->ExecuteOperation(operation);
delete operation;
double origin[3];
origin[0] = obliquePl->GetOrigin()[0];
origin[1] = obliquePl->GetOrigin()[1];
origin[2] = obliquePl->GetOrigin()[2];
slicer->SetResliceAxesOrigin(origin);
mitk::Vector3D right, bottom, normal;
right = obliquePl->GetAxisVector(0);
bottom = obliquePl->GetAxisVector(1);
normal = obliquePl->GetNormal();
right.Normalize();
bottom.Normalize();
normal.Normalize();
double cosines[9];
mitk::vnl2vtk(right.GetVnlVector(), cosines); // x
mitk::vnl2vtk(bottom.GetVnlVector(), cosines + 3); // y
mitk::vnl2vtk(normal.GetVnlVector(), cosines + 6); // n
slicer->SetResliceAxesDirectionCosines(cosines);
slicer->SetOutputDimensionality(2);
slicer->Update();
// set vtk renderwindow
vtkSmartPointer<vtkPlaneSource> vtkPlane = vtkSmartPointer<vtkPlaneSource>::New();
vtkPlane->SetOrigin(0.0, 0.0, 0.0);
// 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 sagittal) afterwards.
vtkPlane->SetPoint1(1.0, 0.0, 0.0); // P1: (xMax, yMin, depth)
vtkPlane->SetPoint2(0.0, 1.0, 0.0); // P2: (xMin, yMax, depth)
// these are not the correct values for all slices, only a square plane by now
vtkSmartPointer<vtkPolyDataMapper> imageMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
imageMapper->SetInputConnection(vtkPlane->GetOutputPort());
vtkSmartPointer<vtkLookupTable> lookupTable = vtkSmartPointer<vtkLookupTable>::New();
// built a default lookuptable
lookupTable->SetRampToLinear();
lookupTable->SetSaturationRange(0.0, 0.0);
lookupTable->SetHueRange(0.0, 0.0);
lookupTable->SetValueRange(0.0, 1.0);
lookupTable->Build();
// map all black values to transparent
lookupTable->SetTableValue(0, 0.0, 0.0, 0.0, 0.0);
lookupTable->SetRange(-255.0, 255.0);
// lookupTable->SetRange(-1022.0, 1184.0);//pic3D range
vtkSmartPointer<vtkTexture> texture = vtkSmartPointer<vtkTexture>::New();
texture->SetInput(slicer->GetOutput());
texture->SetLookupTable(lookupTable);
texture->SetMapColorScalarsThroughLookupTable(true);
vtkSmartPointer<vtkActor> imageActor = vtkSmartPointer<vtkActor>::New();
imageActor->SetMapper(imageMapper);
imageActor->SetTexture(texture);
// Setup renderers
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(imageActor);
// Setup render window
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
// Setup render window interactor
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New();
renderWindowInteractor->SetInteractorStyle(style);
// Render and start interaction
renderWindowInteractor->SetRenderWindow(renderWindow);
// renderer->AddViewProp(imageActor);
renderWindow->Render();
renderWindowInteractor->Start();
// always end with this!
/*================ #END vtk render code ================*/
#endif // SHOW_SLICE_IN_RENDER_WINDOW
MITK_TEST_END()
}
diff --git a/Modules/Core/test/mitkFileReaderRegistryTest.cpp b/Modules/Core/test/mitkFileReaderRegistryTest.cpp
index 94b414c0fc..c42917bf48 100644
--- a/Modules/Core/test/mitkFileReaderRegistryTest.cpp
+++ b/Modules/Core/test/mitkFileReaderRegistryTest.cpp
@@ -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.
============================================================================*/
#include "mitkAbstractFileReader.h"
#include "mitkFileReaderRegistry.h"
#include "mitkIFileReader.h"
#include "mitkTestingMacros.h"
#include <mitkBaseData.h>
#include <mitkCustomMimeType.h>
#include <mitkImage.h>
class DummyReader : public mitk::AbstractFileReader
{
public:
DummyReader(const DummyReader &other) : mitk::AbstractFileReader(other) {}
DummyReader(const std::string &mimeTypeName, const std::string &extension, int priority) : mitk::AbstractFileReader()
{
mitk::CustomMimeType mimeType(mimeTypeName);
mimeType.AddExtension(extension);
mimeType.SetComment("This is a dummy description");
this->SetMimeType(mimeType);
this->SetRanking(priority);
m_ServiceReg = this->RegisterService();
}
~DummyReader() override
{
if (m_ServiceReg)
m_ServiceReg.Unregister();
}
using mitk::AbstractFileReader::Read;
std::vector<itk::SmartPointer<mitk::BaseData>> DoRead() override
{
std::vector<mitk::BaseData::Pointer> result;
return result;
}
private:
DummyReader *Clone() const override { return new DummyReader(*this); }
us::ServiceRegistration<mitk::IFileReader> m_ServiceReg;
}; // End of internal dummy reader
class DummyReader2 : public mitk::AbstractFileReader
{
public:
DummyReader2(const DummyReader2 &other) : mitk::AbstractFileReader(other) {}
DummyReader2(const std::string &mimeTypeName, const std::string &extension, int priority) : mitk::AbstractFileReader()
{
mitk::CustomMimeType mimeType(mimeTypeName);
mimeType.AddExtension(extension);
mimeType.SetComment("This is a second dummy description");
this->SetMimeType(mimeType);
this->SetRanking(priority);
m_ServiceReg = this->RegisterService();
}
~DummyReader2() override
{
if (m_ServiceReg)
m_ServiceReg.Unregister();
}
using mitk::AbstractFileReader::Read;
std::vector<itk::SmartPointer<mitk::BaseData>> DoRead() override
{
std::vector<mitk::BaseData::Pointer> result;
return result;
}
private:
DummyReader2 *Clone() const override { return new DummyReader2(*this); }
us::ServiceRegistration<mitk::IFileReader> m_ServiceReg;
}; // End of internal dummy reader 2
/**
* TODO
*/
int mitkFileReaderRegistryTest(int /*argc*/, char * /*argv*/ [])
{
// always start with this!
MITK_TEST_BEGIN("FileReaderRegistry");
// mitk::FileReaderRegistry::Pointer frm = mitk::FileReaderRegistry::New();
// MITK_TEST_CONDITION_REQUIRED(argc == 2,"Testing FileReaderRegistry instantiation");
// DummyReader testDR("application/dummy", "test",1);
// DummyReader otherDR("application/dummy2", "other",1);
// MITK_TEST_CONDITION_REQUIRED(!testDR.CanRead("/this/is/a/folder/file.tes"),"Negative test of default CanRead()
// implementation");
// mitk::FileReaderRegistry* readerRegistry = new mitk::FileReaderRegistry;
// mitk::IFileReader* returned = readerRegistry->GetReader("bla.test");
// MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileReader&>(testDR) != returned,"Testing correct
// retrieval of FileReader 1/2");
// returned = readerRegistry->GetReader("other");
// MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileReader&>(otherDR) != returned,"Testing correct
// retrieval of FileReader 2/2");
// DummyReader mediocreTestDR("application/dummy", "test", 20);
// DummyReader prettyFlyTestDR("application/dummy", "test", 50);
// DummyReader2 awesomeTestDR("application/dummy", "test", 100);
// returned = readerRegistry->GetReader("test");
- // MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyReader2*>(returned), "Testing correct priorized retrieval of
+ // MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyReader2*>(returned), "Testing correct prioritized retrieval of
// FileReader: Best reader");
// Now to give those readers some options, then we will try again
// mitk::IFileReader::OptionList options;
// options.push_back(std::make_pair("isANiceGuy", true));
// mediocreTestDR.SetOptions(options);
// options.clear();
// options.push_back(std::make_pair("canFly", true));
// prettyFlyTestDR.SetOptions(options);
// options.push_back(std::make_pair("isAwesome", true));
// awesomeTestDR.SetOptions(options); //note: awesomeReader canFly and isAwesome
// // Reset Options, use to define what we want the reader to do
// options.clear();
// mitk::IFileReader::OptionNames optionsFilter;
// optionsFilter.push_back("canFly");
// returned = readerRegistry->GetReader("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileReader&>(awesomeTestDR) != returned, "Testing
// correct retrieval of FileReader with Options: Best reader with options");
// optionsFilter.push_back("isAwesome");
// returned = readerRegistry->GetReader("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileReader&>(awesomeTestDR) != returned, "Testing
// correct retrieval of FileReader with multiple Options: Best reader with options");
// optionsFilter.clear();
// optionsFilter.push_back("isANiceGuy");
// returned = readerRegistry->GetReader("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileReader&>(mediocreTestDR) != returned, "Testing
// correct retrieval of specific FileReader with Options: Low priority reader with specific option");
// optionsFilter.push_back("canFly");
// returned = readerRegistry->GetReader("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returned == nullptr, "Testing correct return of 0 value when no matching reader was
// found");
// // Onward to test the retrieval of multiple readers
// std::vector< mitk::IFileReader* > returnedList;
// returnedList = readerRegistry->GetReaders("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returnedList.empty(), "Testing correct return of zero readers when no matching reader
// was found, asking for all compatibles");
// optionsFilter.clear();
// optionsFilter.push_back("canFly");
// returnedList = readerRegistry->GetReaders("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returnedList.size() == 2, "Testing correct return of two readers when two matching
// reader was found, asking for all compatibles");
- // MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyReader2*>(returnedList.front()), "Testing correct priorization of
+ // MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyReader2*>(returnedList.front()), "Testing correct prioritization of
// returned Readers with options 1/2");
// optionsFilter.clear();
// optionsFilter.push_back("isAwesome");
// returnedList = readerRegistry->GetReaders("test", optionsFilter);
// MITK_TEST_CONDITION_REQUIRED(returnedList.size() == 1, "Testing correct return of one readers when one matching
// reader was found, asking for all compatibles");
// MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyReader2*>(returnedList.front()), "Testing correctness of result
// from former query");
// And now to verify a working read chain for a mps file:
// mitk::PointSetReader::Pointer psr = mitk::PointSetReader::New();
// std::vector<mitk::BaseData::Pointer> basedata;
// basedata = mitk::FileReaderRegistry::Read("F://Build//MITK-Data//pointSet.mps");
// MITK_TEST_CONDITION_REQUIRED(basedata.size() > 0, "Testing correct read of PointSet");
// Testing templated call to ReaderRegistry
// mitk::PointSet::Pointer pointset = mitk::FileReaderRegistry::Read< mitk::PointSet
// >("F://Build//MITK-Data//pointSet.mps");
// MITK_TEST_CONDITION_REQUIRED(pointset.IsNotNull(), "Testing templated call of Read()");
// And now for something completely different... (Debug)
// mitk::LegacyFileReaderService::Pointer lfr = mitk::LegacyFileReaderService::New(".nrrd", "Nearly Raw Raster Data");
// returned = mitk::FileReaderRegistry::GetReader(".nrrd");
// MITK_TEST_CONDITION_REQUIRED(lfr == returned, "Testing correct retrieval of specific FileReader with Options: Low
// priority reader with specific option");
// std::vector<mitk::BaseData::Pointer> image =
// mitk::FileReaderRegistry::Read("F://Build//MITK-Data//Pic2DplusT.nrrd");
// MITK_TEST_CONDITION_REQUIRED(image.size() > 0, "Testing whether image was returned or not");
// mitk::Image::Pointer image2 = dynamic_cast<mitk::Image*> (image.front().GetPointer());
// MITK_TEST_CONDITION_REQUIRED(image2.IsNotNull(), "Testing if BaseData is an image");
// Delete this here because it will call the PrototypeServiceFactory::Unget() method
// of the dummy readers.
// delete readerRegistry;
// always end with this!
MITK_TEST_END();
}
diff --git a/Modules/Core/test/mitkFileWriterRegistryTest.cpp b/Modules/Core/test/mitkFileWriterRegistryTest.cpp
index cf811c6487..cc0ca54639 100644
--- a/Modules/Core/test/mitkFileWriterRegistryTest.cpp
+++ b/Modules/Core/test/mitkFileWriterRegistryTest.cpp
@@ -1,288 +1,288 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkFileReaderRegistry.h>
#include <mitkFileWriterRegistry.h>
#include <mitkIFileWriter.h>
#include <mitkIOUtil.h>
#include <mitkTestingMacros.h>
class DummyBaseData : public mitk::BaseData
{
public:
mitkClassMacro(DummyBaseData, mitk::BaseData) itkNewMacro(Self)
void SetRequestedRegion(const itk::DataObject * /*data*/)
{
}
void SetRequestedRegionToLargestPossibleRegion() {}
bool RequestedRegionIsOutsideOfTheBufferedRegion() { return false; }
bool VerifyRequestedRegion() { return true; }
};
class DummyWriter : public mitk::AbstractFileWriter
{
public:
DummyWriter(const DummyWriter &other) : mitk::AbstractFileWriter(other), m_Content("Hi there stream") {}
DummyWriter(const std::string &basedataType, const std::string &extension, int ranking)
: mitk::AbstractFileWriter(basedataType, extension, "This is a dummy description"), m_Content("Hi there stream")
{
this->SetRanking(ranking);
m_ServiceReg = this->RegisterService();
}
~DummyWriter()
{
if (m_ServiceReg)
m_ServiceReg.Unregister();
}
using AbstractFileWriter::Write;
virtual void Write(const mitk::BaseData *data, std::ostream &stream)
{
MITK_TEST_CONDITION_REQUIRED(dynamic_cast<const DummyBaseData *>(data), "Correct data type")
stream << m_Content;
}
std::string m_Content;
private:
DummyWriter *Clone() const { return new DummyWriter(*this); }
us::ServiceRegistration<mitk::IFileReader> m_ServiceReg;
}; // End of internal dummy Writer
class DummyWriter2 : public mitk::AbstractFileWriter
{
public:
DummyWriter2(const DummyWriter2 &other) : mitk::AbstractFileWriter(other), m_Content("hi there file path") {}
DummyWriter2(const std::string &basedataType, const std::string &extension, int ranking)
: mitk::AbstractFileWriter(basedataType, extension, "This is a dummy description"), m_Content("hi there file path")
{
this->SetRanking(ranking);
m_ServiceReg = this->RegisterService();
}
~DummyWriter2()
{
if (m_ServiceReg)
m_ServiceReg.Unregister();
}
using AbstractFileWriter::Write;
virtual void Write(const mitk::BaseData *data, const std::string &filePath)
{
MITK_TEST_CONDITION_REQUIRED(dynamic_cast<const DummyBaseData *>(data), "Correct data type")
std::ofstream fileStream(filePath.c_str());
fileStream << m_Content;
}
virtual void Write(const mitk::BaseData *data, std::ostream &stream)
{
mitk::AbstractFileWriter::Write(data, stream);
}
virtual bool CanWrite(const mitk::BaseData *data) const { return dynamic_cast<const DummyBaseData *>(data); }
std::string m_Content;
private:
DummyWriter2 *Clone() const { return new DummyWriter2(*this); }
us::ServiceRegistration<mitk::IFileReader> m_ServiceReg;
}; // End of internal dummy Writer 2
void TestStreamMethods()
{
DummyWriter dummyWriter(DummyBaseData::GetStaticNameOfClass(), "stream", 100);
DummyWriter2 dummyWriter2(DummyBaseData::GetStaticNameOfClass(), "file", 50);
mitk::FileWriterRegistry writerRegistry;
// Test DummyWriter, which always uses a ostream for writing, even
// when a file path is used
DummyBaseData dummyData;
std::stringstream oss;
writerRegistry.Write(&dummyData, oss);
MITK_TEST_CONDITION_REQUIRED(dummyWriter.m_Content == oss.str(), "Dummy stream writer")
std::string content;
{
std::ofstream tmpStream;
std::string tmpFileName = mitk::IOUtil::CreateTemporaryFile(tmpStream);
writerRegistry.Write(&dummyData, tmpFileName);
std::ifstream tmpInput(tmpFileName.c_str());
std::getline(tmpInput, content);
tmpInput.close();
tmpStream.close();
std::remove(tmpFileName.c_str());
}
MITK_TEST_CONDITION_REQUIRED(dummyWriter.m_Content == content, "Dummy stream writer")
// Test DummyWriter2, which always uses a real file for writing, even
// when a std::ostream object is given
std::stringstream oss2;
dummyWriter2.Write(&dummyData, oss2);
MITK_TEST_CONDITION_REQUIRED(dummyWriter2.m_Content == oss2.str(), "Dummy 2 stream writer")
std::string content2;
{
std::ofstream tmpStream;
std::string tmpFileName = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.file");
writerRegistry.Write(&dummyData, tmpFileName);
std::ifstream tmpInput(tmpFileName.c_str());
std::getline(tmpInput, content2);
tmpInput.close();
std::remove(tmpFileName.c_str());
}
MITK_TEST_CONDITION_REQUIRED(dummyWriter2.m_Content == content2, "Dummy 2 stream writer")
}
/**
* TODO
*/
int mitkFileWriterRegistryTest(int /*argc*/, char * /*argv*/ [])
{
// always start with this!
MITK_TEST_BEGIN("FileWriterRegistry");
TestStreamMethods();
// mitk::FileWriterRegistry::Pointer frm = mitk::FileWriterRegistry::New();
// MITK_TEST_CONDITION_REQUIRED(argc == 2,"Testing FileWriterRegistry instantiation");
DummyWriter testDR("testdata", "test", 1);
DummyWriter otherDR("testdata", "other", 1);
// MITK_TEST_CONDITION_REQUIRED(testDR->CanWrite("/this/is/a/folder/file.test"),"Positive test of default CanRead()
// implementation");
// MITK_TEST_CONDITION_REQUIRED(!testDR->CanWrite("/this/is/a/folder/file.tes"),"Negative test of default CanRead()
// implementation");
mitk::FileWriterRegistry *writerRegistry = new mitk::FileWriterRegistry;
mitk::IFileWriter *returned = writerRegistry->GetWriter("", "test");
MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileWriter &>(testDR) != returned,
"Testing correct retrieval of FileWriter 1/2");
returned = writerRegistry->GetWriter("", "other");
MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileWriter &>(otherDR) != returned,
"Testing correct retrieval of FileWriter 2/2");
DummyWriter mediocreTestDR("testdata", "test", 20);
DummyWriter prettyFlyTestDR("testdata", "test", 50);
DummyWriter2 awesomeTestDR("testdata", "test", 100);
returned = writerRegistry->GetWriter("test");
MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyWriter2 *>(returned),
- "Testing correct priorized retrieval of FileWriter: Best Writer");
+ "Testing correct prioritized retrieval of FileWriter: Best Writer");
// Now to give those Writers some options, then we will try again
mitk::IFileWriter::OptionList options;
options.push_back(std::make_pair("isANiceGuy", true));
mediocreTestDR.SetOptions(options);
options.clear();
options.push_back(std::make_pair("canFly", true));
prettyFlyTestDR.SetOptions(options);
options.push_back(std::make_pair("isAwesome", true));
awesomeTestDR.SetOptions(options); // note: awesomeWriter canFly and isAwesome
// Reset Options, use to define what we want the Writer to do
mitk::IFileWriter::OptionNames optionFilter;
optionFilter.push_back("canFly");
returned = writerRegistry->GetWriter("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(returned && &static_cast<mitk::IFileWriter &>(awesomeTestDR) != returned,
"Testing correct retrieval of FileWriter with Options: Best Writer with options");
optionFilter.push_back("isAwesome");
returned = writerRegistry->GetWriter("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(
returned && &static_cast<mitk::IFileWriter &>(awesomeTestDR) != returned,
"Testing correct retrieval of FileWriter with multiple Options: Best Writer with options");
optionFilter.clear();
optionFilter.push_back("isANiceGuy");
returned = writerRegistry->GetWriter("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(
returned && &static_cast<mitk::IFileWriter &>(mediocreTestDR) != returned,
"Testing correct retrieval of specific FileWriter with Options: Low priority Writer with specific option");
optionFilter.push_back("canFly");
returned = writerRegistry->GetWriter("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(returned == nullptr, "Testing correct return of 0 value when no matching Writer was found");
// Onward to test the retrieval of multiple Writers
std::vector<mitk::IFileWriter *> returnedList;
returnedList = writerRegistry->GetWriters("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(
returnedList.empty(),
"Testing correct return of zero Writers when no matching Writer was found, asking for all compatibles");
optionFilter.clear();
optionFilter.push_back("canFly");
returnedList = writerRegistry->GetWriters("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(
returnedList.size() == 2,
"Testing correct return of two Writers when two matching Writer was found, asking for all compatibles");
MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyWriter2 *>(returnedList.front()),
- "Testing correct priorization of returned Writers with options 1/2");
+ "Testing correct prioritization of returned Writers with options 1/2");
optionFilter.clear();
optionFilter.push_back("isAwesome");
returnedList = writerRegistry->GetWriters("", "test", optionFilter);
MITK_TEST_CONDITION_REQUIRED(
returnedList.size() == 1,
"Testing correct return of one Writers when one matching Writer was found, asking for all compatibles");
MITK_TEST_CONDITION_REQUIRED(dynamic_cast<DummyWriter2 *>(returnedList.front()),
"Testing correctness of result from former query");
// mitk::CoreObjectFactory::GetInstance();
// mitk::FileReaderRegistry readerRegistry;
// mitk::Image::Pointer image = readerRegistry.Read<mitk::Image>("F://Build//MITK-Data//Pic2DplusT.nrrd");
// writerRegistry->Write(image.GetPointer(), "F://Build//MITK-Data//Pic2DplusTcopy.nrrd");
//// And now to verify a working read chain for a mps file:
// mitk::PointSetWriter::Pointer psr = mitk::PointSetWriter::New();
// mitk::BaseData::Pointer basedata;
// basedata = mitk::FileWriterRegistry::Read("F://Build//MITK-Data//pointSet.mps");
// MITK_TEST_CONDITION_REQUIRED(basedata.IsNotNull(), "Testing correct read of PointSet");
//// Testing templated call to WriterRegistry
// mitk::PointSet::Pointer pointset = mitk::FileWriterRegistry::Read< mitk::PointSet
// >("F://Build//MITK-Data//pointSet.mps");
// MITK_TEST_CONDITION_REQUIRED(pointset.IsNotNull(), "Testing templated call of Read()");
//// And now for something completely different... (Debug)
// mitk::LegacyFileWriterService::Pointer lfr = mitk::LegacyFileWriterService::New(".nrrd", "Nearly Raw Raster Data");
// returned = mitk::FileWriterRegistry::GetWriter(".nrrd");
// MITK_TEST_CONDITION_REQUIRED(lfr == returned, "Testing correct retrieval of specific FileWriter with Options: Low
// priority Writer with specific option");
// mitk::BaseData::Pointer image = mitk::FileWriterRegistry::Read("F://Build//MITK-Data//Pic2DplusT.nrrd");
// MITK_TEST_CONDITION_REQUIRED(image.IsNotNull(), "Testing whether BaseData is empty or not");
// mitk::Image::Pointer image2 = dynamic_cast<mitk::Image*> (image.GetPointer());
// MITK_TEST_CONDITION_REQUIRED(image2.IsNotNull(), "Testing if BaseData is an image");
// Delete this here because it will call the PrototypeServiceFactory::Unget() method
// of the dummy writers.
delete writerRegistry;
//// always end with this!
MITK_TEST_END()
}
diff --git a/Modules/Core/test/mitkImageDimensionConverterTest.cpp b/Modules/Core/test/mitkImageDimensionConverterTest.cpp
index 2428741336..7216be83e4 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).as_ref());
Original_col1.SetVnlVector(
mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref());
Original_col2.SetVnlVector(
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
+ // Save it without conversion and load it again. It should have an identity 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).as_ref());
Loaded2D_col1.SetVnlVector(
imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref());
Loaded2D_col2.SetVnlVector(
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).as_ref());
Converted_col1.SetVnlVector(
mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref());
Converted_col2.SetVnlVector(
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).as_ref());
Loaded_col1.SetVnlVector(
imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref());
Loaded_col2.SetVnlVector(
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/mitkImageToItkTest.cpp b/Modules/Core/test/mitkImageToItkTest.cpp
index a5633761ac..fc9f221ccd 100644
--- a/Modules/Core/test/mitkImageToItkTest.cpp
+++ b/Modules/Core/test/mitkImageToItkTest.cpp
@@ -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.
============================================================================*/
#include "itkDiffusionTensor3D.h"
#include "mitkITKImageImport.h"
#include "mitkImage.h"
#include "mitkReferenceCountWatcher.h"
#include "mitkTestFixture.h"
#include "mitkTestingMacros.h"
#include <fstream>
#include <mitkIOUtil.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
static mitk::Image::Pointer GetEmptyTestImageWithGeometry(mitk::PixelType pt)
{
// create empty image
mitk::Image::Pointer imgMem;
imgMem = mitk::Image::New();
// create geometry information for image
mitk::Point3D origin;
mitk::Vector3D right, bottom;
mitk::Vector3D spacing;
mitk::FillVector3D(origin, 17.0, 19.92, 7.83);
mitk::FillVector3D(right, 1.0, 2.0, 3.0);
mitk::FillVector3D(bottom, 0.0, -3.0, 2.0);
mitk::FillVector3D(spacing, 0.78, 0.91, 2.23);
mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New();
planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing);
planegeometry->SetOrigin(origin);
// initialize image
imgMem->Initialize(pt, 40, *planegeometry);
return imgMem;
}
class mitkImageToItkTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkImageToItkTestSuite);
MITK_TEST(ImageCastIntToFloat_EmptyImage);
MITK_TEST(ImageCastDoubleToFloat_EmptyImage);
MITK_TEST(ImageCastFloatToFloatTensor_EmptyImage_ThrowsException);
MITK_TEST(ImageCastFloatTensorToFloatTensor_EmptyImage);
MITK_TEST(ImageCastDoubleToTensorDouble_EmptyImage_ThrowsException);
MITK_TEST(ImageCastToItkAndBack_SamePointer_Success);
MITK_TEST(ImageCastToItk_TestImage_Success);
CPPUNIT_TEST_SUITE_END();
private:
mitk::Image::Pointer m_TestImage;
public:
void setUp() override
{
// empty on purpose
}
void tearDown() override { m_TestImage = nullptr; }
void ImageCastIntToFloat_EmptyImage()
{
mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakeScalarPixelType<int>());
itk::Image<float, 3>::Pointer itkImage;
mitk::CastToItkImage(m_TestImage, itkImage);
mitk::Image::Pointer mitkImageAfterCast = mitk::ImportItkImage(itkImage);
CPPUNIT_ASSERT_MESSAGE("Cast output is not null", mitkImageAfterCast.IsNotNull());
}
void ImageCastDoubleToFloat_EmptyImage()
{
mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakeScalarPixelType<double>());
itk::Image<float, 3>::Pointer diffImage;
mitk::CastToItkImage(m_TestImage, diffImage);
CPPUNIT_ASSERT_MESSAGE("Casting scalar double (MITK) image to scalar float tensor (ITK). Result shouldn't be nullptr.",
diffImage.IsNotNull());
}
void ImageCastFloatToFloatTensor_EmptyImage_ThrowsException()
{
mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakeScalarPixelType<float>());
itk::Image<itk::DiffusionTensor3D<float>, 3>::Pointer diffImage;
CPPUNIT_ASSERT_THROW_MESSAGE("Casting scalar float (MITK) image to scalar float (ITK) throws exception.",
mitk::CastToItkImage(m_TestImage, diffImage),
mitk::AccessByItkException);
}
void ImageCastFloatTensorToFloatTensor_EmptyImage()
{
typedef itk::Image<itk::DiffusionTensor3D<float>, 3> ItkTensorImageType;
mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakePixelType<ItkTensorImageType>());
itk::Image<itk::DiffusionTensor3D<float>, 3>::Pointer diffImage;
mitk::CastToItkImage(m_TestImage, diffImage);
MITK_TEST_CONDITION_REQUIRED(diffImage.IsNotNull(),
"Casting float tensor (MITK) image to float tensor (ITK). Result shouldn't be nullptr");
mitk::Image::Pointer mitkImageAfterCast = mitk::ImportItkImage(diffImage);
- MITK_ASSERT_EQUAL(mitkImageAfterCast, m_TestImage, "Same type, images shoul be equal.");
+ MITK_ASSERT_EQUAL(mitkImageAfterCast, m_TestImage, "Same type, images should be equal.");
}
void ImageCastDoubleToTensorDouble_EmptyImage_ThrowsException()
{
mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakeScalarPixelType<double>());
itk::Image<itk::DiffusionTensor3D<double>, 3>::Pointer diffImage;
CPPUNIT_ASSERT_THROW(mitk::CastToItkImage(m_TestImage, diffImage), mitk::AccessByItkException);
}
void ImageCastToItkAndBack_SamePointer_Success()
{
typedef itk::Image<short, 3> ItkImageType;
ItkImageType::Pointer itkImage = ItkImageType::New();
std::string m_ImagePath = GetTestDataFilePath("Pic3D.nrrd");
mitk::Image::Pointer testDataImage = mitk::IOUtil::Load<mitk::Image>(m_ImagePath);
// modify ITK image
itk::Matrix<double, 3, 3> dir = itkImage->GetDirection();
dir *= 2;
itkImage->SetDirection(dir);
CPPUNIT_ASSERT_THROW_MESSAGE("No exception thrown for casting back the same memory",
testDataImage = mitk::GrabItkImageMemory(itkImage, testDataImage),
itk::ExceptionObject);
CPPUNIT_ASSERT(testDataImage.IsNotNull());
}
void ImageCastToItk_TestImage_Success()
{
itk::Image<short, 3>::Pointer itkImage;
std::string m_ImagePath = GetTestDataFilePath("Pic3D.nrrd");
mitk::Image::Pointer testDataImage = mitk::IOUtil::Load<mitk::Image>(m_ImagePath);
mitk::CastToItkImage(testDataImage, itkImage);
mitk::Image::Pointer mitkImageAfterCast = mitk::ImportItkImage(itkImage);
// dereference itk image
itkImage = nullptr;
MITK_ASSERT_EQUAL(mitkImageAfterCast, testDataImage, "Cast with test data followed by import produces same images");
}
}; // END TEST SUITE CLASS DECL
MITK_TEST_SUITE_REGISTRATION(mitkImageToItk);
diff --git a/Modules/Core/test/mitkPreferencesTest.cpp b/Modules/Core/test/mitkPreferencesTest.cpp
index d8e4fe6bca..71347a1f83 100644
--- a/Modules/Core/test/mitkPreferencesTest.cpp
+++ b/Modules/Core/test/mitkPreferencesTest.cpp
@@ -1,175 +1,175 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkIPreferencesService.h>
#include <mitkIPreferences.h>
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkIOUtil.h>
class mitkPreferencesTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkPreferencesTestSuite);
MITK_TEST(Node);
MITK_TEST(OnChanged);
MITK_TEST(OnPropertyChanged);
MITK_TEST(GetInt);
MITK_TEST(GetFloat);
CPPUNIT_TEST_SUITE_END();
int m_NumberOfOnChangedEvents = 0;
int m_NumberOfOnPropertyChangedEvents = 0;
public:
void setUp() override
{
const auto filename = mitk::IOUtil::CreateTemporaryFile("prefs_XXXXXX.xml");
- std::filesystem::remove(filename); // We need a temporary filename, not an actual file
+ fs::remove(filename); // We need a temporary filename, not an actual file
auto* preferencesService = mitk::CoreServices::GetPreferencesService();
preferencesService->InitializeStorage(filename);
}
void tearDown() override
{
auto* preferencesService = mitk::CoreServices::GetPreferencesService();
preferencesService->UninitializeStorage();
}
void Node()
{
auto* preferences = mitk::CoreServices::GetPreferencesService()->GetSystemPreferences();
// a > aa
// b > bb > bbb
// b > bb2
auto* aPrefs = preferences->Node("a");
aPrefs->Node("aa");
auto* bPrefs = aPrefs->Node("/b");
bPrefs->Node("bb/bbb");
preferences->Node("/b/bb2");
auto* bbPrefs = bPrefs->Node("bb");
auto nodeNames = preferences->ChildrenNames();
CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(nodeNames.size()));
CPPUNIT_ASSERT(std::find(nodeNames.begin(), nodeNames.end(), "a") != nodeNames.end());
CPPUNIT_ASSERT(std::find(nodeNames.begin(), nodeNames.end(), "b") != nodeNames.end());
nodeNames = aPrefs->ChildrenNames();
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(nodeNames.size()));
CPPUNIT_ASSERT(std::find(nodeNames.begin(), nodeNames.end(), "aa") != nodeNames.end());
nodeNames = bPrefs->ChildrenNames();
CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(nodeNames.size()));
CPPUNIT_ASSERT(std::find(nodeNames.begin(), nodeNames.end(), "bb") != nodeNames.end());
CPPUNIT_ASSERT(std::find(nodeNames.begin(), nodeNames.end(), "bb2") != nodeNames.end());
nodeNames = bbPrefs->ChildrenNames();
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(nodeNames.size()));
CPPUNIT_ASSERT(std::find(nodeNames.begin(), nodeNames.end(), "bbb") != nodeNames.end());
CPPUNIT_ASSERT_EQUAL(std::string("/b/bb/bbb"), bbPrefs->Node("bbb")->AbsolutePath());
}
void CountOnChangedEvents(const mitk::IPreferences*)
{
++m_NumberOfOnChangedEvents;
}
void OnChanged()
{
auto* preferences = mitk::CoreServices::GetPreferencesService()->GetSystemPreferences();
preferences->OnChanged += mitk::MessageDelegate1<mitkPreferencesTestSuite, const mitk::IPreferences*>(this, &mitkPreferencesTestSuite::CountOnChangedEvents);
CPPUNIT_ASSERT_EQUAL(0, m_NumberOfOnChangedEvents);
preferences->Node("a");
CPPUNIT_ASSERT_EQUAL(1, m_NumberOfOnChangedEvents);
preferences->Node("a");
CPPUNIT_ASSERT_EQUAL(1, m_NumberOfOnChangedEvents);
preferences->Node("a/aa/aaa");
CPPUNIT_ASSERT_EQUAL(1, m_NumberOfOnChangedEvents);
preferences->Node("b/bb");
CPPUNIT_ASSERT_EQUAL(2, m_NumberOfOnChangedEvents);
preferences->Node("b");
CPPUNIT_ASSERT_EQUAL(2, m_NumberOfOnChangedEvents);
}
void CountOnPropertyChangedEvents(const mitk::IPreferences::ChangeEvent& e)
{
++m_NumberOfOnPropertyChangedEvents;
CPPUNIT_ASSERT_EQUAL(std::string("pref"), e.GetProperty());
CPPUNIT_ASSERT_EQUAL(std::string(""), e.GetOldValue());
CPPUNIT_ASSERT_EQUAL(std::string("something"), e.GetNewValue());
}
void OnPropertyChanged()
{
auto* preferences = mitk::CoreServices::GetPreferencesService()->GetSystemPreferences();
preferences->OnPropertyChanged += mitk::MessageDelegate1<mitkPreferencesTestSuite, const mitk::IPreferences::ChangeEvent&>(this, &mitkPreferencesTestSuite::CountOnPropertyChangedEvents);
CPPUNIT_ASSERT_EQUAL(0, m_NumberOfOnPropertyChangedEvents);
preferences->Put("pref", "");
CPPUNIT_ASSERT_EQUAL(0, m_NumberOfOnPropertyChangedEvents);
preferences->Put("pref", "something");
CPPUNIT_ASSERT_EQUAL(1, m_NumberOfOnPropertyChangedEvents);
}
void GetInt()
{
auto* preferences = mitk::CoreServices::GetPreferencesService()->GetSystemPreferences();
const int expectedValue = 42;
preferences->PutInt("integer", expectedValue);
CPPUNIT_ASSERT_EQUAL(expectedValue, preferences->GetInt("integer", 0));
preferences->Put("overflow", "4200000000");
CPPUNIT_ASSERT_THROW(preferences->GetInt("overflow", 0), mitk::Exception);
preferences->Put("string", "fourty two");
CPPUNIT_ASSERT_THROW(preferences->GetInt("string", 0), mitk::Exception);
}
void GetFloat()
{
auto* preferences = mitk::CoreServices::GetPreferencesService()->GetSystemPreferences();
float expectedValue = 3.14f;
preferences->PutFloat("float", expectedValue);
CPPUNIT_ASSERT_DOUBLES_EQUAL(expectedValue, preferences->GetFloat("float", 0.0f), mitk::eps);
preferences->Put("overflow", "3.14e100");
CPPUNIT_ASSERT_THROW(preferences->GetFloat("overflow", 0.0f), mitk::Exception);
preferences->Put("string", "pi");
CPPUNIT_ASSERT_THROW(preferences->GetFloat("string", 0.0f), mitk::Exception);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkPreferences)
diff --git a/Modules/CppMicroServices/cmake/CMakePackageConfigHelpers.cmake b/Modules/CppMicroServices/cmake/CMakePackageConfigHelpers.cmake
index 26f6cc264f..8fca1803eb 100644
--- a/Modules/CppMicroServices/cmake/CMakePackageConfigHelpers.cmake
+++ b/Modules/CppMicroServices/cmake/CMakePackageConfigHelpers.cmake
@@ -1,288 +1,288 @@
# - CONFIGURE_PACKAGE_CONFIG_FILE(), WRITE_BASIC_PACKAGE_VERSION_FILE()
#
# CONFIGURE_PACKAGE_CONFIG_FILE(<input> <output> INSTALL_DESTINATION <path>
# [PATH_VARS <var1> <var2> ... <varN>]
# [NO_SET_AND_CHECK_MACRO]
# [NO_CHECK_REQUIRED_COMPONENTS_MACRO])
#
# CONFIGURE_PACKAGE_CONFIG_FILE() should be used instead of the plain
# configure_file() command when creating the <Name>Config.cmake or <Name>-config.cmake
# file for installing a project or library. It helps making the resulting package
# relocatable by avoiding hardcoded paths in the installed Config.cmake file.
#
# In a FooConfig.cmake file there may be code like this to make the
# install destinations know to the using project:
# set(FOO_INCLUDE_DIR "@CMAKE_INSTALL_FULL_INCLUDEDIR@" )
# set(FOO_DATA_DIR "@CMAKE_INSTALL_PREFIX@/@RELATIVE_DATA_INSTALL_DIR@" )
# set(FOO_ICONS_DIR "@CMAKE_INSTALL_PREFIX@/share/icons" )
# ...logic to determine installedPrefix from the own location...
# set(FOO_CONFIG_DIR "${installedPrefix}/@CONFIG_INSTALL_DIR@" )
# All 4 options shown above are not sufficient, since the first 3 hardcode
# the absolute directory locations, and the 4th case works only if the logic
# to determine the installedPrefix is correct, and if CONFIG_INSTALL_DIR contains
# a relative path, which in general cannot be guaranteed.
# This has the effect that the resulting FooConfig.cmake file would work poorly
# under Windows and OSX, where users are used to choose the install location
# of a binary package at install time, independent from how CMAKE_INSTALL_PREFIX
# was set at build/cmake time.
#
# Using CONFIGURE_PACKAGE_CONFIG_FILE() helps. If used correctly, it makes the
# resulting FooConfig.cmake file relocatable.
# Usage:
# 1. write a FooConfig.cmake.in file as you are used to
# 2. insert a line containing only the string "@PACKAGE_INIT@"
# 3. instead of set(FOO_DIR "@SOME_INSTALL_DIR@"), use set(FOO_DIR "@PACKAGE_SOME_INSTALL_DIR@")
# (this must be after the @PACKAGE_INIT@ line)
# 4. instead of using the normal configure_file(), use CONFIGURE_PACKAGE_CONFIG_FILE()
#
# The <input> and <output> arguments are the input and output file, the same way
# as in configure_file().
#
# The <path> given to INSTALL_DESTINATION must be the destination where the FooConfig.cmake
# file will be installed to. This can either be a relative or absolute path, both work.
#
# The variables <var1> to <varN> given as PATH_VARS are the variables which contain
# install destinations. For each of them the macro will create a helper variable
# PACKAGE_<var...>. These helper variables must be used
# in the FooConfig.cmake.in file for setting the installed location. They are calculated
# by CONFIGURE_PACKAGE_CONFIG_FILE() so that they are always relative to the
# installed location of the package. This works both for relative and also for absolute locations.
# For absolute locations it works only if the absolute location is a subdirectory
# of CMAKE_INSTALL_PREFIX.
#
# By default configure_package_config_file() also generates two helper macros,
# set_and_check() and check_required_components() into the FooConfig.cmake file.
#
# set_and_check() should be used instead of the normal set()
# command for setting directories and file locations. Additionally to setting the
# variable it also checks that the referenced file or directory actually exists
# and fails with a FATAL_ERROR otherwise. This makes sure that the created
# FooConfig.cmake file does not contain wrong references.
# When using the NO_SET_AND_CHECK_MACRO, this macro is not generated into the
# FooConfig.cmake file.
#
# check_required_components(<package_name>) should be called at the end of the
# FooConfig.cmake file if the package supports components.
# This macro checks whether all requested, non-optional components have been found,
# and if this is not the case, sets the Foo_FOUND variable to FALSE, so that the package
# is considered to be not found.
# It does that by testing the Foo_<Component>_FOUND variables for all requested
# required components.
# When using the NO_CHECK_REQUIRED_COMPONENTS option, this macro is not generated
# into the FooConfig.cmake file.
#
# For an example see below the documentation for WRITE_BASIC_PACKAGE_VERSION_FILE().
#
#
# WRITE_BASIC_PACKAGE_VERSION_FILE( filename VERSION major.minor.patch COMPATIBILITY (AnyNewerVersion|SameMajorVersion|ExactVersion) )
#
# Writes a file for use as <package>ConfigVersion.cmake file to <filename>.
# See the documentation of find_package() for details on this.
# filename is the output filename, it should be in the build tree.
# major.minor.patch is the version number of the project to be installed
# The COMPATIBILITY mode AnyNewerVersion means that the installed package version
# will be considered compatible if it is newer or exactly the same as the requested version.
# This mode should be used for packages which are fully backward compatible,
# also across major versions.
# If SameMajorVersion is used instead, then the behaviour differs from AnyNewerVersion
# in that the major version number must be the same as requested, e.g. version 2.0 will
# not be considered compatible if 1.0 is requested.
# This mode should be used for packages which guarantee backward compatibility within the
# same major version.
# If ExactVersion is used, then the package is only considered compatible if the requested
# version matches exactly its own version number (not considering the tweak version).
# For example, version 1.2.3 of a package is only considered compatible to requested version 1.2.3.
# This mode is for packages without compatibility guarantees.
# If your project has more elaborated version matching rules, you will need to write your
# own custom ConfigVersion.cmake file instead of using this macro.
#
# Internally, this macro executes configure_file() to create the resulting
# version file. Depending on the COMPATIBLITY, either the file
# BasicConfigVersion-SameMajorVersion.cmake.in or BasicConfigVersion-AnyNewerVersion.cmake.in
# is used. Please note that these two files are internal to CMake and you should
# not call configure_file() on them yourself, but they can be used as starting
-# point to create more sophisticted custom ConfigVersion.cmake files.
+# point to create more sophisticated custom ConfigVersion.cmake files.
#
#
# Example using both configure_package_config_file() and write_basic_package_version_file():
# CMakeLists.txt:
# set(INCLUDE_INSTALL_DIR include/ ... CACHE )
# set(LIB_INSTALL_DIR lib/ ... CACHE )
# set(SYSCONFIG_INSTALL_DIR etc/foo/ ... CACHE )
# ...
# include(CMakePackageConfigHelpers)
# configure_package_config_file(FooConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
# INSTALL_DESTINATION ${LIB_INSTALL_DIR}/Foo/cmake
# PATH_VARS INCLUDE_INSTALL_DIR SYSCONFIG_INSTALL_DIR)
# write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/FooConfigVersion.cmake
# VERSION 1.2.3
# COMPATIBILITY SameMajorVersion )
# install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/FooConfigVersion.cmake
# DESTINATION ${LIB_INSTALL_DIR}/Foo/cmake )
#
# With a FooConfig.cmake.in:
# set(FOO_VERSION x.y.z)
# ...
# @PACKAGE_INIT@
# ...
# set_and_check(FOO_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
# set_and_check(FOO_SYSCONFIG_DIR "@PACKAGE_SYSCONFIG_INSTALL_DIR@")
#
# check_required_components(Foo)
#=============================================================================
# Copyright 2012 Alexander Neundorf <neundorf@kde.org>
#
# CMake - Cross Platform Makefile Generator
# Copyright 2000-2011 Kitware, Inc., Insight Software Consortium
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the names of Kitware, Inc., the Insight Software Consortium,
# nor the names of their contributors may be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# ------------------------------------------------------------------------------
#
# The above copyright and license notice applies to distributions of
# CMake in source and binary form. Some source files contain additional
# notices of original copyright by their contributors; see each source
# for details. Third-party software packages supplied with CMake under
# compatible licenses provide their own copyright notices documented in
# corresponding subdirectories.
#
#------------------------------------------------------------------------------
#
# CMake was initially developed by Kitware with the following sponsorship:
#
# * National Library of Medicine at the National Institutes of Health
# as part of the Insight Segmentation and Registration Toolkit (ITK).
#
# * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel
# Visualization Initiative.
#
# * National Alliance for Medical Image Computing (NAMIC) is funded by the
# National Institutes of Health through the NIH Roadmap for Medical Research,
# Grant U54 EB005149.
#
# * Kitware, Inc.
#=============================================================================
include(CMakeParseArguments)
function(CONFIGURE_PACKAGE_CONFIG_FILE _inputFile _outputFile)
set(options NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO)
set(oneValueArgs INSTALL_DESTINATION )
set(multiValueArgs PATH_VARS )
cmake_parse_arguments(CCF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(CCF_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unknown keywords given to CONFIGURE_PACKAGE_CONFIG_FILE(): \"${CCF_UNPARSED_ARGUMENTS}\"")
endif()
if(NOT CCF_INSTALL_DESTINATION)
message(FATAL_ERROR "No INSTALL_DESTINATION given to CONFIGURE_PACKAGE_CONFIG_FILE()")
endif()
if(IS_ABSOLUTE "${CCF_INSTALL_DESTINATION}")
set(absInstallDir "${CCF_INSTALL_DESTINATION}")
else()
set(absInstallDir "${CMAKE_INSTALL_PREFIX}/${CCF_INSTALL_DESTINATION}")
endif()
file(RELATIVE_PATH PACKAGE_RELATIVE_PATH "${absInstallDir}" "${CMAKE_INSTALL_PREFIX}" )
foreach(var ${CCF_PATH_VARS})
if(NOT DEFINED ${var})
message(FATAL_ERROR "Variable ${var} does not exist")
else()
if(IS_ABSOLUTE "${${var}}")
string(REPLACE "${CMAKE_INSTALL_PREFIX}" "\${PACKAGE_PREFIX_DIR}"
PACKAGE_${var} "${${var}}")
else()
set(PACKAGE_${var} "\${PACKAGE_PREFIX_DIR}/${${var}}")
endif()
endif()
endforeach()
get_filename_component(inputFileName "${_inputFile}" NAME)
set(PACKAGE_INIT "
####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
####### Any changes to this file will be overwritten by the next CMake run ####
####### The input file was ${inputFileName} ########
get_filename_component(PACKAGE_PREFIX_DIR \"\${CMAKE_CURRENT_LIST_DIR}/${PACKAGE_RELATIVE_PATH}\" ABSOLUTE)
")
if("${absInstallDir}" MATCHES "^(/usr)?/lib(64)?/.+")
# Handle "/usr move" symlinks created by some Linux distros.
set(PACKAGE_INIT "${PACKAGE_INIT}
# Use original install prefix when loaded through a \"/usr move\"
# cross-prefix symbolic link such as /lib -> /usr/lib.
get_filename_component(_realCurr \"\${CMAKE_CURRENT_LIST_DIR}\" REALPATH)
get_filename_component(_realOrig \"${absInstallDir}\" REALPATH)
if(_realCurr STREQUAL _realOrig)
set(PACKAGE_PREFIX_DIR \"${CMAKE_INSTALL_PREFIX}\")
endif()
unset(_realOrig)
unset(_realCurr)
")
endif()
if(NOT CCF_NO_SET_AND_CHECK_MACRO)
set(PACKAGE_INIT "${PACKAGE_INIT}
macro(set_and_check _var _file)
set(\${_var} \"\${_file}\")
if(NOT EXISTS \"\${_file}\")
message(FATAL_ERROR \"File or directory \${_file} referenced by variable \${_var} does not exist !\")
endif()
endmacro()
")
endif()
if(NOT CCF_NO_CHECK_REQUIRED_COMPONENTS_MACRO)
set(PACKAGE_INIT "${PACKAGE_INIT}
macro(check_required_components _NAME)
foreach(comp \${\${_NAME}_FIND_COMPONENTS})
if(NOT \${_NAME}_\${comp}_FOUND)
if(\${_NAME}_FIND_REQUIRED_\${comp})
set(\${_NAME}_FOUND FALSE)
endif()
endif()
endforeach()
endmacro()
")
endif()
set(PACKAGE_INIT "${PACKAGE_INIT}
####################################################################################")
configure_file("${_inputFile}" "${_outputFile}" @ONLY)
endfunction()
diff --git a/Modules/CppMicroServices/cmake/usFunctionCheckCompilerFlags.cmake b/Modules/CppMicroServices/cmake/usFunctionCheckCompilerFlags.cmake
index 0ec97da19f..7f6e649d98 100644
--- a/Modules/CppMicroServices/cmake/usFunctionCheckCompilerFlags.cmake
+++ b/Modules/CppMicroServices/cmake/usFunctionCheckCompilerFlags.cmake
@@ -1,68 +1,68 @@
#
# Helper macro allowing to check if the given flags are supported
# by the underlying build tool
#
# If the flag(s) is/are supported, they will be appended to the string identified by RESULT_VAR
#
# Usage:
# usFunctionCheckCompilerFlags(FLAGS_TO_CHECK VALID_FLAGS_VAR)
#
# Example:
#
# set(myflags)
# usFunctionCheckCompilerFlags("-fprofile-arcs" myflags)
# message(1-myflags:${myflags})
# usFunctionCheckCompilerFlags("-fauto-bugfix" myflags)
# message(2-myflags:${myflags})
# usFunctionCheckCompilerFlags("-Wall" myflags)
# message(1-myflags:${myflags})
#
# The output will be:
# 1-myflags: -fprofile-arcs
# 2-myflags: -fprofile-arcs
# 3-myflags: -fprofile-arcs -Wall
include(CheckCXXCompilerFlag)
function(usFunctionCheckCompilerFlags FLAG_TO_TEST RESULT_VAR)
if(FLAG_TO_TEST STREQUAL "")
message(FATAL_ERROR "FLAG_TO_TEST shouldn't be empty")
endif()
# Save the contents of RESULT_VAR temporarily.
# This is needed in case ${RESULT_VAR} is one of the CMAKE_<LANG>_FLAGS_* variables.
set(_saved_result_var ${${RESULT_VAR}})
# Clear all flags. If not, existing flags triggering warnings might lead to
# false-negatives when checking for certain compiler flags.
set(CMAKE_C_FLAGS )
set(CMAKE_C_FLAGS_DEBUG )
set(CMAKE_C_FLAGS_MINSIZEREL )
set(CMAKE_C_FLAGS_RELEASE )
set(CMAKE_C_FLAGS_RELWITHDEBINFO )
set(CMAKE_CXX_FLAGS )
set(CMAKE_CXX_FLAGS_DEBUG )
set(CMAKE_CXX_FLAGS_MINSIZEREL )
set(CMAKE_CXX_FLAGS_RELEASE )
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO )
# Internally, the macro CMAKE_CXX_COMPILER_FLAG calls TRY_COMPILE. To avoid
# the cost of compiling the test each time the project is configured, the variable set by
# the macro is added to the cache so that following invocation of the macro with
# the same variable name skip the compilation step.
# For that same reason, the usFunctionCheckCompilerFlags function appends a unique suffix to
# the HAS_CXX_FLAG variable. This suffix is created using a 'clean version' of the
- # flag to test. The value of HAS_CXX_FLAG_${suffix} additonally needs to be a valid
+ # flag to test. The value of HAS_CXX_FLAG_${suffix} additionally needs to be a valid
# pre-processor token because CHECK_CXX_COMPILER_FLAG adds it as a definition to the compiler
# arguments. An invalid token triggers compiler warnings, which in case of the "-Werror" flag
# leads to false-negative checks.
string(REGEX REPLACE "[/-]" "_" suffix ${FLAG_TO_TEST})
string(REGEX REPLACE "[, \\$\\+\\*\\{\\}\\(\\)\\#]" "" suffix ${suffix})
CHECK_CXX_COMPILER_FLAG(${FLAG_TO_TEST} HAS_CXX_FLAG_${suffix})
if(HAS_CXX_FLAG_${suffix})
set(${RESULT_VAR} "${_saved_result_var} ${FLAG_TO_TEST}" PARENT_SCOPE)
endif()
endfunction()
diff --git a/Modules/CppMicroServices/cmake/usFunctionEmbedResources.cmake b/Modules/CppMicroServices/cmake/usFunctionEmbedResources.cmake
index cf23534b03..fd8fc75974 100644
--- a/Modules/CppMicroServices/cmake/usFunctionEmbedResources.cmake
+++ b/Modules/CppMicroServices/cmake/usFunctionEmbedResources.cmake
@@ -1,194 +1,194 @@
#! \ingroup MicroServicesCMake
#! \brief Embed resources in a library or executable.
#!
#! This CMake function uses an external command line program to generate a ZIP archive
#! containing data from external resources such as text files or images or other ZIP
#! archives. The created archive file is appended or embedded as a binary blob to the target file.
#!
#! \note To set-up correct file dependencies from your module target to your resource
#! files, you have to add a special source file to the source list of the target.
#! The source file name can be retrieved by using usFunctionGetResourceSoruce.
#! This ensures that changed resource files will automatically be re-added to the
#! module.
#!
-#! There are two differend modes for including resources: APPEND and LINK. In APPEND mode,
+#! There are two different modes for including resources: APPEND and LINK. In APPEND mode,
#! the generated zip file is appended at the end of the target file. In LINK mode, the
#! zip file is compiled / linked into the target using platform specific techniques. LINK
#! mode is necessary if certain tools make additional assumptions about the object layout
#! of the target file (e.g. codesign on MacOS). LINK mode may result in slower module
#! initialization and bigger object files. The default mode is LINK mode on MacOS and
#! APPEND mode on all other platforms.
#!
#! Example usage:
#! \code{.cmake}
#! set(module_srcs )
#! usFunctionEmbedResources(TARGET mylib
#! MODULE_NAME org_me_mylib
#! FILES config.properties logo.png
#! )
#! \endcode
#!
#! \param TARGET (required) The target to which the resource files are added.
#! \param MODULE_NAME (required/optional) The module name of the target, as specified in
#! the \c US_MODULE_NAME pre-processor definition of that target. This parameter
#! is optional if a target property with the name US_MODULE_NAME exists, containing
#! the required module name.
#! \param APPEND Append the resources zip file to the target file.
#! \param LINK Link (embed) the resources zip file if possible.
#!
#! For the WORKING_DIRECTORY, COMPRESSION_LEVEL, FILES, ZIP_ARCHIVES parameters see the
#! documentation of the usFunctionAddResources macro which is called with these parameters if set.
#!
#! \sa usFunctionAddResources
#! \sa usFunctionGetResourceSource
#! \sa \ref MicroServices_Resources
#!
function(usFunctionEmbedResources)
cmake_parse_arguments(US_RESOURCE "APPEND;LINK" "TARGET;MODULE_NAME;WORKING_DIRECTORY;COMPRESSION_LEVEL" "FILES;ZIP_ARCHIVES" ${ARGN})
if(NOT US_RESOURCE_TARGET)
message(SEND_ERROR "TARGET argument not specified.")
endif()
if(US_RESOURCE_FILES OR US_RESOURCE_ZIP_ARCHIVES)
usFunctionAddResources(TARGET ${US_RESOURCE_TARGET}
MODULE_NAME ${US_RESOURCE_MODULE_NAME}
WORKING_DIRECTORY ${US_RESOURCE_WORKING_DIRECTORY}
COMPRESSION_LEVEL ${US_RESOURCE_COMPRESSION_LEVEL}
FILES ${US_RESOURCE_FILES}
ZIP_ARCHIVES ${US_RESOURCE_ZIP_ARCHIVES}
)
endif()
get_target_property(_res_zips ${US_RESOURCE_TARGET} _us_resource_zips)
if(NOT _res_zips)
return()
endif()
if(US_RESOURCE_APPEND AND US_RESOURCE_LINK)
message(WARNING "Both APPEND and LINK options specified. Falling back to default behaviour.")
set(US_RESOURCE_APPEND 0)
set(US_RESOURCE_LINK 0)
endif()
if(US_RESOURCE_LINK AND NOT US_RESOURCE_LINKING_AVAILABLE)
message(WARNING "Resource linking not available. Falling back to APPEND mode.")
set(US_RESOURCE_LINK 0)
set(US_RESOURCE_APPEND 1)
endif()
# Set default resource mode
if(NOT US_RESOURCE_APPEND AND NOT US_RESOURCE_LINK)
if(US_DEFAULT_RESOURCE_MODE STREQUAL "LINK")
set(US_RESOURCE_LINK 1)
else()
set(US_RESOURCE_APPEND 1)
endif()
endif()
set(_mode )
if(US_RESOURCE_LINK)
set(_mode LINK)
elseif(US_RESOURCE_APPEND)
set(_mode APPEND)
endif()
usFunctionGetResourceSource(TARGET ${US_RESOURCE_TARGET} OUT _source_output ${_mode})
if(NOT US_RESOURCE_WORKING_DIRECTORY)
set(US_RESOURCE_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endif()
if(NOT IS_ABSOLUTE ${US_RESOURCE_WORKING_DIRECTORY})
set(US_RESOURCE_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${US_RESOURCE_WORKING_DIRECTORY}")
endif()
set(resource_compiler ${US_RCC_EXECUTABLE})
if(TARGET ${US_RCC_EXECUTABLE_NAME})
set(resource_compiler ${US_RCC_EXECUTABLE_NAME})
elseif(NOT resource_compiler)
message(FATAL_ERROR "The CppMicroServices resource compiler was not found. Check the US_RCC_EXECUTABLE CMake variable.")
endif()
set(_zip_archive )
get_target_property(_counter ${US_RESOURCE_TARGET} _us_resource_counter)
if(_counter EQUAL 0)
set(_zip_archive ${_res_zips})
else()
set(_zip_archive ${CMAKE_CURRENT_BINARY_DIR}/us_${US_RESOURCE_TARGET}/res.zip)
add_custom_command(
OUTPUT ${_zip_archive}
COMMAND ${resource_compiler} ${_zip_archive} dummy -m ${_res_zips}
DEPENDS ${_res_zips} ${resource_compiler}
COMMENT "Creating resources zip file for ${US_RESOURCE_TARGET}"
VERBATIM
)
endif()
get_filename_component(_zip_archive_name ${_zip_archive} NAME)
get_filename_component(_zip_archive_path ${_zip_archive} PATH)
if(US_RESOURCE_LINK)
if(APPLE)
add_custom_command(
OUTPUT ${_source_output}
COMMAND ${CMAKE_CXX_COMPILER} -c ${US_CMAKE_RESOURCE_DEPENDENCIES_CPP} -o stub.o
COMMAND ${CMAKE_LINKER} -r -sectcreate __TEXT us_resources ${_zip_archive_name} stub.o -o ${_source_output}
DEPENDS ${_zip_archive}
WORKING_DIRECTORY ${_zip_archive_path}
COMMENT "Linking resources zip file for ${US_RESOURCE_TARGET}"
VERBATIM
)
set_source_files_properties(${_source_output} PROPERTIES EXTERNAL_OBJECT 1 GENERATED 1)
elseif(WIN32 AND CMAKE_RC_COMPILER)
set(US_RESOURCE_ARCHIVE ${_zip_archive})
configure_file(${US_RESOURCE_RC_TEMPLATE} ${_source_output})
add_custom_command(
OUTPUT ${_source_output}
COMMAND ${CMAKE_COMMAND} -E touch ${_source_output}
DEPENDS ${_zip_archive}
WORKING_DIRECTORY ${_zip_archive_path}
COMMENT "Linking resources zip file for ${US_RESOURCE_TARGET}"
VERBATIM
)
elseif(UNIX)
add_custom_command(
OUTPUT ${_source_output}
COMMAND ${CMAKE_LINKER} -r -b binary -o ${_source_output} ${_zip_archive_name}
COMMAND objcopy --rename-section .data=.rodata,alloc,load,readonly,data,contents ${_source_output} ${_source_output}
DEPENDS ${_zip_archive}
WORKING_DIRECTORY ${_zip_archive_path}
COMMENT "Linking resources zip file for ${US_RESOURCE_TARGET}"
VERBATIM
)
set_source_files_properties(${_source_output} PROPERTIES EXTERNAL_OBJECT 1 GENERATED 1)
else()
message(WARNING "Internal error: Resource linking not available. Falling back to APPEND mode.")
set(US_RESOURCE_LINK 0)
set(US_RESOURCE_APPEND 1)
endif()
endif()
if(US_RESOURCE_APPEND)
# This command depends on the given resource files and creates a source
# file which must be added to the source list of the related target.
# This way, the following command is executed if the resources change
# and it just touches the created source file to force a (actually unnecessary)
# re-linking and hence the execution of POST_BUILD commands.
add_custom_command(
OUTPUT ${_source_output}
COMMAND ${CMAKE_COMMAND} -E copy ${US_CMAKE_RESOURCE_DEPENDENCIES_CPP} ${_source_output}
DEPENDS ${_zip_archive}
COMMENT "Checking resource dependencies for ${US_RESOURCE_TARGET}"
VERBATIM
)
add_custom_command(
TARGET ${US_RESOURCE_TARGET}
POST_BUILD
COMMAND ${resource_compiler} --append $<TARGET_FILE:${US_RESOURCE_TARGET}> ${_zip_archive}
WORKING_DIRECTORY ${US_RESOURCE_WORKING_DIRECTORY}
COMMENT "Appending zipped resources to ${US_RESOURCE_TARGET}"
VERBATIM
)
endif()
endfunction()
diff --git a/Modules/CppMicroServices/core/doc/doxygen/MicroServices_ServiceHooks.md b/Modules/CppMicroServices/core/doc/doxygen/MicroServices_ServiceHooks.md
index 3c29df35a0..f056bd3e4e 100644
--- a/Modules/CppMicroServices/core/doc/doxygen/MicroServices_ServiceHooks.md
+++ b/Modules/CppMicroServices/core/doc/doxygen/MicroServices_ServiceHooks.md
@@ -1,92 +1,92 @@
Service Hooks {#MicroServices_ServiceHooks}
=============
The CppMicroServices library implements the Service Hook Service Specification Version 1.1 from
OSGi Core Release 5 for C++. Below is a summary of the concept - consult the OSGi specifications
for more details.
Service hooks provide mechanisms for module writers to closely interact with the CppMicroServices
service registry. These mechanisms are not intended for use by application modules but rather
by modules in need of *hooking* into the service registry and modifying the behaviour of
application modules.
Specific use case for service hooks include proxying of existing services by hiding the original
service and registering a *proxy service* with the same properties or providing services
*on demand* based on registered service listeners from external modules.
## Event Listener Hook
A module can intercept events being delivered to other modules by registering a ServiceEventListenerHook
object as a service. The CppMicroServices library will send all service events to all the registered
hooks using the reversed ordering of their ServiceReference objects. Note that event listener hooks
are called *after* the event was created but *before* it is filtered by the optional filter expression
of the service listeners. Hence an event listener hook receives all \link us::ServiceEvent::REGISTERED
REGISTERED \endlink, \link us::ServiceEvent::MODIFIED MODIFIED \endlink, \link us::ServiceEvent::UNREGISTERING
UNREGISTERING \endlink, and \link us::ServiceEvent::MODIFIED_ENDMATCH MODIFIED_ENDMATCH \endlink events
regardelss of the presence of a service listener filter. It may then remove modules or specific
service listeners from the ServiceEventListenerHook::ShrinkableMapType object passed to the
ServiceEventListenerHook::Event method to hide
service events.
Implementers of the Event Listener Hook must ensure that modules continue to see a consistent set of
service events.
## Find Hook
Find Hook objects registered using the ServiceFindHook interface will be called when modules look up
service references via the ModuleContext::GetServiceReference or ModuleContext::GetServiceReferences
methods. The order in which the CppMicroServices library calls the find hooks is the reverse `operator<`
ordering of their ServiceReference objects. The hooks may remove service references from the
ShrinkableVector object passed to the ServiceFindHook::Find method to hide services from specific modules.
## Listener Hook
The CppMicroServices API provides information about the registration, unregistration, and modification
of services. However, it does not directly allow the introspection of modules to get information about
what services a module is waiting for. Waiting for a service to arrive (via a registered service listener)
before performing its function is a common pattern for modules. Listener Hooks provide a mechanism to
-get informed about all existing, newly registerd, and removed service listeners.
+get informed about all existing, newly registered, and removed service listeners.
A Listener Hook object registered using the ServiceListenerHook interface will be notified about service
listeners by being passed ServiceListenerHook::ListenerInfo objects. Each ListenerInfo object is related to
the registration / unregistration cycle of a specific service listener. That is, registering the same service
-listener again, even with a different filter, will automatically unregister the previouse registration and
+listener again, even with a different filter, will automatically unregister the previous registration and
the newly registered service listener is related to a different ListenerInfo object. ListenerInfo objects
can be stored in unordered containers and compared with each other, e.g. to match ServiceListenerHook::Added
and ServiceListenerHook::Removed calls.
The Listener Hooks are called synchronously in the same order of their registration. However, in rare cases
the removal of a service listener may be reported before its corresponding addition. To handle this case,
the ListenerInfo::IsRemoved() method is provided which can be used in the ServiceListenerHook::Added
method to detect the out of order delivery. A simple strategy is to ignore removed events without
corresponding added events and ignore added events where the ListenerInfo object is already removed:
\snippet uServices-servicelistenerhook/main.cpp 1
## Architectural Notes
### Ordinary Services
All service hooks are treated as ordinary services. If the CppMicroServices library uses them, their
Service References will show that the CppMicroServices modules is using them, and if a hook is a
Service Factory, then the actual instance will be properly created.
The only speciality of the service hooks is that the CppMicroServices library does not use them for
the hooks themselves. That is, the Service Event and Service Find Hooks can not be used to hide the
services from the CppMicroServices library.
### Ordering
The hooks are very sensitive to ordering because they interact directly with the service registry.
In general, implementers of the hooks must be aware that other modules can be loaded before or after
the module which provides the hooks. To ensure early registration of the hooks, they should be registered
within the ModuleActivator::Load method of the program executable or a module being auto-loaded with
the executable.
### Multi Threading
All hooks must be thread-safe because the hooks can be called at any time. All hook methods must be
re-entrant, they can be entered at any time and in rare cases in the wrong order. The CppMicroServices
library calls all hook methods synchronously but the calls might be triggered from any user thread
interacting with the CppMicroServices API. The CppMicroServices API can be called from any of the
hook methods but implementers must be careful to not hold any lock while calling CppMicroServices methods.
diff --git a/Modules/CppMicroServices/core/doc/doxygen/examples/MicroServices_Example6.md b/Modules/CppMicroServices/core/doc/doxygen/examples/MicroServices_Example6.md
index c5150a55bc..0424d7c0fa 100644
--- a/Modules/CppMicroServices/core/doc/doxygen/examples/MicroServices_Example6.md
+++ b/Modules/CppMicroServices/core/doc/doxygen/examples/MicroServices_Example6.md
@@ -1,123 +1,123 @@
Example 6 - Spell Checker Service Module {#MicroServices_Example6}
========================================
In this example, we complicate things further by defining a new service that
uses an arbitrary number of dictionary services to perform its function. More
precisely, we define a spell checker service which will aggregate all dictionary
services and provide another service that allows us to spell check passages
using our underlying dictionary services to verify the spelling of words. Our
module will only provide the spell checker service if there are at least two
dictionary services available. First, we will start by defining the spell checker
service interface in a file called `spellcheckservice/ISpellCheckService.h`:
\snippet spellcheckservice/ISpellCheckService.h service
The service interface is quite simple, with only one method that needs to be
implemented. Because we provide an empty out-of-line destructor (defined in the
file `ISpellCheckService.cpp`) we must export the service interface by using the
module specific `SPELLCHECKSERVICE_EXPORT` macro.
In the following source code, the module needs to create a complete list of all
dictionary services; this is somewhat tricky and must be done carefully if done
-manually via service event listners. Our module makes use of the `ServiceTracker`
+manually via service event listeners. Our module makes use of the `ServiceTracker`
and `ServiceTrackerCustomizer` classes to robustly react to service events related
to dictionary services. The module activator of our module now additionally implements
the `ServiceTrackerCustomizer` class to be automatically notified of arriving, departing,
or modified dictionary services. In case of a newly added dictionary service, our
`ServiceTrackerCustomizer::AddingService()` implementation checks if a spell checker
service was already registered and if not registers a new `ISpellCheckService` instance
if at lead two dictionary services are available.
If the number of dictionary services drops below two, our `ServiceTrackerCustomizer`
implementation un-registers the previously registered spell checker service instance.
These actions must be performed in a synchronized manner to avoid interference from
service events originating from different threads. The implementation of our module
activator is done in a file called `spellcheckservice/Activator.cpp`:
\snippet spellcheckservice/Activator.cpp Activator
Note that we do not need to unregister the service in stop() method, because the
C++ Micro Services library will automatically do so for us. The spell checker service
that we have implemented is very simple; it simply parses a given passage into words
and then loops through all available dictionary services for each word until it
determines that the word is correct. Any incorrect words are added to an error list
that will be returned to the caller. This solution is not optimal and is only intended
for educational purposes.
\note In this example, the service interface and implementation are both
contained in one module which exports the interface class. However, service
implementations almost never need to be exported and in many use cases
it is beneficial to provide the service interface and its implementation(s)
in separate modules. In such a scenario, clients of a service will only
have a link-time dependency on the shared library providing the service interface
(because of the out-of-line destructor) but not on any modules containing
service implementations. This often leads to modules which do not export
any symbols at all and hence need to be loaded into the running process
manually or by using the \ref MicroServices_AutoLoading "auto-loading mechanism".
\note Due to the link dependency of our module to the module containing the
dictionary service interface as well as a default implementation for it, there
might be at least one dictionary service registered when our module is
loaded, depending on your linker settings (e.g. on Windows, the linker usually
by default optimizes the link dependency away since our module does not actually
use any symbols from the dictionaryservice module. On Linux however, the link
dependency is kept by default.) To observe the dynamic registration and
un-registration of our spell checker service, we require the availability of
at least two dictionary services.
For an introduction how to compile our source code, see \ref MicroServices_Example1.
After running the `usCoreExamplesDriver` program we should make sure that the
module from Example 1 is active. We can use the `s` shell command to get
a list of all modules, their state, and their module identifier number.
If the Example 1 module is not active, we should load the module using the
load command and the module's identifier number or name that is displayed
by the `s` command. Now we can load the spell checker service module by
entering the `l spellcheckservice` command which will also trigger the loading
of the dictionaryservice module containing the english dictionary:
\verbatim
CppMicroServices-build> bin/usCoreExamplesDriver
> l eventlistener
Starting to listen for service events.
> l spellcheckservice
Ex1: Service of type IDictionaryService/1.0 registered.
> s
Id | Name | Status
-----------------------------------
- | dictionaryclient | -
- | dictionaryclient2 | -
- | dictionaryclient3 | -
- | frenchdictionary | -
- | spellcheckclient | -
1 | CppMicroServices | LOADED
2 | Event Listener | LOADED
3 | Dictionary Service | LOADED
4 | Spell Check Service | LOADED
>
\endverbatim
To trigger the registration of the spell checker service from our module, we
load the frenchdictionary using the `l frenchdictionary` command. If the module from
\ref MicroServices_Example1 "Example 1" is still active,
then we should see it print out the details of the service event it receives
when our new module registers its spell checker service:
\verbatim
CppMicroServices-build> bin/usCoreExamplesDriver
> l frenchdictionary
Ex1: Service of type IDictionaryService/1.0 registered.
Ex1: Service of type ISpellCheckService/1.0 registered.
>
\endverbatim
We can experiment with our spell checker service's dynamic availability by stopping
the french dictionary service; when the service is stopped,
the eventlistener module will print that our module is no longer offering its
spell checker service. Likewise, when the french dictionary service comes back, so will
our spell checker service. We create a client for our spell checker service in
\ref MicroServices_Example7 "Example 7". To exit the `usCoreExamplesDriver` program, we
use the `q` command.
Next: \ref MicroServices_Example7
Previous: \ref MicroServices_Example5
diff --git a/Modules/CppMicroServices/core/examples/dictionaryservice/IDictionaryService.h b/Modules/CppMicroServices/core/examples/dictionaryservice/IDictionaryService.h
index f80aae1cbc..aa49a50788 100644
--- a/Modules/CppMicroServices/core/examples/dictionaryservice/IDictionaryService.h
+++ b/Modules/CppMicroServices/core/examples/dictionaryservice/IDictionaryService.h
@@ -1,60 +1,60 @@
/*============================================================================
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
https://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 IDICTIONARYSERVICE_H
#define IDICTIONARYSERVICE_H
//! [service]
#include <usServiceInterface.h>
#include <string>
#ifdef US_BUILD_SHARED_LIBS
#ifdef Example_dictionaryservice_EXPORTS
#define DICTIONARYSERVICE_EXPORT US_ABI_EXPORT
#else
#define DICTIONARYSERVICE_EXPORT US_ABI_IMPORT
#endif
#else
#define DICTIONARYSERVICE_EXPORT US_ABI_EXPORT
#endif
/**
* A simple service interface that defines a dictionary service.
* A dictionary service simply verifies the existence of a word.
**/
struct DICTIONARYSERVICE_EXPORT IDictionaryService
{
- // Out-of-line virtual desctructor for proper dynamic cast
+ // Out-of-line virtual destructor for proper dynamic cast
// support with older versions of gcc.
virtual ~IDictionaryService();
/**
* Check for the existence of a word.
* @param word the word to be checked.
* @return true if the word is in the dictionary,
* false otherwise.
**/
virtual bool CheckWord(const std::string& word) = 0;
};
//! [service]
#endif // DICTIONARYSERVICE_H
diff --git a/Modules/CppMicroServices/core/examples/spellcheckservice/ISpellCheckService.h b/Modules/CppMicroServices/core/examples/spellcheckservice/ISpellCheckService.h
index d72b59fe8e..d54acbaadd 100644
--- a/Modules/CppMicroServices/core/examples/spellcheckservice/ISpellCheckService.h
+++ b/Modules/CppMicroServices/core/examples/spellcheckservice/ISpellCheckService.h
@@ -1,66 +1,66 @@
/*============================================================================
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
https://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 ISPELLCHECKSERVICE_H
#define ISPELLCHECKSERVICE_H
//! [service]
#include <usServiceInterface.h>
#include <string>
#include <vector>
#ifdef US_BUILD_SHARED_LIBS
#ifdef Example_spellcheckservice_EXPORTS
#define SPELLCHECKSERVICE_EXPORT US_ABI_EXPORT
#else
#define SPELLCHECKSERVICE_EXPORT US_ABI_IMPORT
#endif
#else
#define SPELLCHECKSERVICE_EXPORT US_ABI_EXPORT
#endif
/**
* A simple service interface that defines a spell check service. A spell check
* service checks the spelling of all words in a given passage. A passage is any
* number of words separated by a space character and the following punctuation
* marks: comma, period, exclamation mark, question mark, semi-colon, and colon.
*/
struct SPELLCHECKSERVICE_EXPORT ISpellCheckService
{
- // Out-of-line virtual desctructor for proper dynamic cast
+ // Out-of-line virtual destructor for proper dynamic cast
// support with older versions of gcc.
virtual ~ISpellCheckService();
/**
* Checks a given passage for spelling errors. A passage is any number of
* words separated by a space and any of the following punctuation marks:
* comma (,), period (.), exclamation mark (!), question mark (?),
* semi-colon (;), and colon(:).
*
* @param passage the passage to spell check.
* @return A list of misspelled words.
*/
virtual std::vector<std::string> Check(const std::string& passage) = 0;
};
//! [service]
//!
#endif // ISPELLCHECKSERVICE_H
diff --git a/Modules/CppMicroServices/core/include/usCoreConfig.h.in b/Modules/CppMicroServices/core/include/usCoreConfig.h.in
index fab29d715c..e3498eff6f 100644
--- a/Modules/CppMicroServices/core/include/usCoreConfig.h.in
+++ b/Modules/CppMicroServices/core/include/usCoreConfig.h.in
@@ -1,43 +1,43 @@
/*
usCoreConfig.h
This file is generated. Do not change!
*/
#ifndef USCORECONFIG_H
#define USCORECONFIG_H
#include <usCoreExport.h>
#cmakedefine US_ENABLE_AUTOLOADING_SUPPORT
///-------------------------------------------------------------------
// Version information
//-------------------------------------------------------------------
#define US_CORE_MAJOR_VERSION @Core_MAJOR_VERSION@
#define US_CORE_MINOR_VERSION @Core_MINOR_VERSION@
#define US_CORE_PATCH_VERSION @Core_PATCH_VERSION@
#define US_CORE_VERSION @Core_VERSION@
#define US_CORE_VERSION_STR "@Core_VERSION@"
//-------------------------------------------------------------------
-// Debuging & Logging
+// Debugging & Logging
//-------------------------------------------------------------------
#cmakedefine US_ENABLE_DEBUG_OUTPUT
US_BEGIN_NAMESPACE
enum MsgType { DebugMsg = 0, InfoMsg = 1, WarningMsg = 2, ErrorMsg = 3 };
typedef void (*MsgHandler)(MsgType, const char *);
US_Core_EXPORT MsgHandler installMsgHandler(MsgHandler);
US_END_NAMESPACE
///-------------------------------------------------------------------
// Macros used by the unit tests
//-------------------------------------------------------------------
#define US_CORE_SOURCE_DIR "@PROJECT_SOURCE_DIR@"
#endif // USCORECONFIG_H
diff --git a/Modules/CppMicroServices/core/include/usModule.h b/Modules/CppMicroServices/core/include/usModule.h
index 163f574ccc..be59522d55 100644
--- a/Modules/CppMicroServices/core/include/usModule.h
+++ b/Modules/CppMicroServices/core/include/usModule.h
@@ -1,367 +1,367 @@
/*============================================================================
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
https://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 USMODULE_H
#define USMODULE_H
#include "usModuleVersion.h"
#include <vector>
US_BEGIN_NAMESPACE
class Any;
class CoreModuleContext;
struct ModuleInfo;
class ModuleContext;
class ModuleResource;
class ModulePrivate;
template<class S>
class ServiceReference;
typedef ServiceReference<void> ServiceReferenceU;
/**
* \ingroup MicroServices
*
* Represents a CppMicroServices module.
*
* <p>
* A <code>%Module</code> object is the access point to a CppMicroServices module.
* Each CppMicroServices module has an associated <code>%Module</code> object.
*
* <p>
* A module has unique identity, a <code>long</code>, chosen by the
* framework. This identity does not change during the lifecycle of a module.
*
* <p>
* A module can be in one of two states:
* <ul>
* <li>LOADED
* <li>UNLOADED
* </ul>
* <p>
* You can determine the current state by using IsLoaded().
*
* <p>
* A module can only execute code when its state is <code>LOADED</code>.
* An <code>UNLOADED</code> module is a
* zombie and can only be reached because it was loaded before. However,
* unloaded modules can be loaded again.
*
* <p>
* The framework is the only entity that is allowed to create
* <code>%Module</code> objects.
*
* @remarks This class is thread safe.
*/
class US_Core_EXPORT Module
{
public:
/**
* Returns the property key for looking up this module's id.
* The property value is of type \c long.
*
* @return The id property key.
*/
static const std::string& PROP_ID();
/**
* Returns the property key for looking up this module's name.
* The property value is of type \c std::string.
*
* @return The name property key.
*/
static const std::string& PROP_NAME();
/**
* Returns the property key for looking up this module's
* location in the file system.
* The property value is of type \c std::string.
*
* @return The location property key.
*/
static const std::string& PROP_LOCATION();
/**
* Returns the property key with a value of \c module.version for looking
* up this module's version identifier.
* The property value is of type \c std::string.
*
* @return The version property key.
*/
static const std::string& PROP_VERSION();
/**
* Returns the property key with a value of \c module.vendor for looking
* up this module's vendor information.
* The property value is of type \c std::string.
*
* @return The vendor property key.
*/
static const std::string& PROP_VENDOR();
/**
* Returns the property key with a value of \c module.description for looking
* up this module's description.
* The property value is of type \c std::string.
*
* @return The description property key.
*/
static const std::string& PROP_DESCRIPTION();
/**
* Returns the property key with a value of \c module.autoload_dir for looking
* up this module's auto-load directory.
* The property value is of type \c std::string.
*
* @return The auto-load directory property key.
*/
static const std::string& PROP_AUTOLOAD_DIR();
/**
* Returns the property key with a value of \c module.autoloaded_modules for
* looking up this module's auto-load modules.
* The property value is of type \c std::vector<std::string> and contains
* the file system locations for the auto-loaded modules triggered by this
* module.
*
* @return The auto-loaded modules property key.
*/
static const std::string& PROP_AUTOLOADED_MODULES();
~Module();
/**
* Returns this module's current state.
*
* <p>
* A module can be in only one state at any time.
*
* @return <code>true</code> if the module is <code>LOADED</code>
* <code>false</code> if it is <code>UNLOADED</code>
*/
bool IsLoaded() const;
/**
* Returns this module's {@link ModuleContext}. The returned
* <code>ModuleContext</code> can be used by the caller to act on behalf
* of this module.
*
* <p>
* If this module is not in the <code>LOADED</code> state, then this
* module has no valid <code>ModuleContext</code>. This method will
* return <code>0</code> if this module has no valid
* <code>ModuleContext</code>.
*
* @return A <code>ModuleContext</code> for this module or
* <code>0</code> if this module has no valid
* <code>ModuleContext</code>.
*/
ModuleContext* GetModuleContext() const;
/**
* Returns this module's unique identifier. This module is assigned a unique
* identifier by the framework when it was loaded.
*
* <p>
* A module's unique identifier has the following attributes:
* <ul>
* <li>Is unique.
* <li>Is a <code>long</code>.
* <li>Its value is not reused for another module, even after a module is
* unloaded.
* <li>Does not change while a module remains loaded.
* <li>Does not change when a module is reloaded.
* </ul>
*
* <p>
* This method continues to return this module's unique identifier while
* this module is in the <code>UNLOADED</code> state.
*
* @return The unique identifier of this module.
*/
long GetModuleId() const;
/**
* Returns this module's location.
*
* <p>
* The location is the full path to the module's shared library.
* This method continues to return this module's location
* while this module is in the <code>UNLOADED</code> state.
*
* @return The string representation of this module's location.
*/
std::string GetLocation() const;
/**
* Returns the name of this module as specified by the
* US_CREATE_MODULE CMake macro. The module
* name together with a version must identify a unique module.
*
* <p>
* This method continues to return this module's name while
* this module is in the <code>UNLOADED</code> state.
*
* @return The name of this module.
*/
std::string GetName() const;
/**
* Returns the version of this module as specified by the
* US_INITIALIZE_MODULE CMake macro. If this module does not have a
* specified version then {@link ModuleVersion::EmptyVersion} is returned.
*
* <p>
* This method continues to return this module's version while
* this module is in the <code>UNLOADED</code> state.
*
* @return The version of this module.
*/
ModuleVersion GetVersion() const;
/**
* Returns the value of the specified property for this module. The
* method returns an empty Any if the property is not found.
*
* @param key The name of the requested property.
* @return The value of the requested property, or an empty string
* if the property is undefined.
*
* @sa GetPropertyKeys()
* @sa \ref MicroServices_ModuleProperties
*/
Any GetProperty(const std::string& key) const;
/**
* Returns a list of top-level property keys for this module.
*
* @return A list of available property keys.
*
* @sa \ref MicroServices_ModuleProperties
*/
std::vector<std::string> GetPropertyKeys() const;
/**
* Returns this module's ServiceReference list for all services it
* has registered or an empty list if this module has no registered
* services.
*
* The list is valid at the time of the call to this method, however,
* as the framework is a very dynamic environment, services can be
* modified or unregistered at anytime.
*
* @return A list of ServiceReference objects for services this
* module has registered.
*/
std::vector<ServiceReferenceU> GetRegisteredServices() const;
/**
* Returns this module's ServiceReference list for all services it is
* using or returns an empty list if this module is not using any
* services. A module is considered to be using a service if its use
* count for that service is greater than zero.
*
* The list is valid at the time of the call to this method, however,
* as the framework is a very dynamic environment, services can be
* modified or unregistered at anytime.
*
* @return A list of ServiceReference objects for all services this
* module is using.
*/
std::vector<ServiceReferenceU> GetServicesInUse() const;
/**
* Returns the resource at the specified \c path in this module.
* The specified \c path is always relative to the root of this module and may
* begin with '/'. A path value of "/" indicates the root of this module.
*
* @param path The path name of the resource.
* @return A ModuleResource object for the given \c path. If the \c path cannot
* be found in this module or the module's state is \c UNLOADED, an invalid
* ModuleResource object is returned.
*/
ModuleResource GetResource(const std::string& path) const;
/**
* Returns resources in this module.
*
* This method is intended to be used to obtain configuration, setup, localization
* and other information from this module.
*
* This method can either return only resources in the specified \c path or recurse
* into subdirectories returning resources in the directory tree beginning at the
* specified path.
*
* Examples:
* \snippet uServices-resources/main.cpp 0
*
* @param path The path name in which to look. The path is always relative to the root
* of this module and may begin with '/'. A path value of "/" indicates the root of this module.
* @param filePattern The resource name pattern for selecting entries in the specified path.
* The pattern is only matched against the last element of the resource path. Substring
- * matching is supported using the wildcard charachter ('*'). If \c filePattern is empty,
+ * matching is supported using the wildcard character ('*'). If \c filePattern is empty,
* this is equivalent to "*" and matches all resources.
* @param recurse If \c true, recurse into subdirectories. Otherwise only return resources
* from the specified path.
* @return A vector of ModuleResource objects for each matching entry.
*/
std::vector<ModuleResource> FindResources(const std::string& path, const std::string& filePattern, bool recurse) const;
private:
friend class CoreModuleActivator;
friend class ModuleRegistry;
friend class ServiceReferencePrivate;
ModulePrivate* d;
Module();
void Init(CoreModuleContext* coreCtx, ModuleInfo* info);
void Uninit();
void Start();
void Stop();
// purposely not implemented
Module(const Module &);
Module& operator=(const Module&);
};
US_END_NAMESPACE
/**
* \ingroup MicroServices
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(Module)& module);
/**
* \ingroup MicroServices
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, US_PREPEND_NAMESPACE(Module) const * module);
#endif // USMODULE_H
diff --git a/Modules/CppMicroServices/core/include/usModuleContext.h b/Modules/CppMicroServices/core/include/usModuleContext.h
index bfee00510d..63a2455273 100644
--- a/Modules/CppMicroServices/core/include/usModuleContext.h
+++ b/Modules/CppMicroServices/core/include/usModuleContext.h
@@ -1,851 +1,851 @@
/*============================================================================
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
https://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 USMODULECONTEXT_H_
#define USMODULECONTEXT_H_
// TODO: Replace includes with forward directives!
#include "usListenerFunctors_p.h"
#include "usServiceInterface.h"
#include "usServiceEvent.h"
#include "usServiceRegistration.h"
#include "usServiceException.h"
#include "usModuleEvent.h"
US_BEGIN_NAMESPACE
typedef US_SERVICE_LISTENER_FUNCTOR ServiceListener;
typedef US_MODULE_LISTENER_FUNCTOR ModuleListener;
class ModuleContextPrivate;
class ServiceFactory;
template<class S> class ServiceObjects;
/**
* \ingroup MicroServices
*
* A module's execution context within the framework. The context is used to
* grant access to other methods so that this module can interact with the
* Micro Services Framework.
*
* <p>
* <code>ModuleContext</code> methods allow a module to:
* <ul>
* <li>Subscribe to events published by the framework.
* <li>Register service objects with the framework service registry.
* <li>Retrieve <code>ServiceReference</code>s from the framework service
* registry.
* <li>Get and release service objects for a referenced service.
* <li>Get the list of modules loaded in the framework.
* <li>Get the {@link Module} object for a module.
* </ul>
*
* <p>
* A <code>ModuleContext</code> object will be created and provided to the
* module associated with this context when it is loaded using the
* us::ModuleActivator::Load method. The same <code>ModuleContext</code>
* object will be passed to the module associated with this context when it is
* unloaded using the us::ModuleActivator::Unload method. A
* <code>ModuleContext</code> object is generally for the private use of its
* associated module and is not meant to be shared with other modules in the
* module environment.
*
* <p>
* The <code>Module</code> object associated with a <code>ModuleContext</code>
* object is called the <em>context module</em>.
*
* <p>
* The <code>ModuleContext</code> object is only valid during the execution of
* its context module; that is, during the period when the context module
* is loaded. If the <code>ModuleContext</code>
* object is used subsequently, a <code>std::logic_error</code> is
* thrown. The <code>ModuleContext</code> object is never reused after
* its context module is unloaded.
*
* <p>
* The framework is the only entity that can create <code>ModuleContext</code>
* objects.
*
* @remarks This class is thread safe.
*/
class US_Core_EXPORT ModuleContext
{
public:
~ModuleContext();
/**
* Returns the <code>Module</code> object associated with this
* <code>ModuleContext</code>. This module is called the context module.
*
* @return The <code>Module</code> object associated with this
* <code>ModuleContext</code>.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
*/
Module* GetModule() const;
/**
* Returns the module with the specified identifier.
*
* @param id The identifier of the module to retrieve.
* @return A <code>Module</code> object or <code>0</code> if the
* identifier does not match any previously loaded module.
*/
Module* GetModule(long id) const;
/**
* Get the module that with the specified module name.
*
* @param name The name of the module to get.
* @return The requested \c Module or \c nullptr.
*/
Module* GetModule(const std::string& name);
/**
* Returns a list of all known modules.
* <p>
* This method returns a list of all modules loaded in the module
* environment at the time of the call to this method. This list will
* also contain modules which might already have been unloaded.
*
* @return A std::vector of <code>Module</code> objects which
* will hold one object per known module.
*/
std::vector<Module*> GetModules() const;
/**
* Registers the specified service object with the specified properties
* under the specified class names into the framework. A
* <code>ServiceRegistration</code> object is returned. The
* <code>ServiceRegistration</code> object is for the private use of the
* module registering the service and should not be shared with other
* modules. The registering module is defined to be the context module.
* Other modules can locate the service by using either the
* GetServiceReferences() or GetServiceReference() method.
*
* <p>
* A module can register a service object that implements the
* ServiceFactory or PrototypeServiceFactory interface to have more
* flexibility in providing service objects to other modules.
*
* <p>
* The following steps are taken when registering a service:
* <ol>
* <li>The framework adds the following service properties to the service
* properties from the specified <code>ServiceProperties</code> (which may be
* omitted): <br/>
* A property named us::ServiceConstants::SERVICE_ID() identifying the
* registration number of the service <br/>
* A property named us::ServiceConstants::OBJECTCLASS() containing all the
* specified classes. <br/>
* A property named us::ServiceConstants::SERVICE_SCOPE() identifying the scope
* of the service. <br/>
* Properties with these names in the specified <code>ServiceProperties</code> will
* be ignored.
* <li>The service is added to the framework service registry and may now be
* used by other modules.
* <li>A service event of type ServiceEvent#REGISTERED is fired.
* <li>A <code>ServiceRegistration</code> object for this registration is
* returned.
* </ol>
*
* @note This is a low-level method and should normally not be used directly.
* Use one of the templated RegisterService methods instead.
*
* @param service The service object, which is a map of interface identifiers
* to raw service pointers.
* @param properties The properties for this service. The keys in the
* properties object must all be <code>std::string</code> objects. See
* us::ServiceConstants for a list of standard service property keys.
* Changes should not be made to this object after calling this
* method. To update the service's properties the
* us::ServiceRegistration::SetProperties method must be called.
* The set of properties may be omitted if the service has
* no properties.
* @return A <code>ServiceRegistration</code> object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
*
* @throws std::invalid_argument If one of the following is true:
* <ul>
* <li><code>service</code> is <code>0</code>.
* <li><code>properties</code> contains case variants of the same key name.
* </ul>
* @throws std::logic_error If this ModuleContext is no longer valid.
*
* @see ServiceRegistration
* @see ServiceFactory
* @see PrototypeServiceFactory
*/
ServiceRegistrationU RegisterService(const InterfaceMap& service,
const ServiceProperties& properties = ServiceProperties());
/**
* Registers the specified service object with the specified properties
* using the specified template argument with the framework.
*
* <p>
* This method is provided as a convenience when <code>service</code> will only be registered under
* a single class name whose type is available to the caller. It is otherwise identical to
* RegisterService(const InterfaceMap&, const ServiceProperties&) but should be preferred
* since it avoids errors in the string literal identifying the class name or interface identifier.
*
* Example usage:
* \snippet uServices-registration/main.cpp 1-1
* \snippet uServices-registration/main.cpp 1-2
*
* @tparam S The type under which the service can be located.
* @param service The service object or a ServiceFactory object.
* @param properties The properties for this service.
* @return A ServiceRegistration object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid or the
* \c service object is nullptr.
*
* @see RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
template<class S>
ServiceRegistration<S> RegisterService(S* service, const ServiceProperties& properties = ServiceProperties())
{
InterfaceMap servicePointers = MakeInterfaceMap<S>(service);
return RegisterService(servicePointers, properties);
}
/**
* Registers the specified service object with the specified properties
* using the specified template argument with the framework.
*
* <p>
* This method is provided as a convenience when registering a service under
* two interface classes whose type is available to the caller. It is otherwise identical to
* RegisterService(const InterfaceMap&, const ServiceProperties&) but should be preferred
* since it avoids errors in the string literal identifying the class name or interface identifier.
*
* Example usage:
* \snippet uServices-registration/main.cpp 2-1
* \snippet uServices-registration/main.cpp 2-2
*
* @tparam I1 The first interface type under which the service can be located.
* @tparam I2 The second interface type under which the service can be located.
* @param impl The service object or a ServiceFactory object.
* @param properties The properties for this service.
* @return A ServiceRegistration object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid or the
* \c service object is nullptr.
*
* @see RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
template<class I1, class I2, class Impl>
ServiceRegistration<I1,I2> RegisterService(Impl* impl, const ServiceProperties& properties = ServiceProperties())
{
InterfaceMap servicePointers = MakeInterfaceMap<I1, I2>(impl);
return RegisterService(servicePointers, properties);
}
/**
* Registers the specified service object with the specified properties
* using the specified template argument with the framework.
*
* <p>
* This method is identical to the RegisterService<I1,I2,Impl>(Impl*, const ServiceProperties&)
* method except that it supports three service interface types.
*
* @tparam I1 The first interface type under which the service can be located.
* @tparam I2 The second interface type under which the service can be located.
* @tparam I3 The third interface type under which the service can be located.
* @param impl The service object or a ServiceFactory object.
* @param properties The properties for this service.
* @return A ServiceRegistration object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid or the
* \c service object is nullptr.
*
* @see RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
template<class I1, class I2, class I3, class Impl>
ServiceRegistration<I1,I2,I3> RegisterService(Impl* impl, const ServiceProperties& properties = ServiceProperties())
{
InterfaceMap servicePointers = MakeInterfaceMap<I1, I2, I3>(impl);
return RegisterService(servicePointers, properties);
}
/**
* Registers the specified service factory as a service with the specified properties
* using the specified template argument as service interface type with the framework.
*
* <p>
* This method is provided as a convenience when <code>factory</code> will only be registered under
* a single class name whose type is available to the caller. It is otherwise identical to
* RegisterService(const InterfaceMap&, const ServiceProperties&) but should be preferred
* since it avoids errors in the string literal identifying the class name or interface identifier.
*
* Example usage:
* \snippet uServices-registration/main.cpp 1-1
* \snippet uServices-registration/main.cpp f1
*
* @tparam S The type under which the service can be located.
* @param factory The ServiceFactory or PrototypeServiceFactory object.
* @param properties The properties for this service.
* @return A ServiceRegistration object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid or the
* \c service factory object is nullptr.
*
* @see RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
template<class S>
ServiceRegistration<S> RegisterService(ServiceFactory* factory, const ServiceProperties& properties = ServiceProperties())
{
InterfaceMap servicePointers = MakeInterfaceMap<S>(factory);
return RegisterService(servicePointers, properties);
}
/**
* Registers the specified service factory as a service with the specified properties
* using the specified template argument as service interface type with the framework.
*
* <p>
* This method is identical to the RegisterService<S>(ServiceFactory*, const ServiceProperties&)
* method except that it supports two service interface types.
*
* Example usage:
* \snippet uServices-registration/main.cpp 2-1
* \snippet uServices-registration/main.cpp f2
*
* @tparam I1 The first interface type under which the service can be located.
* @tparam I2 The second interface type under which the service can be located.
* @param factory The ServiceFactory or PrototypeServiceFactory object.
* @param properties The properties for this service.
* @return A ServiceRegistration object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid or the
* \c service factory object is nullptr.
*
* @see RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
template<class I1, class I2>
ServiceRegistration<I1,I2> RegisterService(ServiceFactory* factory, const ServiceProperties& properties = ServiceProperties())
{
InterfaceMap servicePointers = MakeInterfaceMap<I1,I2>(factory);
return RegisterService(servicePointers, properties);
}
/**
* Registers the specified service factory as a service with the specified properties
* using the specified template argument as service interface type with the framework.
*
* <p>
* This method is identical to the RegisterService<S>(ServiceFactory*, const ServiceProperties&)
* method except that it supports three service interface types.
*
* @tparam I1 The first interface type under which the service can be located.
* @tparam I2 The second interface type under which the service can be located.
* @tparam I3 The third interface type under which the service can be located.
* @param factory The ServiceFactory or PrototypeServiceFactory object.
* @param properties The properties for this service.
* @return A ServiceRegistration object for use by the module
* registering the service to update the service's properties or to
* unregister the service.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid or the
* \c service factory object is nullptr.
*
* @see RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
template<class I1, class I2, class I3>
ServiceRegistration<I1,I2,I3> RegisterService(ServiceFactory* factory, const ServiceProperties& properties = ServiceProperties())
{
InterfaceMap servicePointers = MakeInterfaceMap<I1,I2,I3>(factory);
return RegisterService(servicePointers, properties);
}
/**
* Returns a list of <code>ServiceReference</code> objects. The returned
* list contains services that
* were registered under the specified class and match the specified filter
* expression.
*
* <p>
* The list is valid at the time of the call to this method. However since
* the Micro Services framework is a very dynamic environment, services can be modified or
* unregistered at any time.
*
* <p>
* The specified <code>filter</code> expression is used to select the
* registered services whose service properties contain keys and values
* which satisfy the filter expression. See LDAPFilter for a description
* of the filter syntax. If the specified <code>filter</code> is
* empty, all registered services are considered to match the
* filter. If the specified <code>filter</code> expression cannot be parsed,
* an <code>std::invalid_argument</code> will be thrown with a human readable
* message where the filter became unparsable.
*
* <p>
* The result is a list of <code>ServiceReference</code> objects for all
* services that meet all of the following conditions:
* <ul>
* <li>If the specified class name, <code>clazz</code>, is not
* empty, the service must have been registered with the
* specified class name. The complete list of class names with which a
* service was registered is available from the service's
* us::ServiceConstants::OBJECTCLASS() property.
* <li>If the specified <code>filter</code> is not empty, the
* filter expression must match the service.
* </ul>
*
* @param clazz The class name with which the service was registered or
* an empty string for all services.
* @param filter The filter expression or empty for all
* services.
* @return A list of <code>ServiceReference</code> objects or
* an empty list if no services are registered which satisfy the
* search.
* @throws std::invalid_argument If the specified <code>filter</code>
* contains an invalid filter expression that cannot be parsed.
* @throws std::logic_error If this ModuleContext is no longer valid.
*/
std::vector<ServiceReferenceU> GetServiceReferences(const std::string& clazz, const std::string& filter = std::string());
/**
* Returns a list of <code>ServiceReference</code> objects. The returned
* list contains services that
* were registered under the interface id of the template argument <code>S</code>
* and match the specified filter expression.
*
* <p>
* This method is identical to GetServiceReferences(const std::string&, const std::string&) except that
* the class name for the service object is automatically deduced from the template argument.
*
* @tparam S The type under which the requested service objects must have been registered.
* @param filter The filter expression or empty for all
* services.
* @return A list of <code>ServiceReference</code> objects or
* an empty list if no services are registered which satisfy the
* search.
* @throws std::invalid_argument If the specified <code>filter</code>
* contains an invalid filter expression that cannot be parsed.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If the service type \c S is invalid.
*
* @see GetServiceReferences(const std::string&, const std::string&)
*/
template<class S>
std::vector<ServiceReference<S> > GetServiceReferences(const std::string& filter = std::string())
{
std::string clazz = us_service_interface_iid<S>();
if (clazz.empty()) throw ServiceException("The service interface class has no US_DECLARE_SERVICE_INTERFACE macro");
typedef std::vector<ServiceReferenceU> BaseVectorT;
BaseVectorT serviceRefs = GetServiceReferences(clazz, filter);
std::vector<ServiceReference<S> > result;
for(BaseVectorT::const_iterator i = serviceRefs.begin(); i != serviceRefs.end(); ++i)
{
result.push_back(ServiceReference<S>(*i));
}
return result;
}
/**
* Returns a <code>ServiceReference</code> object for a service that
* implements and was registered under the specified class.
*
* <p>
* The returned <code>ServiceReference</code> object is valid at the time of
* the call to this method. However as the Micro Services framework is a very dynamic
* environment, services can be modified or unregistered at any time.
*
* <p>
* This method is the same as calling
* {@link ModuleContext::GetServiceReferences(const std::string&, const std::string&)} with an
* empty filter expression. It is provided as a convenience for
* when the caller is interested in any service that implements the
* specified class.
* <p>
* If multiple such services exist, the service with the highest ranking (as
* specified in its us::ServiceConstants::SERVICE_RANKING() property) is returned.
* <p>
* If there is a tie in ranking, the service with the lowest service ID (as
* specified in its us::ServiceConstants::SERVICE_ID() property); that is, the
* service that was registered first is returned.
*
* @param clazz The class name with which the service was registered.
* @return A <code>ServiceReference</code> object, or an invalid <code>ServiceReference</code> if
* no services are registered which implement the named class.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException If no service was registered under the given class name.
*
* @see #GetServiceReferences(const std::string&, const std::string&)
*/
ServiceReferenceU GetServiceReference(const std::string& clazz);
/**
* Returns a <code>ServiceReference</code> object for a service that
* implements and was registered under the specified template class argument.
*
* <p>
* This method is identical to GetServiceReference(const std::string&) except that
* the class name for the service object is automatically deduced from the template argument.
*
* @tparam S The type under which the requested service must have been registered.
* @return A <code>ServiceReference</code> object, or an invalid <code>ServiceReference</code> if
* no services are registered which implement the type <code>S</code>.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws ServiceException It no service was registered under the given class name.
* @see #GetServiceReference(const std::string&)
* @see #GetServiceReferences(const std::string&)
*/
template<class S>
ServiceReference<S> GetServiceReference()
{
std::string clazz = us_service_interface_iid<S>();
if (clazz.empty()) throw ServiceException("The service interface class has no US_DECLARE_SERVICE_INTERFACE macro");
return ServiceReference<S>(GetServiceReference(clazz));
}
/**
* Returns the service object referenced by the specified
* <code>ServiceReferenceBase</code> object.
* <p>
* A module's use of a service is tracked by the module's use count of that
* service. Each time a service's service object is returned by
* {@link #GetService(const ServiceReference<S>&)} the context module's use count for
* that service is incremented by one. Each time the service is released by
* {@link #UngetService(const ServiceReferenceBase&)} the context module's use count
* for that service is decremented by one.
* <p>
* When a module's use count for a service drops to zero, the module should
* no longer use that service.
*
* <p>
* This method will always return <code>0</code> when the service
* associated with this <code>reference</code> has been unregistered.
*
* <p>
* The following steps are taken to get the service object:
* <ol>
* <li>If the service has been unregistered, <code>0</code> is returned.
* <li>The context module's use count for this service is incremented by
* one.
* <li>If the context module's use count for the service is currently one
* and the service was registered with an object implementing the
* <code>ServiceFactory</code> interface, the
* us::ServiceFactory::GetService() method is
* called to create a service object for the context module. This service
* object is cached by the framework. While the context module's use count
* for the service is greater than zero, subsequent calls to get the
* services's service object for the context module will return the cached
* service object. <br>
* If the <code>ServiceFactory</code> object throws an
* exception, <code>0</code> is returned and a warning is logged.
* <li>The service object for the service is returned.
* </ol>
*
* @param reference A reference to the service.
* @return A service object for the service associated with
* <code>reference</code> or <code>0</code> if the service is not
* registered or the <code>ServiceFactory</code> threw
* an exception.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @throws std::invalid_argument If the specified
* <code>ServiceReferenceBase</code> is invalid (default constructed).
* @see #UngetService(const ServiceReferenceBase&)
* @see ServiceFactory
*/
void* GetService(const ServiceReferenceBase& reference);
InterfaceMap GetService(const ServiceReferenceU& reference);
/**
* Returns the service object referenced by the specified
* <code>ServiceReference</code> object.
* <p>
* This is a convenience method which is identical to void* GetService(const ServiceReferenceBase&)
* except that it casts the service object to the supplied template argument type
*
* @tparam S The type the service object will be cast to.
* @return A service object for the service associated with
* <code>reference</code> or <code>0</code> if the service is not
* registered, the <code>ServiceFactory</code> threw
* an exception or the service could not be casted to the desired type.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @throws std::invalid_argument If the specified
* <code>ServiceReference</code> is invalid (default constructed).
* @see #GetService(const ServiceReferenceBase&)
* @see #UngetService(const ServiceReferenceBase&)
* @see ServiceFactory
*/
template<class S>
S* GetService(const ServiceReference<S>& reference)
{
const ServiceReferenceBase& baseRef = reference;
return reinterpret_cast<S*>(GetService(baseRef));
}
/**
* Returns the ServiceObjects object for the service referenced by the specified
* ServiceReference object. The ServiceObjects object can be used to obtain
* multiple service objects for services with prototype scope. For services with
* singleton or module scope, the ServiceObjects::GetService() method behaves
* the same as the GetService(const ServiceReference<S>&) method and the
* ServiceObjects::UngetService(const ServiceReferenceBase&) method behaves the
* same as the UngetService(const ServiceReferenceBase&) method. That is, only one,
* use-counted service object is available from the ServiceObjects object.
*
* @tparam S Type of Service.
* @param reference A reference to the service.
* @return A ServiceObjects object for the service associated with the specified
* reference or an invalid instance if the service is not registered.
* @throws std::logic_error If this ModuleContext is no longer valid.
* @throws std::invalid_argument If the specified ServiceReference is invalid
* (default constructed or the service has been unregistered)
*
* @see PrototypeServiceFactory
*/
template<class S>
ServiceObjects<S> GetServiceObjects(const ServiceReference<S>& reference)
{
return ServiceObjects<S>(this, reference);
}
/**
* Releases the service object referenced by the specified
* <code>ServiceReference</code> object. If the context module's use count
* for the service is zero, this method returns <code>false</code>.
* Otherwise, the context modules's use count for the service is decremented
* by one.
*
* <p>
* The service's service object should no longer be used and all references
* to it should be destroyed when a module's use count for the service drops
* to zero.
*
* <p>
* The following steps are taken to unget the service object:
* <ol>
* <li>If the context module's use count for the service is zero or the
* service has been unregistered, <code>false</code> is returned.
* <li>The context module's use count for this service is decremented by
* one.
* <li>If the context module's use count for the service is currently zero
* and the service was registered with a <code>ServiceFactory</code> object,
* the ServiceFactory#UngetService
* method is called to release the service object for the context module.
* <li><code>true</code> is returned.
* </ol>
*
* @param reference A reference to the service to be released.
* @return <code>false</code> if the context module's use count for the
* service is zero or if the service has been unregistered;
* <code>true</code> otherwise.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @see #GetService
* @see ServiceFactory
*/
bool UngetService(const ServiceReferenceBase& reference);
void AddServiceListener(const ServiceListener& delegate,
const std::string& filter = std::string());
void RemoveServiceListener(const ServiceListener& delegate);
void AddModuleListener(const ModuleListener& delegate);
void RemoveModuleListener(const ModuleListener& delegate);
/**
* Adds the specified <code>callback</code> with the
* specified <code>filter</code> to the context modules's list of listeners.
* See LDAPFilter for a description of the filter syntax. Listeners
* are notified when a service has a lifecycle state change.
*
* <p>
- * You must take care to remove registered listeners befor the <code>receiver</code>
+ * You must take care to remove registered listeners before the <code>receiver</code>
* object is destroyed. However, the Micro Services framework takes care
* of removing all listeners registered by this context module's classes
* after the module is unloaded.
*
* <p>
* If the context module's list of listeners already contains a pair <code>(r,c)</code>
* of <code>receiver</code> and <code>callback</code> such that
* <code>(r == receiver && c == callback)</code>, then this
* method replaces that callback's filter (which may be empty)
* with the specified one (which may be empty).
*
* <p>
* The callback is called if the filter criteria is met. To filter based
* upon the class of the service, the filter should reference the
* us::ServiceConstants::OBJECTCLASS() property. If <code>filter</code> is
* empty, all services are considered to match the filter.
*
* <p>
* When using a <code>filter</code>, it is possible that the
* <code>ServiceEvent</code>s for the complete lifecycle of a service
* will not be delivered to the callback. For example, if the
* <code>filter</code> only matches when the property <code>x</code> has
* the value <code>1</code>, the callback will not be called if the
* service is registered with the property <code>x</code> not set to the
* value <code>1</code>. Subsequently, when the service is modified
* setting property <code>x</code> to the value <code>1</code>, the
* filter will match and the callback will be called with a
* <code>ServiceEvent</code> of type <code>MODIFIED</code>. Thus, the
* callback will not be called with a <code>ServiceEvent</code> of type
* <code>REGISTERED</code>.
*
* @tparam R The type of the receiver (containing the member function to be called)
* @param receiver The object to connect to.
* @param callback The member function pointer to call.
* @param filter The filter criteria.
* @throws std::invalid_argument If <code>filter</code> contains an
* invalid filter string that cannot be parsed.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @see ServiceEvent
* @see RemoveServiceListener()
*/
template<class R>
void AddServiceListener(R* receiver, void(R::*callback)(const ServiceEvent),
const std::string& filter = std::string())
{
AddServiceListener(ServiceListenerMemberFunctor(receiver, callback),
static_cast<void*>(receiver), filter);
}
/**
* Removes the specified <code>callback</code> from the context module's
* list of listeners.
*
* <p>
* If the <code>(receiver,callback)</code> pair is not contained in this
* context module's list of listeners, this method does nothing.
*
* @tparam R The type of the receiver (containing the member function to be removed)
* @param receiver The object from which to disconnect.
* @param callback The member function pointer to remove.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @see AddServiceListener()
*/
template<class R>
void RemoveServiceListener(R* receiver, void(R::*callback)(const ServiceEvent))
{
RemoveServiceListener(ServiceListenerMemberFunctor(receiver, callback),
static_cast<void*>(receiver));
}
/**
* Adds the specified <code>callback</code> to the context modules's list
* of listeners. Listeners are notified when a module has a lifecycle
* state change.
*
* <p>
* If the context module's list of listeners already contains a pair <code>(r,c)</code>
* of <code>receiver</code> and <code>callback</code> such that
* <code>(r == receiver && c == callback)</code>, then this method does nothing.
*
* @tparam R The type of the receiver (containing the member function to be called)
* @param receiver The object to connect to.
* @param callback The member function pointer to call.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @see ModuleEvent
*/
template<class R>
void AddModuleListener(R* receiver, void(R::*callback)(const ModuleEvent))
{
AddModuleListener(ModuleListenerMemberFunctor(receiver, callback),
static_cast<void*>(receiver));
}
/**
* Removes the specified <code>callback</code> from the context module's
* list of listeners.
*
* <p>
* If the <code>(receiver,callback)</code> pair is not contained in this
* context module's list of listeners, this method does nothing.
*
* @tparam R The type of the receiver (containing the member function to be removed)
* @param receiver The object from which to disconnect.
* @param callback The member function pointer to remove.
* @throws std::logic_error If this ModuleContext is no
* longer valid.
* @see AddModuleListener()
*/
template<class R>
void RemoveModuleListener(R* receiver, void(R::*callback)(const ModuleEvent))
{
RemoveModuleListener(ModuleListenerMemberFunctor(receiver, callback),
static_cast<void*>(receiver));
}
/**
* Get the absolute path for a file or directory in the persistent
* storage area provided for the module. The returned path
* might be empty if no storage path has been set previously.
* If the path is non-empty, it is safe to assume that the path is writable.
*
* @see ModuleSettings::SetStoragePath(const std::string&)
*
* @param filename A relative name to the file or directory to be accessed.
* @return The absolute path to the persistent storage area for the given file name.
*/
std::string GetDataFile(const std::string& filename) const;
private:
friend class Module;
friend class ModulePrivate;
ModuleContext(ModulePrivate* module);
// purposely not implemented
ModuleContext(const ModuleContext&);
ModuleContext& operator=(const ModuleContext&);
void AddServiceListener(const ServiceListener& delegate, void* data,
const std::string& filter);
void RemoveServiceListener(const ServiceListener& delegate, void* data);
void AddModuleListener(const ModuleListener& delegate, void* data);
void RemoveModuleListener(const ModuleListener& delegate, void* data);
ModuleContextPrivate * const d;
};
US_END_NAMESPACE
#endif /* USMODULECONTEXT_H_ */
diff --git a/Modules/CppMicroServices/core/include/usModuleEvent.h b/Modules/CppMicroServices/core/include/usModuleEvent.h
index 8a745018f3..3fc13eee38 100644
--- a/Modules/CppMicroServices/core/include/usModuleEvent.h
+++ b/Modules/CppMicroServices/core/include/usModuleEvent.h
@@ -1,153 +1,153 @@
/*============================================================================
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
https://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 USMODULEEVENT_H
#define USMODULEEVENT_H
#include <iostream>
#include "usSharedData.h"
US_BEGIN_NAMESPACE
class Module;
class ModuleEventData;
/**
* \ingroup MicroServices
*
* An event from the Micro Services framework describing a module lifecycle change.
* <p>
* <code>ModuleEvent</code> objects are delivered to listeners connected
* via ModuleContext::AddModuleListener() when a change
* occurs in a modules's lifecycle. A type code is used to identify
* the event type for future extendability.
*
* @see ModuleContext#AddModuleListener
*/
class US_Core_EXPORT ModuleEvent
{
SharedDataPointer<ModuleEventData> d;
public:
enum Type {
/**
* The module has been loaded.
* <p>
* The module's
* \link ModuleActivator::Load(ModuleContext*) ModuleActivator Load\endlink method
* has been executed.
*/
LOADED,
/**
* The module has been unloaded.
* <p>
* The module's
* \link ModuleActivator::Unload(ModuleContext*) ModuleActivator Unload\endlink method
* has been executed.
*/
UNLOADED,
/**
* The module is about to be loaded.
* <p>
* The module's
* \link ModuleActivator::Load(ModuleContext*) ModuleActivator Load\endlink method
* is about to be called.
*/
LOADING,
/**
* The module is about to be unloaded.
* <p>
* The module's
* \link ModuleActivator::Unload(ModuleContext*) ModuleActivator Unload\endlink method
* is about to be called.
*/
UNLOADING
};
/**
* Creates an invalid instance.
*/
ModuleEvent();
~ModuleEvent();
/**
* Can be used to check if this ModuleEvent instance is valid,
* or if it has been constructed using the default constructor.
*
* @return <code>true</code> if this event object is valid,
* <code>false</code> otherwise.
*/
bool IsNull() const;
/**
* Creates a module event of the specified type.
*
* @param type The event type.
* @param module The module which had a lifecycle change.
*/
ModuleEvent(Type type, Module* module);
ModuleEvent(const ModuleEvent& other);
ModuleEvent& operator=(const ModuleEvent& other);
/**
* Returns the module which had a lifecycle change.
*
* @return The module that had a change occur in its lifecycle.
*/
Module* GetModule() const;
/**
- * Returns the type of lifecyle event. The type values are:
+ * Returns the type of lifecycle event. The type values are:
* <ul>
* <li>{@link #LOADING}
* <li>{@link #LOADED}
* <li>{@link #UNLOADING}
* <li>{@link #UNLOADED}
* </ul>
*
* @return The type of lifecycle event.
*/
Type GetType() const;
};
/**
* \ingroup MicroServices
* @{
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, ModuleEvent::Type eventType);
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const ModuleEvent& event);
/** @}*/
US_END_NAMESPACE
#endif // USMODULEEVENT_H
diff --git a/Modules/CppMicroServices/core/include/usModuleResource.h b/Modules/CppMicroServices/core/include/usModuleResource.h
index fa06df61e1..3c068aadae 100644
--- a/Modules/CppMicroServices/core/include/usModuleResource.h
+++ b/Modules/CppMicroServices/core/include/usModuleResource.h
@@ -1,312 +1,312 @@
/*============================================================================
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
https://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 USMODULERESOURCE_H
#define USMODULERESOURCE_H
#include <usCoreExport.h>
#include <ostream>
#include <vector>
US_MSVC_PUSH_DISABLE_WARNING(4396)
US_BEGIN_NAMESPACE
class ModuleResourcePrivate;
class ModuleResourceContainer;
/**
* \ingroup MicroServices
*
* Represents a resource (text file, image, etc.) embedded in a CppMicroServices module.
*
* A \c %ModuleResource object provides information about a resource (external file) which
* was embedded into this module's shared library. \c %ModuleResource objects can be obtained
* be calling Module#GetResource or Module#FindResources.
*
- * Example code for retreiving a resource object and reading its contents:
+ * Example code for retrieving a resource object and reading its contents:
* \snippet uServices-resources/main.cpp 1
*
* %ModuleResource objects have value semantics and copies are very inexpensive.
*
* \see ModuleResourceStream
* \see \ref MicroServices_Resources
*/
class US_Core_EXPORT ModuleResource
{
private:
typedef ModuleResourcePrivate* ModuleResource::*bool_type;
public:
/**
* Creates in invalid %ModuleResource object.
*/
ModuleResource();
/**
* Copy constructor.
* @param resource The object to be copied.
*/
ModuleResource(const ModuleResource& resource);
~ModuleResource();
/**
* Assignment operator.
*
* @param resource The %ModuleResource object which is assigned to this instance.
* @return A reference to this %ModuleResource instance.
*/
ModuleResource& operator=(const ModuleResource& resource);
/**
* A less then operator using the full resource path as returned by
* GetResourcePath() to define the ordering.
*
* @param resource The object to which this %ModuleResource object is compared to.
* @return \c true if this %ModuleResource object is less then \c resource,
* \c false otherwise.
*/
bool operator<(const ModuleResource& resource) const;
/**
* Equality operator for %ModuleResource objects.
*
* @param resource The object for testing equality.
* @return \c true if this %ModuleResource object is equal to \c resource, i.e.
* they are coming from the same module (shared or static) and have an equal
* resource path, \c false otherwise.
*/
bool operator==(const ModuleResource& resource) const;
/**
* Inequality operator for %ModuleResource objects.
*
* @param resource The object for testing inequality.
* @return The result of <code>!(*this == resource)</code>.
*/
bool operator!=(const ModuleResource& resource) const;
/**
* Tests this %ModuleResource object for validity.
*
* Invalid %ModuleResource objects are created by the default constructor or
* can be returned by the Module class if the resource path is not found.
*
* @return \c true if this %ModuleReource object is valid and can safely be used,
* \c false otherwise.
*/
bool IsValid() const;
/**
* Boolean conversion operator using IsValid().
*/
operator bool_type() const;
/**
* Returns the name of the resource, excluding the path.
*
* Example:
* \code
* ModuleResource resource = module->GetResource("/data/archive.tar.gz");
* std::string name = resource.GetName(); // name = "archive.tar.gz"
* \endcode
*
* @return The resource name.
* @see GetPath(), GetResourcePath()
*/
std::string GetName() const;
/**
* Returns the resource's path, without the file name.
*
* Example:
* \code
* ModuleResource resource = module->GetResource("/data/archive.tar.gz");
* std::string path = resource.GetPath(); // path = "/data/"
* \endcode
*
* The path with always begin and end with a forward slash.
*
* @return The resource path without the name.
* @see GetResourcePath(), GetName() and IsDir()
*/
std::string GetPath() const;
/**
* Returns the resource path including the file name.
*
* @return The resource path including the file name.
* @see GetPath(), GetName() and IsDir()
*/
std::string GetResourcePath() const;
/**
* Returns the base name of the resource without the path.
*
* Example:
* \code
* ModuleResource resource = module->GetResource("/data/archive.tar.gz");
* std::string base = resource.GetBaseName(); // base = "archive"
* \endcode
*
* @return The resource base name.
* @see GetName(), GetSuffix(), GetCompleteSuffix() and GetCompleteBaseName()
*/
std::string GetBaseName() const;
/**
* Returns the complete base name of the resource without the path.
*
* Example:
* \code
* ModuleResource resource = module->GetResource("/data/archive.tar.gz");
* std::string base = resource.GetCompleteBaseName(); // base = "archive.tar"
* \endcode
*
* @return The resource's complete base name.
* @see GetName(), GetSuffix(), GetCompleteSuffix(), and GetBaseName()
*/
std::string GetCompleteBaseName() const;
/**
* Returns the suffix of the resource.
*
* The suffix consists of all characters in the resource name after (but not
* including) the last '.'.
*
* Example:
* \code
* ModuleResource resource = module->GetResource("/data/archive.tar.gz");
* std::string suffix = resource.GetSuffix(); // suffix = "gz"
* \endcode
*
* @return The resource name suffix.
* @see GetName(), GetCompleteSuffix(), GetBaseName() and GetCompleteBaseName()
*/
std::string GetSuffix() const;
/**
* Returns the complete suffix of the resource.
*
* The suffix consists of all characters in the resource name after (but not
* including) the first '.'.
*
* Example:
* \code
* ModuleResource resource = module->GetResource("/data/archive.tar.gz");
* std::string suffix = resource.GetCompleteSuffix(); // suffix = "tar.gz"
* \endcode
*
* @return The resource name suffix.
* @see GetName(), GetSuffix(), GetBaseName(), and GetCompleteBaseName()
*/
std::string GetCompleteSuffix() const;
/**
* Returns \c true if this %ModuleResource object points to a directory and thus
* may have child resources.
*
* @return \c true if this object points to a directory, \c false otherwise.
*/
bool IsDir() const;
/**
* Returns \c true if this %ModuleResource object points to a file resource.
*
* @return \c true if this object points to an embedded file, \c false otherwise.
*/
bool IsFile() const;
/**
* Returns a list of resource names which are children of this object.
*
* The returned names are relative to the path of this %ModuleResource object
* and may contain file as well as directory entries.
*
* @return A list of child resource names.
*/
std::vector<std::string> GetChildren() const;
/**
* Returns a list of resource objects which are children of this object.
*
* The return ModuleResource objects may contain files as well as
* directory resources.
*
* @return A list of child resource objects.
*/
std::vector<ModuleResource> GetChildResources() const;
/**
* Returns the size of the resource data for this %ModuleResource object.
*
* @return The resource data size.
*/
int GetSize() const;
/**
* Returns the last modified time of this resource in seconds from the epoch.
*
* @return Last modified time of this resource.
*/
time_t GetLastModified() const;
private:
ModuleResource(const std::string& file, const ModuleResourceContainer& resourceContainer);
ModuleResource(int index, const ModuleResourceContainer& resourceContainer);
friend class Module;
friend class ModulePrivate;
friend class ModuleResourceContainer;
friend class ModuleResourceStream;
US_HASH_FUNCTION_FRIEND(ModuleResource);
std::size_t Hash() const;
void* GetData() const;
ModuleResourcePrivate* d;
};
US_END_NAMESPACE
US_MSVC_POP_WARNING
/**
* \ingroup MicroServices
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(ModuleResource)& resource);
US_HASH_FUNCTION_NAMESPACE_BEGIN
US_HASH_FUNCTION_BEGIN(US_PREPEND_NAMESPACE(ModuleResource))
return arg.Hash();
US_HASH_FUNCTION_END
US_HASH_FUNCTION_NAMESPACE_END
#endif // USMODULERESOURCE_H
diff --git a/Modules/CppMicroServices/core/include/usModuleSettings.h b/Modules/CppMicroServices/core/include/usModuleSettings.h
index 7f1c17ee1a..9646c51bc7 100644
--- a/Modules/CppMicroServices/core/include/usModuleSettings.h
+++ b/Modules/CppMicroServices/core/include/usModuleSettings.h
@@ -1,171 +1,171 @@
/*============================================================================
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
https://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 USMODULESETTINGS_H
#define USMODULESETTINGS_H
#include "usCoreConfig.h"
#include <vector>
#include <string>
US_BEGIN_NAMESPACE
/**
* \ingroup MicroServices
*
* Query and set certain properties of the CppMicroServices library.
*
* The following environment variables influence the runtime behavior
* of the CppMicroServices library:
*
* - \e US_DISABLE_AUTOLOADING If set, auto-loading of modules is disabled.
* - \e US_AUTOLOAD_PATHS A ':' (Unix) or ';' (Windows) separated list of paths
* from which modules should be auto-loaded.
*
* \remarks This class is thread safe.
*/
class US_Core_EXPORT ModuleSettings
{
public:
typedef std::vector<std::string> PathList;
/**
* Returns a special string which can be used as an argument for a
* AddAutoLoadPath() call.
*
* When a module is loaded and this string has been added as a path
* to the list of auto-load paths the CppMicroServices library will
* auto-load all modules from the currently being loaded module's
* auto-load directory.
*
* \return A string to be used in AddAutoLoadPath().
*
* \remarks The returned string is contained in the default set of
* auto-load paths, unless a new set of paths is given by a call to
* SetAutoLoadPaths().
*
* \sa MicroServices_AutoLoading
* \sa US_INITIALIZE_MODULE
*/
static std::string CURRENT_MODULE_PATH();
/**
* \return \c true if threading support has been configured into the
* CppMicroServices library, \c false otherwise.
*/
static bool IsThreadingSupportEnabled();
/**
* \return \c true if support for module auto-loading is enabled,
* \c false otherwise.
*
* \remarks This method will always return \c false if support for auto-loading
* has not been configured into the CppMicroServices library or if it has been
* disabled by defining the US_DISABLE_AUTOLOADING environment variable.
*/
static bool IsAutoLoadingEnabled();
/**
* Enable or disable auto-loading support.
*
* \param enable If \c true, enable auto-loading support, disable it otherwise.
*
* \remarks Calling this method will have no effect if support for
* auto-loading has not been configured into the CppMicroServices library of it
- * it has been disabled by defining the US_DISABLE_AUTOLOADING envrionment variable.
+ * it has been disabled by defining the US_DISABLE_AUTOLOADING environment variable.
*/
static void SetAutoLoadingEnabled(bool enable);
/**
* \return A list of paths in the file-system from which modules will be
* auto-loaded.
*/
static PathList GetAutoLoadPaths();
/**
* Set a list of paths in the file-system from which modules should be
* auto-loaded.
* @param paths A list of absolute file-system paths.
*/
static void SetAutoLoadPaths(const PathList& paths);
/**
* Add a path in the file-system to the list of paths from which modules
* will be auto-loaded.
*
* @param path The additional absolute auto-load path in the file-system.
*/
static void AddAutoLoadPath(const std::string& path);
/**
* Set a local storage path for persistend module data.
*
* This path is used as a base directory for providing modules
* with a storage path for writing persistent data. The callee
* must ensure that the provided path exists and is writable.
*
* @see ModuleContext::GetDataFile(const std::string&)
*
* @param path An absolute path for writing persistent data.
*/
static void SetStoragePath(const std::string& path);
/**
* Get the absolute path for persistent data. The returned path
* might be empty. If the path is non-empty, it is safe to assume
* that the path exists and is writable.
*
* @return The absolute path to the persistent storage path.
*/
static std::string GetStoragePath();
/**
* Set the logging level for log messages from CppMicroServices modules.
*
* Higher logging levels will discard messages with lower priority.
* E.g. a logging level of WarningMsg will discard all messages of
* type DebugMsg and InfoMsg.
*
* @param level The new logging level.
*/
static void SetLogLevel(MsgType level);
/**
* Get the current logging level.
*
* @return The currently used logging level.
*/
static MsgType GetLogLevel();
private:
// purposely not implemented
ModuleSettings();
ModuleSettings(const ModuleSettings&);
ModuleSettings& operator=(const ModuleSettings&);
};
US_END_NAMESPACE
#endif // USMODULESETTINGS_H
diff --git a/Modules/CppMicroServices/core/include/usModuleVersion.h b/Modules/CppMicroServices/core/include/usModuleVersion.h
index 88b8fcb48d..ba08b08b6c 100644
--- a/Modules/CppMicroServices/core/include/usModuleVersion.h
+++ b/Modules/CppMicroServices/core/include/usModuleVersion.h
@@ -1,254 +1,254 @@
/*============================================================================
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
https://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 USMODULEVERSION_H
#define USMODULEVERSION_H
#include <usCoreExport.h>
#include <string>
US_BEGIN_NAMESPACE
/**
* \ingroup MicroServices
*
* Version identifier for CppMicroServices modules.
*
* <p>
* Version identifiers have four components.
* <ol>
* <li>Major version. A non-negative integer.</li>
* <li>Minor version. A non-negative integer.</li>
* <li>Micro version. A non-negative integer.</li>
* <li>Qualifier. A text string. See <code>ModuleVersion(const std::string&)</code> for the
* format of the qualifier string.</li>
* </ol>
*
* <p>
* <code>ModuleVersion</code> objects are immutable.
*/
class US_Core_EXPORT ModuleVersion {
private:
friend class ModulePrivate;
unsigned int majorVersion;
unsigned int minorVersion;
unsigned int microVersion;
std::string qualifier;
static const char SEPARATOR; // = "."
bool undefined;
/**
* Called by the ModuleVersion constructors to validate the version components.
*
- * @return <code>true</code> if the validation was successfull, <code>false</code> otherwise.
+ * @return <code>true</code> if the validation was successful, <code>false</code> otherwise.
*/
void Validate();
ModuleVersion& operator=(const ModuleVersion& v);
explicit ModuleVersion(bool undefined = false);
public:
/**
* The empty version "0.0.0".
*/
static ModuleVersion EmptyVersion();
/**
* Creates an undefined version identifier, representing either
* infinity or minus infinity.
*/
static ModuleVersion UndefinedVersion();
/**
* Creates a version identifier from the specified numerical components.
*
* <p>
* The qualifier is set to the empty string.
*
* @param majorVersion Major component of the version identifier.
* @param minorVersion Minor component of the version identifier.
* @param microVersion Micro component of the version identifier.
*
*/
ModuleVersion(unsigned int majorVersion, unsigned int minorVersion, unsigned int microVersion);
/**
* Creates a version identifier from the specified components.
*
* @param majorVersion Major component of the version identifier.
* @param minorVersion Minor component of the version identifier.
* @param microVersion Micro component of the version identifier.
* @param qualifier Qualifier component of the version identifier.
*/
ModuleVersion(unsigned int majorVersion, unsigned int minorVersion, unsigned int microVersion, const std::string& qualifier);
/**
* Created a version identifier from the specified string.
*
* <p>
* Here is the grammar for version strings.
*
* <pre>
* version ::= majorVersion('.'minorVersion('.'microVersion('.'qualifier)?)?)?
* majorVersion ::= digit+
* minorVersion ::= digit+
* microVersion ::= digit+
* qualifier ::= (alpha|digit|'_'|'-')+
* digit ::= [0..9]
* alpha ::= [a..zA..Z]
* </pre>
*
* There must be no whitespace in version.
*
* @param version string representation of the version identifier.
*/
ModuleVersion(const std::string& version);
/**
* Create a version identifier from another.
*
* @param version Another version identifier
*/
ModuleVersion(const ModuleVersion& version);
/**
* Parses a version identifier from the specified string.
*
* <p>
* See <code>ModuleVersion(const std::string&)</code> for the format of the version string.
*
* @param version string representation of the version identifier. Leading
* and trailing whitespace will be ignored.
* @return A <code>ModuleVersion</code> object representing the version
* identifier. If <code>version</code> is the empty string
* then <code>EmptyVersion</code> will be
* returned.
*/
static ModuleVersion ParseVersion(const std::string& version);
/**
* Returns the undefined state of this version identifier.
*
* @return <code>true</code> if this version identifier is undefined,
* <code>false</code> otherwise.
*/
bool IsUndefined() const;
/**
* Returns the majorVersion component of this version identifier.
*
* @return The majorVersion component.
*/
unsigned int GetMajor() const;
/**
* Returns the minorVersion component of this version identifier.
*
* @return The minorVersion component.
*/
unsigned int GetMinor() const;
/**
* Returns the microVersion component of this version identifier.
*
* @return The microVersion component.
*/
unsigned int GetMicro() const;
/**
* Returns the qualifier component of this version identifier.
*
* @return The qualifier component.
*/
std::string GetQualifier() const;
/**
* Returns the string representation of this version identifier.
*
* <p>
* The format of the version string will be <code>majorVersion.minorVersion.microVersion</code>
* if qualifier is the empty string or
* <code>majorVersion.minorVersion.microVersion.qualifier</code> otherwise.
*
* @return The string representation of this version identifier.
*/
std::string ToString() const;
/**
* Compares this <code>ModuleVersion</code> object to another object.
*
* <p>
* A version is considered to be <b>equal to </b> another version if the
* majorVersion, minorVersion and microVersion components are equal and the qualifier component
* is equal.
*
* @param object The <code>ModuleVersion</code> object to be compared.
* @return <code>true</code> if <code>object</code> is a
* <code>ModuleVersion</code> and is equal to this object;
* <code>false</code> otherwise.
*/
bool operator==(const ModuleVersion& object) const;
/**
* Compares this <code>ModuleVersion</code> object to another object.
*
* <p>
* A version is considered to be <b>less than </b> another version if its
* majorVersion component is less than the other version's majorVersion component, or the
* majorVersion components are equal and its minorVersion component is less than the other
* version's minorVersion component, or the majorVersion and minorVersion components are equal
* and its microVersion component is less than the other version's microVersion component,
* or the majorVersion, minorVersion and microVersion components are equal and it's qualifier
* component is less than the other version's qualifier component (using
* <code>std::string::operator<()</code>).
*
* <p>
* A version is considered to be <b>equal to</b> another version if the
* majorVersion, minorVersion and microVersion components are equal and the qualifier component
* is equal.
*
* @param object The <code>ModuleVersion</code> object to be compared.
* @return A negative integer, zero, or a positive integer if this object is
* less than, equal to, or greater than the specified
* <code>ModuleVersion</code> object.
*/
int Compare(const ModuleVersion& object) const;
};
US_END_NAMESPACE
/**
* \ingroup MicroServices
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(ModuleVersion)& v);
#endif // USMODULEVERSION_H
diff --git a/Modules/CppMicroServices/core/include/usServiceEvent.h b/Modules/CppMicroServices/core/include/usServiceEvent.h
index 399928e5d4..7990af55c6 100644
--- a/Modules/CppMicroServices/core/include/usServiceEvent.h
+++ b/Modules/CppMicroServices/core/include/usServiceEvent.h
@@ -1,188 +1,188 @@
/*============================================================================
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
https://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 USSERVICEEVENT_H
#define USSERVICEEVENT_H
#ifdef REGISTERED
#ifdef _WIN32
#error The REGISTERED preprocessor define clashes with the ServiceEvent::REGISTERED\
enum type. Try to reorder your includes, compile with WIN32_LEAN_AND_MEAN, or undef\
- the REGISTERED macro befor including this header.
+ the REGISTERED macro before including this header.
#else
#error The REGISTERED preprocessor define clashes with the ServiceEvent::REGISTERED\
- enum type. Try to reorder your includes or undef the REGISTERED macro befor including\
+ enum type. Try to reorder your includes or undef the REGISTERED macro before including\
this header.
#endif
#endif
#include "usSharedData.h"
#include "usServiceReference.h"
US_BEGIN_NAMESPACE
class ServiceEventData;
/**
* \ingroup MicroServices
*
* An event from the Micro Services framework describing a service lifecycle change.
* <p>
* <code>ServiceEvent</code> objects are delivered to
* listeners connected via ModuleContext::AddServiceListener() when a
* change occurs in this service's lifecycle. A type code is used to identify
* the event type for future extendability.
*/
class US_Core_EXPORT ServiceEvent
{
SharedDataPointer<ServiceEventData> d;
public:
enum Type {
/**
* This service has been registered.
* <p>
* This event is delivered <strong>after</strong> the service
* has been registered with the framework.
*
* @see ModuleContext#RegisterService()
*/
REGISTERED = 0x00000001,
/**
* The properties of a registered service have been modified.
* <p>
* This event is delivered <strong>after</strong> the service
* properties have been modified.
*
* @see ServiceRegistration#SetProperties
*/
MODIFIED = 0x00000002,
/**
* This service is in the process of being unregistered.
* <p>
* This event is delivered <strong>before</strong> the service
* has completed unregistering.
*
* <p>
* If a module is using a service that is <code>UNREGISTERING</code>, the
* module should release its use of the service when it receives this event.
* If the module does not release its use of the service when it receives
* this event, the framework will automatically release the module's use of
* the service while completing the service unregistration operation.
*
* @see ServiceRegistration#Unregister
* @see ModuleContext#UngetService
*/
UNREGISTERING = 0x00000004,
/**
* The properties of a registered service have been modified and the new
* properties no longer match the listener's filter.
* <p>
* This event is delivered <strong>after</strong> the service
* properties have been modified. This event is only delivered to listeners
* which were added with a non-empty filter where the filter
* matched the service properties prior to the modification but the filter
* does not match the modified service properties.
*
* @see ServiceRegistration#SetProperties
*/
MODIFIED_ENDMATCH = 0x00000008
};
/**
* Creates an invalid instance.
*/
ServiceEvent();
~ServiceEvent();
/**
* Can be used to check if this ServiceEvent instance is valid,
* or if it has been constructed using the default constructor.
*
* @return <code>true</code> if this event object is valid,
* <code>false</code> otherwise.
*/
bool IsNull() const;
/**
* Creates a new service event object.
*
* @param type The event type.
* @param reference A <code>ServiceReference</code> object to the service
* that had a lifecycle change.
*/
ServiceEvent(Type type, const ServiceReferenceBase& reference);
ServiceEvent(const ServiceEvent& other);
ServiceEvent& operator=(const ServiceEvent& other);
/**
* Returns a reference to the service that had a change occur in its
* lifecycle.
* <p>
* This reference is the source of the event.
*
* @return Reference to the service that had a lifecycle change.
*/
ServiceReferenceU GetServiceReference() const;
template<class S>
ServiceReference<S> GetServiceReference(InterfaceType<S>) const
{
return GetServiceReference();
}
/**
* Returns the type of event. The event type values are:
* <ul>
* <li>{@link #REGISTERED} </li>
* <li>{@link #MODIFIED} </li>
* <li>{@link #MODIFIED_ENDMATCH} </li>
* <li>{@link #UNREGISTERING} </li>
* </ul>
*
* @return Type of service lifecycle change.
*/
Type GetType() const;
};
/**
* \ingroup MicroServices
* @{
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const ServiceEvent::Type& type);
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const ServiceEvent& event);
/** @}*/
US_END_NAMESPACE
#endif // USSERVICEEVENT_H
diff --git a/Modules/CppMicroServices/core/include/usServiceInterface.h b/Modules/CppMicroServices/core/include/usServiceInterface.h
index 0c1927374a..a7505d757a 100644
--- a/Modules/CppMicroServices/core/include/usServiceInterface.h
+++ b/Modules/CppMicroServices/core/include/usServiceInterface.h
@@ -1,343 +1,343 @@
/*============================================================================
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
https://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 USSERVICEINTERFACE_H
#define USSERVICEINTERFACE_H
#include <usGlobalConfig.h>
#include <usServiceException.h>
#include <map>
#include <string>
#include <typeinfo>
US_BEGIN_NAMESPACE
std::string GetDemangledName(const std::type_info& typeInfo);
US_END_NAMESPACE
/**
* \ingroup MicroServices
*
* Returns a unique id for a given type. By default, the
* demangled name of \c T is returned.
*
* This template method may be specialized directly or be
* using the macro #US_DECLARE_SERVICE_INTERFACE to return
* a custom id for each service interface.
*
* @tparam T The service interface type.
* @return A unique id for the service interface type T.
*/
template<class T> std::string us_service_interface_iid()
{
return US_PREPEND_NAMESPACE(GetDemangledName)(typeid(T));
}
/// \cond
template<> inline std::string us_service_interface_iid<void>() { return std::string(); }
/// \endcond
/**
* \ingroup MicroServices
*
* \brief Declare a service interface id.
*
* This macro associates the given identifier \e _service_interface_id (a string literal) to the
* interface class called _service_interface_type. The Identifier must be unique. For example:
*
* \code
* #include <usServiceInterface.h>
*
* struct ISomeInterace { ... };
*
* US_DECLARE_SERVICE_INTERFACE(ISomeInterface, "com.mycompany.service.ISomeInterface/1.0")
* \endcode
*
* The usage of this macro is optional and the service interface id which is automatically
* associated with any type is usually good enough (the demangled type name). However, care must
* be taken if the default id is compared with a string literal hard-coding a service interface
* id. E.g. the default id for templated types in the STL may differ between platforms. For
* user-defined types and templates the ids are typically consistent, but platform specific
* default template arguments will lead to different ids.
*
* This macro is normally used right after the class definition for _service_interface_type,
* in a header file.
*
* If you want to use #US_DECLARE_SERVICE_INTERFACE with interface classes declared in a
* namespace then you have to make sure the #US_DECLARE_SERVICE_INTERFACE macro call is not
* inside a namespace though. For example:
*
* \code
* #include <usServiceInterface.h>
*
* namespace Foo
* {
* struct ISomeInterface { ... };
* }
*
* US_DECLARE_SERVICE_INTERFACE(Foo::ISomeInterface, "com.mycompany.service.ISomeInterface/1.0")
* \endcode
*
* @param _service_interface_type The service interface type.
* @param _service_interface_id A string literal representing a globally unique identifier.
*/
#define US_DECLARE_SERVICE_INTERFACE(_service_interface_type, _service_interface_id) \
template<> inline std::string us_service_interface_iid<_service_interface_type>() \
{ return _service_interface_id; } \
US_BEGIN_NAMESPACE
class ServiceFactory;
/**
* @ingroup MicroServices
*
* A helper type used in several methods to get proper
* method overload resolutions.
*/
template<class Interface>
struct InterfaceType {};
/**
* @ingroup MicroServices
*
* A map containing interfaces ids and their corresponding service object
* pointers. InterfaceMap instances represent a complete service object
- * which implementes one or more service interfaces. For each implemented
+ * which implements one or more service interfaces. For each implemented
* service interface, there is an entry in the map with the key being
* the service interface id and the value a pointer to the service
* interface implementation.
*
* To create InterfaceMap instances, use the MakeInterfaceMap helper class.
*
* @note This is a low-level type and should only rarely be used.
*
* @see MakeInterfaceMap
*/
typedef std::map<std::string, void*> InterfaceMap;
template<class I>
bool InsertInterfaceType(InterfaceMap& im, I* i)
{
if (us_service_interface_iid<I>().empty())
{
throw ServiceException(std::string("The interface class ") + typeid(I).name() +
" uses an invalid id in its US_DECLARE_SERVICE_INTERFACE macro call.");
}
im.insert(std::make_pair(std::string(us_service_interface_iid<I>()),
static_cast<void*>(static_cast<I*>(i))));
return true;
}
template<>
inline bool InsertInterfaceType<void>(InterfaceMap&, void*)
{
return false;
}
/**
* @ingroup MicroServices
*
* Helper class for constructing InterfaceMap instances based
* on service implementations or service factories.
*
* Example usage:
* \code
- * MyService service; // implementes I1 and I2
+ * MyService service; // implements I1 and I2
* InterfaceMap im = MakeInterfaceMap<I1,I2>(&service);
* \endcode
*
* The MakeInterfaceMap supports service implementations with
* up to three service interfaces.
*
* @see InterfaceMap
*/
template<class I1, class I2 = void, class I3 = void>
struct MakeInterfaceMap
{
ServiceFactory* m_factory;
I1* m_interface1;
I2* m_interface2;
I3* m_interface3;
/**
* Constructor taking a service implementation pointer.
*
* @param impl A service implementation pointer, which must
* be castable to a all specified service interfaces.
*/
template<class Impl>
MakeInterfaceMap(Impl* impl)
: m_factory(nullptr)
, m_interface1(static_cast<I1*>(impl))
, m_interface2(static_cast<I2*>(impl))
, m_interface3(static_cast<I3*>(impl))
{}
/**
* Constructor taking a service factory.
*
* @param factory A service factory.
*/
MakeInterfaceMap(ServiceFactory* factory)
: m_factory(factory)
, m_interface1(nullptr)
, m_interface2(nullptr)
, m_interface3(nullptr)
{
if (factory == nullptr)
{
throw ServiceException("The service factory argument must not be nullptr.");
}
}
operator InterfaceMap ()
{
InterfaceMap sim;
InsertInterfaceType(sim, m_interface1);
InsertInterfaceType(sim, m_interface2);
InsertInterfaceType(sim, m_interface3);
if (m_factory)
{
sim.insert(std::make_pair(std::string("org.cppmicroservices.factory"),
static_cast<void*>(m_factory)));
}
return sim;
}
};
/// \cond
template<class I1, class I2>
struct MakeInterfaceMap<I1,I2,void>
{
ServiceFactory* m_factory;
I1* m_interface1;
I2* m_interface2;
template<class Impl>
MakeInterfaceMap(Impl* impl)
: m_factory(nullptr)
, m_interface1(static_cast<I1*>(impl))
, m_interface2(static_cast<I2*>(impl))
{}
MakeInterfaceMap(ServiceFactory* factory)
: m_factory(factory)
, m_interface1(nullptr)
, m_interface2(nullptr)
{
if (factory == nullptr)
{
throw ServiceException("The service factory argument must not be nullptr.");
}
}
operator InterfaceMap ()
{
InterfaceMap sim;
InsertInterfaceType(sim, m_interface1);
InsertInterfaceType(sim, m_interface2);
if (m_factory)
{
sim.insert(std::make_pair(std::string("org.cppmicroservices.factory"),
static_cast<void*>(m_factory)));
}
return sim;
}
};
template<class I1>
struct MakeInterfaceMap<I1,void,void>
{
ServiceFactory* m_factory;
I1* m_interface1;
template<class Impl>
MakeInterfaceMap(Impl* impl)
: m_factory(nullptr)
, m_interface1(static_cast<I1*>(impl))
{}
MakeInterfaceMap(ServiceFactory* factory)
: m_factory(factory)
, m_interface1(nullptr)
{
if (factory == nullptr)
{
throw ServiceException("The service factory argument must not be nullptr.");
}
}
operator InterfaceMap ()
{
InterfaceMap sim;
InsertInterfaceType(sim, m_interface1);
if (m_factory)
{
sim.insert(std::make_pair(std::string("org.cppmicroservices.factory"),
static_cast<void*>(m_factory)));
}
return sim;
}
};
template<>
struct MakeInterfaceMap<void,void,void>;
/// \endcond
/**
* @ingroup MicroServices
*
* Extract a service interface pointer from a given InterfaceMap instance.
*
* @param map a InterfaceMap instance.
* @return The service interface pointer for the service interface id of the
* \c I1 interface type or nullptr if \c map does not contain an entry
* for the given type.
*
* @see MakeInterfaceMap
*/
template<class I1>
I1* ExtractInterface(const InterfaceMap& map)
{
InterfaceMap::const_iterator iter = map.find(us_service_interface_iid<I1>());
if (iter != map.end())
{
return reinterpret_cast<I1*>(iter->second);
}
return nullptr;
}
US_END_NAMESPACE
#endif // USSERVICEINTERFACE_H
diff --git a/Modules/CppMicroServices/core/include/usServiceReferenceBase.h b/Modules/CppMicroServices/core/include/usServiceReferenceBase.h
index 7ce3d84636..05c3a062e2 100644
--- a/Modules/CppMicroServices/core/include/usServiceReferenceBase.h
+++ b/Modules/CppMicroServices/core/include/usServiceReferenceBase.h
@@ -1,234 +1,234 @@
/*============================================================================
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
https://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 USSERVICEREFERENCEBASE_H
#define USSERVICEREFERENCEBASE_H
#include <usAny.h>
US_MSVC_PUSH_DISABLE_WARNING(4396)
US_BEGIN_NAMESPACE
class Module;
class ServiceRegistrationBasePrivate;
class ServiceReferenceBasePrivate;
/**
* \ingroup MicroServices
*
* A reference to a service.
*
* \note This class is provided as public API for low-level service queries only.
* In almost all cases you should use the template ServiceReference instead.
*/
class US_Core_EXPORT ServiceReferenceBase {
private:
typedef ServiceReferenceBasePrivate* ServiceReferenceBase::*bool_type;
public:
ServiceReferenceBase(const ServiceReferenceBase& ref);
/**
* Converts this ServiceReferenceBase instance into a boolean
* expression. If this instance was default constructed or
* the service it references has been unregistered, the conversion
* returns <code>false</code>, otherwise it returns <code>true</code>.
*/
operator bool_type() const;
/**
* Releases any resources held or locked by this
* <code>ServiceReferenceBase</code> and renders it invalid.
*/
ServiceReferenceBase& operator=(int null);
~ServiceReferenceBase();
/**
* Returns the property value to which the specified property key is mapped
* in the properties <code>ServiceProperties</code> object of the service
* referenced by this <code>ServiceReferenceBase</code> object.
*
* <p>
* Property keys are case-insensitive.
*
* <p>
* This method continues to return property values after the service has
* been unregistered. This is so references to unregistered services can
* still be interrogated.
*
* @param key The property key.
* @return The property value to which the key is mapped; an invalid Any
* if there is no property named after the key.
*/
Any GetProperty(const std::string& key) const;
/**
* Returns a list of the keys in the <code>ServiceProperties</code>
* object of the service referenced by this <code>ServiceReferenceBase</code>
* object.
*
* <p>
* This method will continue to return the keys after the service has been
* unregistered. This is so references to unregistered services can
* still be interrogated.
*
* @param keys A vector being filled with the property keys.
*/
void GetPropertyKeys(std::vector<std::string>& keys) const;
/**
* Returns the module that registered the service referenced by this
* <code>ServiceReferenceBase</code> object.
*
* <p>
* This method must return <code>0</code> when the service has been
* unregistered. This can be used to determine if the service has been
* unregistered.
*
* @return The module that registered the service referenced by this
* <code>ServiceReferenceBase</code> object; <code>0</code> if that
* service has already been unregistered.
* @see ModuleContext::RegisterService(const InterfaceMap&, const ServiceProperties&)
*/
Module* GetModule() const;
/**
* Returns the modules that are using the service referenced by this
* <code>ServiceReferenceBase</code> object. Specifically, this method returns
* the modules whose usage count for that service is greater than zero.
*
* @param modules A list of modules whose usage count for the service referenced
* by this <code>ServiceReferenceBase</code> object is greater than
* zero.
*/
void GetUsingModules(std::vector<Module*>& modules) const;
/**
* Returns the interface identifier this ServiceReferenceBase object
* is bound to.
*
* A default constructed ServiceReferenceBase object is not bound to
* any interface identifier and calling this method will return an
* empty string.
*
* @return The interface identifier for this ServiceReferenceBase object.
*/
std::string GetInterfaceId() const;
/**
- * Checks wether this ServiceReferenceBase object can be converted to
+ * Checks whether this ServiceReferenceBase object can be converted to
* another ServiceReferenceBase object, which will be bound to the
* given interface identifier.
*
* ServiceReferenceBase objects can be converted if the underlying service
* implementation was registered under multiple service interfaces.
*
* @param interfaceid
* @return \c true if this ServiceReferenceBase object can be converted,
* \c false otherwise.
*/
bool IsConvertibleTo(const std::string& interfaceid) const;
/**
* Compares this <code>ServiceReferenceBase</code> with the specified
* <code>ServiceReferenceBase</code> for order.
*
* <p>
* If this <code>ServiceReferenceBase</code> and the specified
* <code>ServiceReferenceBase</code> have the same \link ServiceConstants::SERVICE_ID()
* service id\endlink they are equal. This <code>ServiceReferenceBase</code> is less
* than the specified <code>ServiceReferenceBase</code> if it has a lower
* {@link ServiceConstants::SERVICE_RANKING service ranking} and greater if it has a
* higher service ranking. Otherwise, if this <code>ServiceReferenceBase</code>
* and the specified <code>ServiceReferenceBase</code> have the same
* {@link ServiceConstants::SERVICE_RANKING service ranking}, this
* <code>ServiceReferenceBase</code> is less than the specified
* <code>ServiceReferenceBase</code> if it has a higher
* {@link ServiceConstants::SERVICE_ID service id} and greater if it has a lower
* service id.
*
* @param reference The <code>ServiceReferenceBase</code> to be compared.
* @return Returns a false or true if this
* <code>ServiceReferenceBase</code> is less than or greater
* than the specified <code>ServiceReferenceBase</code>.
*/
bool operator<(const ServiceReferenceBase& reference) const;
bool operator==(const ServiceReferenceBase& reference) const;
ServiceReferenceBase& operator=(const ServiceReferenceBase& reference);
private:
friend class ModulePrivate;
friend class ModuleContext;
friend class ModuleHooks;
friend class ServiceHooks;
friend class ServiceObjectsBase;
friend class ServiceObjectsBasePrivate;
friend class ServiceRegistrationBase;
friend class ServiceRegistrationBasePrivate;
friend class ServiceListeners;
friend class ServiceRegistry;
friend class LDAPFilter;
template<class S> friend class ServiceReference;
US_HASH_FUNCTION_FRIEND(ServiceReferenceBase);
std::size_t Hash() const;
/**
* Creates an invalid ServiceReferenceBase object. You can use
* this object in boolean expressions and it will evaluate to
* <code>false</code>.
*/
ServiceReferenceBase();
ServiceReferenceBase(ServiceRegistrationBasePrivate* reg);
void SetInterfaceId(const std::string& interfaceId);
ServiceReferenceBasePrivate* d;
};
US_END_NAMESPACE
US_MSVC_POP_WARNING
/**
* \ingroup MicroServices
*/
US_Core_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(ServiceReferenceBase)& serviceRef);
US_HASH_FUNCTION_NAMESPACE_BEGIN
US_HASH_FUNCTION_BEGIN(US_PREPEND_NAMESPACE(ServiceReferenceBase))
return arg.Hash();
US_HASH_FUNCTION_END
US_HASH_FUNCTION_NAMESPACE_END
#endif // USSERVICEREFERENCEBASE_H
diff --git a/Modules/CppMicroServices/core/include/usServiceRegistration.h b/Modules/CppMicroServices/core/include/usServiceRegistration.h
index 8c925bd588..a9c8acc6d3 100644
--- a/Modules/CppMicroServices/core/include/usServiceRegistration.h
+++ b/Modules/CppMicroServices/core/include/usServiceRegistration.h
@@ -1,203 +1,203 @@
/*============================================================================
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
https://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 USSERVICEREGISTRATION_H
#define USSERVICEREGISTRATION_H
#include "usServiceRegistrationBase.h"
US_BEGIN_NAMESPACE
/**
* \ingroup MicroServices
*
* A registered service.
*
* <p>
* The framework returns a <code>ServiceRegistration</code> object when a
* <code>ModuleContext#RegisterService()</code> method invocation is successful.
* The <code>ServiceRegistration</code> object is for the private use of the
* registering module and should not be shared with other modules.
* <p>
* The <code>ServiceRegistration</code> object may be used to update the
* properties of the service or to unregister the service.
*
- * @tparam S Class tyoe of the service interface
+ * @tparam S Class type of the service interface
* @see ModuleContext#RegisterService()
* @remarks This class is thread safe.
*/
template<class I1, class I2 = void, class I3 = void>
class ServiceRegistration : public ServiceRegistrationBase
{
public:
/**
* Creates an invalid ServiceRegistration object. You can use
* this object in boolean expressions and it will evaluate to
* <code>false</code>.
*/
ServiceRegistration() : ServiceRegistrationBase()
{
}
///@{
/**
* Returns a <code>ServiceReference</code> object for a service being
* registered.
* <p>
* The <code>ServiceReference</code> object may be shared with other
* modules.
*
* @throws std::logic_error If this
* <code>ServiceRegistration</code> object has already been
* unregistered or if it is invalid.
* @return <code>ServiceReference</code> object.
*/
ServiceReference<I1> GetReference(InterfaceType<I1>) const
{
return this->ServiceRegistrationBase::GetReference(us_service_interface_iid<I1>());
}
ServiceReference<I2> GetReference(InterfaceType<I2>) const
{
return this->ServiceRegistrationBase::GetReference(us_service_interface_iid<I2>());
}
ServiceReference<I3> GetReference(InterfaceType<I3>) const
{
return this->ServiceRegistrationBase::GetReference(us_service_interface_iid<I3>());
}
///@}
using ServiceRegistrationBase::operator=;
private:
friend class ModuleContext;
ServiceRegistration(const ServiceRegistrationBase& base)
: ServiceRegistrationBase(base)
{
}
};
/// \cond
template<class I1, class I2>
class ServiceRegistration<I1, I2, void> : public ServiceRegistrationBase
{
public:
ServiceRegistration() : ServiceRegistrationBase()
{
}
ServiceReference<I1> GetReference(InterfaceType<I1>) const
{
return ServiceReference<I1>(this->ServiceRegistrationBase::GetReference(us_service_interface_iid<I1>()));
}
ServiceReference<I2> GetReference(InterfaceType<I2>) const
{
return ServiceReference<I2>(this->ServiceRegistrationBase::GetReference(us_service_interface_iid<I2>()));
}
using ServiceRegistrationBase::operator=;
private:
friend class ModuleContext;
ServiceRegistration(const ServiceRegistrationBase& base)
: ServiceRegistrationBase(base)
{
}
};
template<class I1>
class ServiceRegistration<I1, void, void> : public ServiceRegistrationBase
{
public:
ServiceRegistration() : ServiceRegistrationBase()
{
}
ServiceReference<I1> GetReference() const
{
return this->GetReference(InterfaceType<I1>());
}
ServiceReference<I1> GetReference(InterfaceType<I1>) const
{
return ServiceReference<I1>(this->ServiceRegistrationBase::GetReference(us_service_interface_iid<I1>()));
}
using ServiceRegistrationBase::operator=;
private:
friend class ModuleContext;
ServiceRegistration(const ServiceRegistrationBase& base)
: ServiceRegistrationBase(base)
{
}
};
template<>
class ServiceRegistration<void, void, void> : public ServiceRegistrationBase
{
public:
/**
* Creates an invalid ServiceReference object. You can use
* this object in boolean expressions and it will evaluate to
* <code>false</code>.
*/
ServiceRegistration() : ServiceRegistrationBase()
{
}
ServiceRegistration(const ServiceRegistrationBase& base)
: ServiceRegistrationBase(base)
{
}
using ServiceRegistrationBase::operator=;
};
/// \endcond
/**
* \ingroup MicroServices
*
* A service registration object of unknown type.
*/
typedef ServiceRegistration<void> ServiceRegistrationU;
US_END_NAMESPACE
#endif // USSERVICEREGISTRATION_H
diff --git a/Modules/CppMicroServices/core/include/usServiceTracker.h b/Modules/CppMicroServices/core/include/usServiceTracker.h
index abff1471e6..daa7f03005 100644
--- a/Modules/CppMicroServices/core/include/usServiceTracker.h
+++ b/Modules/CppMicroServices/core/include/usServiceTracker.h
@@ -1,597 +1,597 @@
/*============================================================================
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
https://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 USSERVICETRACKER_H
#define USSERVICETRACKER_H
#include <map>
#include "usServiceReference.h"
#include "usServiceTrackerCustomizer.h"
#include "usLDAPFilter.h"
US_BEGIN_NAMESPACE
template<class S, class T> class TrackedService;
template<class S, class T> class ServiceTrackerPrivate;
class ModuleContext;
/**
* \ingroup MicroServices
*
* A base class template for type traits for objects tracked by a
* ServiceTracker instance. It provides the \c TrackedType typedef
* and two dummy method definitions.
*
* Tracked type traits (TTT) classes must additionally provide the
* following methods:
*
* <ul>
* <li><em>static bool IsValid(const TrackedType& t)</em> Returns \c true if \c t is a valid object, \c false otherwise.</li>
* <li><em>static void Dispose(TrackedType& t)</em> Clears any resources held by the tracked object \c t.</li>
* <li><em>static TrackedType DefaultValue()</em> Returns the default value for newly created tracked objects.</li>
* </ul>
*
* @tparam T The type of the tracked object.
* @tparam TTT The tracked type traits class deriving from this class.
*
* @see ServiceTracker
*/
template<class T, class TTT>
struct TrackedTypeTraitsBase
{
typedef T TrackedType;
// Needed for S == void
static TrackedType ConvertToTrackedType(const InterfaceMap&)
{
throw std::runtime_error("A custom ServiceTrackerCustomizer instance is required for custom tracked objects.");
//return TTT::DefaultValue();
}
// Needed for S != void
static TrackedType ConvertToTrackedType(void*)
{
throw std::runtime_error("A custom ServiceTrackerCustomizer instance is required for custom tracked objects.");
//return TTT::DefaultValue();
}
};
/// \cond
template<class S, class T>
struct TrackedTypeTraits;
/// \endcond
/**
* \ingroup MicroServices
*
* Default type traits for custom tracked objects of pointer type.
*
* Use this tracked type traits template for custom tracked objects of
* pointer type with the ServiceTracker class.
*
* @tparam S The type of the service being tracked.
* @tparam T The type of the tracked object.
*/
template<class S, class T>
struct TrackedTypeTraits<S,T*> : public TrackedTypeTraitsBase<T*,TrackedTypeTraits<S,T*> >
{
typedef T* TrackedType;
static bool IsValid(const TrackedType& t)
{
return t != nullptr;
}
static TrackedType DefaultValue()
{
return nullptr;
}
static void Dispose(TrackedType& t)
{
t = nullptr;
}
};
/// \cond
template<class S>
struct TrackedTypeTraits<S,S*>
{
typedef S* TrackedType;
static bool IsValid(const TrackedType& t)
{
return t != nullptr;
}
static TrackedType DefaultValue()
{
return nullptr;
}
static void Dispose(TrackedType& t)
{
t = nullptr;
}
static TrackedType ConvertToTrackedType(S* s)
{
return s;
}
};
/// \endcond
/// \cond
/*
* This specialization is "special" because the tracked type is not
* void* (as specified in the second template parameter) but InterfaceMap.
* This is in line with the ModuleContext::GetService(...) overloads to
- * return either S* or InterfaceMap dependening on the template parameter.
+ * return either S* or InterfaceMap depending on the template parameter.
*/
template<>
struct TrackedTypeTraits<void,void*>
{
typedef InterfaceMap TrackedType;
static bool IsValid(const TrackedType& t)
{
return !t.empty();
}
static TrackedType DefaultValue()
{
return TrackedType();
}
static void Dispose(TrackedType& t)
{
t.clear();
}
static TrackedType ConvertToTrackedType(const InterfaceMap& im)
{
return im;
}
};
/// \endcond
/**
* \ingroup MicroServices
*
* The <code>ServiceTracker</code> class simplifies using services from the
* framework's service registry.
* <p>
* A <code>ServiceTracker</code> object is constructed with search criteria and
* a <code>ServiceTrackerCustomizer</code> object. A <code>ServiceTracker</code>
* can use a <code>ServiceTrackerCustomizer</code> to customize the service
* objects to be tracked. The <code>ServiceTracker</code> can then be opened to
* begin tracking all services in the framework's service registry that match
* the specified search criteria. The <code>ServiceTracker</code> correctly
* handles all of the details of listening to <code>ServiceEvent</code>s and
* getting and ungetting services.
* <p>
* The <code>GetServiceReferences</code> method can be called to get references
* to the services being tracked. The <code>GetService</code> and
* <code>GetServices</code> methods can be called to get the service objects for
* the tracked service.
*
* \note The <code>ServiceTracker</code> class is thread-safe. It does not call a
* <code>ServiceTrackerCustomizer</code> while holding any locks.
* <code>ServiceTrackerCustomizer</code> implementations must also be
* thread-safe.
*
* Customization of the services to be tracked requires a custom tracked type traits
* class if the custom tracked type is not a pointer type. To customize a tracked
* service using a custom type with value-semantics like
* \snippet uServices-servicetracker/main.cpp tt
* the custom tracked type traits class should look like this:
* \snippet uServices-servicetracker/main.cpp ttt
*
* For a custom tracked type, a ServiceTrackerCustomizer is required, which knows
* how to associate the tracked service with the custom tracked type:
* \snippet uServices-servicetracker/main.cpp customizer
* The custom tracking type traits class and customizer can now be used to instantiate
* a ServiceTracker:
* \snippet uServices-servicetracker/main.cpp tracker
*
* If the custom tracked type is a pointer type, a suitable tracked type traits
* template is provided by the framework and only a ServiceTrackerCustomizer needs
* to be provided:
* \snippet uServices-servicetracker/main.cpp tracker2
*
*
* @tparam S The type of the service being tracked. The type S* must be an
* assignable datatype.
* @tparam TTT Type traits of the tracked object. The type traits class provides
* information about the customized service object, see TrackedTypeTraitsBase.
*
* @remarks This class is thread safe.
*/
template<class S, class TTT = TrackedTypeTraits<S,S*> >
class ServiceTracker : protected ServiceTrackerCustomizer<S,typename TTT::TrackedType>
{
public:
/// The type of the service being tracked
typedef S ServiceType;
/// The type of the tracked object
typedef typename TTT::TrackedType T;
typedef ServiceReference<S> ServiceReferenceType;
typedef std::map<ServiceReference<S>,T> TrackingMap;
~ServiceTracker() override;
/**
* Create a <code>ServiceTracker</code> on the specified
* <code>ServiceReference</code>.
*
* <p>
* The service referenced by the specified <code>ServiceReference</code>
* will be tracked by this <code>ServiceTracker</code>.
*
* @param context The <code>ModuleContext</code> against which the tracking
* is done.
* @param reference The <code>ServiceReference</code> for the service to be
* tracked.
* @param customizer The customizer object to call when services are added,
* modified, or removed in this <code>ServiceTracker</code>. If
* customizer is <code>null</code>, then this
* <code>ServiceTracker</code> will be used as the
* <code>ServiceTrackerCustomizer</code> and this
* <code>ServiceTracker</code> will call the
* <code>ServiceTrackerCustomizer</code> methods on itself.
*/
ServiceTracker(ModuleContext* context,
const ServiceReferenceType& reference,
ServiceTrackerCustomizer<S,T>* customizer = nullptr);
/**
* Create a <code>ServiceTracker</code> on the specified class name.
*
* <p>
* Services registered under the specified class name will be tracked by
* this <code>ServiceTracker</code>.
*
* @param context The <code>ModuleContext</code> against which the tracking
* is done.
* @param clazz The class name of the services to be tracked.
* @param customizer The customizer object to call when services are added,
* modified, or removed in this <code>ServiceTracker</code>. If
* customizer is <code>null</code>, then this
* <code>ServiceTracker</code> will be used as the
* <code>ServiceTrackerCustomizer</code> and this
* <code>ServiceTracker</code> will call the
* <code>ServiceTrackerCustomizer</code> methods on itself.
*/
ServiceTracker(ModuleContext* context, const std::string& clazz,
ServiceTrackerCustomizer<S,T>* customizer = 0);
/**
* Create a <code>ServiceTracker</code> on the specified
* <code>LDAPFilter</code> object.
*
* <p>
* Services which match the specified <code>LDAPFilter</code> object will be
* tracked by this <code>ServiceTracker</code>.
*
* @param context The <code>ModuleContext</code> against which the tracking
* is done.
* @param filter The <code>LDAPFilter</code> to select the services to be
* tracked.
* @param customizer The customizer object to call when services are added,
* modified, or removed in this <code>ServiceTracker</code>. If
* customizer is null, then this <code>ServiceTracker</code> will be
* used as the <code>ServiceTrackerCustomizer</code> and this
* <code>ServiceTracker</code> will call the
* <code>ServiceTrackerCustomizer</code> methods on itself.
*/
ServiceTracker(ModuleContext* context, const LDAPFilter& filter,
ServiceTrackerCustomizer<S,T>* customizer = nullptr);
/**
* Create a <code>ServiceTracker</code> on the class template
* argument S.
*
* <p>
* Services registered under the interface name of the class template
* argument S will be tracked by this <code>ServiceTracker</code>.
*
* @param context The <code>ModuleContext</code> against which the tracking
* is done.
* @param customizer The customizer object to call when services are added,
* modified, or removed in this <code>ServiceTracker</code>. If
* customizer is null, then this <code>ServiceTracker</code> will be
* used as the <code>ServiceTrackerCustomizer</code> and this
* <code>ServiceTracker</code> will call the
* <code>ServiceTrackerCustomizer</code> methods on itself.
*/
ServiceTracker(ModuleContext* context, ServiceTrackerCustomizer<S,T>* customizer = nullptr);
/**
* Open this <code>ServiceTracker</code> and begin tracking services.
*
* <p>
* Services which match the search criteria specified when this
* <code>ServiceTracker</code> was created are now tracked by this
* <code>ServiceTracker</code>.
*
* @throws std::logic_error If the <code>ModuleContext</code>
* with which this <code>ServiceTracker</code> was created is no
* longer valid.
*/
virtual void Open();
/**
* Close this <code>ServiceTracker</code>.
*
* <p>
* This method should be called when this <code>ServiceTracker</code> should
* end the tracking of services.
*
* <p>
* This implementation calls GetServiceReferences() to get the list
* of tracked services to remove.
*/
virtual void Close();
/**
* Wait for at least one service to be tracked by this
* <code>ServiceTracker</code>. This method will also return when this
* <code>ServiceTracker</code> is closed.
*
* <p>
* It is strongly recommended that <code>WaitForService</code> is not used
* during the calling of the <code>ModuleActivator</code> methods.
* <code>ModuleActivator</code> methods are expected to complete in a short
* period of time.
*
* <p>
* This implementation calls GetService() to determine if a service
* is being tracked.
*
* @return Returns the result of GetService().
*/
virtual T WaitForService(unsigned long timeoutMillis = 0);
/**
* Return a list of <code>ServiceReference</code>s for all services being
* tracked by this <code>ServiceTracker</code>.
*
* @return List of <code>ServiceReference</code>s.
*/
virtual std::vector<ServiceReferenceType> GetServiceReferences() const;
/**
* Returns a <code>ServiceReference</code> for one of the services being
* tracked by this <code>ServiceTracker</code>.
*
* <p>
* If multiple services are being tracked, the service with the highest
* ranking (as specified in its <code>service.ranking</code> property) is
* returned. If there is a tie in ranking, the service with the lowest
* service ID (as specified in its <code>service.id</code> property); that
* is, the service that was registered first is returned. This is the same
* algorithm used by <code>ModuleContext::GetServiceReference()</code>.
*
* <p>
* This implementation calls GetServiceReferences() to get the list
* of references for the tracked services.
*
* @return A <code>ServiceReference</code> for a tracked service.
* @throws ServiceException if no services are being tracked.
*/
virtual ServiceReferenceType GetServiceReference() const;
/**
* Returns the service object for the specified
* <code>ServiceReference</code> if the specified referenced service is
* being tracked by this <code>ServiceTracker</code>.
*
* @param reference The reference to the desired service.
* @return A service object or <code>null</code> if the service referenced
* by the specified <code>ServiceReference</code> is not being
* tracked.
*/
virtual T GetService(const ServiceReferenceType& reference) const;
/**
* Return a list of service objects for all services being tracked by this
* <code>ServiceTracker</code>.
*
* <p>
* This implementation calls GetServiceReferences() to get the list
* of references for the tracked services and then calls
* GetService(const ServiceReference&) for each reference to get the
* tracked service object.
*
* @return A list of service objects or an empty list if no services
* are being tracked.
*/
virtual std::vector<T> GetServices() const;
/**
* Returns a service object for one of the services being tracked by this
* <code>ServiceTracker</code>.
*
* <p>
* If any services are being tracked, this implementation returns the result
* of calling <code>%GetService(%GetServiceReference())</code>.
*
* @return A service object or <code>null</code> if no services are being
* tracked.
*/
virtual T GetService() const;
/**
* Remove a service from this <code>ServiceTracker</code>.
*
* The specified service will be removed from this
* <code>ServiceTracker</code>. If the specified service was being tracked
* then the <code>ServiceTrackerCustomizer::RemovedService</code> method will
* be called for that service.
*
* @param reference The reference to the service to be removed.
*/
virtual void Remove(const ServiceReferenceType& reference);
/**
* Return the number of services being tracked by this
* <code>ServiceTracker</code>.
*
* @return The number of services being tracked.
*/
virtual int Size() const;
/**
* Returns the tracking count for this <code>ServiceTracker</code>.
*
* The tracking count is initialized to 0 when this
* <code>ServiceTracker</code> is opened. Every time a service is added,
* modified or removed from this <code>ServiceTracker</code>, the tracking
* count is incremented.
*
* <p>
* The tracking count can be used to determine if this
* <code>ServiceTracker</code> has added, modified or removed a service by
* comparing a tracking count value previously collected with the current
* tracking count value. If the value has not changed, then no service has
* been added, modified or removed from this <code>ServiceTracker</code>
* since the previous tracking count was collected.
*
* @return The tracking count for this <code>ServiceTracker</code> or -1 if
* this <code>ServiceTracker</code> is not open.
*/
virtual int GetTrackingCount() const;
/**
* Return a sorted map of the <code>ServiceReference</code>s and
* service objects for all services being tracked by this
* <code>ServiceTracker</code>. The map is sorted in natural order
* of <code>ServiceReference</code>. That is, the last entry is the service
* with the highest ranking and the lowest service id.
*
* @param tracked A <code>TrackingMap</code> with the <code>ServiceReference</code>s
* and service objects for all services being tracked by this
* <code>ServiceTracker</code>. If no services are being tracked,
* then the returned map is empty.
*/
virtual void GetTracked(TrackingMap& tracked) const;
/**
* Return if this <code>ServiceTracker</code> is empty.
*
* @return <code>true</code> if this <code>ServiceTracker</code> is not tracking any
* services.
*/
virtual bool IsEmpty() const;
protected:
/**
* Default implementation of the
* <code>ServiceTrackerCustomizer::AddingService</code> method.
*
* <p>
* This method is only called when this <code>ServiceTracker</code> has been
* constructed with a <code>null</code> ServiceTrackerCustomizer argument.
*
* <p>
* This implementation returns the result of calling <code>GetService</code>
* on the <code>ModuleContext</code> with which this
* <code>ServiceTracker</code> was created passing the specified
* <code>ServiceReference</code>.
* <p>
* This method can be overridden in a subclass to customize the service
* object to be tracked for the service being added. In that case, take care
* not to rely on the default implementation of
* \link RemovedService(const ServiceReferenceType&, T service) removedService\endlink
* to unget the service.
*
* @param reference The reference to the service being added to this
* <code>ServiceTracker</code>.
* @return The service object to be tracked for the service added to this
* <code>ServiceTracker</code>.
* @see ServiceTrackerCustomizer::AddingService(const ServiceReference&)
*/
T AddingService(const ServiceReferenceType& reference) override;
/**
* Default implementation of the
* <code>ServiceTrackerCustomizer::ModifiedService</code> method.
*
* <p>
* This method is only called when this <code>ServiceTracker</code> has been
* constructed with a <code>null</code> ServiceTrackerCustomizer argument.
*
* <p>
* This implementation does nothing.
*
* @param reference The reference to modified service.
* @param service The service object for the modified service.
* @see ServiceTrackerCustomizer::ModifiedService(const ServiceReference&, T)
*/
void ModifiedService(const ServiceReferenceType& reference, T service) override;
/**
* Default implementation of the
* <code>ServiceTrackerCustomizer::RemovedService</code> method.
*
* <p>
* This method is only called when this <code>ServiceTracker</code> has been
* constructed with a <code>null</code> ServiceTrackerCustomizer argument.
*
* <p>
* This implementation calls <code>UngetService</code>, on the
* <code>ModuleContext</code> with which this <code>ServiceTracker</code>
* was created, passing the specified <code>ServiceReference</code>.
* <p>
* This method can be overridden in a subclass. If the default
* implementation of \link AddingService(const ServiceReferenceType&) AddingService\endlink
* method was used, this method must unget the service.
*
* @param reference The reference to removed service.
* @param service The service object for the removed service.
* @see ServiceTrackerCustomizer::RemovedService(const ServiceReferenceType&, T)
*/
void RemovedService(const ServiceReferenceType& reference, T service) override;
private:
typedef ServiceTracker<S,TTT> _ServiceTracker;
typedef TrackedService<S,TTT> _TrackedService;
typedef ServiceTrackerPrivate<S,TTT> _ServiceTrackerPrivate;
typedef ServiceTrackerCustomizer<S,T> _ServiceTrackerCustomizer;
friend class TrackedService<S,TTT>;
friend class ServiceTrackerPrivate<S,TTT>;
_ServiceTrackerPrivate* const d;
};
US_END_NAMESPACE
#include "usServiceTracker.tpp"
#endif // USSERVICETRACKER_H
diff --git a/Modules/CppMicroServices/core/src/module/usCoreModuleContext_p.h b/Modules/CppMicroServices/core/src/module/usCoreModuleContext_p.h
index 85af6542d6..70b9c5995c 100644
--- a/Modules/CppMicroServices/core/src/module/usCoreModuleContext_p.h
+++ b/Modules/CppMicroServices/core/src/module/usCoreModuleContext_p.h
@@ -1,76 +1,76 @@
/*============================================================================
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
https://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 USCOREMODULECONTEXT_H
#define USCOREMODULECONTEXT_H
#include "usServiceListeners_p.h"
#include "usServiceRegistry_p.h"
#include "usModuleHooks_p.h"
#include "usServiceHooks_p.h"
US_BEGIN_NAMESPACE
/**
* This class is not part of the public API.
*/
class CoreModuleContext
{
public:
/**
* All listeners in this framework.
*/
ServiceListeners listeners;
/**
* All registered services in this framework.
*/
ServiceRegistry services;
/**
* All service hooks.
*/
ServiceHooks serviceHooks;
/**
* All module hooks.
*/
ModuleHooks moduleHooks;
/**
- * Contruct a core context
+ * Construct a core context
*
*/
CoreModuleContext();
~CoreModuleContext();
void Init();
void Uninit();
};
US_END_NAMESPACE
#endif // USCOREMODULECONTEXT_H
diff --git a/Modules/CppMicroServices/core/src/service/usServiceReferenceBasePrivate.h b/Modules/CppMicroServices/core/src/service/usServiceReferenceBasePrivate.h
index f3a0be1ca9..15fd96f4da 100644
--- a/Modules/CppMicroServices/core/src/service/usServiceReferenceBasePrivate.h
+++ b/Modules/CppMicroServices/core/src/service/usServiceReferenceBasePrivate.h
@@ -1,152 +1,152 @@
/*============================================================================
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
https://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 USSERVICEREFERENCEBASEPRIVATE_H
#define USSERVICEREFERENCEBASEPRIVATE_H
#include "usAtomicInt_p.h"
#include "usServiceInterface.h"
#include <string>
US_BEGIN_NAMESPACE
class Any;
class Module;
class ServicePropertiesImpl;
class ServiceRegistrationBasePrivate;
class ServiceReferenceBasePrivate;
/**
* \ingroup MicroServices
*/
class ServiceReferenceBasePrivate
{
public:
ServiceReferenceBasePrivate(ServiceRegistrationBasePrivate* reg);
~ServiceReferenceBasePrivate();
/**
* Get the service object.
*
* @param module requester of service.
* @return Service requested or null in case of failure.
*/
void* GetService(Module* module);
InterfaceMap GetServiceInterfaceMap(Module* module);
/**
* Get new service instance.
*
* @param module requester of service.
* @return Service requested or null in case of failure.
*/
InterfaceMap GetPrototypeService(Module* module);
/**
* Unget the service object.
*
* @param module Module who wants remove service.
- * @param checkRefCounter If true decrement refence counter and remove service
+ * @param checkRefCounter If true decrement reference counter and remove service
* if we reach zero. If false remove service without
- * checking refence counter.
+ * checking reference counter.
* @return True if service was removed or false if only reference counter was
* decremented.
*/
bool UngetService(Module* module, bool checkRefCounter);
/**
* Unget prototype scope service objects.
*
* @param module Module who wants to remove a prototype scope service.
* @param service The prototype scope service pointer.
* @return \c true if the service was removed, \c false otherwise.
*/
bool UngetPrototypeService(Module* module, void* service);
bool UngetPrototypeService(Module* module, const InterfaceMap& service);
/**
* Get all properties registered with this service.
*
* @return A ServiceProperties object containing properties or being empty
* if service has been removed.
*/
const ServicePropertiesImpl& GetProperties() const;
/**
* Returns the property value to which the specified property key is mapped
* in the properties <code>ServiceProperties</code> object of the service
* referenced by this <code>ServiceReference</code> object.
*
* <p>
* Property keys are case-insensitive.
*
* <p>
* This method must continue to return property values after the service has
* been unregistered. This is so references to unregistered services can
* still be interrogated.
*
* @param key The property key.
* @param lock If <code>true</code>, access of the properties of the service
* referenced by this <code>ServiceReference</code> object will be
* synchronized.
* @return The property value to which the key is mapped; an invalid Any
* if there is no property named after the key.
*/
Any GetProperty(const std::string& key, bool lock) const;
bool IsConvertibleTo(const std::string& interfaceId) const;
/**
* Reference count for implicitly shared private implementation.
*/
AtomicInt ref;
/**
* Link to registration object for this reference.
*/
ServiceRegistrationBasePrivate* const registration;
/**
* The service interface id for this reference.
*/
std::string interfaceId;
private:
InterfaceMap GetServiceFromFactory(Module* module, ServiceFactory* factory,
bool isModuleScope);
// purposely not implemented
ServiceReferenceBasePrivate(const ServiceReferenceBasePrivate&);
ServiceReferenceBasePrivate& operator=(const ServiceReferenceBasePrivate&);
};
US_END_NAMESPACE
#endif // USSERVICEREFERENCEBASEPRIVATE_H
diff --git a/Modules/CppMicroServices/core/src/service/usServiceRegistry_p.h b/Modules/CppMicroServices/core/src/service/usServiceRegistry_p.h
index 7f0d74e277..4e55d4a4ad 100644
--- a/Modules/CppMicroServices/core/src/service/usServiceRegistry_p.h
+++ b/Modules/CppMicroServices/core/src/service/usServiceRegistry_p.h
@@ -1,189 +1,189 @@
/*============================================================================
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
https://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 USSERVICEREGISTRY_H
#define USSERVICEREGISTRY_H
#include "usServiceInterface.h"
#include "usServiceRegistration.h"
#include "usThreads_p.h"
US_BEGIN_NAMESPACE
class CoreModuleContext;
class ModulePrivate;
class ServicePropertiesImpl;
/**
* Here we handle all the CppMicroServices services that are registered.
*/
class ServiceRegistry
{
public:
typedef Mutex MutexType;
mutable MutexType mutex;
/**
* Creates a new ServiceProperties object containing <code>in</code>
* with the keys converted to lower case.
*
* @param classes A list of class names which will be added to the
* created ServiceProperties object under the key
* ModuleConstants::OBJECTCLASS.
* @param sid A service id which will be used instead of a default one.
*/
static ServicePropertiesImpl CreateServiceProperties(const ServiceProperties& in,
const std::vector<std::string>& classes = std::vector<std::string>(),
bool isFactory = false, bool isPrototypeFactory = false, long sid = -1);
typedef US_UNORDERED_MAP_TYPE<ServiceRegistrationBase, std::vector<std::string> > MapServiceClasses;
typedef US_UNORDERED_MAP_TYPE<std::string, std::vector<ServiceRegistrationBase> > MapClassServices;
/**
* All registered services in the current framework.
* Mapping of registered service to class names under which
- * the service is registerd.
+ * the service is registered.
*/
MapServiceClasses services;
std::vector<ServiceRegistrationBase> serviceRegistrations;
/**
* Mapping of classname to registered service.
* The List of registered services are ordered with the highest
* ranked service first.
*/
MapClassServices classServices;
CoreModuleContext* core;
ServiceRegistry(CoreModuleContext* coreCtx);
~ServiceRegistry();
void Clear();
/**
* Register a service in the framework wide register.
*
* @param module The module registering the service.
* @param classes The class names under which the service can be located.
* @param service The service object.
* @param properties The properties for this service.
* @return A ServiceRegistration object.
* @exception std::invalid_argument If one of the following is true:
* <ul>
* <li>The service object is 0.</li>
* <li>The service parameter is not a ServiceFactory or an
* instance of all the named classes in the classes parameter.</li>
* </ul>
*/
ServiceRegistrationBase RegisterService(ModulePrivate* module,
const InterfaceMap& service,
const ServiceProperties& properties);
/**
* Service ranking changed, reorder registered services
* according to ranking.
*
* @param serviceRegistration The ServiceRegistrationPrivate object.
* @param rank New rank of object.
*/
void UpdateServiceRegistrationOrder(const ServiceRegistrationBase& sr,
const std::vector<std::string>& classes);
/**
* Get all services implementing a certain class.
* Only used internally by the framework.
*
* @param clazz The class name of the requested service.
* @return A sorted list of {@link ServiceRegistrationPrivate} objects.
*/
void Get(const std::string& clazz, std::vector<ServiceRegistrationBase>& serviceRegs) const;
/**
* Get a service implementing a certain class.
*
* @param module The module requesting reference
* @param clazz The class name of the requested service.
* @return A {@link ServiceReference} object.
*/
ServiceReferenceBase Get(ModulePrivate* module, const std::string& clazz) const;
/**
* Get all services implementing a certain class and then
* filter these with a property filter.
*
* @param clazz The class name of requested service.
* @param filter The property filter.
* @param module The module requesting reference.
* @return A list of {@link ServiceReference} object.
*/
void Get(const std::string& clazz, const std::string& filter,
ModulePrivate* module, std::vector<ServiceReferenceBase>& serviceRefs) const;
/**
* Remove a registered service.
*
* @param sr The ServiceRegistration object that is registered.
*/
void RemoveServiceRegistration(const ServiceRegistrationBase& sr) ;
/**
* Get all services that a module has registered.
*
* @param p The module
* @return A set of {@link ServiceRegistration} objects
*/
void GetRegisteredByModule(ModulePrivate* m, std::vector<ServiceRegistrationBase>& serviceRegs) const;
/**
* Get all services that a module uses.
*
* @param p The module
* @return A set of {@link ServiceRegistration} objects
*/
void GetUsedByModule(Module* m, std::vector<ServiceRegistrationBase>& serviceRegs) const;
private:
friend class ServiceHooks;
void Get_unlocked(const std::string& clazz, std::vector<ServiceRegistrationBase>& serviceRegs) const;
void Get_unlocked(const std::string& clazz, const std::string& filter,
ModulePrivate* module, std::vector<ServiceReferenceBase>& serviceRefs) const;
// purposely not implemented
ServiceRegistry(const ServiceRegistry&);
ServiceRegistry& operator=(const ServiceRegistry&);
};
US_END_NAMESPACE
#endif // USSERVICEREGISTRY_H
diff --git a/Modules/CppMicroServices/core/src/util/usWaitCondition_p.h b/Modules/CppMicroServices/core/src/util/usWaitCondition_p.h
index a020bd1391..50b429addc 100644
--- a/Modules/CppMicroServices/core/src/util/usWaitCondition_p.h
+++ b/Modules/CppMicroServices/core/src/util/usWaitCondition_p.h
@@ -1,376 +1,376 @@
/*============================================================================
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
https://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 USWAITCONDITION_P_H
#define USWAITCONDITION_P_H
#include "usCoreConfig.h"
#include "usLog_p.h"
#include "usUtils_p.h"
#include "usThreads_p.h"
#ifdef US_PLATFORM_POSIX
#include <sys/time.h>
#include <cerrno>
#endif
US_BEGIN_NAMESPACE
/**
* \brief A thread synchronization object used to suspend execution until some
* condition on shared data is met.
*
* A thread calls Wait() to suspend its execution until the condition is
* met. Each call to Notify() from an executing thread will then cause a single
* waiting thread to be released. A call to Notify() means, "signal
* that the condition is true." NotifyAll() releases all threads waiting on
* the condition variable.
*
* The WaitCondition implementation is consistent with the standard
* definition and use of condition variables in pthreads and other common
* thread libraries.
*
* IMPORTANT: A condition variable always requires an associated mutex
* object. The mutex object is used to avoid a dangerous race condition when
* Wait() and Notify() are called simultaneously from two different
* threads.
*
* On systems using pthreads, this implementation abstracts the
* standard calls to the pthread condition variable. On Win32
* systems, there is no system provided condition variable. This
* class implements a condition variable using a critical section, a
- * semphore, an event and a number of counters. The implementation is
+ * semaphore, an event and a number of counters. The implementation is
* almost an extract translation of the implementation presented by
* Douglas C Schmidt and Irfan Pyarali in "Strategies for Implementing
* POSIX Condition Variables on Win32".
*/
template<class MutexHost>
class WaitCondition
{
public:
WaitCondition();
~WaitCondition();
bool Wait(unsigned long time = 0);
/** Notify that the condition is true and release one waiting thread */
void Notify();
/** Notify that the condition is true and release all waiting threads */
void NotifyAll();
private:
// purposely not implemented
WaitCondition(const WaitCondition& other);
const WaitCondition& operator=(const WaitCondition&);
#ifdef US_ENABLE_THREADING_SUPPORT
/** Suspend execution of this thread until the condition is signaled. The
* argument is a SimpleMutex object that must be locked prior to calling
* this method. */
bool Wait(Mutex& mutex, unsigned long time = 0);
bool Wait(Mutex* mutex, unsigned long time = 0);
#ifdef US_PLATFORM_POSIX
pthread_cond_t m_WaitCondition;
#else
int m_NumberOfWaiters; // number of waiting threads
CRITICAL_SECTION m_NumberOfWaitersLock; // Serialize access to
// m_NumberOfWaiters
HANDLE m_Semaphore; // Semaphore to queue threads
HANDLE m_WaitersAreDone; // Auto-reset event used by the
// broadcast/signal thread to
// wait for all the waiting
// threads to wake up and
// release the semaphore
std::size_t m_WasNotifyAll; // Keeps track of whether we
// were broadcasting or signaling
#endif
#endif // US_ENABLE_THREADING_SUPPORT
};
template<class MutexHost>
class NoWaitCondition
{
public:
NoWaitCondition() {}
private:
// purposely not implemented
NoWaitCondition(const NoWaitCondition& other);
const NoWaitCondition& operator=(const NoWaitCondition&);
};
// ------------------------------------------------------------------------
// WaitCondition implementation
// ------------------------------------------------------------------------
#ifdef US_ENABLE_THREADING_SUPPORT
template<class MutexHost>
WaitCondition<MutexHost>::WaitCondition()
{
#ifdef US_PLATFORM_POSIX
pthread_cond_init(&m_WaitCondition, nullptr);
#else
m_NumberOfWaiters = 0;
m_WasNotifyAll = 0;
m_Semaphore = CreateSemaphore(0, // no security
0, // initial value
0x7fffffff, // max count
0); // unnamed
InitializeCriticalSection(&m_NumberOfWaitersLock);
m_WaitersAreDone = CreateEvent(0, // no security
FALSE, // auto-reset
FALSE, // non-signaled initially
0 ); // unnamed
#endif
}
template<class MutexHost>
WaitCondition<MutexHost>::~WaitCondition()
{
#ifdef US_PLATFORM_POSIX
pthread_cond_destroy(&m_WaitCondition);
#else
CloseHandle(m_Semaphore);
CloseHandle(m_WaitersAreDone);
DeleteCriticalSection(&m_NumberOfWaitersLock);
#endif
}
template<class MutexHost>
bool WaitCondition<MutexHost>::Wait(unsigned long time)
{
return this->Wait(static_cast<MutexHost*>(this)->m_Mtx, time);
}
template<class MutexHost>
void WaitCondition<MutexHost>::Notify()
{
#ifdef US_PLATFORM_POSIX
pthread_cond_signal(&m_WaitCondition);
#else
EnterCriticalSection(&m_NumberOfWaitersLock);
int haveWaiters = m_NumberOfWaiters > 0;
LeaveCriticalSection(&m_NumberOfWaitersLock);
// if there were not any waiters, then this is a no-op
if (haveWaiters)
{
ReleaseSemaphore(m_Semaphore, 1, 0);
}
#endif
}
template<class MutexHost>
void WaitCondition<MutexHost>::NotifyAll()
{
#ifdef US_PLATFORM_POSIX
pthread_cond_broadcast(&m_WaitCondition);
#else
// This is needed to ensure that m_NumberOfWaiters and m_WasNotifyAll are
// consistent
EnterCriticalSection(&m_NumberOfWaitersLock);
int haveWaiters = 0;
if (m_NumberOfWaiters > 0)
{
// We are broadcasting, even if there is just one waiter...
// Record that we are broadcasting, which helps optimize Notify()
// for the non-broadcast case
m_WasNotifyAll = 1;
haveWaiters = 1;
}
if (haveWaiters)
{
// Wake up all waiters atomically
ReleaseSemaphore(m_Semaphore, m_NumberOfWaiters, 0);
LeaveCriticalSection(&m_NumberOfWaitersLock);
// Wait for all the awakened threads to acquire the counting
// semaphore
WaitForSingleObject(m_WaitersAreDone, INFINITE);
// This assignment is ok, even without the m_NumberOfWaitersLock held
// because no other waiter threads can wake up to access it.
m_WasNotifyAll = 0;
}
else
{
LeaveCriticalSection(&m_NumberOfWaitersLock);
}
#endif
}
template<class MutexHost>
bool WaitCondition<MutexHost>::Wait(Mutex* mutex, unsigned long timeoutMillis)
{
return Wait(*mutex, timeoutMillis);
}
template<class MutexHost>
bool WaitCondition<MutexHost>::Wait(Mutex& mutex, unsigned long timeoutMillis)
{
#ifdef US_PLATFORM_POSIX
struct timespec ts, * pts = nullptr;
if (timeoutMillis)
{
pts = &ts;
struct timeval tv;
int error = gettimeofday(&tv, nullptr);
if (error)
{
US_ERROR << "gettimeofday error: " << GetLastErrorStr();
return false;
}
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = tv.tv_usec * 1000;
ts.tv_sec += timeoutMillis / 1000;
ts.tv_nsec += (timeoutMillis % 1000) * 1000000;
ts.tv_sec += ts.tv_nsec / 1000000000;
ts.tv_nsec = ts.tv_nsec % 1000000000;
}
if (pts)
{
int error = pthread_cond_timedwait(&m_WaitCondition, &mutex.m_Mtx, pts);
if (error == 0)
{
return true;
}
else
{
if (error != ETIMEDOUT)
{
US_ERROR << "pthread_cond_timedwait error: " << GetLastErrorStr();
}
return false;
}
}
else
{
int error = pthread_cond_wait(&m_WaitCondition, &mutex.m_Mtx);
if (error)
{
US_ERROR << "pthread_cond_wait error: " << GetLastErrorStr();
return false;
}
return true;
}
#else
// Avoid race conditions
EnterCriticalSection(&m_NumberOfWaitersLock);
m_NumberOfWaiters++;
LeaveCriticalSection(&m_NumberOfWaitersLock);
DWORD dw;
bool result = true;
// This call atomically releases the mutex and waits on the
// semaphore until signaled
dw = SignalObjectAndWait(mutex.m_Mtx, m_Semaphore, timeoutMillis ? timeoutMillis : INFINITE, FALSE);
if (dw == WAIT_TIMEOUT)
{
result = false;
}
else if (dw == WAIT_FAILED)
{
result = false;
US_ERROR << "SignalObjectAndWait failed: " << GetLastErrorStr();
}
// Reacquire lock to avoid race conditions
EnterCriticalSection(&m_NumberOfWaitersLock);
// We're no longer waiting....
m_NumberOfWaiters--;
// Check to see if we're the last waiter after the broadcast
int lastWaiter = m_WasNotifyAll && m_NumberOfWaiters == 0;
LeaveCriticalSection(&m_NumberOfWaitersLock);
// If we're the last waiter thread during this particular broadcast
// then let the other threads proceed
if (lastWaiter)
{
// This call atomically signals the m_WaitersAreDone event and waits
// until it can acquire the external mutex. This is required to
// ensure fairness
dw = SignalObjectAndWait(m_WaitersAreDone, mutex.m_Mtx,
INFINITE, FALSE);
if (result && dw == WAIT_FAILED)
{
result = false;
US_ERROR << "SignalObjectAndWait failed: " << GetLastErrorStr();
}
}
else
{
- // Always regain the external mutex since that's the guarentee we
+ // Always regain the external mutex since that's the guarantee we
// give to our callers
dw = WaitForSingleObject(mutex.m_Mtx, INFINITE);
if (result && dw == WAIT_FAILED)
{
result = false;
US_ERROR << "SignalObjectAndWait failed: " << GetLastErrorStr();
}
}
return result;
#endif
}
#else
template<class MutexHost>
WaitCondition<MutexHost>::WaitCondition() {}
template<class MutexHost>
WaitCondition<MutexHost>::~WaitCondition() {}
template<class MutexHost>
bool WaitCondition<MutexHost>::Wait(unsigned long)
{
return true;
}
template<class MutexHost>
void WaitCondition<MutexHost>::Notify() {}
template<class MutexHost>
void WaitCondition<MutexHost>::NotifyAll() {}
#endif
US_END_NAMESPACE
#endif // USWAITCONDITION_P_H
diff --git a/Modules/CppMicroServices/core/test/usModuleTest.cpp b/Modules/CppMicroServices/core/test/usModuleTest.cpp
index 713d16e17f..0c944b663a 100644
--- a/Modules/CppMicroServices/core/test/usModuleTest.cpp
+++ b/Modules/CppMicroServices/core/test/usModuleTest.cpp
@@ -1,350 +1,350 @@
/*============================================================================
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
https://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 <usModule.h>
#include <usModuleEvent.h>
#include <usServiceEvent.h>
#include <usModuleContext.h>
#include <usGetModuleContext.h>
#include <usModuleRegistry.h>
#include <usModuleActivator.h>
#include <usModuleSettings.h>
#include <usSharedLibrary.h>
#include "usTestUtilModuleListener.h"
#include "usTestDriverActivator.h"
#include "usTestingMacros.h"
#include "usTestingConfig.h"
US_USE_NAMESPACE
namespace {
#ifdef US_PLATFORM_WINDOWS
static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY;
static const char PATH_SEPARATOR = '\\';
#else
static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY;
static const char PATH_SEPARATOR = '/';
#endif
// Check that the executable's activator was loaded and called
void frame01()
{
US_TEST_CONDITION_REQUIRED(TestDriverActivator::LoadCalled(), "ModuleActivator::Load() called for executable")
}
// Verify that the same member function pointers registered as listeners
// with different receivers works.
void frame02a()
{
ModuleContext* mc = GetModuleContext();
TestModuleListener listener1;
TestModuleListener listener2;
try
{
mc->RemoveModuleListener(&listener1, &TestModuleListener::ModuleChanged);
mc->AddModuleListener(&listener1, &TestModuleListener::ModuleChanged);
mc->RemoveModuleListener(&listener2, &TestModuleListener::ModuleChanged);
mc->AddModuleListener(&listener2, &TestModuleListener::ModuleChanged);
}
catch (const std::logic_error& ise)
{
US_TEST_FAILED_MSG( << "module listener registration failed " << ise.what()
<< " : frameSL02a:FAIL" );
}
SharedLibrary target(LIB_PATH, "TestModuleA");
#ifdef US_BUILD_SHARED_LIBS
// Start the test target
try
{
target.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG( << "Failed to load module, got exception: "
<< e.what() << " + in frameSL02a:FAIL" );
}
#endif
Module* moduleA = ModuleRegistry::GetModule("TestModuleA");
US_TEST_CONDITION_REQUIRED(moduleA != nullptr, "Test for existing module TestModuleA")
std::vector<ModuleEvent> pEvts;
#ifdef US_BUILD_SHARED_LIBS
pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleA));
pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleA));
#endif
std::vector<ServiceEvent> seEvts;
US_TEST_CONDITION(listener1.CheckListenerEvents(pEvts, seEvts), "Check first module listener")
US_TEST_CONDITION(listener2.CheckListenerEvents(pEvts, seEvts), "Check second module listener")
mc->RemoveModuleListener(&listener1, &TestModuleListener::ModuleChanged);
mc->RemoveModuleListener(&listener2, &TestModuleListener::ModuleChanged);
target.Unload();
}
// Verify information from the ModuleInfo struct
void frame005a(ModuleContext* mc)
{
Module* m = mc->GetModule();
// check expected headers
US_TEST_CONDITION("main" == m->GetName(), "Test module name")
US_TEST_CONDITION(ModuleVersion(0,1,0) == m->GetVersion(), "Test test driver module version")
US_TEST_CONDITION(ModuleVersion(CppMicroServices_MAJOR_VERSION, CppMicroServices_MINOR_VERSION, CppMicroServices_PATCH_VERSION) == ModuleRegistry::GetModule(1)->GetVersion(), "Test CppMicroServices version")
}
// Get context id, location and status of the module
void frame010a(ModuleContext* mc)
{
Module* m = mc->GetModule();
long int contextid = m->GetModuleId();
US_DEBUG << "CONTEXT ID:" << contextid;
std::string location = m->GetLocation();
US_DEBUG << "LOCATION:" << location;
US_TEST_CONDITION(!location.empty(), "Test for non-empty module location")
US_TEST_CONDITION(m->IsLoaded(), "Test for loaded flag")
US_TEST_CONDITION(ModuleSettings::GetStoragePath().empty(), "Test for empty base storage path")
US_TEST_CONDITION(m->GetModuleContext()->GetDataFile("").empty(), "Test for empty data path")
US_TEST_CONDITION(m->GetModuleContext()->GetDataFile("bla").empty(), "Test for empty data file path")
}
//----------------------------------------------------------------------------
//Test result of GetService(ServiceReference()). Should throw std::invalid_argument
void frame018a(ModuleContext* mc)
{
try
{
mc->GetService(ServiceReferenceU());
US_DEBUG << "Got service object, expected std::invalid_argument exception";
- US_TEST_FAILED_MSG(<< "Got service object, excpected std::invalid_argument exception")
+ US_TEST_FAILED_MSG(<< "Got service object, expected std::invalid_argument exception")
}
catch (const std::invalid_argument& )
{}
catch (...)
{
US_TEST_FAILED_MSG(<< "Got wrong exception, expected std::invalid_argument")
}
}
// Load libA and check that it exists and that the service it registers exists,
// also check that the expected events occur and that the storage paths are correct
void frame020a(ModuleContext* mc, TestModuleListener& listener,
#ifdef US_BUILD_SHARED_LIBS
SharedLibrary& libA)
{
try
{
libA.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG(<< "Load module exception: " << e.what())
}
#else
SharedLibrary& /*libA*/)
{
#endif
ModuleSettings::SetStoragePath(std::string("/tmp") + PATH_SEPARATOR);
US_TEST_CONDITION(ModuleSettings::GetStoragePath() == "/tmp", "Test for valid base storage path")
Module* moduleA = ModuleRegistry::GetModule("TestModuleA");
US_TEST_CONDITION_REQUIRED(moduleA != nullptr, "Test for existing module TestModuleA")
US_TEST_CONDITION(moduleA->GetName() == "TestModuleA", "Test module name")
std::cout << moduleA->GetModuleContext()->GetDataFile("") << std::endl;
std::stringstream ss;
ss << moduleA->GetModuleId();
const std::string baseStoragePath = std::string("/tmp") + PATH_SEPARATOR + ss.str() + "_TestModuleA" + PATH_SEPARATOR;
US_TEST_CONDITION(moduleA->GetModuleContext()->GetDataFile("") == baseStoragePath, "Test for valid data path")
US_TEST_CONDITION(moduleA->GetModuleContext()->GetDataFile("bla") == baseStoragePath + "bla", "Test for valid data file path")
// Check if libA registered the expected service
try
{
ServiceReferenceU sr1 = mc->GetServiceReference("us::TestModuleAService");
InterfaceMap o1 = mc->GetService(sr1);
US_TEST_CONDITION(!o1.empty(), "Test if service object found");
try
{
US_TEST_CONDITION(mc->UngetService(sr1), "Test if Service UnGet returns true");
}
catch (const std::logic_error& le)
{
US_TEST_FAILED_MSG(<< "UnGetService exception: " << le.what())
}
// check the listeners for events
std::vector<ModuleEvent> pEvts;
#ifdef US_BUILD_SHARED_LIBS
pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleA));
pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleA));
#endif
std::vector<ServiceEvent> seEvts;
#ifdef US_BUILD_SHARED_LIBS
seEvts.push_back(ServiceEvent(ServiceEvent::REGISTERED, sr1));
#endif
US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events");
}
catch (const ServiceException& /*se*/)
{
US_TEST_FAILED_MSG(<< "test module, expected service not found");
}
US_TEST_CONDITION(moduleA->IsLoaded() == true, "Test if loaded correctly");
}
// Unload libA and check for correct events
void frame030b(ModuleContext* mc, TestModuleListener& listener, SharedLibrary& libA)
{
Module* moduleA = ModuleRegistry::GetModule("TestModuleA");
US_TEST_CONDITION_REQUIRED(moduleA != nullptr, "Test for non-null module")
ServiceReferenceU sr1
= mc->GetServiceReference("us::TestModuleAService");
US_TEST_CONDITION(sr1, "Test for valid service reference")
try
{
libA.Unload();
#ifdef US_BUILD_SHARED_LIBS
US_TEST_CONDITION(moduleA->IsLoaded() == false, "Test for unloaded state")
#endif
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG(<< "UnLoad module exception: " << e.what())
}
std::vector<ModuleEvent> pEvts;
#ifdef US_BUILD_SHARED_LIBS
pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADING, moduleA));
pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADED, moduleA));
#endif
std::vector<ServiceEvent> seEvts;
#ifdef US_BUILD_SHARED_LIBS
seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, sr1));
#endif
US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events");
}
struct LocalListener {
void ServiceChanged(const ServiceEvent) {}
};
// Add a service listener with a broken LDAP filter to Get an exception
void frame045a(ModuleContext* mc)
{
LocalListener sListen1;
std::string brokenFilter = "A broken LDAP filter";
try
{
mc->AddServiceListener(&sListen1, &LocalListener::ServiceChanged, brokenFilter);
}
catch (const std::invalid_argument& /*ia*/)
{
//assertEquals("InvalidSyntaxException.GetFilter should be same as input string", brokenFilter, ise.GetFilter());
}
catch (...)
{
US_TEST_FAILED_MSG(<< "test module, wrong exception on broken LDAP filter:");
}
}
} // end unnamed namespace
int usModuleTest(int /*argc*/, char* /*argv*/[])
{
//US_TEST_BEGIN("ModuleTest");
std::vector<Module*> modules = ModuleRegistry::GetModules();
for (std::vector<Module*>::iterator iter = modules.begin(), iterEnd = modules.end();
iter != iterEnd; ++iter)
{
std::cout << "----- " << (*iter)->GetName() << std::endl;
}
frame01();
frame02a();
ModuleContext* mc = GetModuleContext();
TestModuleListener listener;
try
{
mc->AddModuleListener(&listener, &TestModuleListener::ModuleChanged);
}
catch (const std::logic_error& ise)
{
US_TEST_OUTPUT( << "module listener registration failed " << ise.what() );
throw;
}
try
{
mc->AddServiceListener(&listener, &TestModuleListener::ServiceChanged);
}
catch (const std::logic_error& ise)
{
US_TEST_OUTPUT( << "service listener registration failed " << ise.what() );
throw;
}
frame005a(mc);
frame010a(mc);
frame018a(mc);
SharedLibrary libA(LIB_PATH, "TestModuleA");
frame020a(mc, listener, libA);
frame030b(mc, listener, libA);
frame045a(mc);
mc->RemoveModuleListener(&listener, &TestModuleListener::ModuleChanged);
mc->RemoveServiceListener(&listener, &TestModuleListener::ServiceChanged);
//US_TEST_END()
return 0;
}
diff --git a/Modules/CppMicroServices/core/test/usServiceHooksTest.cpp b/Modules/CppMicroServices/core/test/usServiceHooksTest.cpp
index 46b4ed2a44..ccb20a1149 100644
--- a/Modules/CppMicroServices/core/test/usServiceHooksTest.cpp
+++ b/Modules/CppMicroServices/core/test/usServiceHooksTest.cpp
@@ -1,414 +1,414 @@
/*============================================================================
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
https://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 <usModule.h>
#include <usModuleEvent.h>
#include <usModuleContext.h>
#include <usGetModuleContext.h>
#include <usLDAPProp.h>
#include <usServiceFindHook.h>
#include <usServiceEventListenerHook.h>
#include <usServiceListenerHook.h>
#include <usSharedLibrary.h>
#include "usTestingMacros.h"
#include "usTestingConfig.h"
US_USE_NAMESPACE
namespace {
#ifdef US_PLATFORM_WINDOWS
static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY;
#else
static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY;
#endif
class TestServiceListener
{
public:
void ServiceChanged(const ServiceEvent serviceEvent)
{
this->events.push_back(serviceEvent);
}
std::vector<ServiceEvent> events;
};
class TestServiceEventListenerHook : public ServiceEventListenerHook
{
private:
int id;
public:
TestServiceEventListenerHook(int id)
: id(id)
{
}
typedef ShrinkableMap<ModuleContext*, ShrinkableVector<ServiceListenerHook::ListenerInfo> > MapType;
void Event(const ServiceEvent& /*event*/, MapType& listeners) override
{
US_TEST_CONDITION_REQUIRED(listeners.size() > 0 && listeners.find(GetModuleContext()) != listeners.end(), "Check listener content");
ShrinkableVector<ServiceListenerHook::ListenerInfo>& listenerInfos = listeners[GetModuleContext()];
// listener count should be 2 because the event listener hooks are called with
- // the list of listeners before filtering them according to ther LDAP filter
+ // the list of listeners before filtering them according to their LDAP filter
if (id == 1)
{
#ifdef US_BUILD_SHARED_LIBS
US_TEST_CONDITION(listenerInfos.size() == 2, "2 service listeners expected");
#else
US_TEST_CONDITION(listenerInfos.size() >= 2, "2 service listeners expected");
#endif
US_TEST_CONDITION(listenerInfos[0].IsRemoved() == false, "Listener is not removed");
US_TEST_CONDITION(listenerInfos[1].IsRemoved() == false, "Listener is not removed");
US_TEST_CONDITION(!(listenerInfos[0] == listenerInfos[1]), "listener info inequality");
}
else
{
// there is already one listener filtered out
#ifdef US_BUILD_SHARED_LIBS
US_TEST_CONDITION(listenerInfos.size() == 1, "1 service listener expected");
#else
US_TEST_CONDITION(listenerInfos.size() >= 1, "1 service listener expected");
#endif
US_TEST_CONDITION(listenerInfos[0].IsRemoved() == false, "Listener is not removed");
}
if (listenerInfo.IsNull())
{
listenerInfo = listenerInfos[0];
}
else
{
US_TEST_CONDITION(listenerInfo == listenerInfos[0], "Equal listener info objects");
}
// Remove the listener without a filter from the list
for(ShrinkableVector<ServiceListenerHook::ListenerInfo>::iterator infoIter = listenerInfos.begin();
infoIter != listenerInfos.end();)
{
if (infoIter->GetFilter().empty())
{
infoIter = listenerInfos.erase(infoIter);
}
else
{
++infoIter;
}
}
#ifdef US_BUILD_SHARED_LIBS
US_TEST_CONDITION(listenerInfos.size() == 1, "One listener with LDAP filter should remain");
#else
US_TEST_CONDITION(listenerInfos.size() >= 1, "One listener with LDAP filter should remain");
#endif
ordering.push_back(id);
}
ServiceListenerHook::ListenerInfo listenerInfo;
static std::vector<int> ordering;
};
std::vector<int> TestServiceEventListenerHook::ordering;
class TestServiceFindHook : public ServiceFindHook
{
private:
int id;
public:
TestServiceFindHook(int id)
: id(id)
{
}
void Find(const ModuleContext* context, const std::string& /*name*/,
const std::string& /*filter*/, ShrinkableVector<ServiceReferenceBase>& references) override
{
US_TEST_CONDITION(context == GetModuleContext(), "Module context");
references.clear();
ordering.push_back(id);
}
static std::vector<int> ordering;
};
std::vector<int> TestServiceFindHook::ordering;
class TestServiceListenerHook : public ServiceListenerHook
{
private:
int id;
public:
TestServiceListenerHook(int id)
: id(id)
{
}
void Added(const std::vector<ListenerInfo>& listeners) override
{
for (std::vector<ListenerInfo>::const_iterator iter = listeners.begin();
iter != listeners.end(); ++iter)
{
if (iter->IsRemoved() || iter->GetModuleContext() != GetModuleContext()) continue;
listenerInfos.insert(*iter);
lastAdded = listeners.back();
ordering.push_back(id);
}
}
void Removed(const std::vector<ListenerInfo>& listeners) override
{
for (std::vector<ListenerInfo>::const_iterator iter = listeners.begin();
iter != listeners.end(); ++iter)
{
listenerInfos.erase(*iter);
ordering.push_back(id*10);
}
lastRemoved = listeners.back();
}
static std::vector<int> ordering;
US_UNORDERED_SET_TYPE<ListenerInfo> listenerInfos;
ListenerInfo lastAdded;
ListenerInfo lastRemoved;
};
std::vector<int> TestServiceListenerHook::ordering;
void TestEventListenerHook()
{
ModuleContext* context = GetModuleContext();
TestServiceListener serviceListener1;
TestServiceListener serviceListener2;
context->AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged);
context->AddServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged, LDAPProp(ServiceConstants::OBJECTCLASS()) == "bla");
TestServiceEventListenerHook serviceEventListenerHook1(1);
ServiceProperties hookProps1;
hookProps1[ServiceConstants::SERVICE_RANKING()] = 10;
ServiceRegistration<ServiceEventListenerHook> eventListenerHookReg1 =
context->RegisterService<ServiceEventListenerHook>(&serviceEventListenerHook1, hookProps1);
TestServiceEventListenerHook serviceEventListenerHook2(2);
ServiceProperties hookProps2;
hookProps2[ServiceConstants::SERVICE_RANKING()] = 0;
ServiceRegistration<ServiceEventListenerHook> eventListenerHookReg2 =
context->RegisterService<ServiceEventListenerHook>(&serviceEventListenerHook2, hookProps2);
std::vector<int> expectedOrdering;
expectedOrdering.push_back(1);
expectedOrdering.push_back(1);
expectedOrdering.push_back(2);
US_TEST_CONDITION(serviceEventListenerHook1.ordering == expectedOrdering, "Event listener hook call order");
US_TEST_CONDITION(serviceListener1.events.empty(), "service event of service event listener hook");
US_TEST_CONDITION(serviceListener2.events.empty(), "no service event for filtered listener");
#ifdef US_BUILD_SHARED_LIBS
SharedLibrary libA(LIB_PATH, "TestModuleA");
try
{
libA.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG(<< "Load module exception: " << e.what())
}
expectedOrdering.push_back(1);
expectedOrdering.push_back(2);
US_TEST_CONDITION(serviceEventListenerHook1.ordering == expectedOrdering, "Event listener hook call order");
libA.Unload();
#endif
US_TEST_CONDITION(serviceListener1.events.empty(), "no service event due to service event listener hook");
US_TEST_CONDITION(serviceListener2.events.empty(), "no service event for filtered listener due to service event listener hook");
eventListenerHookReg2.Unregister();
eventListenerHookReg1.Unregister();
context->RemoveServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged);
context->RemoveServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged);
}
void TestListenerHook()
{
ModuleContext* context = GetModuleContext();
TestServiceListener serviceListener1;
TestServiceListener serviceListener2;
context->AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged);
context->AddServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged, LDAPProp(ServiceConstants::OBJECTCLASS()) == "bla");
TestServiceListenerHook serviceListenerHook1(1);
ServiceProperties hookProps1;
hookProps1[ServiceConstants::SERVICE_RANKING()] = 0;
ServiceRegistration<ServiceListenerHook> listenerHookReg1 =
context->RegisterService<ServiceListenerHook>(&serviceListenerHook1, hookProps1);
TestServiceListenerHook serviceListenerHook2(2);
ServiceProperties hookProps2;
hookProps2[ServiceConstants::SERVICE_RANKING()] = 10;
ServiceRegistration<ServiceListenerHook> listenerHookReg2 =
context->RegisterService<ServiceListenerHook>(&serviceListenerHook2, hookProps2);
#ifdef US_BUILD_SHARED_LIBS
// check if hooks got notified about the existing listeners
US_TEST_CONDITION_REQUIRED(serviceListenerHook1.listenerInfos.size() == 2, "Notification about existing listeners")
#endif
const std::size_t listenerInfoSizeOld = serviceListenerHook1.listenerInfos.size() - 2;
context->AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged);
ServiceListenerHook::ListenerInfo lastAdded = serviceListenerHook1.lastAdded;
#ifdef US_BUILD_SHARED_LIBS
std::vector<int> expectedOrdering;
expectedOrdering.push_back(1);
expectedOrdering.push_back(1);
expectedOrdering.push_back(2);
expectedOrdering.push_back(2);
expectedOrdering.push_back(20);
expectedOrdering.push_back(10);
expectedOrdering.push_back(2);
expectedOrdering.push_back(1);
US_TEST_CONDITION(serviceListenerHook1.ordering == expectedOrdering, "Listener hook call order");
#endif
context->AddServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged, LDAPProp(ServiceConstants::OBJECTCLASS()) == "blub");
US_TEST_CONDITION(lastAdded == serviceListenerHook1.lastRemoved, "Same ListenerInfo object)");
US_TEST_CONDITION(!(lastAdded == serviceListenerHook1.lastAdded), "New ListenerInfo object)");
#ifdef US_BUILD_SHARED_LIBS
expectedOrdering.push_back(20);
expectedOrdering.push_back(10);
expectedOrdering.push_back(2);
expectedOrdering.push_back(1);
US_TEST_CONDITION(serviceListenerHook1.ordering == expectedOrdering, "Listener hook call order");
#endif
context->RemoveServiceListener(&serviceListener1, &TestServiceListener::ServiceChanged);
context->RemoveServiceListener(&serviceListener2, &TestServiceListener::ServiceChanged);
#ifdef US_BUILD_SHARED_LIBS
expectedOrdering.push_back(20);
expectedOrdering.push_back(10);
expectedOrdering.push_back(20);
expectedOrdering.push_back(10);
US_TEST_CONDITION(serviceListenerHook1.ordering == expectedOrdering, "Listener hook call order");
#endif
US_TEST_CONDITION_REQUIRED(serviceListenerHook1.listenerInfos.size() == listenerInfoSizeOld, "Removed listener infos")
listenerHookReg2.Unregister();
listenerHookReg1.Unregister();
}
void TestFindHook()
{
ModuleContext* context = GetModuleContext();
TestServiceFindHook serviceFindHook1(1);
ServiceProperties hookProps1;
hookProps1[ServiceConstants::SERVICE_RANKING()] = 0;
ServiceRegistration<ServiceFindHook> findHookReg1 =
context->RegisterService<ServiceFindHook>(&serviceFindHook1, hookProps1);
TestServiceFindHook serviceFindHook2(2);
ServiceProperties hookProps2;
hookProps2[ServiceConstants::SERVICE_RANKING()] = 10;
ServiceRegistration<ServiceFindHook> findHookReg2 =
context->RegisterService<ServiceFindHook>(&serviceFindHook2, hookProps2);
std::vector<int> expectedOrdering;
US_TEST_CONDITION(serviceFindHook1.ordering == expectedOrdering, "Find hook call order");
TestServiceListener serviceListener;
context->AddServiceListener(&serviceListener, &TestServiceListener::ServiceChanged);
#ifdef US_BUILD_SHARED_LIBS
SharedLibrary libA(LIB_PATH, "TestModuleA");
try
{
libA.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG(<< "Load module exception: " << e.what())
}
US_TEST_CONDITION(serviceListener.events.size() == 1, "Service registered");
#endif
std::vector<ServiceReferenceU> refs = context->GetServiceReferences("us::TestModuleAService");
US_TEST_CONDITION(refs.empty(), "Empty references");
ServiceReferenceU ref = context->GetServiceReference("us::TestModuleAService");
US_TEST_CONDITION(!ref, "Invalid reference (filtered out)");
expectedOrdering.push_back(2);
expectedOrdering.push_back(1);
expectedOrdering.push_back(2);
expectedOrdering.push_back(1);
US_TEST_CONDITION(serviceFindHook1.ordering == expectedOrdering, "Find hook call order");
findHookReg2.Unregister();
findHookReg1.Unregister();
refs = context->GetServiceReferences("us::TestModuleAService");
US_TEST_CONDITION(!refs.empty(), "Non-empty references");
ref = context->GetServiceReference("us::TestModuleAService");
US_TEST_CONDITION(ref, "Valid reference");
#ifdef US_BUILD_SHARED_LIBS
libA.Unload();
#endif
context->RemoveServiceListener(&serviceListener, &TestServiceListener::ServiceChanged);
}
} // end unnamed namespace
int usServiceHooksTest(int /*argc*/, char* /*argv*/[])
{
US_TEST_BEGIN("ServiceHooksTest");
TestListenerHook();
TestFindHook();
TestEventListenerHook();
US_TEST_END()
}
diff --git a/Modules/CppMicroServices/core/test/usServiceListenerTest.cpp b/Modules/CppMicroServices/core/test/usServiceListenerTest.cpp
index cd80f8e2bb..2b1dc91d4c 100644
--- a/Modules/CppMicroServices/core/test/usServiceListenerTest.cpp
+++ b/Modules/CppMicroServices/core/test/usServiceListenerTest.cpp
@@ -1,565 +1,565 @@
/*============================================================================
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
https://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 <usTestingMacros.h>
#include <usTestingConfig.h>
#include <usModule.h>
#include <usModuleContext.h>
#include <usGetModuleContext.h>
#include <usSharedLibrary.h>
#include <usModulePropsInterface.h>
US_USE_NAMESPACE
#ifdef US_PLATFORM_WINDOWS
static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY;
#else
static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY;
#endif
class TestServiceListener
{
private:
friend bool runLoadUnloadTest(const std::string&, int cnt, SharedLibrary&,
const std::vector<ServiceEvent::Type>&);
const bool checkUsingModules;
std::vector<ServiceEvent> events;
bool teststatus;
ModuleContext* mc;
public:
TestServiceListener(ModuleContext* mc, bool checkUsingModules = true)
: checkUsingModules(checkUsingModules), events(), teststatus(true), mc(mc)
{}
bool getTestStatus() const
{
return teststatus;
}
void clearEvents()
{
events.clear();
}
bool checkEvents(const std::vector<ServiceEvent::Type>& eventTypes)
{
if (events.size() != eventTypes.size())
{
dumpEvents(eventTypes);
return false;
}
for (std::size_t i=0; i < eventTypes.size(); ++i)
{
if (eventTypes[i] != events[i].GetType())
{
dumpEvents(eventTypes);
return false;
}
}
return true;
}
void serviceChanged(const ServiceEvent evt)
{
events.push_back(evt);
US_TEST_OUTPUT( << "ServiceEvent: " << evt );
if (ServiceEvent::UNREGISTERING == evt.GetType())
{
ServiceReferenceU sr = evt.GetServiceReference();
// Validate that no module is marked as using the service
std::vector<Module*> usingModules;
sr.GetUsingModules(usingModules);
if (checkUsingModules && !usingModules.empty())
{
teststatus = false;
printUsingModules(sr, "*** Using modules (unreg) should be empty but is: ");
}
// Check if the service can be fetched
InterfaceMap service = mc->GetService(sr);
sr.GetUsingModules(usingModules);
// if (UNREGISTERSERVICE_VALID_DURING_UNREGISTERING) {
// In this mode the service shall be obtainable during
// unregistration.
if (service.empty())
{
teststatus = false;
US_TEST_OUTPUT( << "*** Service should be available to ServiceListener "
<< "while handling unregistering event." );
}
US_TEST_OUTPUT( << "Service (unreg): " << service.begin()->first << " -> " << service.begin()->second );
if (checkUsingModules && usingModules.size() != 1)
{
teststatus = false;
printUsingModules(sr, "*** One using module expected "
"(unreg, after getService), found: ");
}
else
{
printUsingModules(sr, "Using modules (unreg, after getService): ");
}
// } else {
// // In this mode the service shall NOT be obtainable during
// // unregistration.
// if (null!=service) {
// teststatus = false;
// out.print("*** Service should not be available to ServiceListener "
// +"while handling unregistering event.");
// }
// if (checkUsingBundles && null!=usingBundles) {
// teststatus = false;
// printUsingBundles(sr,
// "*** Using bundles (unreg, after getService), "
// +"should be null but is: ");
// } else {
// printUsingBundles(sr,
// "Using bundles (unreg, after getService): null");
// }
// }
mc->UngetService(sr);
// Check that the UNREGISTERING service can not be looked up
// using the service registry.
try
{
long sid = any_cast<long>(sr.GetProperty(ServiceConstants::SERVICE_ID()));
std::stringstream ss;
ss << "(" << ServiceConstants::SERVICE_ID() << "=" << sid << ")";
std::vector<ServiceReferenceU> srs = mc->GetServiceReferences("", ss.str());
if (srs.empty())
{
US_TEST_OUTPUT( << "ServiceReference for UNREGISTERING service is not"
" found in the service registry; ok." );
}
else
{
teststatus = false;
US_TEST_OUTPUT( << "*** ServiceReference for UNREGISTERING service,"
<< sr << ", not found in the service registry; fail." );
US_TEST_OUTPUT( << "Found the following Service references:") ;
for(std::vector<ServiceReferenceU>::const_iterator sr = srs.begin();
sr != srs.end(); ++sr)
{
US_TEST_OUTPUT( << (*sr) );
}
}
}
catch (const std::exception& e)
{
teststatus = false;
- US_TEST_OUTPUT( << "*** Unexpected excpetion when trying to lookup a"
+ US_TEST_OUTPUT( << "*** Unexpected exception when trying to lookup a"
" service while it is in state UNREGISTERING: "
<< e.what() );
}
}
}
void printUsingModules(const ServiceReferenceU& sr, const std::string& caption)
{
std::vector<Module*> usingModules;
sr.GetUsingModules(usingModules);
US_TEST_OUTPUT( << (caption.empty() ? "Using modules: " : caption) );
for(std::vector<Module*>::const_iterator module = usingModules.begin();
module != usingModules.end(); ++module)
{
US_TEST_OUTPUT( << " -" << (*module) );
}
}
// Print expected and actual service events.
void dumpEvents(const std::vector<ServiceEvent::Type>& eventTypes)
{
std::size_t max = events.size() > eventTypes.size() ? events.size() : eventTypes.size();
US_TEST_OUTPUT( << "Expected event type -- Actual event" );
for (std::size_t i=0; i < max; ++i)
{
ServiceEvent evt = i < events.size() ? events[i] : ServiceEvent();
if (i < eventTypes.size())
{
US_TEST_OUTPUT( << " " << eventTypes[i] << "--" << evt );
}
else
{
US_TEST_OUTPUT( << " " << "- NONE - " << "--" << evt );
}
}
}
}; // end of class ServiceListener
bool runLoadUnloadTest(const std::string& name, int cnt, SharedLibrary& target,
const std::vector<ServiceEvent::Type>& events)
{
bool teststatus = true;
ModuleContext* mc = GetModuleContext();
for (int i = 0; i < cnt && teststatus; ++i)
{
TestServiceListener sListen(mc);
try
{
mc->AddServiceListener(&sListen, &TestServiceListener::serviceChanged);
//mc->AddServiceListener(MessageDelegate1<ServiceListener, const ServiceEvent&>(&sListen, &ServiceListener::serviceChanged));
}
catch (const std::logic_error& ise)
{
teststatus = false;
US_TEST_FAILED_MSG( << "service listener registration failed " << ise.what()
<< " :" << name << ":FAIL" );
}
// Start the test target to get a service published.
try
{
target.Load();
}
catch (const std::exception& e)
{
teststatus = false;
US_TEST_FAILED_MSG( << "Failed to load module, got exception: "
<< e.what() << " + in " << name << ":FAIL" );
}
// Stop the test target to get a service unpublished.
try
{
target.Unload();
}
catch (const std::exception& e)
{
teststatus = false;
US_TEST_FAILED_MSG( << "Failed to unload module, got exception: "
<< e.what() << " + in " << name << ":FAIL" );
}
if (teststatus && !sListen.checkEvents(events))
{
teststatus = false;
US_TEST_FAILED_MSG( << "Service listener event notification error :"
<< name << ":FAIL" );
}
try
{
mc->RemoveServiceListener(&sListen, &TestServiceListener::serviceChanged);
teststatus &= sListen.teststatus;
sListen.clearEvents();
}
catch (const std::logic_error& ise)
{
teststatus = false;
US_TEST_FAILED_MSG( << "service listener removal failed " << ise.what()
<< " :" << name << ":FAIL" );
}
}
return teststatus;
}
void frameSL02a()
{
ModuleContext* mc = GetModuleContext();
TestServiceListener listener1(mc);
TestServiceListener listener2(mc);
try
{
mc->RemoveServiceListener(&listener1, &TestServiceListener::serviceChanged);
mc->AddServiceListener(&listener1, &TestServiceListener::serviceChanged);
mc->RemoveServiceListener(&listener2, &TestServiceListener::serviceChanged);
mc->AddServiceListener(&listener2, &TestServiceListener::serviceChanged);
}
catch (const std::logic_error& ise)
{
US_TEST_FAILED_MSG( << "service listener registration failed " << ise.what()
<< " : frameSL02a:FAIL" );
}
SharedLibrary target(LIB_PATH, "TestModuleA");
// Start the test target to get a service published.
try
{
target.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG( << "Failed to load module, got exception: "
<< e.what() << " + in frameSL02a:FAIL" );
}
std::vector<ServiceEvent::Type> events;
events.push_back(ServiceEvent::REGISTERED);
US_TEST_CONDITION(listener1.checkEvents(events), "Check first service listener")
US_TEST_CONDITION(listener2.checkEvents(events), "Check second service listener")
mc->RemoveServiceListener(&listener1, &TestServiceListener::serviceChanged);
mc->RemoveServiceListener(&listener2, &TestServiceListener::serviceChanged);
target.Unload();
}
void frameSL05a()
{
std::vector<ServiceEvent::Type> events;
events.push_back(ServiceEvent::REGISTERED);
events.push_back(ServiceEvent::UNREGISTERING);
SharedLibrary libA(LIB_PATH, "TestModuleA");
bool testStatus = runLoadUnloadTest("FrameSL05a", 1, libA, events);
US_TEST_CONDITION(testStatus, "FrameSL05a")
}
void frameSL10a()
{
std::vector<ServiceEvent::Type> events;
events.push_back(ServiceEvent::REGISTERED);
events.push_back(ServiceEvent::UNREGISTERING);
SharedLibrary libA2(LIB_PATH, "TestModuleA2");
bool testStatus = runLoadUnloadTest("FrameSL10a", 1, libA2, events);
US_TEST_CONDITION(testStatus, "FrameSL10a")
}
void frameSL25a()
{
ModuleContext* mc = GetModuleContext();
TestServiceListener sListen(mc, false);
try
{
mc->AddServiceListener(&sListen, &TestServiceListener::serviceChanged);
}
catch (const std::logic_error& ise)
{
US_TEST_OUTPUT( << "service listener registration failed " << ise.what() );
throw;
}
SharedLibrary libSL1(LIB_PATH, "TestModuleSL1");
SharedLibrary libSL3(LIB_PATH, "TestModuleSL3");
SharedLibrary libSL4(LIB_PATH, "TestModuleSL4");
std::vector<ServiceEvent::Type> expectedServiceEventTypes;
// Startup
expectedServiceEventTypes.push_back(ServiceEvent::REGISTERED); // at start of libSL1
expectedServiceEventTypes.push_back(ServiceEvent::REGISTERED); // FooService at start of libSL4
expectedServiceEventTypes.push_back(ServiceEvent::REGISTERED); // at start of libSL3
// Stop libSL4
expectedServiceEventTypes.push_back(ServiceEvent::UNREGISTERING); // FooService at first stop of libSL4
#ifdef US_BUILD_SHARED_LIBS
// Shutdown
expectedServiceEventTypes.push_back(ServiceEvent::UNREGISTERING); // at stop of libSL1
expectedServiceEventTypes.push_back(ServiceEvent::UNREGISTERING); // at stop of libSL3
#endif
// Start libModuleTestSL1 to ensure that the Service interface is available.
try
{
US_TEST_OUTPUT( << "Starting libModuleTestSL1: " << libSL1.GetFilePath() );
libSL1.Load();
}
catch (const std::exception& e)
{
US_TEST_OUTPUT( << "Failed to load module, got exception: " << e.what() );
throw;
}
- // Start libModuleTestSL4 that will require the serivce interface and publish
+ // Start libModuleTestSL4 that will require the service interface and publish
// us::FooService
try
{
US_TEST_OUTPUT( << "Starting libModuleTestSL4: " << libSL4.GetFilePath() );
libSL4.Load();
}
catch (const std::exception& e)
{
US_TEST_OUTPUT( << "Failed to load module, got exception: " << e.what() );
throw;
}
- // Start libModuleTestSL3 that will require the serivce interface and get the service
+ // Start libModuleTestSL3 that will require the service interface and get the service
try
{
US_TEST_OUTPUT( << "Starting libModuleTestSL3: " << libSL3.GetFilePath() );
libSL3.Load();
}
catch (const std::exception& e)
{
US_TEST_OUTPUT( << "Failed to load module, got exception: " << e.what() );
throw;
}
// Check that libSL3 has been notified about the FooService.
US_TEST_OUTPUT( << "Check that FooService is added to service tracker in libSL3" );
try
{
ServiceReferenceU libSL3SR = mc->GetServiceReference("ActivatorSL3");
InterfaceMap libSL3Activator = mc->GetService(libSL3SR);
US_TEST_CONDITION_REQUIRED(!libSL3Activator.empty(), "ActivatorSL3 service != 0");
ServiceReference<ModulePropsInterface> libSL3PropsI(libSL3SR);
ModulePropsInterface* propsInterface = mc->GetService(libSL3PropsI);
US_TEST_CONDITION_REQUIRED(propsInterface, "ModulePropsInterface != 0");
ModulePropsInterface::Properties::const_iterator i = propsInterface->GetProperties().find("serviceAdded");
US_TEST_CONDITION_REQUIRED(i != propsInterface->GetProperties().end(), "Property serviceAdded");
Any serviceAddedField3 = i->second;
US_TEST_CONDITION_REQUIRED(!serviceAddedField3.Empty() && any_cast<bool>(serviceAddedField3),
"libSL3 notified about presence of FooService");
mc->UngetService(libSL3SR);
}
catch (const ServiceException& se)
{
US_TEST_FAILED_MSG( << "Failed to get service reference:" << se );
}
// Check that libSL1 has been notified about the FooService.
US_TEST_OUTPUT( << "Check that FooService is added to service tracker in libSL1" );
try
{
ServiceReferenceU libSL1SR = mc->GetServiceReference("ActivatorSL1");
InterfaceMap libSL1Activator = mc->GetService(libSL1SR);
US_TEST_CONDITION_REQUIRED(!libSL1Activator.empty(), "ActivatorSL1 service != 0");
ServiceReference<ModulePropsInterface> libSL1PropsI(libSL1SR);
ModulePropsInterface* propsInterface = mc->GetService(libSL1PropsI);
US_TEST_CONDITION_REQUIRED(propsInterface, "Cast to ModulePropsInterface");
ModulePropsInterface::Properties::const_iterator i = propsInterface->GetProperties().find("serviceAdded");
US_TEST_CONDITION_REQUIRED(i != propsInterface->GetProperties().end(), "Property serviceAdded");
Any serviceAddedField1 = i->second;
US_TEST_CONDITION_REQUIRED(!serviceAddedField1.Empty() && any_cast<bool>(serviceAddedField1),
"libSL1 notified about presence of FooService");
mc->UngetService(libSL1SR);
}
catch (const ServiceException& se)
{
US_TEST_FAILED_MSG( << "Failed to get service reference:" << se );
}
// Stop the service provider: libSL4
try
{
US_TEST_OUTPUT( << "Stop libSL4: " << libSL4.GetFilePath() );
libSL4.Unload();
}
catch (const std::exception& e)
{
US_TEST_OUTPUT( << "Failed to unload module, got exception:" << e.what() );
throw;
}
// Check that libSL3 has been notified about the removal of FooService.
US_TEST_OUTPUT( << "Check that FooService is removed from service tracker in libSL3" );
try
{
ServiceReferenceU libSL3SR = mc->GetServiceReference("ActivatorSL3");
InterfaceMap libSL3Activator = mc->GetService(libSL3SR);
US_TEST_CONDITION_REQUIRED(!libSL3Activator.empty(), "ActivatorSL3 service != 0");
ServiceReference<ModulePropsInterface> libSL3PropsI(libSL3SR);
ModulePropsInterface* propsInterface = mc->GetService(libSL3PropsI);
US_TEST_CONDITION_REQUIRED(propsInterface, "Cast to ModulePropsInterface");
ModulePropsInterface::Properties::const_iterator i = propsInterface->GetProperties().find("serviceRemoved");
US_TEST_CONDITION_REQUIRED(i != propsInterface->GetProperties().end(), "Property serviceRemoved");
Any serviceRemovedField3 = i->second;
US_TEST_CONDITION(!serviceRemovedField3.Empty() && any_cast<bool>(serviceRemovedField3),
"libSL3 notified about removal of FooService");
mc->UngetService(libSL3SR);
}
catch (const ServiceException& se)
{
US_TEST_FAILED_MSG( << "Failed to get service reference:" << se );
}
// Stop libSL1
try
{
US_TEST_OUTPUT( << "Stop libSL1:" << libSL1.GetFilePath() );
libSL1.Unload();
}
catch (const std::exception& e)
{
US_TEST_OUTPUT( << "Failed to unload module got exception" << e.what() );
throw;
}
// Stop pSL3
try
{
US_TEST_OUTPUT( << "Stop libSL3:" << libSL3.GetFilePath() );
libSL3.Unload();
}
catch (const std::exception& e)
{
US_TEST_OUTPUT( << "Failed to unload module got exception" << e.what() );
throw;
}
// Check service events seen by this class
US_TEST_OUTPUT( << "Checking ServiceEvents(ServiceListener):" );
if (!sListen.checkEvents(expectedServiceEventTypes))
{
US_TEST_FAILED_MSG( << "Service listener event notification error" );
}
US_TEST_CONDITION_REQUIRED(sListen.getTestStatus(), "Service listener checks");
try
{
mc->RemoveServiceListener(&sListen, &TestServiceListener::serviceChanged);
sListen.clearEvents();
}
catch (const std::logic_error& ise)
{
US_TEST_FAILED_MSG( << "service listener removal failed: " << ise.what() );
}
}
int usServiceListenerTest(int /*argc*/, char* /*argv*/[])
{
US_TEST_BEGIN("ServiceListenerTest");
frameSL02a();
frameSL05a();
frameSL10a();
frameSL25a();
US_TEST_END()
}
diff --git a/Modules/CppMicroServices/core/test/usServiceTrackerTest.cpp b/Modules/CppMicroServices/core/test/usServiceTrackerTest.cpp
index a6cb2f70ef..68f8ae6cc6 100644
--- a/Modules/CppMicroServices/core/test/usServiceTrackerTest.cpp
+++ b/Modules/CppMicroServices/core/test/usServiceTrackerTest.cpp
@@ -1,287 +1,287 @@
/*============================================================================
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
https://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 <usTestingMacros.h>
#include <usTestingConfig.h>
#include <usModule.h>
#include <usModuleContext.h>
#include <usGetModuleContext.h>
#include <usServiceInterface.h>
#include <usServiceTracker.h>
#include <usSharedLibrary.h>
#include "usServiceControlInterface.h"
#include <memory>
US_USE_NAMESPACE
bool CheckConvertibility(const std::vector<ServiceReferenceU>& refs,
std::vector<std::string>::const_iterator idBegin,
std::vector<std::string>::const_iterator idEnd)
{
std::vector<std::string> ids;
ids.assign(idBegin, idEnd);
for (std::vector<ServiceReferenceU>::const_iterator sri = refs.begin();
sri != refs.end(); ++sri)
{
for (std::vector<std::string>::iterator idIter = ids.begin();
idIter != ids.end(); ++idIter)
{
if (sri->IsConvertibleTo(*idIter))
{
ids.erase(idIter);
break;
}
}
}
return ids.empty();
}
struct MyInterfaceOne {
virtual ~MyInterfaceOne() {}
};
struct MyInterfaceTwo {
virtual ~MyInterfaceTwo() {}
};
class MyCustomizer : public us::ServiceTrackerCustomizer<MyInterfaceOne>
{
public:
MyCustomizer(us::ModuleContext* context)
: m_context(context)
{}
MyInterfaceOne* AddingService(const ServiceReferenceType& reference) override
{
US_TEST_CONDITION_REQUIRED(reference, "AddingService() valid reference")
return m_context->GetService(reference);
}
void ModifiedService(const ServiceReferenceType& reference, MyInterfaceOne* service) override
{
US_TEST_CONDITION(reference, "ModifiedService() valid reference")
US_TEST_CONDITION(service, "ModifiedService() valid service")
}
void RemovedService(const ServiceReferenceType& reference, MyInterfaceOne* service) override
{
US_TEST_CONDITION(reference, "RemovedService() valid reference")
US_TEST_CONDITION(service, "RemovedService() valid service")
}
private:
us::ModuleContext* m_context;
};
void TestFilterString()
{
us::ModuleContext* context = us::GetModuleContext();
MyCustomizer customizer(context);
us::LDAPFilter filter("(" + us::ServiceConstants::SERVICE_ID() + ">=0)");
us::ServiceTracker<MyInterfaceOne> tracker(context, filter, &customizer);
tracker.Open();
struct MyServiceOne : public MyInterfaceOne {};
struct MyServiceTwo : public MyInterfaceTwo {};
MyServiceOne serviceOne;
MyServiceTwo serviceTwo;
ServiceRegistration<MyInterfaceOne> reg1 = context->RegisterService<MyInterfaceOne>(&serviceOne);
ServiceRegistration<MyInterfaceTwo> reg2 = context->RegisterService<MyInterfaceTwo>(&serviceTwo);
US_TEST_CONDITION(tracker.GetServiceReferences().size() == 1, "tracking count")
reg1.Unregister();
reg2.Unregister();
}
void TestServiceTracker()
{
#ifdef US_PLATFORM_WINDOWS
const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY;
#else
const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY;
#endif
ModuleContext* mc = GetModuleContext();
SharedLibrary libS(LIB_PATH, "TestModuleS");
#ifdef US_BUILD_SHARED_LIBS
// Start the test target to get a service published.
try
{
libS.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG( << "Failed to load module, got exception: " << e.what() );
}
#endif
// 1. Create a ServiceTracker with ServiceTrackerCustomizer == null
std::string s1("us::TestModuleSService");
ServiceReferenceU servref = mc->GetServiceReference(s1 + "0");
US_TEST_CONDITION_REQUIRED(servref != nullptr, "Test if registered service of id us::TestModuleSService0");
ServiceReference<ServiceControlInterface> servCtrlRef = mc->GetServiceReference<ServiceControlInterface>();
- US_TEST_CONDITION_REQUIRED(servCtrlRef != nullptr, "Test if constrol service was registered");
+ US_TEST_CONDITION_REQUIRED(servCtrlRef != nullptr, "Test if control service was registered");
ServiceControlInterface* serviceController = mc->GetService(servCtrlRef);
US_TEST_CONDITION_REQUIRED(serviceController != nullptr, "Test valid service controller");
std::unique_ptr<ServiceTracker<void>> st1(new ServiceTracker<void>(mc, servref));
// 2. Check the size method with an unopened service tracker
US_TEST_CONDITION_REQUIRED(st1->Size() == 0, "Test if size == 0");
// 3. Open the service tracker and see what it finds,
// expect to find one instance of the implementation,
// "org.cppmicroservices.TestModuleSService0"
st1->Open();
std::vector<ServiceReferenceU> sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.size() == 1, "Checking ServiceTracker size");
US_TEST_CONDITION_REQUIRED(s1 + "0" == sa2[0].GetInterfaceId(), "Checking service implementation name");
// 5. Close this service tracker
st1->Close();
// 6. Check the size method, now when the servicetracker is closed
US_TEST_CONDITION_REQUIRED(st1->Size() == 0, "Checking ServiceTracker size");
// 7. Check if we still track anything , we should get null
sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.empty(), "Checking ServiceTracker size");
// 8. A new Servicetracker, this time with a filter for the object
std::string fs = std::string("(") + ServiceConstants::OBJECTCLASS() + "=" + s1 + "*" + ")";
LDAPFilter f1(fs);
st1.reset(new ServiceTracker<void>(mc, f1));
// add a service
serviceController->ServiceControl(1, "register", 7);
// 9. Open the service tracker and see what it finds,
// expect to find two instances of references to
// "org.cppmicroservices.TestModuleSService*"
// i.e. they refer to the same piece of code
std::vector<std::string> ids;
ids.push_back((s1 + "0"));
ids.push_back((s1 + "1"));
ids.push_back((s1 + "2"));
ids.push_back((s1 + "3"));
st1->Open();
sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.size() == 2, "Checking service reference count");
US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa2, ids.begin(), ids.begin()+2), "Check for expected interface id [0]");
US_TEST_CONDITION_REQUIRED(sa2[1].IsConvertibleTo(s1 + "1"), "Check for expected interface id [1]");
// 10. Get libTestModuleS to register one more service and see if it appears
serviceController->ServiceControl(2, "register", 1);
sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.size() == 3, "Checking service reference count");
US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa2, ids.begin(), ids.begin()+3), "Check for expected interface id [2]");
// 11. Get libTestModuleS to register one more service and see if it appears
serviceController->ServiceControl(3, "register", 2);
sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.size() == 4, "Checking service reference count");
US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa2, ids.begin(), ids.end()), "Check for expected interface id [3]");
// 12. Get libTestModuleS to unregister one service and see if it disappears
serviceController->ServiceControl(3, "unregister", 0);
sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.size() == 3, "Checking service reference count");
// 13. Get the highest ranking service reference, it should have ranking 7
ServiceReferenceU h1 = st1->GetServiceReference();
int rank = any_cast<int>(h1.GetProperty(ServiceConstants::SERVICE_RANKING()));
US_TEST_CONDITION_REQUIRED(rank == 7, "Check service rank");
// 14. Get the service of the highest ranked service reference
InterfaceMap o1 = st1->GetService(h1);
US_TEST_CONDITION_REQUIRED(!o1.empty(), "Check for non-null service");
// 14a Get the highest ranked service, directly this time
InterfaceMap o3 = st1->GetService();
US_TEST_CONDITION_REQUIRED(!o3.empty(), "Check for non-null service");
US_TEST_CONDITION_REQUIRED(o1 == o3, "Check for equal service instances");
// 15. Now release the tracking of that service and then try to get it
// from the servicetracker, which should yield a null object
serviceController->ServiceControl(1, "unregister", 7);
InterfaceMap o2 = st1->GetService(h1);
- US_TEST_CONDITION_REQUIRED(o2.empty(), "Checkt that service is null");
+ US_TEST_CONDITION_REQUIRED(o2.empty(), "Check that service is null");
// 16. Get all service objects this tracker tracks, it should be 2
std::vector<InterfaceMap> ts1 = st1->GetServices();
US_TEST_CONDITION_REQUIRED(ts1.size() == 2, "Check service count");
// 17. Test the remove method.
// First register another service, then remove it being tracked
serviceController->ServiceControl(1, "register", 7);
h1 = st1->GetServiceReference();
std::vector<ServiceReferenceU> sa3 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa3.size() == 3, "Check service reference count");
US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa3, ids.begin(), ids.begin()+3), "Check for expected interface id [0]");
st1->Remove(h1); // remove tracking on one servref
sa2 = st1->GetServiceReferences();
US_TEST_CONDITION_REQUIRED(sa2.size() == 2, "Check service reference count");
// 18. Test the addingService method,add a service reference
// 19. Test the removedService method, remove a service reference
// 20. Test the waitForService method
InterfaceMap o9 = st1->WaitForService(50);
US_TEST_CONDITION_REQUIRED(!o9.empty(), "Checking WaitForService method");
}
int usServiceTrackerTest(int /*argc*/, char* /*argv*/[])
{
US_TEST_BEGIN("ServiceTrackerTest")
TestFilterString();
TestServiceTracker();
US_TEST_END()
}
diff --git a/Modules/CppMicroServices/core/test/usStaticModuleTest.cpp b/Modules/CppMicroServices/core/test/usStaticModuleTest.cpp
index ee433b74ed..d7fdc83f16 100644
--- a/Modules/CppMicroServices/core/test/usStaticModuleTest.cpp
+++ b/Modules/CppMicroServices/core/test/usStaticModuleTest.cpp
@@ -1,183 +1,183 @@
/*============================================================================
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
https://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 <usModule.h>
#include <usModuleEvent.h>
#include <usServiceEvent.h>
#include <usModuleContext.h>
#include <usGetModuleContext.h>
#include <usModuleRegistry.h>
#include <usModuleActivator.h>
#include <usSharedLibrary.h>
#include "usTestUtilModuleListener.h"
#include "usTestingMacros.h"
#include "usTestingConfig.h"
US_USE_NAMESPACE
namespace {
#ifdef US_PLATFORM_WINDOWS
static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY;
#else
static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY;
#endif
// Load libTestModuleB and check that it exists and that the service it registers exists,
// also check that the expected events occur
void frame020a(ModuleContext* mc, TestModuleListener& listener,
#ifdef US_BUILD_SHARED_LIBS
SharedLibrary& libB)
{
try
{
libB.Load();
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG(<< "Load module exception: " << e.what())
}
#else
SharedLibrary& /*libB*/)
{
#endif
Module* moduleB = ModuleRegistry::GetModule("TestModuleB");
US_TEST_CONDITION_REQUIRED(moduleB != nullptr, "Test for existing module TestModuleB")
Module* moduleImportedByB = ModuleRegistry::GetModule("TestModuleImportedByB");
US_TEST_CONDITION_REQUIRED(moduleImportedByB != nullptr, "Test for existing module TestModuleImportedByB")
US_TEST_CONDITION(moduleB->GetName() == "TestModuleB", "Test module name")
// Check if libB registered the expected service
try
{
std::vector<ServiceReferenceU> refs = mc->GetServiceReferences("us::TestModuleBService");
- US_TEST_CONDITION_REQUIRED(refs.size() == 2, "Test that both the service from the shared and imported library are regsitered");
+ US_TEST_CONDITION_REQUIRED(refs.size() == 2, "Test that both the service from the shared and imported library are registered");
InterfaceMap o1 = mc->GetService(refs.front());
US_TEST_CONDITION(!o1.empty(), "Test if first service object found");
InterfaceMap o2 = mc->GetService(refs.back());
US_TEST_CONDITION(!o2.empty(), "Test if second service object found");
try
{
US_TEST_CONDITION(mc->UngetService(refs.front()), "Test if Service UnGet for first service returns true");
US_TEST_CONDITION(mc->UngetService(refs.back()), "Test if Service UnGet for second service returns true");
}
catch (const std::logic_error &le)
{
US_TEST_FAILED_MSG(<< "UnGetService exception: " << le.what())
}
// check the listeners for events
std::vector<ModuleEvent> pEvts;
#ifdef US_BUILD_SHARED_LIBS
pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleB));
pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleB));
pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleImportedByB));
pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleImportedByB));
#endif
std::vector<ServiceEvent> seEvts;
#ifdef US_BUILD_SHARED_LIBS
seEvts.push_back(ServiceEvent(ServiceEvent::REGISTERED, refs.back()));
seEvts.push_back(ServiceEvent(ServiceEvent::REGISTERED, refs.front()));
#endif
US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events");
}
catch (const ServiceException& /*se*/)
{
US_TEST_FAILED_MSG(<< "test module, expected service not found");
}
#ifdef US_BUILD_SHARED_LIBS
US_TEST_CONDITION(moduleB->IsLoaded() == true, "Test if loaded correctly");
#endif
}
// Unload libB and check for correct events
void frame030b(ModuleContext* mc, TestModuleListener& listener, SharedLibrary& libB)
{
Module* moduleB = ModuleRegistry::GetModule("TestModuleB");
US_TEST_CONDITION_REQUIRED(moduleB != nullptr, "Test for non-null module")
Module* moduleImportedByB = ModuleRegistry::GetModule("TestModuleImportedByB");
US_TEST_CONDITION_REQUIRED(moduleImportedByB != nullptr, "Test for non-null module")
std::vector<ServiceReferenceU> refs
= mc->GetServiceReferences("us::TestModuleBService");
US_TEST_CONDITION(refs.front(), "Test for first valid service reference")
US_TEST_CONDITION(refs.back(), "Test for second valid service reference")
try
{
libB.Unload();
#ifdef US_BUILD_SHARED_LIBS
US_TEST_CONDITION(moduleB->IsLoaded() == false, "Test for unloaded state")
#endif
}
catch (const std::exception& e)
{
US_TEST_FAILED_MSG(<< "UnLoad module exception: " << e.what())
}
std::vector<ModuleEvent> pEvts;
#ifdef US_BUILD_SHARED_LIBS
pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADING, moduleImportedByB));
pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADED, moduleImportedByB));
pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADING, moduleB));
pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADED, moduleB));
#endif
std::vector<ServiceEvent> seEvts;
#ifdef US_BUILD_SHARED_LIBS
seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, refs.front()));
seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, refs.back()));
#endif
US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events");
}
} // end unnamed namespace
int usStaticModuleTest(int /*argc*/, char* /*argv*/[])
{
US_TEST_BEGIN("StaticModuleTest");
ModuleContext* mc = GetModuleContext();
TestModuleListener listener;
ModuleListenerRegistrationHelper<TestModuleListener> ml(mc, &listener, &TestModuleListener::ModuleChanged);
ServiceListenerRegistrationHelper<TestModuleListener> sl(mc, &listener, &TestModuleListener::ServiceChanged);
SharedLibrary libB(LIB_PATH, "TestModuleB");
frame020a(mc, listener, libB);
frame030b(mc, listener, libB);
US_TEST_END()
}
diff --git a/Modules/CppMicroServices/core/test/usTestingMacros.h b/Modules/CppMicroServices/core/test/usTestingMacros.h
index 036cc1cbd2..ae3e0dfa5a 100644
--- a/Modules/CppMicroServices/core/test/usTestingMacros.h
+++ b/Modules/CppMicroServices/core/test/usTestingMacros.h
@@ -1,138 +1,138 @@
/*============================================================================
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
https://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 USTESTINGMACROS_H_
#define USTESTINGMACROS_H_
#include <exception>
#include <string>
#include <iostream>
#include <cstdlib>
#include "usTestManager.h"
US_BEGIN_NAMESPACE
/** \brief Indicate a failed test. */
class TestFailedException : public std::exception {
public:
TestFailedException() {}
};
US_END_NAMESPACE
/**
*
* \brief Output some text without generating a terminating newline.
*
* */
#define US_TEST_OUTPUT_NO_ENDL(x) \
std::cout x << std::flush;
/** \brief Output some text. */
#define US_TEST_OUTPUT(x) \
US_TEST_OUTPUT_NO_ENDL(x << "\n")
/** \brief Do some general test preparations. Must be called first in the
main test function. */
#define US_TEST_BEGIN(testName) \
std::string usTestName(#testName); \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().Initialize(); \
try {
/** \brief Fail and finish test with message MSG */
#define US_TEST_FAILED_MSG(MSG) \
US_TEST_OUTPUT(MSG) \
throw US_PREPEND_NAMESPACE(TestFailedException)();
/** \brief Must be called last in the main test function. */
#define US_TEST_END() \
} catch (const US_PREPEND_NAMESPACE(TestFailedException)&) { \
US_TEST_OUTPUT(<< "Further test execution skipped.") \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestFailed(); \
} catch (const std::exception& ex) { \
- US_TEST_OUTPUT(<< "Exception occured " << ex.what()) \
+ US_TEST_OUTPUT(<< "Exception occurred " << ex.what()) \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestFailed(); \
} \
if (US_PREPEND_NAMESPACE(TestManager)::GetInstance().NumberOfFailedTests() > 0) { \
US_TEST_OUTPUT(<< usTestName << ": [DONE FAILED] , subtests passed: " << \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().NumberOfPassedTests() << " failed: " << \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().NumberOfFailedTests() ) \
return EXIT_FAILURE; \
} else { \
US_TEST_OUTPUT(<< usTestName << ": " \
<< US_PREPEND_NAMESPACE(TestManager)::GetInstance().NumberOfPassedTests() \
<< " tests [DONE PASSED]") \
return EXIT_SUCCESS; \
}
#define US_TEST_CONDITION(COND,MSG) \
US_TEST_OUTPUT_NO_ENDL(<< MSG) \
if ( ! (COND) ) { \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestFailed(); \
US_TEST_OUTPUT(<< " [FAILED]\n" << "In " << __FILE__ \
<< ", line " << __LINE__ \
<< ": " #COND " : [FAILED]") \
} else { \
US_TEST_OUTPUT(<< " [PASSED]") \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestPassed(); \
}
#define US_TEST_CONDITION_REQUIRED(COND,MSG) \
US_TEST_OUTPUT_NO_ENDL(<< MSG) \
if ( ! (COND) ) { \
US_TEST_FAILED_MSG(<< " [FAILED]\n" << " +--> in " << __FILE__ \
<< ", line " << __LINE__ \
<< ", expression is false: \"" #COND "\"") \
} else { \
US_TEST_OUTPUT(<< " [PASSED]") \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestPassed(); \
}
/**
* \brief Begin block which should be checked for exceptions
*
* This macro, together with US_TEST_FOR_EXCEPTION_END, can be used
* to test whether a code block throws an expected exception. The test FAILS if the
* exception is NOT thrown.
*/
#define US_TEST_FOR_EXCEPTION_BEGIN(EXCEPTIONCLASS) \
try {
#define US_TEST_FOR_EXCEPTION_END(EXCEPTIONCLASS) \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestFailed(); \
US_TEST_OUTPUT( << "Expected an '" << #EXCEPTIONCLASS << "' exception. [FAILED]") \
} \
catch (const EXCEPTIONCLASS &) { \
US_TEST_OUTPUT(<< "Caught an expected '" << #EXCEPTIONCLASS \
<< "' exception. [PASSED]") \
US_PREPEND_NAMESPACE(TestManager)::GetInstance().TestPassed(); \
}
/**
* \brief Simplified version of US_TEST_FOR_EXCEPTION_BEGIN / END for
* a single statement
*/
#define US_TEST_FOR_EXCEPTION(EXCEPTIONCLASS, STATEMENT) \
US_TEST_FOR_EXCEPTION_BEGIN(EXCEPTIONCLASS) \
STATEMENT ; \
US_TEST_FOR_EXCEPTION_END(EXCEPTIONCLASS)
#endif // USTESTINGMACROS_H_
diff --git a/Modules/CppMicroServices/doc/CMakeDoxygenFilter.cpp b/Modules/CppMicroServices/doc/CMakeDoxygenFilter.cpp
deleted file mode 100644
index f04f7f0d18..0000000000
--- a/Modules/CppMicroServices/doc/CMakeDoxygenFilter.cpp
+++ /dev/null
@@ -1,495 +0,0 @@
-/*============================================================================
-
- 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
-
- https://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 <cstdlib>
-#include <string>
-#include <fstream>
-#include <iostream>
-
-#include <assert.h>
-
-//--------------------------------------
-// Utilitiy classes and functions
-//--------------------------------------
-
-struct ci_char_traits : public std::char_traits<char>
- // just inherit all the other functions
- // that we don't need to override
-{
- static bool eq(char c1, char c2)
- { return toupper(c1) == toupper(c2); }
-
- static bool ne(char c1, char c2)
- { return toupper(c1) != toupper(c2); }
-
- static bool lt(char c1, char c2)
- { return toupper(c1) < toupper(c2); }
-
- static bool gt(char c1, char c2)
- { return toupper(c1) > toupper(c2); }
-
- static int compare(const char* s1, const char* s2, std::size_t n)
- {
- while (n-- > 0)
- {
- if (lt(*s1, *s2)) return -1;
- if (gt(*s1, *s2)) return 1;
- ++s1; ++s2;
- }
- return 0;
- }
-
- static const char* find(const char* s, int n, char a)
- {
- while (n-- > 0 && toupper(*s) != toupper(a))
- {
- ++s;
- }
- return s;
- }
-};
-
-typedef std::basic_string<char, ci_char_traits> ci_string;
-
-//--------------------------------------
-// Lexer
-//--------------------------------------
-
-class CMakeLexer
-{
-public:
-
- enum Token {
- TOK_EOF = -1,
- TOK_EOL = -2,
-
- // commands
- TOK_MACRO = -3, TOK_ENDMACRO = -4,
- TOK_FUNCTION = -5, TOK_ENDFUNCTION = -6,
- TOK_DOXYGEN_COMMENT = -7,
- TOK_SET = -8,
- TOK_STRING_LITERAL = -100,
- TOK_NUMBER_LITERAL = -102,
-
- // primary
- TOK_IDENTIFIER = -200
- };
-
- CMakeLexer(std::istream& is)
- : _lastChar(' '), _is(is), _line(1), _col(1)
- {}
-
- int getToken()
- {
- // skip whitespace
- while (isspace(_lastChar) && _lastChar != '\r' && _lastChar != '\n')
- {
- _lastChar = getChar();
- }
-
- if (isalpha(_lastChar) || _lastChar == '_')
- {
- _identifier = _lastChar;
- while (isalnum(_lastChar = getChar()) || _lastChar == '-' || _lastChar == '_')
- {
- _identifier += _lastChar;
- }
-
- if (_identifier == "set")
- return TOK_SET;
- if (_identifier == "function")
- return TOK_FUNCTION;
- if (_identifier == "macro")
- return TOK_MACRO;
- if (_identifier == "endfunction")
- return TOK_ENDFUNCTION;
- if (_identifier == "endmacro")
- return TOK_ENDMACRO;
- return TOK_IDENTIFIER;
- }
-
- if (isdigit(_lastChar))
- {
- // very lax!! number detection
- _identifier = _lastChar;
- while (isalnum(_lastChar = getChar()) || _lastChar == '.' || _lastChar == ',')
- {
- _identifier += _lastChar;
- }
- return TOK_NUMBER_LITERAL;
- }
-
- if (_lastChar == '#')
- {
- _lastChar = getChar();
- if (_lastChar == '!')
- {
- // found a doxygen comment marker
- _identifier.clear();
-
- _lastChar = getChar();
- while (_lastChar != EOF && _lastChar != '\n' && _lastChar != '\r')
- {
- _identifier += _lastChar;
- _lastChar = getChar();
- }
- return TOK_DOXYGEN_COMMENT;
- }
-
- // skip the comment
- while (_lastChar != EOF && _lastChar != '\n' && _lastChar != '\r')
- {
- _lastChar = getChar();
- }
- }
-
- if (_lastChar == '"')
- {
- _lastChar = getChar();
- _identifier.clear();
- while (_lastChar != EOF && _lastChar != '"')
- {
- _identifier += _lastChar;
- _lastChar = getChar();
- }
-
- // eat the closing "
- _lastChar = getChar();
- return TOK_STRING_LITERAL;
- }
-
- // don't eat the EOF
- if (_lastChar == EOF) return TOK_EOF;
-
- // don't eat the EOL
- if (_lastChar == '\r' || _lastChar == '\n')
- {
- if (_lastChar == '\r') _lastChar = getChar();
- if (_lastChar == '\n') _lastChar = getChar();
- return TOK_EOL;
- }
-
- // return the character as its ascii value
- int thisChar = _lastChar;
- _lastChar = getChar();
- return thisChar;
- }
-
- std::string getIdentifier() const
- {
- return std::string(_identifier.c_str());
- }
-
- int curLine() const
- { return _line; }
-
- int curCol() const
- { return _col; }
-
- int getChar()
- {
- int c = _is.get();
- updateLoc(c);
- return c;
- }
-
-private:
-
- void updateLoc(int c)
- {
- if (c == '\n' || c == '\r')
- {
- ++_line;
- _col = 1;
- }
- else
- {
- ++_col;
- }
- }
-
- ci_string _identifier;
- int _lastChar;
- std::istream& _is;
-
- int _line;
- int _col;
-};
-
-//--------------------------------------
-// Parser
-//--------------------------------------
-
-class CMakeParser
-{
-
-public:
-
- CMakeParser(std::istream& is, std::ostream& os)
- : _os(os), _lexer(is), _curToken(CMakeLexer::TOK_EOF), _lastToken(CMakeLexer::TOK_EOF)
- { }
-
- int curToken()
- {
- return _curToken;
- }
-
- int nextToken()
- {
- _lastToken = _curToken;
- _curToken = _lexer.getToken();
- while (_curToken == CMakeLexer::TOK_EOL)
- {
- // Try to preserve lines in output to allow correct line number referencing by doxygen.
- _os << std::endl;
- _curToken = _lexer.getToken();
- }
- return _curToken;
- }
-
- void handleMacro()
- {
- if(!parseMacro())
- {
- // skip token for error recovery
- nextToken();
- }
- }
-
- void handleFunction()
- {
- if(!parseFunction())
- {
- // skip token for error recovery
- nextToken();
- }
- }
-
- void handleSet()
- {
- // SET(var ...) following a documentation block is assumed to be a variable declaration.
- if (_lastToken != CMakeLexer::TOK_DOXYGEN_COMMENT)
- {
- // No comment block before
- nextToken();
- } else if(!parseSet())
- {
- // skip token for error recovery
- nextToken();
- }
- }
-
- void handleDoxygenComment()
- {
- _os << "///" << _lexer.getIdentifier();
- nextToken();
- }
-
- void handleTopLevelExpression()
- {
- // skip token
- nextToken();
- }
-
-private:
-
- void printError(const char* str)
- {
- std::cerr << "Error: " << str << " (at line " << _lexer.curLine() << ", col " << _lexer.curCol() << ")";
- }
-
- bool parseMacro()
- {
- if (nextToken() != '(')
- {
- printError("Expected '(' after MACRO");
- return false;
- }
-
- nextToken();
- std::string macroName = _lexer.getIdentifier();
- if (curToken() != CMakeLexer::TOK_IDENTIFIER || macroName.empty())
- {
- printError("Expected macro name");
- return false;
- }
-
- _os << macroName << '(';
- if (nextToken() == CMakeLexer::TOK_IDENTIFIER)
- {
- _os << _lexer.getIdentifier();
- while (nextToken() == CMakeLexer::TOK_IDENTIFIER)
- {
- _os << ", " << _lexer.getIdentifier();
- }
- }
-
- if (curToken() != ')')
- {
- printError("Missing expected ')'");
- }
- else
- {
- _os << ");";
- }
-
- // eat the ')'
- nextToken();
- return true;
- }
-
- bool parseSet()
- {
- if (nextToken() != '(')
- {
- printError("Expected '(' after SET");
- return false;
- }
-
- nextToken();
- std::string variableName = _lexer.getIdentifier();
- if (curToken() != CMakeLexer::TOK_IDENTIFIER || variableName.empty())
- {
- printError("Expected variable name");
- return false;
- }
-
- _os << "CMAKE_VARIABLE " << variableName;
-
- nextToken();
- while ((curToken() == CMakeLexer::TOK_IDENTIFIER)
- || (curToken() == CMakeLexer::TOK_STRING_LITERAL)
- || (curToken() == CMakeLexer::TOK_NUMBER_LITERAL))
- {
- nextToken();
- }
-
- if (curToken() != ')')
- {
- printError("Missing expected ')'");
- }
- else
- {
- _os << ";";
- }
-
- // eat the ')'
- nextToken();
- return true;
- }
-
- bool parseFunction()
- {
- if (nextToken() != '(')
- {
- printError("Expected '(' after FUNCTION");
- return false;
- }
-
- nextToken();
- std::string funcName = _lexer.getIdentifier();
- if (curToken() != CMakeLexer::TOK_IDENTIFIER || funcName.empty())
- {
- printError("Expected function name");
- return false;
- }
-
- _os << funcName << '(';
- if (nextToken() == CMakeLexer::TOK_IDENTIFIER)
- {
- _os << _lexer.getIdentifier();
- while (nextToken() == CMakeLexer::TOK_IDENTIFIER)
- {
- _os << ", " << _lexer.getIdentifier();
- }
- }
-
- if (curToken() != ')')
- {
- printError("Missing expected ')'");
- }
- else
- {
- _os << ");";
- }
-
- // eat the ')'
- nextToken();
-
- return true;
- }
-
- std::ostream& _os;
- CMakeLexer _lexer;
- int _curToken;
- int _lastToken;
-};
-
-
-#define STRINGIFY(a) #a
-#define DOUBLESTRINGIFY(a) STRINGIFY(a)
-
-int main(int argc, char** argv)
-{
- assert(argc > 1);
-
- for (int i = 1; i < argc; ++i)
- {
- std::ifstream ifs(argv[i]);
- std::ostream& os = std::cout;
-
- #ifdef USE_NAMESPACE
- os << "namespace " << DOUBLESTRINGIFY(USE_NAMESPACE) << " {\n";
- #endif
-
- CMakeParser parser(ifs, os);
- parser.nextToken();
- while (ifs.good())
- {
- switch (parser.curToken())
- {
- case CMakeLexer::TOK_EOF:
- return ifs.get(); // eat EOF
- case CMakeLexer::TOK_MACRO:
- parser.handleMacro();
- break;
- case CMakeLexer::TOK_FUNCTION:
- parser.handleFunction();
- break;
- case CMakeLexer::TOK_SET:
- parser.handleSet();
- break;
- case CMakeLexer::TOK_DOXYGEN_COMMENT:
- parser.handleDoxygenComment();
- break;
- default:
- parser.handleTopLevelExpression();
- break;
- }
- }
-
- #ifdef USE_NAMESPACE
- os << "}\n";
- #endif
- }
-
- return EXIT_SUCCESS;
-}
diff --git a/Modules/CppMicroServices/doc/CMakeLists.txt b/Modules/CppMicroServices/doc/CMakeLists.txt
index c152886b07..9a4883eaa3 100644
--- a/Modules/CppMicroServices/doc/CMakeLists.txt
+++ b/Modules/CppMicroServices/doc/CMakeLists.txt
@@ -1,78 +1,64 @@
if(NOT US_IS_EMBEDDED AND NOT US_NO_DOCUMENTATION)
find_package(Doxygen)
if(DOXYGEN_FOUND)
option(US_DOCUMENTATION_FOR_WEBPAGE "Build Doxygen documentation for the webpage" OFF)
set(US_DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR} CACHE PATH "Doxygen output directory")
mark_as_advanced(US_DOCUMENTATION_FOR_WEBPAGE US_DOXYGEN_OUTPUT_DIR)
set(US_HAVE_DOT "NO")
if(DOXYGEN_DOT_EXECUTABLE)
set(US_HAVE_DOT "YES")
endif()
if(NOT DEFINED US_DOXYGEN_DOT_NUM_THREADS)
set(US_DOXYGEN_DOT_NUM_THREADS 4)
endif()
# We are in standalone mode, so we generate a "mainpage"
set(US_DOXYGEN_MAIN_PAGE_CMD "\\mainpage")
set(US_DOXYGEN_ENABLED_SECTIONS "us_standalone")
if(US_DOCUMENTATION_FOR_WEBPAGE)
configure_file(doxygen/header.html
${CMAKE_CURRENT_BINARY_DIR}/header.html COPYONLY)
set(US_DOXYGEN_HEADER header.html)
configure_file(doxygen/footer.html
${CMAKE_CURRENT_BINARY_DIR}/footer.html COPYONLY)
set(US_DOXYGEN_FOOTER footer.html)
configure_file(doxygen/doxygen_extra.css
${CMAKE_CURRENT_BINARY_DIR}/doxygen_extra.css COPYONLY)
set(US_DOXYGEN_EXTRA_CSS doxygen_extra.css)
set(US_DOXYGEN_OUTPUT_DIR ${PROJECT_SOURCE_DIR}/gh-pages)
if(${PROJECT_NAME}_MINOR_VERSION EQUAL 99)
set(US_DOXYGEN_HTML_OUTPUT "doc_latest")
else()
set(US_DOXYGEN_HTML_OUTPUT "doc_${${PROJECT_NAME}_MAJOR_VERSION}_${${PROJECT_NAME}_MINOR_VERSION}")
endif()
else()
set(US_DOXYGEN_HEADER )
set(US_DOXYGEN_FOOTER )
set(US_DOXYGEN_CSS )
set(US_DOXYGEN_HTML_OUTPUT "html")
endif()
- # Compile a command line tool which transforms comments in CMake scripts into
- # Doxygen parseable C code.
- set(CMakeDoxygenFilter_EXECUTABLE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CMakeDoxygenFilter${CMAKE_EXECUTABLE_SUFFIX}")
- try_compile(_result_var
- "${CMAKE_CURRENT_BINARY_DIR}"
- "${CMAKE_CURRENT_SOURCE_DIR}/CMakeDoxygenFilter.cpp"
- OUTPUT_VARIABLE _compile_output
- COPY_FILE ${CMakeDoxygenFilter_EXECUTABLE}
- )
-
- if(NOT _result_var)
- message(FATAL_ERROR "error: Faild to compile ${CMAKE_CURRENT_SOURCE_DIR}/CMakeDoxygenFilter.cpp (result: ${result_var})\n${_compile_output}")
- endif()
-
configure_file(doxygen.conf.in
${CMAKE_CURRENT_BINARY_DIR}/doxygen.conf)
add_custom_target(doc
${DOXYGEN} ${CMAKE_CURRENT_BINARY_DIR}/doxygen.conf
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VERBATIM
)
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" --build \"${PROJECT_BINARY_DIR}\" --target doc)")
install(DIRECTORY "${US_DOXYGEN_OUTPUT_DIR}/${US_DOXYGEN_HTML_OUTPUT}"
DESTINATION "${AUXILIARY_INSTALL_DIR}/doc/"
COMPONENT doc)
endif()
endif()
diff --git a/Modules/CppMicroServices/doc/doxygen.conf.in b/Modules/CppMicroServices/doc/doxygen.conf.in
index 89881d49b2..d74c99dc7c 100644
--- a/Modules/CppMicroServices/doc/doxygen.conf.in
+++ b/Modules/CppMicroServices/doc/doxygen.conf.in
@@ -1,2749 +1,2745 @@
# Doxyfile 1.9.6
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
#
# All text after a double hash (##) is considered a comment and is placed in
# front of the TAG it is preceding.
#
# All text after a single hash (#) is considered a comment and will be ignored.
# The format is:
# TAG = value [value, ...]
# For lists, items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (\" \").
#
# Note:
#
# Use doxygen to compare the used configuration file with the template
# configuration file:
# doxygen -x [configFile]
# Use doxygen to compare the used configuration file with the template
# configuration file without replacing the environment variables or CMake type
# replacement variables:
# doxygen -x_noenv [configFile]
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
# This tag specifies the encoding used for all characters in the configuration
# file that follow. The default is UTF-8 which is also the encoding used for all
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
# iconv built into libc) for the transcoding. See
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
# double-quotes, unless you are using Doxywizard) that should identify the
# project for which the documentation is generated. This name is used in the
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = "C++ Micro Services"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = @CppMicroServices_VERSION@
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF = "A dynamic OSGi-like C++ service registry"
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
# the logo to the output directory.
PROJECT_LOGO =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = @US_DOXYGEN_OUTPUT_DIR@
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# sub-directories (in 2 levels) under the output directory of each output format
# and will distribute the generated files over these directories. Enabling this
# option can be useful when feeding doxygen a huge amount of source files, where
# putting all generated files in the same directory would otherwise causes
# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to
# control the number of sub-directories.
# The default value is: NO.
CREATE_SUBDIRS = NO
# Controls the number of sub-directories that will be created when
# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every
# level increment doubles the number of directories, resulting in 4096
# directories at level 8 which is the default and also the maximum value. The
# sub-directories are organized in 2 levels, the first level always has a fixed
# number of 16 directories.
# Minimum value: 0, maximum value: 8, default value: 8.
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
CREATE_SUBDIRS_LEVEL = 8
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
# characters to appear in the names of generated files. If set to NO, non-ASCII
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
# U+3044.
# The default value is: NO.
ALLOW_UNICODE_NAMES = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with
# English messages), Korean, Korean-en (Korean with English messages), Latvian,
# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,
# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,
# Swedish, Turkish, Ukrainian and Vietnamese.
# The default value is: English.
OUTPUT_LANGUAGE = English
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this.
# The default value is: YES.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
# description of a member or function before the detailed description
#
# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
# The default value is: YES.
REPEAT_BRIEF = YES
# This tag implements a quasi-intelligent brief description abbreviator that is
# used to form the text in various listings. Each string in this list, if found
# as the leading text of the brief description, will be stripped from the text
# and the result, after processing the whole list, is used as the annotated
# text. Otherwise, the brief description is used as-is. If left blank, the
# following values are used ($name is automatically replaced with the name of
# the entity):The $name class, The $name widget, The $name file, is, provides,
# specifies, contains, represents, a, an and the.
ABBREVIATE_BRIEF =
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# doxygen will generate a detailed section even if there is only a brief
# description.
# The default value is: NO.
ALWAYS_DETAILED_SEC = NO
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
# inherited members of a class in the documentation of that class as if those
# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
# The default value is: NO.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
# before files name in the file list and in the header files. If set to NO the
# shortest path that makes the file name unique will be used
# The default value is: YES.
FULL_PATH_NAMES = NO
# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
# Stripping is only done if one of the specified strings matches the left-hand
# part of the path. The tag can be used to show relative paths in the file list.
# If left blank the directory from which doxygen is run is used as the path to
# strip.
#
# Note that you can specify absolute paths here, but also relative paths, which
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH =
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
# header file to include in order to use a class. If left blank only the name of
# the header file containing the class definition is used. Otherwise one should
# specify the list of include paths that are normally passed to the compiler
# using the -I flag.
STRIP_FROM_INC_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
# less readable) file names. This can be useful is your file systems doesn't
# support long names like on DOS, Mac, or CD-ROM.
# The default value is: NO.
SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
# first line (until the first dot) of a Javadoc-style comment as the brief
# description. If set to NO, the Javadoc-style will behave just like regular Qt-
# style comments (thus requiring an explicit @brief command for a brief
# description.)
# The default value is: NO.
JAVADOC_AUTOBRIEF = YES
# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
# such as
# /***************
# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
# Javadoc-style will behave just like regular comments and it will not be
# interpreted by doxygen.
# The default value is: NO.
JAVADOC_BANNER = NO
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
# line (until the first dot) of a Qt-style comment as the brief description. If
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
# requiring an explicit \brief command for a brief description.)
# The default value is: NO.
QT_AUTOBRIEF = NO
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
# a brief description. This used to be the default behavior. The new default is
# to treat a multi-line C++ comment block as a detailed description. Set this
# tag to YES if you prefer the old behavior instead.
#
# Note that setting this tag to YES also means that rational rose comments are
# not recognized any more.
# The default value is: NO.
MULTILINE_CPP_IS_BRIEF = NO
# By default Python docstrings are displayed as preformatted text and doxygen's
# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
# doxygen's special commands can be used and the contents of the docstring
# documentation blocks is shown as doxygen documentation.
# The default value is: YES.
PYTHON_DOCSTRING = YES
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.
# The default value is: YES.
INHERIT_DOCS = YES
# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
# page for each member. If set to NO, the documentation of a member will be part
# of the file/class/namespace that contains it.
# The default value is: NO.
SEPARATE_MEMBER_PAGES = NO
# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
# uses this value to replace tabs by spaces in code fragments.
# Minimum value: 1, maximum value: 16, default value: 4.
TAB_SIZE = 2
# This tag can be used to specify a number of aliases that act as commands in
# the documentation. An alias has the form:
# name=value
# For example adding
# "sideeffect=@par Side Effects:^^"
# will allow you to put the command \sideeffect (or @sideeffect) in the
# documentation, which will result in a user-defined paragraph with heading
# "Side Effects:". Note that you cannot put \n's in the value part of an alias
# to insert newlines (in the resulting output). You can put ^^ in the value part
# of an alias to insert a newline as if a physical newline was in the original
# file. When you need a literal { or } or , in the value part of an alias you
# have to escape them by means of a backslash (\), this can lead to conflicts
# with the commands \{ and \} for these it is advised to use the version @{ and
# @} or use a double escape (\\{ and \\})
ALIASES = "FIXME=\par Fix Me's:\n" \
"embmainpage{1}=@US_DOXYGEN_MAIN_PAGE_CMD@"
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
# members will be omitted, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_FOR_C = NO
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
# Python sources only. Doxygen will then generate output that is more tailored
# for that language. For instance, namespaces will be presented as packages,
# qualified scopes will look different, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
# sources. Doxygen will then generate output that is tailored for Fortran.
# The default value is: NO.
OPTIMIZE_FOR_FORTRAN = NO
# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
# sources. Doxygen will then generate output that is tailored for VHDL.
# The default value is: NO.
OPTIMIZE_OUTPUT_VHDL = NO
# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
# sources only. Doxygen will then generate output that is more tailored for that
# language. For instance, namespaces will be presented as modules, types will be
# separated into more groups, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_SLICE = NO
# Doxygen selects the parser to use depending on the extension of the files it
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
# default for Fortran type files). For instance to make doxygen treat .inc files
# as Fortran files (default is PHP), and .f files as C (default is Fortran),
# use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
# the files are not read by doxygen. When specifying no_extension you should add
# * to the FILE_PATTERNS.
#
# Note see also the list of default file extension mappings.
EXTENSION_MAPPING =
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
# documentation. See https://daringfireball.net/projects/markdown/ for details.
# The output of markdown processing is further processed by doxygen, so you can
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
# case of backward compatibilities issues.
# The default value is: YES.
MARKDOWN_SUPPORT = YES
# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
# Minimum value: 0, maximum value: 99, default value: 5.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 5
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or
# globally by setting AUTOLINK_SUPPORT to NO.
# The default value is: YES.
AUTOLINK_SUPPORT = YES
# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
# to include (a tag file for) the STL sources as input, then you should set this
# tag to YES in order to let doxygen match functions declarations and
# definitions whose arguments contain STL classes (e.g. func(std::string);
# versus func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
# The default value is: NO.
BUILTIN_STL_SUPPORT = YES
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
# The default value is: NO.
CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
# will parse them like normal C++ but will assume all classes use public instead
# of private inheritance when no explicit protection keyword is present.
# The default value is: NO.
SIP_SUPPORT = NO
# For Microsoft's IDL there are propget and propput attributes to indicate
# getter and setter methods for a property. Setting this option to YES will make
# doxygen to replace the get and set methods by a property in the documentation.
# This will only work if the methods are indeed getting or setting a simple
# type. If this is not the case, or you want to show the methods anyway, you
# should set this option to NO.
# The default value is: YES.
IDL_PROPERTY_SUPPORT = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
# The default value is: NO.
DISTRIBUTE_GROUP_DOC = YES
# If one adds a struct or class to a group and this option is enabled, then also
# any nested class or struct is added to the same group. By default this option
# is disabled and one has to add nested compounds explicitly via \ingroup.
# The default value is: NO.
GROUP_NESTED_COMPOUNDS = NO
# Set the SUBGROUPING tag to YES to allow class member groups of the same type
# (for instance a group of public functions) to be put as a subgroup of that
# type (e.g. under the Public Functions section). Set it to NO to prevent
# subgrouping. Alternatively, this can be done per class using the
# \nosubgrouping command.
# The default value is: YES.
SUBGROUPING = YES
# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
# are shown inside the group in which they are included (e.g. using \ingroup)
# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
# and RTF).
#
# Note that this feature does not work in combination with
# SEPARATE_MEMBER_PAGES.
# The default value is: NO.
INLINE_GROUPED_CLASSES = NO
# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
# with only public data fields or simple typedef fields will be shown inline in
# the documentation of the scope in which they are defined (i.e. file,
# namespace, or group documentation), provided this scope is documented. If set
# to NO, structs, classes, and unions are shown on a separate page (for HTML and
# Man pages) or section (for LaTeX and RTF).
# The default value is: NO.
INLINE_SIMPLE_STRUCTS = NO
# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
# enum is documented as struct, union, or enum with the name of the typedef. So
# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
# with name TypeT. When disabled the typedef will appear as a member of a file,
# namespace, or class. And the struct will be named TypeS. This can typically be
# useful for C code in case the coding convention dictates that all compound
# types are typedef'ed and only the typedef is referenced, never the tag name.
# The default value is: NO.
TYPEDEF_HIDES_STRUCT = NO
# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
# cache is used to resolve symbols given their name and scope. Since this can be
# an expensive process and often the same symbol appears multiple times in the
# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
# doxygen will become slower. If the cache is too large, memory is wasted. The
# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
# symbols. At the end of a run doxygen will report the cache usage and suggest
# the optimal cache size from a speed point of view.
# Minimum value: 0, maximum value: 9, default value: 0.
LOOKUP_CACHE_SIZE = 0
# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use
# during processing. When set to 0 doxygen will based this on the number of
# cores available in the system. You can set it explicitly to a value larger
# than 0 to get more control over the balance between CPU load and processing
# speed. At this moment only the input processing can be done using multiple
# threads. Since this is still an experimental feature the default is set to 1,
# which effectively disables parallel processing. Please report any issues you
# encounter. Generating dot graphs in parallel is controlled by the
# DOT_NUM_THREADS setting.
# Minimum value: 0, maximum value: 32, default value: 1.
NUM_PROC_THREADS = 1
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
# documentation are documented, even if no documentation was available. Private
# class members and static file members will be hidden unless the
# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
# Note: This will also disable the warnings about undocumented members that are
# normally produced when WARNINGS is set to YES.
# The default value is: NO.
EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
# be included in the documentation.
# The default value is: NO.
EXTRACT_PRIVATE = NO
# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
# methods of a class will be included in the documentation.
# The default value is: NO.
EXTRACT_PRIV_VIRTUAL = NO
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
# The default value is: NO.
EXTRACT_PACKAGE = NO
# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
# included in the documentation.
# The default value is: NO.
EXTRACT_STATIC = YES
# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
# locally in source files will be included in the documentation. If set to NO,
# only classes defined in header files are included. Does not have any effect
# for Java sources.
# The default value is: YES.
EXTRACT_LOCAL_CLASSES = NO
# This flag is only useful for Objective-C code. If set to YES, local methods,
# which are defined in the implementation section but not in the interface are
# included in the documentation. If set to NO, only methods in the interface are
# included.
# The default value is: NO.
EXTRACT_LOCAL_METHODS = NO
# If this flag is set to YES, the members of anonymous namespaces will be
# extracted and appear in the documentation as a namespace called
# 'anonymous_namespace{file}', where file will be replaced with the base name of
# the file that contains the anonymous namespace. By default anonymous namespace
# are hidden.
# The default value is: NO.
EXTRACT_ANON_NSPACES = NO
# If this flag is set to YES, the name of an unnamed parameter in a declaration
# will be determined by the corresponding definition. By default unnamed
# parameters remain unnamed in the output.
# The default value is: YES.
RESOLVE_UNNAMED_PARAMS = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
# members will be included in the various overviews, but no documentation
# section is generated. This option has no effect if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set
# to NO, these classes will be included in the various overviews. This option
# will also hide undocumented C++ concepts if enabled. This option has no effect
# if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
# declarations. If set to NO, these declarations will be included in the
# documentation.
# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = YES
# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
# documentation blocks found inside the body of a function. If set to NO, these
# blocks will be appended to the function's detailed documentation block.
# The default value is: NO.
HIDE_IN_BODY_DOCS = NO
# The INTERNAL_DOCS tag determines if documentation that is typed after a
# \internal command is included. If the tag is set to NO then the documentation
# will be excluded. Set it to YES to include the internal documentation.
# The default value is: NO.
INTERNAL_DOCS = NO
# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
# able to match the capabilities of the underlying filesystem. In case the
# filesystem is case sensitive (i.e. it supports files in the same directory
# whose names only differ in casing), the option must be set to YES to properly
# deal with such files in case they appear in the input. For filesystems that
# are not case sensitive the option should be set to NO to properly deal with
# output files written for symbols that only differ in casing, such as for two
# classes, one named CLASS and the other named Class, and to also support
# references to files without having to specify the exact matching casing. On
# Windows (including Cygwin) and MacOS, users should typically set this option
# to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES.
# Possible values are: SYSTEM, NO and YES.
# The default value is: SYSTEM.
CASE_SENSE_NAMES = YES
# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
# their full class and namespace scopes in the documentation. If set to YES, the
# scope will be hidden.
# The default value is: NO.
HIDE_SCOPE_NAMES = NO
# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
# append additional text to a page's title, such as Class Reference. If set to
# YES the compound reference will be hidden.
# The default value is: NO.
HIDE_COMPOUND_REFERENCE= NO
# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
# will show which file needs to be included to use the class.
# The default value is: YES.
SHOW_HEADERFILE = YES
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
# the files that are included by a file in the documentation of that file.
# The default value is: YES.
SHOW_INCLUDE_FILES = NO
# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
# grouped member an include statement to the documentation, telling the reader
# which file to include in order to use the member.
# The default value is: NO.
SHOW_GROUPED_MEMB_INC = NO
# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
# files with double quotes in the documentation rather than with sharp brackets.
# The default value is: NO.
FORCE_LOCAL_INCLUDES = NO
# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
# documentation for inline members.
# The default value is: YES.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
# (detailed) documentation of file and class members alphabetically by member
# name. If set to NO, the members will appear in declaration order.
# The default value is: YES.
SORT_MEMBER_DOCS = YES
# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
# descriptions of file, namespace and class members alphabetically by member
# name. If set to NO, the members will appear in declaration order. Note that
# this will also influence the order of the classes in the class list.
# The default value is: NO.
SORT_BRIEF_DOCS = NO
# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
# (brief and detailed) documentation of class members so that constructors and
# destructors are listed first. If set to NO the constructors will appear in the
# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
# member documentation.
# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
# detailed member documentation.
# The default value is: NO.
SORT_MEMBERS_CTORS_1ST = NO
# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
# of group names into alphabetical order. If set to NO the group names will
# appear in their defined order.
# The default value is: NO.
SORT_GROUP_NAMES = NO
# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
# fully-qualified names, including namespaces. If set to NO, the class list will
# be sorted only by class name, not including the namespace part.
# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
# Note: This option applies only to the class list, not to the alphabetical
# list.
# The default value is: NO.
SORT_BY_SCOPE_NAME = YES
# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
# type resolution of all parameters of a function it will reject a match between
# the prototype and the implementation of a member function even if there is
# only one candidate or it is obvious which candidate to choose by doing a
# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
# accept a match between prototype and implementation in such cases.
# The default value is: NO.
STRICT_PROTO_MATCHING = NO
# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
# list. This list is created by putting \todo commands in the documentation.
# The default value is: YES.
GENERATE_TODOLIST = YES
# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
# list. This list is created by putting \test commands in the documentation.
# The default value is: YES.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
# list. This list is created by putting \bug commands in the documentation.
# The default value is: YES.
GENERATE_BUGLIST = YES
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
# the deprecated list. This list is created by putting \deprecated commands in
# the documentation.
# The default value is: YES.
GENERATE_DEPRECATEDLIST= YES
# The ENABLED_SECTIONS tag can be used to enable conditional documentation
# sections, marked by \if <section_label> ... \endif and \cond <section_label>
# ... \endcond blocks.
ENABLED_SECTIONS = @US_DOXYGEN_ENABLED_SECTIONS@
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
# initial value of a variable or macro / define can have for it to appear in the
# documentation. If the initializer consists of more lines than specified here
# it will be hidden. Use a value of 0 to hide initializers completely. The
# appearance of the value of individual variables and macros / defines can be
# controlled using \showinitializer or \hideinitializer command in the
# documentation regardless of this setting.
# Minimum value: 0, maximum value: 10000, default value: 30.
MAX_INITIALIZER_LINES = 0
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
# the bottom of the documentation of classes and structs. If set to YES, the
# list will mention the files that were used to generate the documentation.
# The default value is: YES.
SHOW_USED_FILES = NO
# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
# will remove the Files entry from the Quick Index and from the Folder Tree View
# (if specified).
# The default value is: YES.
SHOW_FILES = NO
# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
# page. This will remove the Namespaces entry from the Quick Index and from the
# Folder Tree View (if specified).
# The default value is: YES.
SHOW_NAMESPACES = YES
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from
# the version control system). Doxygen will invoke the program by executing (via
# popen()) the command command input-file, where command is the value of the
# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
# by doxygen. Whatever the program writes to standard output is used as the file
# version. For an example see the documentation.
FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
# by doxygen. The layout file controls the global structure of the generated
# output files in an output format independent way. To create the layout file
# that represents doxygen's defaults, run doxygen with the -l option. You can
# optionally specify a file name after the option, if omitted DoxygenLayout.xml
# will be used as the name of the layout file. See also section "Changing the
# layout of pages" for information.
#
# Note that if you run doxygen from a directory containing a file called
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
# tag is left empty.
LAYOUT_FILE =
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib
# extension is automatically appended if omitted. This requires the bibtex tool
# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references.
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated to
# standard output by doxygen. If QUIET is set to YES this implies that the
# messages are off.
# The default value is: NO.
QUIET = NO
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
# this implies that the warnings are on.
#
# Tip: Turn warnings on while writing the documentation.
# The default value is: YES.
WARNINGS = YES
# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: YES.
WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as documenting some parameters in
# a documented function twice, or documenting parameters that don't exist or
# using markup commands wrongly.
# The default value is: YES.
WARN_IF_DOC_ERROR = YES
# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
# function parameter documentation. If set to NO, doxygen will accept that some
# parameters have no documentation without warning.
# The default value is: YES.
WARN_IF_INCOMPLETE_DOC = YES
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return
# value. If set to NO, doxygen will only warn about wrong parameter
# documentation, but not about the absence of documentation. If EXTRACT_ALL is
# set to YES then this flag will automatically be disabled. See also
# WARN_IF_INCOMPLETE_DOC
# The default value is: NO.
WARN_NO_PARAMDOC = YES
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
# undocumented enumeration values. If set to NO, doxygen will accept
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: NO.
WARN_IF_UNDOC_ENUM_VAL = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
# at the end of the doxygen process doxygen will return with a non-zero status.
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
# The default value is: NO.
WARN_AS_ERROR = NO
# The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which
# will be replaced by the file and line number from which the warning originated
# and the warning text. Optionally the format may contain $version, which will
# be replaced by the version of the file (if it could be obtained via
# FILE_VERSION_FILTER)
# See also: WARN_LINE_FORMAT
# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
# In the $text part of the WARN_FORMAT command it is possible that a reference
# to a more specific place is given. To make it easier to jump to this place
# (outside of doxygen) the user can define a custom "cut" / "paste" string.
# Example:
# WARN_LINE_FORMAT = "'vi $file +$line'"
# See also: WARN_FORMAT
# The default value is: at line $line of file $file.
WARN_LINE_FORMAT = "at line $line of file $file"
# The WARN_LOGFILE tag can be used to specify a file to which warning and error
# messages should be written. If left blank the output is written to standard
# error (stderr). In case the file specified cannot be opened for writing the
# warning and error messages are written to standard error. When as file - is
# specified the warning and error messages are written to standard output
# (stdout).
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag is used to specify the files and/or directories that contain
# documented source files. You may enter file names like myfile.cpp or
# directories like /usr/src/myproject. Separate the files or directories with
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = "@PROJECT_SOURCE_DIR@" \
- "@PROJECT_SOURCE_DIR@/cmake/usFunctionAddResources.cmake" \
- "@PROJECT_SOURCE_DIR@/cmake/usFunctionEmbedResources.cmake" \
- "@PROJECT_SOURCE_DIR@/cmake/usFunctionGenerateModuleInit.cmake" \
- "@PROJECT_SOURCE_DIR@/cmake/usFunctionGetResourceSource.cmake" \
"@PROJECT_BINARY_DIR@/include/usGlobalConfig.h" \
"@PROJECT_BINARY_DIR@/core/include"
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see:
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# See also: INPUT_FILE_ENCODING
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
# This tag can be used to specify the character encoding of the source files
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
# character encoding on a per file pattern basis. Doxygen will compare the file
# name with each pattern and apply the encoding instead of the default
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
# "INPUT_ENCODING" for further information on supported encodings.
INPUT_FILE_ENCODING =
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen.
#
# Note the list of default checked file patterns might differ from the list of
# default file extension mappings.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.h \
*.dox \
*.md
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
# The default value is: NO.
RECURSIVE = YES
# The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
#
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE = "@PROJECT_SOURCE_DIR@/README.md" \
"@PROJECT_SOURCE_DIR@/gh-pages/" \
"@PROJECT_SOURCE_DIR@/third_party/" \
"@PROJECT_SOURCE_DIR@/.git/"
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
# The default value is: NO.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS = */test/* \
*/snippets/* \
*/core/examples/* \
*/.git/* \
*_p.h \
*Private.*
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# ANamespace::AClass, ANamespace::*Test
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS = us \
US_NAMESPACE \
*Private* \
ModuleInfo \
ServiceObjectsBase* \
TrackedService*
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
# command).
EXAMPLE_PATH = "@PROJECT_SOURCE_DIR@/core/doc/snippets/" \
"@PROJECT_SOURCE_DIR@/core/examples/"
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank all
# files are included.
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude commands
# irrespective of the value of the RECURSIVE tag.
# The default value is: NO.
EXAMPLE_RECURSIVE = YES
# The IMAGE_PATH tag can be used to specify one or more files or directories
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command:
#
# <filter> <input-file>
#
# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
# name of an input file. Doxygen will then use the output that the filter
# program writes to standard output. If FILTER_PATTERNS is specified, this tag
# will be ignored.
#
# Note that the filter must not add or remove lines; it is applied before the
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
#
# Note that doxygen will use the data processed and written to standard output
# for further processing, therefore nothing else, like debug statements or used
# commands (so in case of a Windows batch file always use @echo OFF), should be
# written to standard output.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the
# filter if there is a match. The filters are a list of the form: pattern=filter
# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
# patterns match the file name, INPUT_FILTER is applied.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
-FILTER_PATTERNS = *.cmake=@CMakeDoxygenFilter_EXECUTABLE@
+FILTER_PATTERNS =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will also be used to filter the input files that are used for
# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
# The default value is: NO.
FILTER_SOURCE_FILES = NO
# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
# it is also possible to disable source filtering for a specific pattern using
# *.ext= (so without naming a filter).
# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
FILTER_SOURCE_PATTERNS =
# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
# is part of the input, its contents will be placed on the main page
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE =
# The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common
# extension is to allow longer lines before the automatic comment starts. The
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
# be processed before the automatic comment starts.
# Minimum value: 7, maximum value: 10000, default value: 72.
FORTRAN_COMMENT_AFTER = 72
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
# generated. Documented entities will be cross-referenced with these sources.
#
# Note: To get rid of all source code in the generated output, make sure that
# also VERBATIM_HEADERS is set to NO.
# The default value is: NO.
SOURCE_BROWSER = NO
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
# classes and enums directly into the documentation.
# The default value is: NO.
INLINE_SOURCES = NO
# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
# special comment blocks from generated source code fragments. Normal C, C++ and
# Fortran comments will always remain visible.
# The default value is: YES.
STRIP_CODE_COMMENTS = NO
# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
# entity all documented functions referencing it will be listed.
# The default value is: NO.
REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES then for each documented function
# all documented entities called/used by that function will be listed.
# The default value is: NO.
REFERENCES_RELATION = YES
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
# to YES then the hyperlinks from functions in REFERENCES_RELATION and
# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
# link to the documentation.
# The default value is: YES.
REFERENCES_LINK_SOURCE = YES
# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
# source code will show a tooltip with additional information such as prototype,
# brief description and links to the definition and documentation. Since this
# will make the HTML file larger and loading of large files a bit slower, you
# can opt to disable this feature.
# The default value is: YES.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
SOURCE_TOOLTIPS = YES
# If the USE_HTAGS tag is set to YES then the references to source code will
# point to the HTML generated by the htags(1) tool instead of doxygen built-in
# source browser. The htags tool is part of GNU's global source tagging system
# (see https://www.gnu.org/software/global/global.html). You will need version
# 4.8.6 or higher.
#
# To use it do the following:
# - Install the latest version of global
# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
# - Make sure the INPUT points to the root of the source tree
# - Run doxygen as normal
#
# Doxygen will invoke htags (and that will in turn invoke gtags), so these
# tools must be available from the command line (i.e. in the search path).
#
# The result: instead of the source browser generated by doxygen, the links to
# source code will now point to the output of htags.
# The default value is: NO.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
USE_HTAGS = NO
# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
# verbatim copy of the header file for each class for which an include is
# specified. Set to NO to disable this.
# See also: Section \class.
# The default value is: YES.
VERBATIM_HEADERS = NO
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
# clang parser (see:
# http://clang.llvm.org/) for more accurate parsing at the cost of reduced
# performance. This can be particularly helpful with template rich C++ code for
# which doxygen's built-in parser lacks the necessary type information.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS
# tag is set to YES then doxygen will add the directory of each input to the
# include path.
# The default value is: YES.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_ADD_INC_PATHS = YES
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
# If clang assisted parsing is enabled you can provide the clang parser with the
# path to the directory containing a file called compile_commands.json. This
# file is the compilation database (see:
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
# options used when the source files were built. This is equivalent to
# specifying the -p option to a clang tool, such as clang-check. These options
# will then be passed to the parser. Any options specified with CLANG_OPTIONS
# will be added as well.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
CLANG_DATABASE_PATH =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
# compounds will be generated. Enable this if the project contains a lot of
# classes, structs, unions or interfaces.
# The default value is: YES.
ALPHABETICAL_INDEX = YES
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
# that should be ignored while generating the index headers. The IGNORE_PREFIX
# tag works for classes, function and member names. The entity will be placed in
# the alphabetical list under the first letter of the entity name that remains
# after removing the prefix.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
# The default value is: YES.
GENERATE_HTML = YES
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = @US_DOXYGEN_HTML_OUTPUT@
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).
# The default value is: .html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FILE_EXTENSION = .html
# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
# each generated HTML page. If the tag is left blank doxygen will generate a
# standard header.
#
# To get valid HTML the header file that includes any scripts and style sheets
# that doxygen needs, which is dependent on the configuration options used (e.g.
# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
# default header using
# doxygen -w html new_header.html new_footer.html new_stylesheet.css
# YourConfigFile
# and then modify the file new_header.html. See also section "Doxygen usage"
# for information on how to generate the default header that doxygen normally
# uses.
# Note: The header is subject to change so you typically have to regenerate the
# default header when upgrading to a newer version of doxygen. For a description
# of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER = @US_DOXYGEN_HEADER@
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
# generated HTML page. If the tag is left blank doxygen will generate a standard
# footer. See HTML_HEADER for more information on how to generate a default
# footer and what special commands can be used inside the footer. See also
# section "Doxygen usage" for information on how to generate the default footer
# that doxygen normally uses.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FOOTER = @US_DOXYGEN_FOOTER@
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
# sheet that is used by each HTML page. It can be used to fine-tune the look of
# the HTML output. If left blank doxygen will generate a default style sheet.
# See also section "Doxygen usage" for information on how to generate the style
# sheet that doxygen normally uses.
# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
# it is more robust and this tag (HTML_STYLESHEET) will in the future become
# obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_STYLESHEET =
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
# created by doxygen. Using this option one can overrule certain style aspects.
# This is preferred over using HTML_STYLESHEET since it does not replace the
# standard style sheet and is therefore more robust against future updates.
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list).
# Note: Since the styling of scrollbars can currently not be overruled in
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
# one or more extra stylesheets have been specified. So if scrollbar
# customization is desired it has to be added explicitly. For an example see the
# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET = "@US_DOXYGEN_EXTRA_CSS@"
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
# that these files will be copied to the base HTML output directory. Use the
# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
# files will be copied as-is; there are no commands or markers available.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_FILES =
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
# should be rendered with a dark or light theme.
# Possible values are: LIGHT always generate light mode output, DARK always
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
# the user preference, use light mode if no preference is set (the default),
# AUTO_DARK automatically set the mode according to the user preference, use
# dark mode if no preference is set and TOGGLE allow to user to switch between
# light and dark mode via a button.
# The default value is: AUTO_LIGHT.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE = AUTO_LIGHT
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a color-wheel, see
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again.
# Minimum value: 0, maximum value: 359, default value: 220.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_HUE = 220
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
# in the HTML output. For a value of 0 the output will use gray-scales only. A
# value of 255 will produce the most vivid colors.
# Minimum value: 0, maximum value: 255, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_SAT = 100
# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
# luminance component of the colors in the HTML output. Values below 100
# gradually make the output lighter, whereas values above 100 make the output
# darker. The value divided by 100 is the actual gamma applied, so 80 represents
# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
# change the gamma.
# Minimum value: 40, maximum value: 240, default value: 80.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
# to YES can help to show when doxygen was last run and thus if the
# documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = YES
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
# are dynamically created via JavaScript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
# page. Disable this option to support browsers that do not have JavaScript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_MENUS = YES
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_SECTIONS = NO
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand
# and collapse entries dynamically later on. Doxygen will expand the tree to
# such a level that at most the specified number of entries are visible (unless
# a fully collapsed tree already exceeds this amount). So setting the number of
# entries 1 will produce a full collapsed tree by default. 0 is a special value
# representing an infinite number of entries and will result in a full expanded
# tree by default.
# Minimum value: 0, maximum value: 9999, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
# environment (see:
# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
# create a documentation set, doxygen will generate a Makefile in the HTML
# output directory. Running make will produce the docset in that directory and
# running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_DOCSET = NO
# This tag determines the name of the docset feed. A documentation feed provides
# an umbrella under which multiple documentation sets from a single provider
# (such as a company or product suite) can be grouped.
# The default value is: Doxygen generated docs.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDNAME = "Doxygen generated docs"
# This tag determines the URL of the docset feed. A documentation feed provides
# an umbrella under which multiple documentation sets from a single provider
# (such as a company or product suite) can be grouped.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDURL =
# This tag specifies a string that should uniquely identify the documentation
# set bundle. This should be a reverse domain-name style string, e.g.
# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_BUNDLE_ID = org.doxygen.Project
# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
# the documentation publisher. This should be a reverse domain-name style
# string, e.g. com.mycompany.MyDocSet.documentation.
# The default value is: org.doxygen.Publisher.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
# The default value is: Publisher.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
# on Windows. In the beginning of 2021 Microsoft took the original page, with
# a.o. the download links, offline the HTML help workshop was already many years
# in maintenance mode). You can download the HTML help workshop from the web
# archives at Installation executable (see:
# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
# files are now used as the Windows 98 help format, and will replace the old
# Windows help format (.hlp) on all Windows platforms in the future. Compressed
# HTML files also contain an index, a table of contents, and you can search for
# words in the documentation. The HTML workshop also contains a viewer for
# compressed HTML files.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_HTMLHELP = NO
# The CHM_FILE tag can be used to specify the file name of the resulting .chm
# file. You can add a path in front of the file if the result should not be
# written to the html output directory.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_FILE =
# The HHC_LOCATION tag can be used to specify the location (absolute path
# including file name) of the HTML help compiler (hhc.exe). If non-empty,
# doxygen will try to run the HTML help compiler on the generated index.hhp.
# The file has to be specified with full path.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated
# (YES) or that it should be included in the main .chm file (NO).
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
GENERATE_CHI = NO
# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
# and project file content.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_INDEX_ENCODING =
# The BINARY_TOC flag controls whether a binary table of contents is generated
# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
# enables the Previous and Next buttons.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members to
# the table of contents of the HTML help documentation and to the tree view.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
TOC_EXPAND = NO
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
# (.qch) of the generated HTML documentation.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_QHP = NO
# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
# the file name of the resulting .qch file. The path specified is relative to
# the HTML output folder.
# This tag requires that the tag GENERATE_QHP is set to YES.
QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
# (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_NAMESPACE =
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_VIRTUAL_FOLDER =
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
# Filters (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
# The QHG_LOCATION tag can be used to specify the location (absolute path
# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
# run qhelpgenerator on the generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
# generated, together with the HTML files, they form an Eclipse help plugin. To
# install this plugin and make it available under the help contents menu in
# Eclipse, the contents of the directory containing the HTML and XML files needs
# to be copied into the plugins directory of eclipse. The name of the directory
# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
# After copying Eclipse needs to be restarted before the help appears.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_ECLIPSEHELP = NO
# A unique identifier for the Eclipse help plugin. When installing the plugin
# the directory name containing the HTML and XML files should also have this
# name. Each documentation set should have its own identifier.
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
ECLIPSE_DOC_ID = org.doxygen.Project
# If you want full control over the layout of the generated HTML pages it might
# be necessary to disable the index and replace it with your own. The
# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
# of each HTML page. A value of NO enables the index and the value YES disables
# it. Since the tabs in the index contain the same information as the navigation
# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
DISABLE_INDEX = NO
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
# structure should be generated to display hierarchical information. If the tag
# value is set to YES, a side panel will be generated containing a tree-like
# index structure (just like the one that is generated for HTML Help). For this
# to work a browser that supports JavaScript, DHTML, CSS and frames is required
# (i.e. any modern browser). Windows users are probably better off using the
# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
# further fine tune the look of the index (see "Fine-tuning the output"). As an
# example, the default style sheet generated by doxygen has an example that
# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
# Since the tree basically has the same information as the tab index, you could
# consider setting DISABLE_INDEX to YES when enabling this option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = NO
# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
# area (value NO) or if it should extend to the full height of the window (value
# YES). Setting this to YES gives a layout similar to
# https://docs.readthedocs.io with more room for contents, but less room for the
# project logo, title, and description. If either GENERATE_TREEVIEW or
# DISABLE_INDEX is set to NO, this option has no effect.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
FULL_SIDEBAR = NO
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
# doxygen will group on one line in the generated HTML documentation.
#
# Note that a value of 0 will completely suppress the enum values from appearing
# in the overview section.
# Minimum value: 0, maximum value: 20, default value: 4.
# This tag requires that the tag GENERATE_HTML is set to YES.
ENUM_VALUES_PER_LINE = 4
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
# to set the initial width (in pixels) of the frame in which the tree is shown.
# Minimum value: 0, maximum value: 1500, default value: 250.
# This tag requires that the tag GENERATE_HTML is set to YES.
TREEVIEW_WIDTH = 300
# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
# external symbols imported via tag files in a separate window.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
EXT_LINKS_IN_WINDOW = NO
# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
# addresses.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
OBFUSCATE_EMAILS = YES
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
# the HTML output. These images will generally look nicer at scaled resolutions.
# Possible values are: png (the default) and svg (looks nicer but requires the
# pdf2svg or inkscape tool).
# The default value is: png.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FORMULA_FORMAT = png
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# doxygen run you need to manually remove any form_*.png images from the HTML
# output directory to force them to be regenerated.
# Minimum value: 8, maximum value: 50, default value: 10.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_FONTSIZE = 10
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details.
FORMULA_MACROFILE =
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# https://www.mathjax.org) which uses client side JavaScript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
# to it using the MATHJAX_RELPATH option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
USE_MATHJAX = NO
# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
# Note that the different versions of MathJax have different requirements with
# regards to the different settings, so it is possible that also other MathJax
# settings have to be changed when switching between the different MathJax
# versions.
# Possible values are: MathJax_2 and MathJax_3.
# The default value is: MathJax_2.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_VERSION = MathJax_2
# When MathJax is enabled you can set the default output format to be used for
# the MathJax output. For more details about the output format see MathJax
# version 2 (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
# (see:
# http://docs.mathjax.org/en/latest/web/components/output.html).
# Possible values are: HTML-CSS (which is slower, but has the best
# compatibility. This is the name for Mathjax version 2, for MathJax version 3
# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
# is the name for Mathjax version 3, for MathJax version 2 this will be
# translated into HTML-CSS) and SVG.
# The default value is: HTML-CSS.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_FORMAT = HTML-CSS
# When MathJax is enabled you need to specify the location relative to the HTML
# output directory using the MATHJAX_RELPATH option. The destination directory
# should contain the MathJax.js script. For instance, if the mathjax directory
# is located at the same level as the HTML output directory, then
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from https://www.mathjax.org before deployment. The default value is:
# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://www.mathjax.org/mathjax
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example
# for MathJax version 2 (see
# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# For example for MathJax version 3 (see
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
# MATHJAX_EXTENSIONS = ams
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
# (see:
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_CODEFILE =
# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
# the HTML output. The underlying search engine uses javascript and DHTML and
# should work on any modern browser. Note that when using HTML help
# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
# there is already a search function so this one should typically be disabled.
# For large projects the javascript based search engine can be slow, then
# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
# search using the keyboard; to jump to the search box use <access key> + S
# (what the <access key> is depends on the OS and browser, but it is typically
# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
# key> to jump into the search results window, the results can be navigated
# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
# the search. The filter options can be selected when the cursor is inside the
# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
# to select a filter and <Enter> or <escape> to activate or cancel the filter
# option.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
# implemented using a web server instead of a web client using JavaScript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
# and searching needs to be provided by external tools. See the section
# "External Indexing and Searching" for details.
# The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES.
SERVER_BASED_SEARCH = NO
# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
# script for searching. Instead the search results are written to an XML file
# which needs to be processed by an external indexer. Doxygen will invoke an
# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
# search results.
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see:
# https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH = NO
# The SEARCHENGINE_URL should point to a search engine hosted by a web server
# which will return the search results when EXTERNAL_SEARCH is enabled.
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see:
# https://xapian.org/). See the section "External Indexing and Searching" for
# details.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
# search data is written to a file for indexing by an external tool. With the
# SEARCHDATA_FILE tag the name of this file can be specified.
# The default file is: searchdata.xml.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHDATA_FILE = searchdata.xml
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
# projects and redirect the results back to the right project.
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH_ID =
# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
# projects other than the one defined by this configuration file, but that are
# all added to the same external search index. Each project needs to have a
# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
# to a relative location where the documentation can be found. The format is:
# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
# The default value is: YES.
GENERATE_LATEX = NO
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: latex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked.
#
# Note that when not enabling USE_PDFLATEX the default is latex when enabling
# USE_PDFLATEX the default is pdflatex and when in the later case latex is
# chosen this is overwritten by pdflatex. For specific output languages the
# default can have been set differently, this depends on the implementation of
# the output language.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
# index for LaTeX.
# Note: This tag is used in the Makefile / make.bat.
# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
# (.tex).
# The default file is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
# generate index for LaTeX. In case there is no backslash (\) as first character
# it will be automatically added in the LaTeX code.
# Note: This tag is used in the generated output file (.tex).
# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
# The default value is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_MAKEINDEX_CMD = makeindex
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used by the
# printer.
# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
# 14 inches) and executive (7.25 x 10.5 inches).
# The default value is: a4.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PAPER_TYPE = a4wide
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. The package can be specified just
# by its name or with the correct syntax as to be used with the LaTeX
# \usepackage command. To get the times font for instance you can specify :
# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
# To use the option intlimits with the amsmath package you can specify:
# EXTRA_PACKAGES=[intlimits]{amsmath}
# If left blank no extra packages will be included.
# This tag requires that the tag GENERATE_LATEX is set to YES.
EXTRA_PACKAGES = amssymb
# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
# the generated LaTeX document. The header should contain everything until the
# first chapter. If it is left blank doxygen will generate a standard header. It
# is highly recommended to start with a default header using
# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
# and then modify the file new_header.tex. See also section "Doxygen usage" for
# information on how to generate the default header that doxygen normally uses.
#
# Note: Only use a user-defined header if you know what you are doing!
# Note: The header is subject to change so you typically have to regenerate the
# default header when upgrading to a newer version of doxygen. The following
# commands have a special meaning inside the header (and footer): For a
# description of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
# the generated LaTeX document. The footer should contain everything after the
# last chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer. See also section "Doxygen
# usage" for information on how to generate the default footer that doxygen
# normally uses. Note: Only use a user-defined footer if you know what you are
# doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# LaTeX style sheets that are included after the standard style sheets created
# by doxygen. Using this option one can overrule certain style aspects. Doxygen
# will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list).
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_STYLESHEET =
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the LATEX_OUTPUT output
# directory. Note that the files will be copied as-is; there are no commands or
# markers available.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_FILES =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
# contain links (just like the HTML output) instead of page references. This
# makes the output suitable for online browsing using a PDF viewer.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = NO
# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
# files. Set this option to YES, to get a higher quality PDF documentation.
#
# See also section LATEX_CMD_NAME for selecting the engine.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = NO
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
# if errors occur, instead of asking the user for help.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BATCHMODE = NO
# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
# index chapters (such as File Index, Compound Index, etc.) in the output.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HIDE_INDICES = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# The default value is: plain.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered,
# it will be relative to the LATEX_OUTPUT directory. If left blank the
# LATEX_OUTPUT directory will be used.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EMOJI_DIRECTORY =
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
# RTF output is optimized for Word 97 and may not look too pretty with other RTF
# readers/editors.
# The default value is: NO.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: rtf.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
# contain hyperlink fields. The RTF file will contain links (just like the HTML
# output) instead of page references. This makes the output suitable for online
# browsing using Word or some other Word compatible readers that support those
# fields.
#
# Note: WordPad (write) and others do not support links.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# configuration file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
#
# See also section "Doxygen usage" for information on how to generate the
# default style sheet that doxygen normally uses.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
# similar to doxygen's configuration file. A template extensions file can be
# generated using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
# classes and files.
# The default value is: NO.
GENERATE_MAN = NO
# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it. A directory man3 will be created inside the directory specified by
# MAN_OUTPUT.
# The default directory is: man.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_OUTPUT = man
# The MAN_EXTENSION tag determines the extension that is added to the generated
# man pages. In case the manual section does not start with a number, the number
# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
# optional.
# The default value is: .3.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_EXTENSION = .3
# The MAN_SUBDIR tag determines the name of the directory created within
# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
# MAN_EXTENSION with the initial . removed.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_SUBDIR =
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
# will generate one additional man file for each entity documented in the real
# man page(s). These additional files only source the real man page, but without
# them the man command would be unable to find the correct page.
# The default value is: NO.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_LINKS = NO
#---------------------------------------------------------------------------
# Configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
# captures the structure of the code including all documentation.
# The default value is: NO.
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: xml.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_OUTPUT = xml
# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
# listings (including syntax highlighting and cross-referencing information) to
# the XML output. Note that enabling this will significantly increase the size
# of the XML output.
# The default value is: YES.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_PROGRAMLISTING = YES
# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
# namespace members in file scope as well, matching the HTML output.
# The default value is: NO.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_NS_MEMB_FILE_SCOPE = NO
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
# that can be used to generate PDF.
# The default value is: NO.
GENERATE_DOCBOOK = NO
# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
# front of it.
# The default directory is: docbook.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_OUTPUT = docbook
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
# the structure of the code including all documentation. Note that this feature
# is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
# file that captures the structure of the code including all documentation.
#
# Note that this feature is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_PERLMOD = NO
# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
# output from the Perl module output.
# The default value is: NO.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
# formatted so it can be parsed by a human reader. This is useful if you want to
# understand what is going on. On the other hand, if this tag is set to NO, the
# size of the Perl module output will be much smaller and Perl will parse it
# just the same.
# The default value is: YES.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_PRETTY = YES
# The names of the make variables in the generated doxyrules.make file are
# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
# so different doxyrules.make files included by the same Makefile don't
# overwrite each other's variables.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
# C-preprocessor directives found in the sources and include files.
# The default value is: YES.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
# in the source code. If set to NO, only conditional compilation will be
# performed. Macro expansion can be done in a controlled way by setting
# EXPAND_ONLY_PREDEF to YES.
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
# EXPAND_AS_DEFINED tags.
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = YES
# If the SEARCH_INCLUDES tag is set to YES, the include files in the
# INCLUDE_PATH will be searched if a #include is found.
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by the
# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
# RECURSIVE has no effect here.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH = "@PROJECT_BINARY_DIR@/include/"
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will be
# used.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
INCLUDE_FILE_PATTERNS = *.h
# The PREDEFINED tag can be used to specify one or more macro names that are
# defined before the preprocessor is started (similar to the -D option of e.g.
# gcc). The argument of the tag is a list of macros of the form: name or
# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
# is assumed. To prevent a macro definition from being undefined via #undef or
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = US_PREPEND_NAMESPACE(x)=x \
US_BEGIN_NAMESPACE= \
US_END_NAMESPACE= \
US_Core_EXPORT= \
US_ABI_LOCAL= \
US_MSVC_POP_WARNING=
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
# macro definition that is found in the sources will be used. Use the PREDEFINED
# tag if you want to use a different macro definition that overrules the
# definition found in the source code.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all references to function-like macros that are alone on a line, have
# an all uppercase name, and do not end with a semicolon. Such function macros
# are typically used for boiler-plate code, and will confuse the parser if not
# removed.
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration options related to external references
#---------------------------------------------------------------------------
# The TAGFILES tag can be used to specify one or more tag files. For each tag
# file the location of the external documentation should be added. The format of
# a tag file without this location is as follows:
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
# TAGFILES = file1=loc1 "file2 = loc2" ...
# where loc1 and loc2 can be relative or absolute paths or URLs. See the
# section "Linking to external documentation" for more information about the use
# of tag files.
# Note: Each tag file must have a unique name (where the name does NOT include
# the path). If a tag file is not located in the directory in which doxygen is
# run, you must also specify the path to the tagfile here.
TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
# tag file that is based on the input files it reads. See section "Linking to
# external documentation" for more information about the usage of tag files.
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# the class index. If set to NO, only the inherited external classes will be
# listed.
# The default value is: NO.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will be
# listed.
# The default value is: YES.
EXTERNAL_GROUPS = NO
# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
# the related pages index. If set to NO, only the current project's pages will
# be listed.
# The default value is: YES.
EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
DIA_PATH =
# If set to YES the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class.
# The default value is: YES.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz (see:
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: NO.
HAVE_DOT = @US_HAVE_DOT@
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of
# processors available in the system. You can set it explicitly to a value
# larger than 0 to get control over the balance between CPU load and processing
# speed.
# Minimum value: 0, maximum value: 32, default value: 0.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NUM_THREADS = @US_DOXYGEN_DOT_NUM_THREADS@
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
# subgraphs. When you want a differently looking font in the dot files that
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
# Edge and Graph Attributes specification</a> You need to make sure dot is able
# to find the font, which can be done by putting it in a standard location or by
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
# directory containing the font. Default graphviz fontsize is 14.
# The default value is: fontname=Helvetica,fontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
# arrows shapes.</a>
# The default value is: labelfontname=Helvetica,labelfontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
# around nodes set 'shape=plain' or 'shape=plaintext' <a
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
# The default value is: shape=box,height=0.2,width=0.4.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
# You can set the path where dot can find font specified with fontname in
# DOT_COMMON_ATTR and others dot attributes.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
# graph for each documented class showing the direct and indirect inheritance
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
# to TEXT the direct and indirect inheritance relations will be shown as texts /
# links.
# Possible values are: NO, YES, TEXT and GRAPH.
# The default value is: YES.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
# graph for each documented class showing the direct and indirect implementation
# dependencies (inheritance, containment, and class references variables) of the
# class with other documented classes.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
# groups, showing the direct groups dependencies. See also the chapter Grouping
# in the manual.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GROUP_GRAPHS = YES
# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
UML_LOOK = NO
# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
# class node. If there are many fields or methods and many nodes the graph may
# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
# number of items for each type to make the size more manageable. Set this to 0
# for no limit. Note that the threshold may be exceeded by 50% before the limit
# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
# but if the number exceeds 15, the total amount of fields shown is limited to
# 10.
# Minimum value: 0, maximum value: 100, default value: 10.
# This tag requires that the tag UML_LOOK is set to YES.
UML_LIMIT_NUM_FIELDS = 10
# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
# tag is set to YES, doxygen will add type and arguments for attributes and
# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
# will not generate fields with class member information in the UML graphs. The
# class diagrams will look similar to the default class diagrams but using UML
# notation for the relationships.
# Possible values are: NO, YES and NONE.
# The default value is: NO.
# This tag requires that the tag UML_LOOK is set to YES.
DOT_UML_DETAILS = NO
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold
# significantly it will wrapped across multiple lines. Some heuristics are apply
# to avoid ugly line breaks.
# Minimum value: 0, maximum value: 1000, default value: 17.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_WRAP_THRESHOLD = 17
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
# collaboration graphs will show the relations between templates and their
# instances.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
TEMPLATE_RELATIONS = YES
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
# YES then doxygen will generate a graph for each documented file showing the
# direct and indirect include dependencies of the file with other documented
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDE_GRAPH = NO
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
# set to YES then doxygen will generate a graph for each documented file showing
# the direct and indirect include dependencies of the file with other documented
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDED_BY_GRAPH = NO
# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
# dependency graph for every global function or class method.
#
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable call graphs for selected
# functions only using the \callgraph command. Disabling a call graph can be
# accomplished by means of the command \hidecallgraph.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
CALL_GRAPH = NO
# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
# dependency graph for every global function or class method.
#
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable caller graphs for selected
# functions only using the \callergraph command. Disabling a caller graph can be
# accomplished by means of the command \hidecallergraph.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
CALLER_GRAPH = NO
# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
# hierarchy of all classes instead of a textual one.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GRAPHICAL_HIERARCHY = NO
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
# dependencies a directory has on other directories in a graphical way. The
# dependency relations are determined by the #include relations between the
# files in the directories.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
DIRECTORY_GRAPH = NO
# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels
# of child directories generated in directory dependency graphs by dot.
# Minimum value: 1, maximum value: 25, default value: 1.
# This tag requires that the tag DIRECTORY_GRAPH is set to YES.
DIR_GRAPH_MAX_DEPTH = 1
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see:
# http://www.graphviz.org/)).
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus.
# The default value is: png.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_IMAGE_FORMAT = png
# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
# enable generation of interactive SVG images that allow zooming and panning.
#
# Note that this requires a modern browser other than Internet Explorer. Tested
# and working are Firefox, Chrome, Safari, and Opera.
# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
# the SVG files visible. Older versions of IE do not have SVG support.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
INTERACTIVE_SVG = NO
# The DOT_PATH tag can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found in the path.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_PATH = @US_DOXYGEN_DOT_PATH@
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the \dotfile
# command).
# This tag requires that the tag HAVE_DOT is set to YES.
DOTFILE_DIRS =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
MSCFILE_DIRS =
# The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile
# command).
DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
# path where java can find the plantuml.jar file or to the filename of jar file
# to be used. If left blank, it is assumed PlantUML is not used or called during
# a preprocessing step. Doxygen will generate a warning when it encounters a
# \startuml command in this case and will not generate output for the diagram.
PLANTUML_JAR_PATH =
# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
# configuration file for plantuml.
PLANTUML_CFG_FILE =
# When using plantuml, the specified paths are searched for files specified by
# the !include statement in a plantuml block.
PLANTUML_INCLUDE_PATH =
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
# that will be shown in the graph. If the number of nodes in a graph becomes
# larger than this value, doxygen will truncate the graph, which is visualized
# by representing a node as a red box. Note that doxygen if the number of direct
# children of the root node in a graph is already larger than
# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
# Minimum value: 0, maximum value: 10000, default value: 50.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_GRAPH_MAX_NODES = 50
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
# generated by dot. A depth value of 3 means that only nodes reachable from the
# root by following a path via at most 3 edges will be shown. Nodes that lay
# further from the root node will be omitted. Note that setting this option to 1
# or 2 may greatly reduce the computation time needed for large code bases. Also
# note that the size of a graph can be further restricted by
# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
# Minimum value: 0, maximum value: 1000, default value: 0.
# This tag requires that the tag HAVE_DOT is set to YES.
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
# this, this feature is disabled by default.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_MULTI_TARGETS = NO
# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
# explaining the meaning of the various boxes and arrows in the dot generated
# graphs.
# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal
# graphical representation for inheritance and collaboration diagrams is used.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs.
#
# Note: This setting is not only used for dot files but also for msc temporary
# files.
# The default value is: YES.
DOT_CLEANUP = YES
diff --git a/Modules/CppMicroServices/third_party/miniz.c b/Modules/CppMicroServices/third_party/miniz.c
index 044942157c..4b93de6fc5 100644
--- a/Modules/CppMicroServices/third_party/miniz.c
+++ b/Modules/CppMicroServices/third_party/miniz.c
@@ -1,4928 +1,4928 @@
/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
See "unlicense" statement at the end of this file.
Rich Geldreich <richgel99@gmail.com>, last updated Oct. 13, 2013
Implements RFC 1950: https://www.ietf.org/rfc/rfc1950.txt and RFC 1951: https://www.ietf.org/rfc/rfc1951.txt
Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define
MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros).
* Change History
10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!):
- Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug
- would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place()
+ would only have occurred in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place()
(which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag).
- Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size
- Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries.
Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice).
- Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes
- mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed
- Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6.
- Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti
- Merged MZ_FORCEINLINE fix from hdeanclark
- Fix <time.h> include before config #ifdef, thanks emil.brink
- Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can
set it to 1 for real-time compression).
- Merged in some compiler fixes from paulharris's github repro.
- Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3.
- Added example6.c, which dumps an image of the mandelbrot set to a PNG file.
- Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more.
- In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled
- In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch
5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include <time.h> (thanks fermtect).
5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit.
- Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files.
- Eliminated a bunch of warnings when compiling with GCC 32-bit/64.
- Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly
"Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning).
- Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64.
- Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test.
- Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives.
- Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.)
- Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself).
4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's.
level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson <bruced@valvesoftware.com> for the feedback/bug report.
5/28/11 v1.11 - Added statement from unlicense.org
5/27/11 v1.10 - Substantial compressor optimizations:
- Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a
- Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86).
- Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types.
- Refactored the compression code for better readability and maintainability.
- Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large
drop in throughput on some files).
5/15/11 v1.09 - Initial stable release.
* Low-level Deflate/Inflate implementation notes:
Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or
greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses
approximately as well as zlib.
Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function
coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory
block large enough to hold the entire file.
The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation.
* zlib-style API notes:
miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in
zlib replacement in many apps:
The z_stream struct, optional memory allocation callbacks
deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound
inflateInit/inflateInit2/inflate/inflateEnd
compress, compress2, compressBound, uncompress
CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines.
Supports raw deflate streams or standard zlib streams with adler-32 checking.
Limitations:
The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries.
I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but
there are no guarantees that miniz.c pulls this off perfectly.
* PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by
Alex Evans. Supports 1-4 bytes/pixel images.
* ZIP archive API notes:
The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to
get the job done with minimal fuss. There are simple API's to retrieve file information, read files from
existing archives, create new archives, append new files to existing archives, or clone archive data from
one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h),
or you can specify custom file read/write callbacks.
- Archive reading: Just call this function to read a single file from a disk archive:
void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name,
size_t *pSize, mz_uint zip_flags);
For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central
directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files.
- Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file:
int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
The locate operation can optionally check file comments too, which (as one example) can be used to identify
multiple versions of the same file in an archive. This function uses a simple linear search through the central
directory, so it's not very fast.
Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and
retrieve detailed info on each file by calling mz_zip_reader_file_stat().
- Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data
to disk and builds an exact image of the central directory in memory. The central directory image is written
all at once at the end of the archive file when the archive is finalized.
The archive writer can optionally align each file's local header and file data to any power of 2 alignment,
which can be useful when the archive will be read from optical media. Also, the writer supports placing
arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still
readable by any ZIP tool.
- Archive appending: The simple way to add a single file to an archive is to call this function:
mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name,
const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
The archive will be created if it doesn't already exist, otherwise it'll be appended to.
Note the appending is done in-place and is not an atomic operation, so if something goes wrong
during the operation it's possible the archive could be left without a central directory (although the local
file headers and file data will be fine, so the archive will be recoverable).
For more complex archive modification scenarios:
1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to
preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the
compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and
you're done. This is safe but requires a bunch of temporary disk space or heap memory.
2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(),
append new files as needed, then finalize the archive which will write an updated central directory to the
original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a
possibility that the archive's central directory could be lost with this method if anything goes wrong, though.
- ZIP archive support limitations:
No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files.
Requires streams capable of seeking.
* This is a header file library, like stb_image.c. To get only a header file, either cut and paste the
below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it.
* Important: For best perf. be sure to customize the below macros for your target platform:
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
#define MINIZ_LITTLE_ENDIAN 1
#define MINIZ_HAS_64BIT_REGISTERS 1
* On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz
uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files
(i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes).
*/
#ifndef MINIZ_HEADER_INCLUDED
#define MINIZ_HEADER_INCLUDED
#include <stdlib.h>
// Defines to completely disable specific portions of miniz.c:
// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl.
// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O.
//#define MINIZ_NO_STDIO
// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or
// get/set file times, and the C run-time funcs that get/set times won't be called.
// The current downside is the times written to your archives will be from 1979.
//#define MINIZ_NO_TIME
// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's.
//#define MINIZ_NO_ARCHIVE_APIS
// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's.
//#define MINIZ_NO_ARCHIVE_WRITING_APIS
// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's.
//#define MINIZ_NO_ZLIB_APIS
// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib.
//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc.
// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc
// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user
// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work.
//#define MINIZ_NO_MALLOC
#if defined(__TINYC__) && (defined(__linux) || defined(__linux__))
// TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux
#define MINIZ_NO_TIME
#endif
#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS)
#include <time.h>
#endif
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__)
// MINIZ_X86_OR_X64_CPU is only used to help set the below macros.
#define MINIZ_X86_OR_X64_CPU 1
#endif
#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian.
#define MINIZ_LITTLE_ENDIAN 1
#endif
#if MINIZ_X86_OR_X64_CPU
// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses.
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
#endif
#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)
// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions).
#define MINIZ_HAS_64BIT_REGISTERS 1
#endif
#ifdef __cplusplus
extern "C" {
#endif
// ------------------- zlib-style API Definitions.
// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits!
typedef unsigned long mz_ulong;
// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap.
void mz_free(void *p);
#define MZ_ADLER32_INIT (1)
// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL.
mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len);
#define MZ_CRC32_INIT (0)
// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL.
mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len);
// Compression strategies.
enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 };
// Method
#define MZ_DEFLATED 8
#ifndef MINIZ_NO_ZLIB_APIS
// Heap allocation callbacks.
// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long.
typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size);
typedef void (*mz_free_func)(void *opaque, void *address);
typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size);
#define MZ_VERSION "9.1.15"
#define MZ_VERNUM 0x91F0
#define MZ_VER_MAJOR 9
#define MZ_VER_MINOR 1
#define MZ_VER_REVISION 15
#define MZ_VER_SUBREVISION 0
// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs).
enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 };
// Return status codes. MZ_PARAM_ERROR is non-standard.
enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 };
// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL.
enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 };
// Window bits
#define MZ_DEFAULT_WINDOW_BITS 15
struct mz_internal_state;
// Compression/decompression stream struct.
typedef struct mz_stream_s
{
const unsigned char *next_in; // pointer to next byte to read
unsigned int avail_in; // number of bytes available at next_in
mz_ulong total_in; // total number of bytes consumed so far
unsigned char *next_out; // pointer to next byte to write
unsigned int avail_out; // number of bytes that can be written to next_out
mz_ulong total_out; // total number of bytes produced so far
char *msg; // error msg (unused)
struct mz_internal_state *state; // internal state, allocated by zalloc/zfree
mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc)
mz_free_func zfree; // optional heap free function (defaults to free)
void *opaque; // heap alloc function user pointer
int data_type; // data_type (unused)
mz_ulong adler; // adler32 of the source or uncompressed data
mz_ulong reserved; // not used
} mz_stream;
typedef mz_stream *mz_streamp;
// Returns the version string of miniz.c.
const char *mz_version(void);
// mz_deflateInit() initializes a compressor with default options:
// Parameters:
// pStream must point to an initialized mz_stream struct.
// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION].
// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio.
// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.)
// Return values:
// MZ_OK on success.
// MZ_STREAM_ERROR if the stream is bogus.
// MZ_PARAM_ERROR if the input parameters are bogus.
// MZ_MEM_ERROR on out of memory.
int mz_deflateInit(mz_streamp pStream, int level);
// mz_deflateInit2() is like mz_deflate(), except with more control:
// Additional parameters:
// method must be MZ_DEFLATED
// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer)
// mem_level must be between [1, 9] (it's checked but ignored by miniz.c)
int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy);
// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2().
int mz_deflateReset(mz_streamp pStream);
// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible.
// Parameters:
// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH.
// Return values:
// MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full).
// MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore.
// MZ_STREAM_ERROR if the stream is bogus.
// MZ_PARAM_ERROR if one of the parameters is invalid.
// MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.)
int mz_deflate(mz_streamp pStream, int flush);
// mz_deflateEnd() deinitializes a compressor:
// Return values:
// MZ_OK on success.
// MZ_STREAM_ERROR if the stream is bogus.
int mz_deflateEnd(mz_streamp pStream);
// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH.
mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len);
// Single-call compression functions mz_compress() and mz_compress2():
// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure.
int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level);
// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress().
mz_ulong mz_compressBound(mz_ulong source_len);
// Initializes a decompressor.
int mz_inflateInit(mz_streamp pStream);
// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer:
// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate).
int mz_inflateInit2(mz_streamp pStream, int window_bits);
// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible.
// Parameters:
// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH.
// On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster).
// MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data.
// Return values:
// MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full.
// MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified.
// MZ_STREAM_ERROR if the stream is bogus.
// MZ_DATA_ERROR if the deflate stream is invalid.
// MZ_PARAM_ERROR if one of the parameters is invalid.
// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again
// with more input data, or with more room in the output buffer (except when using single call decompression, described above).
int mz_inflate(mz_streamp pStream, int flush);
// Deinitializes a decompressor.
int mz_inflateEnd(mz_streamp pStream);
// Single-call decompression.
// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure.
int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
// Returns a string description of the specified error code, or NULL if the error code is invalid.
const char *mz_error(int err);
// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports.
// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project.
#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
typedef unsigned char Byte;
typedef unsigned int uInt;
typedef mz_ulong uLong;
typedef Byte Bytef;
typedef uInt uIntf;
typedef char charf;
typedef int intf;
typedef void *voidpf;
typedef uLong uLongf;
typedef void *voidp;
typedef void *const voidpc;
#define Z_NULL 0
#define Z_NO_FLUSH MZ_NO_FLUSH
#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH
#define Z_SYNC_FLUSH MZ_SYNC_FLUSH
#define Z_FULL_FLUSH MZ_FULL_FLUSH
#define Z_FINISH MZ_FINISH
#define Z_BLOCK MZ_BLOCK
#define Z_OK MZ_OK
#define Z_STREAM_END MZ_STREAM_END
#define Z_NEED_DICT MZ_NEED_DICT
#define Z_ERRNO MZ_ERRNO
#define Z_STREAM_ERROR MZ_STREAM_ERROR
#define Z_DATA_ERROR MZ_DATA_ERROR
#define Z_MEM_ERROR MZ_MEM_ERROR
#define Z_BUF_ERROR MZ_BUF_ERROR
#define Z_VERSION_ERROR MZ_VERSION_ERROR
#define Z_PARAM_ERROR MZ_PARAM_ERROR
#define Z_NO_COMPRESSION MZ_NO_COMPRESSION
#define Z_BEST_SPEED MZ_BEST_SPEED
#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION
#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION
#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY
#define Z_FILTERED MZ_FILTERED
#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY
#define Z_RLE MZ_RLE
#define Z_FIXED MZ_FIXED
#define Z_DEFLATED MZ_DEFLATED
#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS
#define alloc_func mz_alloc_func
#define free_func mz_free_func
#define internal_state mz_internal_state
#define z_stream mz_stream
#define deflateInit mz_deflateInit
#define deflateInit2 mz_deflateInit2
#define deflateReset mz_deflateReset
#define deflate mz_deflate
#define deflateEnd mz_deflateEnd
#define deflateBound mz_deflateBound
#define compress mz_compress
#define compress2 mz_compress2
#define compressBound mz_compressBound
#define inflateInit mz_inflateInit
#define inflateInit2 mz_inflateInit2
#define inflate mz_inflate
#define inflateEnd mz_inflateEnd
#define uncompress mz_uncompress
#define crc32 mz_crc32
#define adler32 mz_adler32
#define MAX_WBITS 15
#define MAX_MEM_LEVEL 9
#define zError mz_error
#define ZLIB_VERSION MZ_VERSION
#define ZLIB_VERNUM MZ_VERNUM
#define ZLIB_VER_MAJOR MZ_VER_MAJOR
#define ZLIB_VER_MINOR MZ_VER_MINOR
#define ZLIB_VER_REVISION MZ_VER_REVISION
#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION
#define zlibVersion mz_version
#define zlib_version mz_version()
#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
#endif // MINIZ_NO_ZLIB_APIS
// ------------------- Types and macros
typedef unsigned char mz_uint8;
typedef signed short mz_int16;
typedef unsigned short mz_uint16;
typedef unsigned int mz_uint32;
typedef unsigned int mz_uint;
typedef long long mz_int64;
typedef unsigned long long mz_uint64;
typedef int mz_bool;
#define MZ_FALSE (0)
#define MZ_TRUE (1)
// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message.
#ifdef _MSC_VER
#define MZ_MACRO_END while (0, 0)
#else
#define MZ_MACRO_END while (0)
#endif
// ------------------- ZIP archive reading/writing
#ifndef MINIZ_NO_ARCHIVE_APIS
enum
{
MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024,
MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260,
MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256
};
typedef struct
{
mz_uint32 m_file_index;
mz_uint32 m_central_dir_ofs;
mz_uint16 m_version_made_by;
mz_uint16 m_version_needed;
mz_uint16 m_bit_flag;
mz_uint16 m_method;
#ifndef MINIZ_NO_TIME
time_t m_time;
#endif
mz_uint32 m_crc32;
mz_uint64 m_comp_size;
mz_uint64 m_uncomp_size;
mz_uint16 m_internal_attr;
mz_uint32 m_external_attr;
mz_uint64 m_local_header_ofs;
mz_uint32 m_comment_size;
char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE];
char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE];
} mz_zip_archive_file_stat;
typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n);
typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n);
struct mz_zip_internal_state_tag;
typedef struct mz_zip_internal_state_tag mz_zip_internal_state;
typedef enum
{
MZ_ZIP_MODE_INVALID = 0,
MZ_ZIP_MODE_READING = 1,
MZ_ZIP_MODE_WRITING = 2,
MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3
} mz_zip_mode;
typedef struct mz_zip_archive_tag
{
mz_uint64 m_archive_size;
mz_uint64 m_archive_file_ofs;
mz_uint64 m_central_directory_file_ofs;
mz_uint m_total_files;
mz_zip_mode m_zip_mode;
mz_uint m_file_offset_alignment;
mz_alloc_func m_pAlloc;
mz_free_func m_pFree;
mz_realloc_func m_pRealloc;
void *m_pAlloc_opaque;
mz_file_read_func m_pRead;
mz_file_write_func m_pWrite;
void *m_pIO_opaque;
mz_zip_internal_state *m_pState;
} mz_zip_archive;
typedef enum
{
MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100,
MZ_ZIP_FLAG_IGNORE_PATH = 0x0200,
MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400,
MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800
} mz_zip_flags;
// ZIP archive reading
// Inits a ZIP archive reader.
// These functions read and validate the archive's central directory.
mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags);
mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags);
#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags);
#endif
// Returns the total number of files in the archive.
mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip);
// Returns detailed information about an archive file entry.
mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);
// Determines if an archive file entry is a directory entry.
mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index);
mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index);
// Retrieves the filename of an archive file entry.
// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename.
mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size);
// Attempts to locates a file in the archive's central directory.
// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH
// Returns -1 if the file cannot be found.
int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
// Extracts a archive file to a memory buffer using no memory allocation.
mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
// Extracts a archive file to a memory buffer.
mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags);
mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags);
// Extracts a archive file to a dynamically allocated heap buffer.
void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags);
void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags);
// Extracts a archive file using a callback function to output the file's data.
mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
#ifndef MINIZ_NO_STDIO
// Extracts a archive file to a disk file and sets its last accessed and modified times.
// This function only extracts files, not archive directory records.
mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags);
mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags);
#endif
// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used.
mz_bool mz_zip_reader_end(mz_zip_archive *pZip);
// ZIP archive writing
#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
// Inits a ZIP archive writer.
mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size);
mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size);
#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning);
#endif
// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive.
// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called.
// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it).
// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL.
// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before
// the archive is finalized the file's central directory will be hosed.
mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename);
// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive.
// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer.
// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags);
mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32);
#ifndef MINIZ_NO_STDIO
// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive.
// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
#endif
// Adds a file to an archive by fully cloning the data from another archive.
// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields.
mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index);
// Finalizes the archive by writing the central directory records followed by the end of central directory record.
// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end().
// An archive must be manually finalized by calling this function for it to be valid.
mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip);
mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize);
// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used.
// Note for the archive to be valid, it must have been finalized before ending.
mz_bool mz_zip_writer_end(mz_zip_archive *pZip);
// Misc. high-level helper functions:
// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive.
// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
// Reads a single file from an archive into a heap block.
// Returns NULL on failure.
void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags);
#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
#endif // #ifndef MINIZ_NO_ARCHIVE_APIS
// ------------------- Low-level Decompression API Definitions
// Decompression flags used by tinfl_decompress().
// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream.
// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input.
// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB).
// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes.
enum
{
TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
TINFL_FLAG_HAS_MORE_INPUT = 2,
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
TINFL_FLAG_COMPUTE_ADLER32 = 8
};
// High level decompression functions:
// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc().
// On entry:
// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress.
// On return:
// Function returns a pointer to the decompressed data, or NULL on failure.
// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data.
// The caller must call mz_free() on the returned block when it's no longer needed.
void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory.
// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success.
#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer.
// Returns 1 on success or 0 on failure.
typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor;
// Max size of LZ dictionary.
#define TINFL_LZ_DICT_SIZE 32768
// Return status.
typedef enum
{
TINFL_STATUS_BAD_PARAM = -3,
TINFL_STATUS_ADLER32_MISMATCH = -2,
TINFL_STATUS_FAILED = -1,
TINFL_STATUS_DONE = 0,
TINFL_STATUS_NEEDS_MORE_INPUT = 1,
TINFL_STATUS_HAS_MORE_OUTPUT = 2
} tinfl_status;
// Initializes the decompressor to its initial state.
#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END
#define tinfl_get_adler32(r) (r)->m_check_adler32
// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability.
// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output.
tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
// Internal/private bits follow.
enum
{
TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19,
TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
};
typedef struct
{
mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];
mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
} tinfl_huff_table;
#if MINIZ_HAS_64BIT_REGISTERS
#define TINFL_USE_64BIT_BITBUF 1
#endif
#if TINFL_USE_64BIT_BITBUF
typedef mz_uint64 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (64)
#else
typedef mz_uint32 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (32)
#endif
struct tinfl_decompressor_tag
{
mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
tinfl_bit_buf_t m_bit_buf;
size_t m_dist_from_out_buf_start;
tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];
mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
};
// ------------------- Low-level Compression API Definitions
// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently).
#define TDEFL_LESS_MEMORY 0
// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search):
// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression).
enum
{
TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF
};
// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data.
// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers).
// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing.
// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory).
// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1)
// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled.
// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables.
// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks.
// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK).
enum
{
TDEFL_WRITE_ZLIB_HEADER = 0x01000,
TDEFL_COMPUTE_ADLER32 = 0x02000,
TDEFL_GREEDY_PARSING_FLAG = 0x04000,
TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000,
TDEFL_RLE_MATCHES = 0x10000,
TDEFL_FILTER_MATCHES = 0x20000,
TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000,
TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000
};
// High level compression functions:
// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc().
// On entry:
// pSrc_buf, src_buf_len: Pointer and size of source block to compress.
// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression.
// On return:
// Function returns a pointer to the compressed data, or NULL on failure.
// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data.
// The caller must free() the returned block when it's no longer needed.
void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory.
// Returns 0 on failure.
size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
// Compresses an image to a compressed PNG file in memory.
// On entry:
// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4.
// The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory.
// level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL
// If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps).
// On return:
// Function returns a pointer to the compressed data, or NULL on failure.
// *pLen_out will be set to the size of the PNG image file.
// The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed.
void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip);
void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out);
// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time.
typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally.
mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 };
// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes).
#if TDEFL_LESS_MEMORY
enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
#else
enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
#endif
// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions.
typedef enum
{
TDEFL_STATUS_BAD_PARAM = -2,
TDEFL_STATUS_PUT_BUF_FAILED = -1,
TDEFL_STATUS_OKAY = 0,
TDEFL_STATUS_DONE = 1,
} tdefl_status;
// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums
typedef enum
{
TDEFL_NO_FLUSH = 0,
TDEFL_SYNC_FLUSH = 2,
TDEFL_FULL_FLUSH = 3,
TDEFL_FINISH = 4
} tdefl_flush;
// tdefl's compression state structure.
typedef struct
{
tdefl_put_buf_func_ptr m_pPut_buf_func;
void *m_pPut_buf_user;
mz_uint m_flags, m_max_probes[2];
int m_greedy_parsing;
mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size;
mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end;
mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer;
mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish;
tdefl_status m_prev_return_status;
const void *m_pIn_buf;
void *m_pOut_buf;
size_t *m_pIn_buf_size, *m_pOut_buf_size;
tdefl_flush m_flush;
const mz_uint8 *m_pSrc;
size_t m_src_buf_left, m_out_buf_ofs;
mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1];
mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE];
mz_uint16 m_next[TDEFL_LZ_DICT_SIZE];
mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE];
mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE];
} tdefl_compressor;
// Initializes the compressor.
// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory.
// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression.
// If pBut_buf_func is NULL the user should always call the tdefl_compress() API.
// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.)
tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible.
tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush);
// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr.
// tdefl_compress_buffer() always consumes the entire input buffer.
tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush);
tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d);
mz_uint32 tdefl_get_adler32(tdefl_compressor *d);
// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros.
#ifndef MINIZ_NO_ZLIB_APIS
// Create tdefl_compress() flags given zlib-style compression parameters.
// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files)
// window_bits may be -15 (raw deflate) or 15 (zlib)
// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED
mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy);
#endif // #ifndef MINIZ_NO_ZLIB_APIS
#ifdef __cplusplus
}
#endif
#endif // MINIZ_HEADER_INCLUDED
// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.)
#ifndef MINIZ_HEADER_FILE_ONLY
typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1];
typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1];
typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1];
#include <string.h>
#include <assert.h>
#define MZ_ASSERT(x) assert(x)
#ifdef MINIZ_NO_MALLOC
#define MZ_MALLOC(x) NULL
#define MZ_FREE(x) (void)x, ((void)0)
#define MZ_REALLOC(p, x) NULL
#else
#define MZ_MALLOC(x) malloc(x)
#define MZ_FREE(x) free(x)
#define MZ_REALLOC(p, x) realloc(p, x)
#endif
#define MZ_MAX(a,b) (((a)>(b))?(a):(b))
#define MZ_MIN(a,b) (((a)<(b))?(a):(b))
#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
#define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
#define MZ_READ_LE32(p) *((const mz_uint32 *)(p))
#else
#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U))
#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
#endif
#ifdef _MSC_VER
#define MZ_FORCEINLINE __forceinline
#elif defined(__GNUC__)
#define MZ_FORCEINLINE inline __attribute__((__always_inline__))
#else
#define MZ_FORCEINLINE inline
#endif
#ifdef __cplusplus
extern "C" {
#endif
// ------------------- zlib-style API's
mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len)
{
mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552;
if (!ptr) return MZ_ADLER32_INIT;
while (buf_len) {
for (i = 0; i + 7 < block_len; i += 8, ptr += 8) {
s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
}
for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
}
return (s2 << 16) + s1;
}
// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed"
mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)
{
static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
mz_uint32 crcu32 = (mz_uint32)crc;
if (!ptr) return MZ_CRC32_INIT;
crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; }
return ~crcu32;
}
void mz_free(void *p)
{
MZ_FREE(p);
}
#ifndef MINIZ_NO_ZLIB_APIS
static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); }
static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); }
static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); }
const char *mz_version(void)
{
return MZ_VERSION;
}
int mz_deflateInit(mz_streamp pStream, int level)
{
return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY);
}
int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy)
{
tdefl_compressor *pComp;
mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy);
if (!pStream) return MZ_STREAM_ERROR;
if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR;
pStream->data_type = 0;
pStream->adler = MZ_ADLER32_INIT;
pStream->msg = NULL;
pStream->reserved = 0;
pStream->total_in = 0;
pStream->total_out = 0;
if (!pStream->zalloc) pStream->zalloc = def_alloc_func;
if (!pStream->zfree) pStream->zfree = def_free_func;
pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor));
if (!pComp)
return MZ_MEM_ERROR;
pStream->state = (struct mz_internal_state *)pComp;
if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY)
{
mz_deflateEnd(pStream);
return MZ_PARAM_ERROR;
}
return MZ_OK;
}
int mz_deflateReset(mz_streamp pStream)
{
if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR;
pStream->total_in = pStream->total_out = 0;
tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags);
return MZ_OK;
}
int mz_deflate(mz_streamp pStream, int flush)
{
size_t in_bytes, out_bytes;
mz_ulong orig_total_in, orig_total_out;
int mz_status = MZ_OK;
if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR;
if (!pStream->avail_out) return MZ_BUF_ERROR;
if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;
if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE)
return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR;
orig_total_in = pStream->total_in; orig_total_out = pStream->total_out;
for ( ; ; )
{
tdefl_status defl_status;
in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;
defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush);
pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state);
pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes;
pStream->total_out += (mz_uint)out_bytes;
if (defl_status < 0)
{
mz_status = MZ_STREAM_ERROR;
break;
}
else if (defl_status == TDEFL_STATUS_DONE)
{
mz_status = MZ_STREAM_END;
break;
}
else if (!pStream->avail_out)
break;
else if ((!pStream->avail_in) && (flush != MZ_FINISH))
{
if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out))
break;
return MZ_BUF_ERROR; // Can't make forward progress without some input.
}
}
return mz_status;
}
int mz_deflateEnd(mz_streamp pStream)
{
if (!pStream) return MZ_STREAM_ERROR;
if (pStream->state)
{
pStream->zfree(pStream->opaque, pStream->state);
pStream->state = NULL;
}
return MZ_OK;
}
mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len)
{
(void)pStream;
// This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.)
return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5);
}
int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level)
{
int status;
mz_stream stream;
memset(&stream, 0, sizeof(stream));
// In case mz_ulong is 64-bits (argh I hate longs).
if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;
stream.next_in = pSource;
stream.avail_in = (mz_uint32)source_len;
stream.next_out = pDest;
stream.avail_out = (mz_uint32)*pDest_len;
status = mz_deflateInit(&stream, level);
if (status != MZ_OK) return status;
status = mz_deflate(&stream, MZ_FINISH);
if (status != MZ_STREAM_END)
{
mz_deflateEnd(&stream);
return (status == MZ_OK) ? MZ_BUF_ERROR : status;
}
*pDest_len = stream.total_out;
return mz_deflateEnd(&stream);
}
int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
{
return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION);
}
mz_ulong mz_compressBound(mz_ulong source_len)
{
return mz_deflateBound(NULL, source_len);
}
typedef struct
{
tinfl_decompressor m_decomp;
mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits;
mz_uint8 m_dict[TINFL_LZ_DICT_SIZE];
tinfl_status m_last_status;
} inflate_state;
int mz_inflateInit2(mz_streamp pStream, int window_bits)
{
inflate_state *pDecomp;
if (!pStream) return MZ_STREAM_ERROR;
if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR;
pStream->data_type = 0;
pStream->adler = 0;
pStream->msg = NULL;
pStream->total_in = 0;
pStream->total_out = 0;
pStream->reserved = 0;
if (!pStream->zalloc) pStream->zalloc = def_alloc_func;
if (!pStream->zfree) pStream->zfree = def_free_func;
pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state));
if (!pDecomp) return MZ_MEM_ERROR;
pStream->state = (struct mz_internal_state *)pDecomp;
tinfl_init(&pDecomp->m_decomp);
pDecomp->m_dict_ofs = 0;
pDecomp->m_dict_avail = 0;
pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;
pDecomp->m_first_call = 1;
pDecomp->m_has_flushed = 0;
pDecomp->m_window_bits = window_bits;
return MZ_OK;
}
int mz_inflateInit(mz_streamp pStream)
{
return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS);
}
int mz_inflate(mz_streamp pStream, int flush)
{
inflate_state* pState;
mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;
size_t in_bytes, out_bytes, orig_avail_in;
tinfl_status status;
if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR;
if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;
if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
pState = (inflate_state*)pStream->state;
if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER;
orig_avail_in = pStream->avail_in;
first_call = pState->m_first_call; pState->m_first_call = 0;
if (pState->m_last_status < 0) return MZ_DATA_ERROR;
if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
pState->m_has_flushed |= (flush == MZ_FINISH);
if ((flush == MZ_FINISH) && (first_call))
{
// MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file.
decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;
status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags);
pState->m_last_status = status;
pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes;
pStream->adler = tinfl_get_adler32(&pState->m_decomp);
pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes;
if (status < 0)
return MZ_DATA_ERROR;
else if (status != TINFL_STATUS_DONE)
{
pState->m_last_status = TINFL_STATUS_FAILED;
return MZ_BUF_ERROR;
}
return MZ_STREAM_END;
}
// flush != MZ_FINISH then we must assume there's more input.
if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT;
if (pState->m_dict_avail)
{
n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
}
for ( ; ; )
{
in_bytes = pStream->avail_in;
out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs;
status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags);
pState->m_last_status = status;
pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp);
pState->m_dict_avail = (mz_uint)out_bytes;
n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
if (status < 0)
return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well).
else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in))
return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH.
else if (flush == MZ_FINISH)
{
// The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH.
if (status == TINFL_STATUS_DONE)
return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END;
// status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong.
else if (!pStream->avail_out)
return MZ_BUF_ERROR;
}
else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail))
break;
}
return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
}
int mz_inflateEnd(mz_streamp pStream)
{
if (!pStream)
return MZ_STREAM_ERROR;
if (pStream->state)
{
pStream->zfree(pStream->opaque, pStream->state);
pStream->state = NULL;
}
return MZ_OK;
}
int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
{
mz_stream stream;
int status;
memset(&stream, 0, sizeof(stream));
// In case mz_ulong is 64-bits (argh I hate longs).
if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;
stream.next_in = pSource;
stream.avail_in = (mz_uint32)source_len;
stream.next_out = pDest;
stream.avail_out = (mz_uint32)*pDest_len;
status = mz_inflateInit(&stream);
if (status != MZ_OK)
return status;
status = mz_inflate(&stream, MZ_FINISH);
if (status != MZ_STREAM_END)
{
mz_inflateEnd(&stream);
return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status;
}
*pDest_len = stream.total_out;
return mz_inflateEnd(&stream);
}
const char *mz_error(int err)
{
static struct { int m_err; const char *m_pDesc; } s_error_descs[] =
{
{ MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" },
{ MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" }
};
mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc;
return NULL;
}
#endif //MINIZ_NO_ZLIB_APIS
// ------------------- Low-level Decompression (completely independent from all compression API's)
#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
#define TINFL_MEMSET(p, c, l) memset(p, c, l)
#define TINFL_CR_BEGIN switch(r->m_state) { case 0:
#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END
#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END
#define TINFL_CR_FINISH }
// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never
// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario.
#define TINFL_GET_BYTE(state_index, c) do { \
if (pIn_buf_cur >= pIn_buf_end) { \
for ( ; ; ) { \
if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \
TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \
if (pIn_buf_cur < pIn_buf_end) { \
c = *pIn_buf_cur++; \
break; \
} \
} else { \
c = 0; \
break; \
} \
} \
} else c = *pIn_buf_cur++; } MZ_MACRO_END
#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n))
#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2.
// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a
// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the
// bit buffer contains >=15 bits (deflate's max. Huffman code size).
#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \
do { \
temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
if (temp >= 0) { \
code_len = temp >> 9; \
if ((code_len) && (num_bits >= code_len)) \
break; \
} else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \
code_len = TINFL_FAST_LOOKUP_BITS; \
do { \
temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
} while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \
} TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \
} while (num_bits < 15);
// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read
// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully
// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32.
// The slow path is only executed at the very end of the input buffer.
#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \
int temp; mz_uint code_len, c; \
if (num_bits < 15) { \
if ((pIn_buf_end - pIn_buf_cur) < 2) { \
TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \
} else { \
bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \
} \
} \
if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
code_len = temp >> 9, temp &= 511; \
else { \
code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \
} sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END
tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
{
static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 };
static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
static const int s_min_table_sizes[3] = { 257, 1, 4 };
tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf;
const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size;
size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;
// Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter).
if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; }
num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start;
TINFL_CR_BEGIN
bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1;
if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
{
TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1);
counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); }
}
do
{
TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1;
if (r->m_type == 0)
{
TINFL_SKIP_BITS(5, num_bits & 7);
for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); }
if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); }
while ((counter) && (num_bits))
{
TINFL_GET_BITS(51, dist, 8);
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = (mz_uint8)dist;
counter--;
}
while (counter)
{
size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); }
while (pIn_buf_cur >= pIn_buf_end)
{
if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT)
{
TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT);
}
else
{
TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED);
}
}
n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n;
}
}
else if (r->m_type == 3)
{
TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);
}
else
{
if (r->m_type == 1)
{
mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i;
r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);
for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8;
}
else
{
for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; }
MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; }
r->m_table_sizes[2] = 19;
}
for ( ; (int)r->m_type >= 0; r->m_type--)
{
int tree_next, tree_cur; tinfl_huff_table *pTable;
mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree);
for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++;
used_syms = 0, total = 0; next_code[0] = next_code[1] = 0;
for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); }
if ((65536 != total) && (used_syms > 1))
{
TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);
}
for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
{
mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue;
cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1);
if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; }
if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; }
rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);
for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
{
tree_cur -= ((rev_code >>= 1) & 1);
if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1];
}
tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index;
}
if (r->m_type == 2)
{
for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); )
{
mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; }
if ((dist == 16) && (!counter))
{
TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);
}
num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16];
TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s;
}
if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
{
TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
}
TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
}
}
for ( ; ; )
{
mz_uint8 *pSrc;
for ( ; ; )
{
if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
{
TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]);
if (counter >= 256)
break;
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = (mz_uint8)counter;
}
else
{
int sym2; mz_uint code_len;
#if TINFL_USE_64BIT_BITBUF
if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; }
#else
if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
#endif
if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
code_len = sym2 >> 9;
else
{
code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
}
counter = sym2; bit_buf >>= code_len; num_bits -= code_len;
if (counter & 256)
break;
#if !TINFL_USE_64BIT_BITBUF
if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
#endif
if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
code_len = sym2 >> 9;
else
{
code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
}
bit_buf >>= code_len; num_bits -= code_len;
pOut_buf_cur[0] = (mz_uint8)counter;
if (sym2 & 256)
{
pOut_buf_cur++;
counter = sym2;
break;
}
pOut_buf_cur[1] = (mz_uint8)sym2;
pOut_buf_cur += 2;
}
}
if ((counter &= 511) == 256) break;
num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257];
if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; }
TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]);
num_extra = s_dist_extra[dist]; dist = s_dist_base[dist];
if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; }
dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
{
TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);
}
pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);
if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
{
while (counter--)
{
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
}
continue;
}
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
else if ((counter >= 9) && (counter <= dist))
{
const mz_uint8 *pSrc_end = pSrc + (counter & ~7);
do
{
((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];
((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];
pOut_buf_cur += 8;
} while ((pSrc += 8) < pSrc_end);
if ((counter &= 7) < 3)
{
if (counter)
{
pOut_buf_cur[0] = pSrc[0];
if (counter > 1)
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur += counter;
}
continue;
}
}
#endif
do
{
pOut_buf_cur[0] = pSrc[0];
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur[2] = pSrc[2];
pOut_buf_cur += 3; pSrc += 3;
} while ((int)(counter -= 3) > 2);
if ((int)counter > 0)
{
pOut_buf_cur[0] = pSrc[0];
if ((int)counter > 1)
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur += counter;
}
}
}
} while (!(r->m_final & 1));
if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
{
TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; }
}
TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);
TINFL_CR_FINISH
common_exit:
r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start;
*pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
{
const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size;
mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552;
while (buf_len)
{
for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
{
s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
}
for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
}
r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH;
}
return status;
}
// Higher level helper functions.
void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
{
tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0;
*pOut_len = 0;
tinfl_init(&decomp);
for ( ; ; )
{
size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity;
tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size,
(flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT))
{
MZ_FREE(pBuf); *pOut_len = 0; return NULL;
}
src_buf_ofs += src_buf_size;
*pOut_len += dst_buf_size;
if (status == TINFL_STATUS_DONE) break;
new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128;
pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity);
if (!pNew_buf)
{
MZ_FREE(pBuf); *pOut_len = 0; return NULL;
}
pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity;
}
return pBuf;
}
size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
{
tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp);
status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len;
}
int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
{
int result = 0;
tinfl_decompressor decomp;
mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0;
if (!pDict)
return TINFL_STATUS_FAILED;
tinfl_init(&decomp);
for ( ; ; )
{
size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs;
tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size,
(flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)));
in_buf_ofs += in_buf_size;
if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user)))
break;
if (status != TINFL_STATUS_HAS_MORE_OUTPUT)
{
result = (status == TINFL_STATUS_DONE);
break;
}
dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1);
}
MZ_FREE(pDict);
*pIn_buf_size = in_buf_ofs;
return result;
}
// ------------------- Low-level Compression (independent from all decompression API's)
// Purposely making these tables static for faster init and thread safety.
static const mz_uint16 s_tdefl_len_sym[256] = {
257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272,
273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276,
277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,
279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,
281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,
282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,
283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,
284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 };
static const mz_uint8 s_tdefl_len_extra[256] = {
0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 };
static const mz_uint8 s_tdefl_small_dist_sym[512] = {
0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,
11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,
13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 };
static const mz_uint8 s_tdefl_small_dist_extra[512] = {
0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7 };
static const mz_uint8 s_tdefl_large_dist_sym[128] = {
0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,
26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,
28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 };
static const mz_uint8 s_tdefl_large_dist_extra[128] = {
0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 };
// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values.
typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq;
static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1)
{
mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist);
for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; }
while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--;
for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8)
{
const mz_uint32* pHist = &hist[pass << 8];
mz_uint offsets[256], cur_ofs = 0;
for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; }
for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i];
{ tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; }
}
return pCur_syms;
}
// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996.
static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n)
{
int root, leaf, next, avbl, used, dpth;
if (n==0) return; else if (n==1) { A[0].m_key = 1; return; }
A[0].m_key += A[1].m_key; root = 0; leaf = 2;
for (next=1; next < n-1; next++)
{
if (leaf>=n || A[root].m_key<A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } else A[next].m_key = A[leaf++].m_key;
if (leaf>=n || (root<next && A[root].m_key<A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key);
}
A[n-2].m_key = 0; for (next=n-3; next>=0; next--) A[next].m_key = A[A[next].m_key].m_key+1;
avbl = 1; used = dpth = 0; root = n-2; next = n-1;
while (avbl>0)
{
while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; }
while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; }
avbl = 2*used; dpth++; used = 0;
}
}
// Limits canonical Huffman code table's max code size.
enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 };
static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size)
{
int i; mz_uint32 total = 0; if (code_list_len <= 1) return;
for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i];
for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i));
while (total != (1UL << max_code_size))
{
pNum_codes[max_code_size]--;
for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; }
total--;
}
}
static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table)
{
int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes);
if (static_table)
{
for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++;
}
else
{
tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms;
int num_used_syms = 0;
const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0];
for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; }
pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms);
for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++;
tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit);
MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]);
for (i = 1, j = num_used_syms; i <= code_size_limit; i++)
for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i);
}
next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1);
for (i = 0; i < table_len; i++)
{
mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue;
code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1);
d->m_huff_codes[table_num][i] = (mz_uint16)rev_code;
}
}
#define TDEFL_PUT_BITS(b, l) do { \
mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \
d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \
while (d->m_bits_in >= 8) { \
if (d->m_pOutput_buf < d->m_pOutput_buf_end) \
*d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \
d->m_bit_buffer >>= 8; \
d->m_bits_in -= 8; \
} \
} MZ_MACRO_END
#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \
if (rle_repeat_count < 3) { \
d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \
while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \
} else { \
d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \
} rle_repeat_count = 0; } }
#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \
if (rle_z_count < 3) { \
d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \
} else if (rle_z_count <= 10) { \
d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \
} else { \
d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \
} rle_z_count = 0; } }
static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
static void tdefl_start_dynamic_block(tdefl_compressor *d)
{
int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index;
mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF;
d->m_huff_count[0][256] = 1;
tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE);
tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE);
for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break;
for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break;
memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes);
memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes);
total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0;
memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2);
for (i = 0; i < total_code_sizes_to_pack; i++)
{
mz_uint8 code_size = code_sizes_to_pack[i];
if (!code_size)
{
TDEFL_RLE_PREV_CODE_SIZE();
if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); }
}
else
{
TDEFL_RLE_ZERO_CODE_SIZE();
if (code_size != prev_code_size)
{
TDEFL_RLE_PREV_CODE_SIZE();
d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size;
}
else if (++rle_repeat_count == 6)
{
TDEFL_RLE_PREV_CODE_SIZE();
}
}
prev_code_size = code_size;
}
if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); }
tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE);
TDEFL_PUT_BITS(2, 2);
TDEFL_PUT_BITS(num_lit_codes - 257, 5);
TDEFL_PUT_BITS(num_dist_codes - 1, 5);
for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break;
num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4);
for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3);
for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; )
{
mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2);
TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]);
if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]);
}
}
static void tdefl_start_static_block(tdefl_compressor *d)
{
mz_uint i;
mz_uint8 *p = &d->m_huff_code_sizes[0][0];
for (i = 0; i <= 143; ++i) *p++ = 8;
for ( ; i <= 255; ++i) *p++ = 9;
for ( ; i <= 279; ++i) *p++ = 7;
for ( ; i <= 287; ++i) *p++ = 8;
memset(d->m_huff_code_sizes[1], 5, 32);
tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE);
tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE);
TDEFL_PUT_BITS(1, 2);
}
static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS
static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
{
mz_uint flags;
mz_uint8 *pLZ_codes;
mz_uint8 *pOutput_buf = d->m_pOutput_buf;
mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf;
mz_uint64 bit_buffer = d->m_bit_buffer;
mz_uint bits_in = d->m_bits_in;
#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); }
flags = 1;
for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1)
{
if (flags == 1)
flags = *pLZ_codes++ | 0x100;
if (flags & 1)
{
mz_uint s0, s1, n0, n1, sym, num_extra_bits;
mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3;
MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
// This sequence coaxes MSVC into using cmov's vs. jmp's.
s0 = s_tdefl_small_dist_sym[match_dist & 511];
n0 = s_tdefl_small_dist_extra[match_dist & 511];
s1 = s_tdefl_large_dist_sym[match_dist >> 8];
n1 = s_tdefl_large_dist_extra[match_dist >> 8];
sym = (match_dist < 512) ? s0 : s1;
num_extra_bits = (match_dist < 512) ? n0 : n1;
MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
}
else
{
mz_uint lit = *pLZ_codes++;
MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
{
flags >>= 1;
lit = *pLZ_codes++;
MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
{
flags >>= 1;
lit = *pLZ_codes++;
MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
}
}
}
if (pOutput_buf >= d->m_pOutput_buf_end)
return MZ_FALSE;
*(mz_uint64*)pOutput_buf = bit_buffer;
pOutput_buf += (bits_in >> 3);
bit_buffer >>= (bits_in & ~7);
bits_in &= 7;
}
#undef TDEFL_PUT_BITS_FAST
d->m_pOutput_buf = pOutput_buf;
d->m_bits_in = 0;
d->m_bit_buffer = 0;
while (bits_in)
{
mz_uint32 n = MZ_MIN(bits_in, 16);
TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n);
bit_buffer >>= n;
bits_in -= n;
}
TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
return (d->m_pOutput_buf < d->m_pOutput_buf_end);
}
#else
static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
{
mz_uint flags;
mz_uint8 *pLZ_codes;
flags = 1;
for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1)
{
if (flags == 1)
flags = *pLZ_codes++ | 0x100;
if (flags & 1)
{
mz_uint sym, num_extra_bits;
mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3;
MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
if (match_dist < 512)
{
sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist];
}
else
{
sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8];
}
MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
}
else
{
mz_uint lit = *pLZ_codes++;
MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
}
}
TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
return (d->m_pOutput_buf < d->m_pOutput_buf_end);
}
#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS
static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block)
{
if (static_block)
tdefl_start_static_block(d);
else
tdefl_start_dynamic_block(d);
return tdefl_compress_lz_codes(d);
}
static int tdefl_flush_block(tdefl_compressor *d, int flush)
{
mz_uint saved_bit_buf, saved_bits_in;
mz_uint8 *pSaved_output_buf;
mz_bool comp_block_succeeded = MZ_FALSE;
int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size;
mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf;
d->m_pOutput_buf = pOutput_buf_start;
d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16;
MZ_ASSERT(!d->m_output_flush_remaining);
d->m_output_flush_ofs = 0;
d->m_output_flush_remaining = 0;
*d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left);
d->m_pLZ_code_buf -= (d->m_num_flags_left == 8);
if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index))
{
TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8);
}
TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1);
pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in;
if (!use_raw_block)
comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48));
// If the block gets expanded, forget the current contents of the output buffer and send a raw block instead.
if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) &&
((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) )
{
mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
TDEFL_PUT_BITS(0, 2);
if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); }
for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF)
{
TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16);
}
for (i = 0; i < d->m_total_lz_bytes; ++i)
{
TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8);
}
}
// Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes.
else if (!comp_block_succeeded)
{
d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
tdefl_compress_block(d, MZ_TRUE);
}
if (flush)
{
if (flush == TDEFL_FINISH)
{
if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); }
if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } }
}
else
{
mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); }
}
}
MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end);
memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++;
if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0)
{
if (d->m_pPut_buf_func)
{
*d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user))
return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED);
}
else if (pOutput_buf_start == d->m_output_buf)
{
int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs));
memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy);
d->m_out_buf_ofs += bytes_to_copy;
if ((n -= bytes_to_copy) != 0)
{
d->m_output_flush_ofs = bytes_to_copy;
d->m_output_flush_remaining = n;
}
}
else
{
d->m_out_buf_ofs += n;
}
}
return d->m_output_flush_remaining;
}
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p)
static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
{
mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q;
mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s);
MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return;
for ( ; ; )
{
for ( ; ; )
{
if (--num_probes_left == 0) return;
#define TDEFL_PROBE \
next_probe_pos = d->m_next[probe_pos]; \
if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \
probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break;
TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE;
}
if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32;
do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&
(TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
if (!probe_len)
{
*pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break;
}
else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len)
{
*pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break;
c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]);
}
}
}
#else
static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
{
mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
const mz_uint8 *s = d->m_dict + pos, *p, *q;
mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1];
MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return;
for ( ; ; )
{
for ( ; ; )
{
if (--num_probes_left == 0) return;
#define TDEFL_PROBE \
next_probe_pos = d->m_next[probe_pos]; \
if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \
probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break;
TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE;
}
if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break;
if (probe_len > match_len)
{
*pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return;
c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1];
}
}
}
#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
static mz_bool tdefl_compress_fast(tdefl_compressor *d)
{
// Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio.
mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left;
mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags;
mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size)))
{
const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096;
mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size);
d->m_src_buf_left -= num_bytes_to_process;
lookahead_size += num_bytes_to_process;
while (num_bytes_to_process)
{
mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process);
memcpy(d->m_dict + dst_pos, d->m_pSrc, n);
if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos));
d->m_pSrc += n;
dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK;
num_bytes_to_process -= n;
}
dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size);
if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break;
while (lookahead_size >= 4)
{
mz_uint cur_match_dist, cur_match_len = 1;
mz_uint8 *pCur_dict = d->m_dict + cur_pos;
mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF;
mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK;
mz_uint probe_pos = d->m_hash[hash];
d->m_hash[hash] = (mz_uint16)lookahead_pos;
if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram))
{
const mz_uint16 *p = (const mz_uint16 *)pCur_dict;
const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos);
mz_uint32 probe_len = 32;
do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&
(TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q);
if (!probe_len)
cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0;
if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)))
{
cur_match_len = 1;
*pLZ_code_buf++ = (mz_uint8)first_trigram;
*pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
d->m_huff_count[0][(mz_uint8)first_trigram]++;
}
else
{
mz_uint32 s0, s1;
cur_match_len = MZ_MIN(cur_match_len, lookahead_size);
MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE));
cur_match_dist--;
pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN);
*(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist;
pLZ_code_buf += 3;
*pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80);
s0 = s_tdefl_small_dist_sym[cur_match_dist & 511];
s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8];
d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++;
d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++;
}
}
else
{
*pLZ_code_buf++ = (mz_uint8)first_trigram;
*pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
d->m_huff_count[0][(mz_uint8)first_trigram]++;
}
if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; }
total_lz_bytes += cur_match_len;
lookahead_pos += cur_match_len;
dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE);
cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;
MZ_ASSERT(lookahead_size >= cur_match_len);
lookahead_size -= cur_match_len;
if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
{
int n;
d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
if ((n = tdefl_flush_block(d, 0)) != 0)
return (n < 0) ? MZ_FALSE : MZ_TRUE;
total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left;
}
}
while (lookahead_size)
{
mz_uint8 lit = d->m_dict[cur_pos];
total_lz_bytes++;
*pLZ_code_buf++ = lit;
*pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; }
d->m_huff_count[0][lit]++;
lookahead_pos++;
dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE);
cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
lookahead_size--;
if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
{
int n;
d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
if ((n = tdefl_flush_block(d, 0)) != 0)
return (n < 0) ? MZ_FALSE : MZ_TRUE;
total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left;
}
}
}
d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
return MZ_TRUE;
}
#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit)
{
d->m_total_lz_bytes++;
*d->m_pLZ_code_buf++ = lit;
*d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; }
d->m_huff_count[0][lit]++;
}
static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist)
{
mz_uint32 s0, s1;
MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE));
d->m_total_lz_bytes += match_len;
d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN);
match_dist -= 1;
d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF);
d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3;
*d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; }
s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127];
d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++;
if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++;
}
static mz_bool tdefl_compress_normal(tdefl_compressor *d)
{
const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left;
tdefl_flush flush = d->m_flush;
while ((src_buf_left) || ((flush) && (d->m_lookahead_size)))
{
mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos;
// Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN.
if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1))
{
mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2;
mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK];
mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size);
const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process;
src_buf_left -= num_bytes_to_process;
d->m_lookahead_size += num_bytes_to_process;
while (pSrc != pSrc_end)
{
mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos);
dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++;
}
}
else
{
while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
{
mz_uint8 c = *pSrc++;
mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
src_buf_left--;
d->m_dict[dst_pos] = c;
if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN)
{
mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2;
mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos);
}
}
}
d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size);
if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
break;
// Simple lazy/greedy parsing state machine.
len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS))
{
if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))
{
mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK];
cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; }
if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1;
}
}
else
{
tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len);
}
if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5)))
{
cur_match_dist = cur_match_len = 0;
}
if (d->m_saved_match_len)
{
if (cur_match_len > d->m_saved_match_len)
{
tdefl_record_literal(d, (mz_uint8)d->m_saved_lit);
if (cur_match_len >= 128)
{
tdefl_record_match(d, cur_match_len, cur_match_dist);
d->m_saved_match_len = 0; len_to_move = cur_match_len;
}
else
{
d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len;
}
}
else
{
tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist);
len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0;
}
}
else if (!cur_match_dist)
tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]);
else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128))
{
tdefl_record_match(d, cur_match_len, cur_match_dist);
len_to_move = cur_match_len;
}
else
{
d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len;
}
// Move the lookahead forward by len_to_move bytes.
d->m_lookahead_pos += len_to_move;
MZ_ASSERT(d->m_lookahead_size >= len_to_move);
d->m_lookahead_size -= len_to_move;
d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE);
// Check if it's time to flush the current LZ codes to the internal output buffer.
if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ||
( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) )
{
int n;
d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left;
if ((n = tdefl_flush_block(d, 0)) != 0)
return (n < 0) ? MZ_FALSE : MZ_TRUE;
}
}
d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left;
return MZ_TRUE;
}
static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d)
{
if (d->m_pIn_buf_size)
{
*d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
}
if (d->m_pOut_buf_size)
{
size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining);
memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n);
d->m_output_flush_ofs += (mz_uint)n;
d->m_output_flush_remaining -= (mz_uint)n;
d->m_out_buf_ofs += n;
*d->m_pOut_buf_size = d->m_out_buf_ofs;
}
return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY;
}
tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush)
{
if (!d)
{
if (pIn_buf_size) *pIn_buf_size = 0;
if (pOut_buf_size) *pOut_buf_size = 0;
return TDEFL_STATUS_BAD_PARAM;
}
d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size;
d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size;
d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0;
d->m_out_buf_ofs = 0;
d->m_flush = flush;
if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) ||
(d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) )
{
if (pIn_buf_size) *pIn_buf_size = 0;
if (pOut_buf_size) *pOut_buf_size = 0;
return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM);
}
d->m_wants_to_finish |= (flush == TDEFL_FINISH);
if ((d->m_output_flush_remaining) || (d->m_finished))
return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) &&
((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) &&
((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0))
{
if (!tdefl_compress_fast(d))
return d->m_prev_return_status;
}
else
#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
{
if (!tdefl_compress_normal(d))
return d->m_prev_return_status;
}
if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf))
d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf);
if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining))
{
if (tdefl_flush_block(d, flush) < 0)
return d->m_prev_return_status;
d->m_finished = (flush == TDEFL_FINISH);
if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; }
}
return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
}
tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush)
{
MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush);
}
tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
{
d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user;
d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0;
d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3;
if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash);
d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0;
d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0;
d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8;
d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY;
d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1;
d->m_pIn_buf = NULL; d->m_pOut_buf = NULL;
d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL;
d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0;
memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
return TDEFL_STATUS_OKAY;
}
tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d)
{
return d->m_prev_return_status;
}
mz_uint32 tdefl_get_adler32(tdefl_compressor *d)
{
return d->m_adler32;
}
mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
{
tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE;
pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE;
succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY);
succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE);
MZ_FREE(pComp); return succeeded;
}
typedef struct
{
size_t m_size, m_capacity;
mz_uint8 *m_pBuf;
mz_bool m_expandable;
} tdefl_output_buffer;
static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser)
{
tdefl_output_buffer *p = (tdefl_output_buffer *)pUser;
size_t new_size = p->m_size + len;
if (new_size > p->m_capacity)
{
size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE;
do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity);
pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE;
p->m_pBuf = pNew_buf; p->m_capacity = new_capacity;
}
memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size;
return MZ_TRUE;
}
void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
{
tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf);
if (!pOut_len) return MZ_FALSE; else *pOut_len = 0;
out_buf.m_expandable = MZ_TRUE;
if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL;
*pOut_len = out_buf.m_size; return out_buf.m_pBuf;
}
size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
{
tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf);
if (!pOut_buf) return 0;
out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len;
if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0;
return out_buf.m_size;
}
#ifndef MINIZ_NO_ZLIB_APIS
static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files).
mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy)
{
mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER;
if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES;
else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK;
else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS;
else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES;
return comp_flags;
}
#endif //MINIZ_NO_ZLIB_APIS
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal)
#endif
// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299
// This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck.
void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip)
{
// Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined.
static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0;
if (!pComp) return NULL;
MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; }
// write dummy header
for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf);
// compress image data
tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER);
for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); }
if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
// write real header
*pLen_out = out_buf.m_size-41;
{
static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06};
mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0,
(mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54};
c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24);
memcpy(out_buf.m_pBuf, pnghdr, 41);
}
// write footer (IDAT CRC-32, followed by IEND chunk)
if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24);
// compute final size of file, grab compressed data buffer and return
*pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf;
}
void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out)
{
// Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out)
return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE);
}
#ifdef _MSC_VER
#pragma warning (pop)
#endif
// ------------------- .ZIP archive reading
#ifndef MINIZ_NO_ARCHIVE_APIS
#ifdef MINIZ_NO_STDIO
#define MZ_FILE void *
#else
#include <stdio.h>
#include <sys/stat.h>
#if defined(_MSC_VER) || defined(__MINGW64__)
static FILE *mz_fopen(const char *pFilename, const char *pMode)
{
FILE* pFile = NULL;
fopen_s(&pFile, pFilename, pMode);
return pFile;
}
static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream)
{
FILE* pFile = NULL;
if (freopen_s(&pFile, pPath, pMode, pStream))
return NULL;
return pFile;
}
#ifndef MINIZ_NO_TIME
#include <sys/utime.h>
#endif
#define MZ_FILE FILE
#define MZ_FOPEN mz_fopen
#define MZ_FCLOSE fclose
#define MZ_FREAD fread
#define MZ_FWRITE fwrite
#define MZ_FTELL64 _ftelli64
#define MZ_FSEEK64 _fseeki64
#define MZ_FILE_STAT_STRUCT _stat
#define MZ_FILE_STAT _stat
#define MZ_FFLUSH fflush
#define MZ_FREOPEN mz_freopen
#define MZ_DELETE_FILE remove
#elif defined(__MINGW32__)
#ifndef MINIZ_NO_TIME
#include <sys/utime.h>
#endif
#define MZ_FILE FILE
#define MZ_FOPEN(f, m) fopen(f, m)
#define MZ_FCLOSE fclose
#define MZ_FREAD fread
#define MZ_FWRITE fwrite
#define MZ_FTELL64 ftello64
#define MZ_FSEEK64 fseeko64
#define MZ_FILE_STAT_STRUCT _stat
#define MZ_FILE_STAT _stat
#define MZ_FFLUSH fflush
#define MZ_FREOPEN(f, m, s) freopen(f, m, s)
#define MZ_DELETE_FILE remove
#elif defined(__TINYC__)
#ifndef MINIZ_NO_TIME
#include <sys/utime.h>
#endif
#define MZ_FILE FILE
#define MZ_FOPEN(f, m) fopen(f, m)
#define MZ_FCLOSE fclose
#define MZ_FREAD fread
#define MZ_FWRITE fwrite
#define MZ_FTELL64 ftell
#define MZ_FSEEK64 fseek
#define MZ_FILE_STAT_STRUCT stat
#define MZ_FILE_STAT stat
#define MZ_FFLUSH fflush
#define MZ_FREOPEN(f, m, s) freopen(f, m, s)
#define MZ_DELETE_FILE remove
#elif defined(__GNUC__) && _LARGEFILE64_SOURCE
#ifndef MINIZ_NO_TIME
#include <utime.h>
#endif
#define MZ_FILE FILE
#define MZ_FOPEN(f, m) fopen64(f, m)
#define MZ_FCLOSE fclose
#define MZ_FREAD fread
#define MZ_FWRITE fwrite
#define MZ_FTELL64 ftello64
#define MZ_FSEEK64 fseeko64
#define MZ_FILE_STAT_STRUCT stat64
#define MZ_FILE_STAT stat64
#define MZ_FFLUSH fflush
#define MZ_FREOPEN(p, m, s) freopen64(p, m, s)
#define MZ_DELETE_FILE remove
#else
#ifndef MINIZ_NO_TIME
#include <utime.h>
#endif
#define MZ_FILE FILE
#define MZ_FOPEN(f, m) fopen(f, m)
#define MZ_FCLOSE fclose
#define MZ_FREAD fread
#define MZ_FWRITE fwrite
#define MZ_FTELL64 ftello
#define MZ_FSEEK64 fseeko
#define MZ_FILE_STAT_STRUCT stat
#define MZ_FILE_STAT stat
#define MZ_FFLUSH fflush
#define MZ_FREOPEN(f, m, s) freopen(f, m, s)
#define MZ_DELETE_FILE remove
#endif // #ifdef _MSC_VER
#endif // #ifdef MINIZ_NO_STDIO
#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c))
// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff.
enum
{
// ZIP archive identifiers and record sizes
MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50,
MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,
// Central directory header record offsets
MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8,
MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16,
MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30,
MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42,
// Local directory header offsets
MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10,
MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22,
MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28,
// End of central directory offsets
MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8,
MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,
};
typedef struct
{
void *m_p;
size_t m_size, m_capacity;
mz_uint m_element_size;
} mz_zip_array;
struct mz_zip_internal_state_tag
{
mz_zip_array m_central_dir;
mz_zip_array m_central_dir_offsets;
mz_zip_array m_sorted_central_dir_offsets;
MZ_FILE *m_pFile;
void *m_pMem;
size_t m_mem_size;
size_t m_mem_capacity;
};
#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size
#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index]
static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p);
memset(pArray, 0, sizeof(mz_zip_array));
}
static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing)
{
void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE;
if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; }
if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE;
pArray->m_p = pNew_p; pArray->m_capacity = new_capacity;
return MZ_TRUE;
}
static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing)
{
if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; }
return MZ_TRUE;
}
static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing)
{
if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; }
pArray->m_size = new_size;
return MZ_TRUE;
}
static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n)
{
return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE);
}
static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n)
{
size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE;
memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size);
return MZ_TRUE;
}
#ifndef MINIZ_NO_TIME
static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date)
{
struct tm tm;
memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1;
tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31;
tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62;
return mktime(&tm);
}
static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
{
#ifdef _MSC_VER
struct tm tm_struct;
struct tm *tm = &tm_struct;
errno_t err = localtime_s(tm, &time);
if (err)
{
*pDOS_date = 0; *pDOS_time = 0;
return;
}
#else
struct tm *tm = localtime(&time);
#endif
*pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1));
*pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday);
}
#endif
#ifndef MINIZ_NO_STDIO
static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
{
#ifdef MINIZ_NO_TIME
(void)pFilename; *pDOS_date = *pDOS_time = 0;
#else
struct MZ_FILE_STAT_STRUCT file_stat;
// On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh.
if (MZ_FILE_STAT(pFilename, &file_stat) != 0)
return MZ_FALSE;
mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date);
#endif // #ifdef MINIZ_NO_TIME
return MZ_TRUE;
}
#ifndef MINIZ_NO_TIME
static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time)
{
struct utimbuf t; t.actime = access_time; t.modtime = modified_time;
return !utime(pFilename, &t);
}
#endif // #ifndef MINIZ_NO_TIME
#endif // #ifndef MINIZ_NO_STDIO
static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags)
{
(void)flags;
if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
return MZ_FALSE;
if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func;
if (!pZip->m_pFree) pZip->m_pFree = def_free_func;
if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func;
pZip->m_zip_mode = MZ_ZIP_MODE_READING;
pZip->m_archive_size = 0;
pZip->m_central_directory_file_ofs = 0;
pZip->m_total_files = 0;
if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
return MZ_FALSE;
memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
return MZ_TRUE;
}
static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index)
{
const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index));
mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS);
mz_uint8 l = 0, r = 0;
pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
pE = pL + MZ_MIN(l_len, r_len);
while (pL < pE)
{
if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
break;
pL++; pR++;
}
return (pL == pE) ? (l_len < r_len) : (l < r);
}
#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END
// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.)
static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip)
{
mz_zip_internal_state *pState = pZip->m_pState;
const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
const mz_zip_array *pCentral_dir = &pState->m_central_dir;
mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
const int size = pZip->m_total_files;
int start = (size - 2) >> 1, end;
while (start >= 0)
{
int child, root = start;
for ( ; ; )
{
if ((child = (root << 1) + 1) >= size)
break;
child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])));
if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
break;
MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
}
start--;
}
end = size - 1;
while (end > 0)
{
int child, root = 0;
MZ_SWAP_UINT32(pIndices[end], pIndices[0]);
for ( ; ; )
{
if ((child = (root << 1) + 1) >= end)
break;
child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]));
if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
break;
MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
}
end--;
}
}
static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags)
{
mz_uint cdir_size, num_this_disk, cdir_disk_index;
mz_uint64 cdir_ofs;
mz_int64 cur_file_ofs;
const mz_uint8 *p;
mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
mz_bool zip_signature_found = 0;
// Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there.
if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
return MZ_FALSE;
// Find the end of central directory record by scanning the file from the end towards the beginning.
cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);
for ( ; ; )
{
int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)
return MZ_FALSE;
for (i = n - 4; i >= 0; --i)
{
if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
{
// Read and verify the end of central directory record.
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs + i, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
continue;
if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) ||
((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS)))
continue;
zip_signature_found = 1;
break;
}
}
if (zip_signature_found)
{
cur_file_ofs += i;
break;
}
if ((!cur_file_ofs) || (cur_file_ofs < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))
return MZ_FALSE;
cur_file_ofs = MZ_MAX(cur_file_ofs - (mz_int64)(sizeof(buf_u32) - 3), 0);
}
num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);
cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);
if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1)))
return MZ_FALSE;
if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
return MZ_FALSE;
cdir_ofs = cur_file_ofs - cdir_size;
if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size)
return MZ_FALSE;
pZip->m_central_directory_file_ofs = cdir_ofs;
pZip->m_archive_file_ofs = cdir_ofs - MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
if (pZip->m_archive_file_ofs > pZip->m_archive_size)
return MZ_FALSE;
if (pZip->m_total_files)
{
mz_uint i, n;
// Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices.
if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) ||
(!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE)))
return MZ_FALSE;
if (sort_central_dir)
{
if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE))
return MZ_FALSE;
}
if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size)
return MZ_FALSE;
// Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported).
p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i)
{
mz_uint total_header_size, comp_size, decomp_size, disk_index;
if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG))
return MZ_FALSE;
MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p);
if (sort_central_dir)
MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i;
comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF))
return MZ_FALSE;
disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS);
if ((disk_index != num_this_disk) && (disk_index != 1))
return MZ_FALSE;
if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
return MZ_FALSE;
if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n)
return MZ_FALSE;
n -= total_header_size; p += total_header_size;
}
}
if (sort_central_dir)
mz_zip_reader_sort_central_dir_offsets_by_filename(pZip);
return MZ_TRUE;
}
mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags)
{
if ((!pZip) || (!pZip->m_pRead))
return MZ_FALSE;
if (!mz_zip_reader_init_internal(pZip, flags))
return MZ_FALSE;
pZip->m_archive_size = size;
if (!mz_zip_reader_read_central_dir(pZip, flags))
{
mz_zip_reader_end(pZip);
return MZ_FALSE;
}
return MZ_TRUE;
}
static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
{
mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n);
memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s);
return s;
}
mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags)
{
if (!mz_zip_reader_init_internal(pZip, flags))
return MZ_FALSE;
pZip->m_archive_size = size;
pZip->m_pRead = mz_zip_mem_read_func;
pZip->m_pIO_opaque = pZip;
#ifdef __cplusplus
pZip->m_pState->m_pMem = const_cast<void *>(pMem);
#else
pZip->m_pState->m_pMem = (void *)pMem;
#endif
pZip->m_pState->m_mem_size = size;
if (!mz_zip_reader_read_central_dir(pZip, flags))
{
mz_zip_reader_end(pZip);
return MZ_FALSE;
}
return MZ_TRUE;
}
#ifndef MINIZ_NO_STDIO
static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
{
mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
return 0;
return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile);
}
mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags)
{
mz_uint64 file_size;
MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb");
if (!pFile)
return MZ_FALSE;
if (MZ_FSEEK64(pFile, 0, SEEK_END))
{
MZ_FCLOSE(pFile);
return MZ_FALSE;
}
file_size = MZ_FTELL64(pFile);
if (!mz_zip_reader_init_internal(pZip, flags))
{
MZ_FCLOSE(pFile);
return MZ_FALSE;
}
pZip->m_pRead = mz_zip_file_read_func;
pZip->m_pIO_opaque = pZip;
pZip->m_pState->m_pFile = pFile;
pZip->m_archive_size = file_size;
if (!mz_zip_reader_read_central_dir(pZip, flags))
{
mz_zip_reader_end(pZip);
return MZ_FALSE;
}
return MZ_TRUE;
}
#endif // #ifndef MINIZ_NO_STDIO
mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip)
{
return pZip ? pZip->m_total_files : 0;
}
static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index)
{
if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
return NULL;
return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
}
mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index)
{
mz_uint m_bit_flag;
const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
if (!p)
return MZ_FALSE;
m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
return (m_bit_flag & 1);
}
mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index)
{
mz_uint filename_len, external_attr;
const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
if (!p)
return MZ_FALSE;
// First see if the filename ends with a '/' character.
filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
if (filename_len)
{
if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/')
return MZ_TRUE;
}
// Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct.
// Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field.
// FIXME: Remove this check? Is it necessary - we already check the filename.
external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
if ((external_attr & 0x10) != 0)
return MZ_TRUE;
return MZ_FALSE;
}
mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat)
{
mz_uint n;
const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
if ((!p) || (!pStat))
return MZ_FALSE;
// Unpack the central directory record.
pStat->m_file_index = file_index;
pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index);
pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS);
pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS);
pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS);
#ifndef MINIZ_NO_TIME
pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS));
#endif
pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS);
pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS);
pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
// Copy as much of the filename and comment as possible.
n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1);
memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0';
n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1);
pStat->m_comment_size = n;
memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0';
return MZ_TRUE;
}
mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size)
{
mz_uint n;
const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; }
n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
if (filename_buf_size)
{
n = MZ_MIN(n, filename_buf_size - 1);
memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);
pFilename[n] = '\0';
}
return n + 1;
}
static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags)
{
mz_uint i;
if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE)
return 0 == memcmp(pA, pB, len);
for (i = 0; i < len; ++i)
if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i]))
return MZ_FALSE;
return MZ_TRUE;
}
static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len)
{
const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS);
mz_uint8 l = 0, r = 0;
pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
pE = pL + MZ_MIN(l_len, r_len);
while (pL < pE)
{
if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
break;
pL++; pR++;
}
return (pL == pE) ? (int)(l_len - r_len) : (l - r);
}
static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename)
{
mz_zip_internal_state *pState = pZip->m_pState;
const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
const mz_zip_array *pCentral_dir = &pState->m_central_dir;
mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
const int size = pZip->m_total_files;
const mz_uint filename_len = (mz_uint)strlen(pFilename);
int l = 0, h = size - 1;
while (l <= h)
{
int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len);
if (!comp)
return file_index;
else if (comp < 0)
l = m + 1;
else
h = m - 1;
}
return -1;
}
int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags)
{
mz_uint file_index; size_t name_len, comment_len;
if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
return -1;
if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size))
return mz_zip_reader_locate_file_binary_search(pZip, pName);
name_len = strlen(pName); if (name_len > 0xFFFF) return -1;
comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1;
for (file_index = 0; file_index < pZip->m_total_files; file_index++)
{
const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
if (filename_len < name_len)
continue;
if (comment_len)
{
mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS);
const char *pFile_comment = pFilename + filename_len + file_extra_len;
if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags)))
continue;
}
if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len))
{
int ofs = filename_len - 1;
do
{
if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':'))
break;
} while (--ofs >= 0);
ofs++;
pFilename += ofs; filename_len -= ofs;
}
if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags)))
return file_index;
}
return -1;
}
mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
{
int status = TINFL_STATUS_DONE;
mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail;
mz_zip_archive_file_stat file_stat;
void *pRead_buf;
mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
tinfl_decompressor inflator;
if ((buf_size) && (!pBuf))
return MZ_FALSE;
if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
return MZ_FALSE;
// Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes)
if (!file_stat.m_comp_size)
return MZ_TRUE;
// Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers).
// I'm torn how to handle this case - should it fail instead?
if (mz_zip_reader_is_file_a_directory(pZip, file_index))
return MZ_TRUE;
// Encryption and patch files are not supported.
if (file_stat.m_bit_flag & (1 | 32))
return MZ_FALSE;
// This function only supports stored and deflate.
if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
return MZ_FALSE;
// Ensure supplied output buffer is large enough.
needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size;
if (buf_size < needed_size)
return MZ_FALSE;
// Read and parse the local directory entry.
cur_file_ofs = pZip->m_archive_file_ofs + file_stat.m_local_header_ofs;
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
return MZ_FALSE;
if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
return MZ_FALSE;
cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
return MZ_FALSE;
if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
{
// The file is stored or the caller has requested the compressed data.
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size)
return MZ_FALSE;
return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32);
}
// Decompress the file either directly from memory or from a file input buffer.
tinfl_init(&inflator);
if (pZip->m_pState->m_pMem)
{
// Read directly from the archive in memory.
pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
read_buf_size = read_buf_avail = file_stat.m_comp_size;
comp_remaining = 0;
}
else if (pUser_read_buf)
{
// Use a user provided read buffer.
if (!user_read_buf_size)
return MZ_FALSE;
pRead_buf = (mz_uint8 *)pUser_read_buf;
read_buf_size = user_read_buf_size;
read_buf_avail = 0;
comp_remaining = file_stat.m_comp_size;
}
else
{
// Temporarily allocate a read buffer.
read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE);
#ifdef _MSC_VER
if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
#else
if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
#endif
return MZ_FALSE;
if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
return MZ_FALSE;
read_buf_avail = 0;
comp_remaining = file_stat.m_comp_size;
}
do
{
size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs);
if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
{
read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
{
status = TINFL_STATUS_FAILED;
break;
}
cur_file_ofs += read_buf_avail;
comp_remaining -= read_buf_avail;
read_buf_ofs = 0;
}
in_buf_size = (size_t)read_buf_avail;
status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0));
read_buf_avail -= in_buf_size;
read_buf_ofs += in_buf_size;
out_buf_ofs += out_buf_size;
} while (status == TINFL_STATUS_NEEDS_MORE_INPUT);
if (status == TINFL_STATUS_DONE)
{
// Make sure the entire file was decompressed, and check its CRC.
if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32))
status = TINFL_STATUS_FAILED;
}
if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf))
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
return status == TINFL_STATUS_DONE;
}
mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
{
int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
if (file_index < 0)
return MZ_FALSE;
return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size);
}
mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags)
{
return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0);
}
mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags)
{
return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0);
}
void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags)
{
mz_uint64 comp_size, uncomp_size, alloc_size;
const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
void *pBuf;
if (pSize)
*pSize = 0;
if (!p)
return NULL;
comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size;
#ifdef _MSC_VER
if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
#else
if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
#endif
return NULL;
if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size)))
return NULL;
if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags))
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
return NULL;
}
if (pSize) *pSize = (size_t)alloc_size;
return pBuf;
}
void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags)
{
int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
if (file_index < 0)
{
if (pSize) *pSize = 0;
return MZ_FALSE;
}
return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags);
}
mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
{
int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT;
mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs;
mz_zip_archive_file_stat file_stat;
void *pRead_buf = NULL; void *pWrite_buf = NULL;
mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
return MZ_FALSE;
// Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes)
if (!file_stat.m_comp_size)
return MZ_TRUE;
// Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers).
// I'm torn how to handle this case - should it fail instead?
if (mz_zip_reader_is_file_a_directory(pZip, file_index))
return MZ_TRUE;
// Encryption and patch files are not supported.
if (file_stat.m_bit_flag & (1 | 32))
return MZ_FALSE;
// This function only supports stored and deflate.
if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
return MZ_FALSE;
// Read and parse the local directory entry.
cur_file_ofs = file_stat.m_local_header_ofs;
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
return MZ_FALSE;
if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
return MZ_FALSE;
cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
return MZ_FALSE;
// Decompress the file either directly from memory or from a file input buffer.
if (pZip->m_pState->m_pMem)
{
pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
read_buf_size = read_buf_avail = file_stat.m_comp_size;
comp_remaining = 0;
}
else
{
read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE);
if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
return MZ_FALSE;
read_buf_avail = 0;
comp_remaining = file_stat.m_comp_size;
}
if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
{
// The file is stored or the caller has requested the compressed data.
if (pZip->m_pState->m_pMem)
{
#ifdef _MSC_VER
if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF))
#else
if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF))
#endif
return MZ_FALSE;
if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size)
status = TINFL_STATUS_FAILED;
else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size);
cur_file_ofs += file_stat.m_comp_size;
out_buf_ofs += file_stat.m_comp_size;
comp_remaining = 0;
}
else
{
while (comp_remaining)
{
read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
{
status = TINFL_STATUS_FAILED;
break;
}
if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail);
if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
{
status = TINFL_STATUS_FAILED;
break;
}
cur_file_ofs += read_buf_avail;
out_buf_ofs += read_buf_avail;
comp_remaining -= read_buf_avail;
}
}
}
else
{
tinfl_decompressor inflator;
tinfl_init(&inflator);
if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE)))
status = TINFL_STATUS_FAILED;
else
{
do
{
mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
{
read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
{
status = TINFL_STATUS_FAILED;
break;
}
cur_file_ofs += read_buf_avail;
comp_remaining -= read_buf_avail;
read_buf_ofs = 0;
}
in_buf_size = (size_t)read_buf_avail;
status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0);
read_buf_avail -= in_buf_size;
read_buf_ofs += in_buf_size;
if (out_buf_size)
{
if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size)
{
status = TINFL_STATUS_FAILED;
break;
}
file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size);
if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size)
{
status = TINFL_STATUS_FAILED;
break;
}
}
} while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT));
}
}
if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))
{
// Make sure the entire file was decompressed, and check its CRC.
if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32))
status = TINFL_STATUS_FAILED;
}
if (!pZip->m_pState->m_pMem)
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
if (pWrite_buf)
pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf);
return status == TINFL_STATUS_DONE;
}
mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
{
int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
if (file_index < 0)
return MZ_FALSE;
return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags);
}
#ifndef MINIZ_NO_STDIO
static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n)
{
(void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque);
}
mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags)
{
mz_bool status;
mz_zip_archive_file_stat file_stat;
MZ_FILE *pFile;
if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
return MZ_FALSE;
pFile = MZ_FOPEN(pDst_filename, "wb");
if (!pFile)
return MZ_FALSE;
status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);
if (MZ_FCLOSE(pFile) == EOF)
return MZ_FALSE;
#ifndef MINIZ_NO_TIME
if (status)
mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time);
#endif
return status;
}
#endif // #ifndef MINIZ_NO_STDIO
mz_bool mz_zip_reader_end(mz_zip_archive *pZip)
{
if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
return MZ_FALSE;
if (pZip->m_pState)
{
mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL;
mz_zip_array_clear(pZip, &pState->m_central_dir);
mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
#ifndef MINIZ_NO_STDIO
if (pState->m_pFile)
{
MZ_FCLOSE(pState->m_pFile);
pState->m_pFile = NULL;
}
#endif // #ifndef MINIZ_NO_STDIO
pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
}
pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
return MZ_TRUE;
}
#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags)
{
int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags);
if (file_index < 0)
return MZ_FALSE;
return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags);
}
#endif
// ------------------- .ZIP archive writing
#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); }
static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); }
#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v))
#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v))
mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size)
{
if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
return MZ_FALSE;
if (pZip->m_file_offset_alignment)
{
// Ensure user specified file offset alignment is a power of 2.
if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1))
return MZ_FALSE;
}
if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func;
if (!pZip->m_pFree) pZip->m_pFree = def_free_func;
if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func;
pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
pZip->m_archive_size = existing_size;
pZip->m_central_directory_file_ofs = 0;
pZip->m_total_files = 0;
if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
return MZ_FALSE;
memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
return MZ_TRUE;
}
static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
{
mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
mz_zip_internal_state *pState = pZip->m_pState;
mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size);
#ifdef _MSC_VER
if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)))
#else
if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)))
#endif
return 0;
if (new_size > pState->m_mem_capacity)
{
void *pNew_block;
size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2;
if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity)))
return 0;
pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity;
}
memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n);
pState->m_mem_size = (size_t)new_size;
return n;
}
mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size)
{
pZip->m_pWrite = mz_zip_heap_write_func;
pZip->m_pIO_opaque = pZip;
if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning))
return MZ_FALSE;
if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning)))
{
if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size)))
{
mz_zip_writer_end(pZip);
return MZ_FALSE;
}
pZip->m_pState->m_mem_capacity = initial_allocation_size;
}
return MZ_TRUE;
}
#ifndef MINIZ_NO_STDIO
static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
{
mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
return 0;
return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile);
}
mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning)
{
MZ_FILE *pFile;
pZip->m_pWrite = mz_zip_file_write_func;
pZip->m_pIO_opaque = pZip;
if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning))
return MZ_FALSE;
if (NULL == (pFile = MZ_FOPEN(pFilename, "wb")))
{
mz_zip_writer_end(pZip);
return MZ_FALSE;
}
pZip->m_pState->m_pFile = pFile;
if (size_to_reserve_at_beginning)
{
mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf);
do
{
size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning);
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n)
{
mz_zip_writer_end(pZip);
return MZ_FALSE;
}
cur_ofs += n; size_to_reserve_at_beginning -= n;
} while (size_to_reserve_at_beginning);
}
return MZ_TRUE;
}
#endif // #ifndef MINIZ_NO_STDIO
mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename)
{
mz_zip_internal_state *pState;
if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
return MZ_FALSE;
// No sense in trying to write to an archive that's already at the support max size
if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
return MZ_FALSE;
pState = pZip->m_pState;
if (pState->m_pFile)
{
#ifdef MINIZ_NO_STDIO
pFilename; return MZ_FALSE;
#else
// Archive is being read from stdio - try to reopen as writable.
if (pZip->m_pIO_opaque != pZip)
return MZ_FALSE;
if (!pFilename)
return MZ_FALSE;
pZip->m_pWrite = mz_zip_file_write_func;
if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile)))
{
// The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it.
mz_zip_reader_end(pZip);
return MZ_FALSE;
}
#endif // #ifdef MINIZ_NO_STDIO
}
else if (pState->m_pMem)
{
// Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback.
if (pZip->m_pIO_opaque != pZip)
return MZ_FALSE;
pState->m_mem_capacity = pState->m_mem_size;
pZip->m_pWrite = mz_zip_heap_write_func;
}
// Archive is being read via a user provided read function - make sure the user has specified a write function too.
else if (!pZip->m_pWrite)
return MZ_FALSE;
// Start writing new files at the archive's current central directory location.
pZip->m_archive_size = pZip->m_central_directory_file_ofs;
pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
pZip->m_central_directory_file_ofs = 0;
return MZ_TRUE;
}
mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags)
{
return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0);
}
typedef struct
{
mz_zip_archive *m_pZip;
mz_uint64 m_cur_archive_file_ofs;
mz_uint64 m_comp_size;
} mz_zip_writer_add_state;
static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser)
{
mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser;
if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len)
return MZ_FALSE;
pState->m_cur_archive_file_ofs += len;
pState->m_comp_size += len;
return MZ_TRUE;
}
static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date)
{
(void)pZip;
memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE);
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date);
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32);
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size);
MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size);
MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size);
return MZ_TRUE;
}
static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
{
(void)pZip;
memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date);
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32);
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size);
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size);
MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size);
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes);
MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs);
return MZ_TRUE;
}
static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
{
mz_zip_internal_state *pState = pZip->m_pState;
mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size;
size_t orig_central_dir_size = pState->m_central_dir.m_size;
mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
// No zip64 support yet
if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF))
return MZ_FALSE;
if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes))
return MZ_FALSE;
if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) ||
(!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) ||
(!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) ||
(!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) ||
(!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &central_dir_ofs, 1)))
{
// Try to push the central directory array back into its original state.
mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
return MZ_FALSE;
}
return MZ_TRUE;
}
static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name)
{
// Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes.
if (*pArchive_name == '/')
return MZ_FALSE;
while (*pArchive_name)
{
if ((*pArchive_name == '\\') || (*pArchive_name == ':'))
return MZ_FALSE;
pArchive_name++;
}
return MZ_TRUE;
}
static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip)
{
mz_uint32 n;
if (!pZip->m_file_offset_alignment)
return 0;
n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1));
return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1);
}
static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n)
{
char buf[4096];
memset(buf, 0, MZ_MIN(sizeof(buf), n));
while (n)
{
mz_uint32 s = MZ_MIN(sizeof(buf), n);
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s)
return MZ_FALSE;
cur_file_ofs += s; n -= s;
}
return MZ_TRUE;
}
mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32)
{
mz_uint16 method = 0, dos_time = 0, dos_date = 0;
mz_uint level, ext_attributes = 0, num_alignment_padding_bytes;
mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0;
size_t archive_name_size;
mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
tdefl_compressor *pComp = NULL;
mz_bool store_data_uncompressed;
mz_zip_internal_state *pState;
if ((int)level_and_flags < 0)
level_and_flags = MZ_DEFAULT_LEVEL;
level = level_and_flags & 0xF;
store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA));
if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION))
return MZ_FALSE;
pState = pZip->m_pState;
if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size))
return MZ_FALSE;
// No zip64 support yet
if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF))
return MZ_FALSE;
if (!mz_zip_writer_validate_archive_name(pArchive_name))
return MZ_FALSE;
#ifndef MINIZ_NO_TIME
{
time_t cur_time; time(&cur_time);
mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date);
}
#endif // #ifndef MINIZ_NO_TIME
archive_name_size = strlen(pArchive_name);
if (archive_name_size > 0xFFFF)
return MZ_FALSE;
num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
// no zip64 support yet
if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF))
return MZ_FALSE;
if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/'))
{
// Set DOS Subdirectory attribute bit.
ext_attributes |= 0x10;
// Subdirectories cannot contain data.
if ((buf_size) || (uncomp_size))
return MZ_FALSE;
}
// Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.)
if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1)))
return MZ_FALSE;
if ((!store_data_uncompressed) && (buf_size))
{
if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor))))
return MZ_FALSE;
}
if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
return MZ_FALSE;
}
local_dir_header_ofs += num_alignment_padding_bytes;
if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
MZ_CLEAR_OBJ(local_dir_header);
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
return MZ_FALSE;
}
cur_archive_file_ofs += archive_name_size;
if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
{
uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size);
uncomp_size = buf_size;
if (uncomp_size <= 3)
{
level = 0;
store_data_uncompressed = MZ_TRUE;
}
}
if (store_data_uncompressed)
{
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
return MZ_FALSE;
}
cur_archive_file_ofs += buf_size;
comp_size = buf_size;
if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
method = MZ_DEFLATED;
}
else if (buf_size)
{
mz_zip_writer_add_state state;
state.m_pZip = pZip;
state.m_cur_archive_file_ofs = cur_archive_file_ofs;
state.m_comp_size = 0;
if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) ||
(tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE))
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
return MZ_FALSE;
}
comp_size = state.m_comp_size;
cur_archive_file_ofs = state.m_cur_archive_file_ofs;
method = MZ_DEFLATED;
}
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
pComp = NULL;
// no zip64 support yet
if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF))
return MZ_FALSE;
if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
return MZ_FALSE;
if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
return MZ_FALSE;
if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
return MZ_FALSE;
pZip->m_total_files++;
pZip->m_archive_size = cur_archive_file_ofs;
return MZ_TRUE;
}
#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
{
mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes;
mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0;
mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0;
size_t archive_name_size;
mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
MZ_FILE *pSrc_file = NULL;
if ((int)level_and_flags < 0)
level_and_flags = MZ_DEFAULT_LEVEL;
level = level_and_flags & 0xF;
if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))
return MZ_FALSE;
if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
return MZ_FALSE;
if (!mz_zip_writer_validate_archive_name(pArchive_name))
return MZ_FALSE;
archive_name_size = strlen(pArchive_name);
if (archive_name_size > 0xFFFF)
return MZ_FALSE;
num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
// no zip64 support yet
if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF))
return MZ_FALSE;
if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date))
return MZ_FALSE;
pSrc_file = MZ_FOPEN(pSrc_filename, "rb");
if (!pSrc_file)
return MZ_FALSE;
MZ_FSEEK64(pSrc_file, 0, SEEK_END);
uncomp_size = MZ_FTELL64(pSrc_file);
MZ_FSEEK64(pSrc_file, 0, SEEK_SET);
if (uncomp_size > 0xFFFFFFFF)
{
// No zip64 support yet
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
if (uncomp_size <= 3)
level = 0;
if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
{
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
local_dir_header_ofs += num_alignment_padding_bytes;
if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
MZ_CLEAR_OBJ(local_dir_header);
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
{
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
cur_archive_file_ofs += archive_name_size;
if (uncomp_size)
{
mz_uint64 uncomp_remaining = uncomp_size;
void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE);
if (!pRead_buf)
{
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
if (!level)
{
while (uncomp_remaining)
{
mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining);
if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n))
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);
uncomp_remaining -= n;
cur_archive_file_ofs += n;
}
comp_size = uncomp_size;
}
else
{
mz_bool result = MZ_FALSE;
mz_zip_writer_add_state state;
tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor));
if (!pComp)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
state.m_pZip = pZip;
state.m_cur_archive_file_ofs = cur_archive_file_ofs;
state.m_comp_size = 0;
if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
for ( ; ; )
{
size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE);
tdefl_status status;
if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size)
break;
uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size);
uncomp_remaining -= in_buf_size;
status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH);
if (status == TDEFL_STATUS_DONE)
{
result = MZ_TRUE;
break;
}
else if (status != TDEFL_STATUS_OKAY)
break;
}
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
if (!result)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
MZ_FCLOSE(pSrc_file);
return MZ_FALSE;
}
comp_size = state.m_comp_size;
cur_archive_file_ofs = state.m_cur_archive_file_ofs;
method = MZ_DEFLATED;
}
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
}
MZ_FCLOSE(pSrc_file); pSrc_file = NULL;
// no zip64 support yet
if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF))
return MZ_FALSE;
if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
return MZ_FALSE;
if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
return MZ_FALSE;
if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
return MZ_FALSE;
pZip->m_total_files++;
pZip->m_archive_size = cur_archive_file_ofs;
return MZ_TRUE;
}
#endif // #ifndef MINIZ_NO_STDIO
mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index)
{
mz_uint n, bit_flags, num_alignment_padding_bytes;
mz_uint64 comp_bytes_remaining, local_dir_header_ofs;
mz_uint64 cur_src_file_ofs, cur_dst_file_ofs;
mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
size_t orig_central_dir_size;
mz_zip_internal_state *pState;
void *pBuf; const mz_uint8 *pSrc_central_header;
if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
return MZ_FALSE;
if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index)))
return MZ_FALSE;
pState = pZip->m_pState;
num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
// no zip64 support yet
if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
return MZ_FALSE;
cur_src_file_ofs = pSource_zip->m_archive_file_ofs + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
cur_dst_file_ofs = pZip->m_archive_size;
if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
return MZ_FALSE;
if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
return MZ_FALSE;
cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes))
return MZ_FALSE;
cur_dst_file_ofs += num_alignment_padding_bytes;
local_dir_header_ofs = cur_dst_file_ofs;
if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
return MZ_FALSE;
cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining)))))
return MZ_FALSE;
while (comp_bytes_remaining)
{
n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining);
if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
return MZ_FALSE;
}
cur_src_file_ofs += n;
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
return MZ_FALSE;
}
cur_dst_file_ofs += n;
comp_bytes_remaining -= n;
}
bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);
if (bit_flags & 8)
{
// Copy data descriptor
if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
return MZ_FALSE;
}
n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3);
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
return MZ_FALSE;
}
cur_src_file_ofs += n;
cur_dst_file_ofs += n;
}
pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
// no zip64 support yet
if (cur_dst_file_ofs > 0xFFFFFFFF)
return MZ_FALSE;
orig_central_dir_size = pState->m_central_dir.m_size;
memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs);
if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE))
return MZ_FALSE;
n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS);
if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n))
{
mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
return MZ_FALSE;
}
if (pState->m_central_dir.m_size > 0xFFFFFFFF)
return MZ_FALSE;
n = (mz_uint32)orig_central_dir_size;
if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1))
{
mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
return MZ_FALSE;
}
pZip->m_total_files++;
pZip->m_archive_size = cur_dst_file_ofs;
return MZ_TRUE;
}
mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
{
mz_zip_internal_state *pState;
mz_uint64 central_dir_ofs, central_dir_size;
mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE];
if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
return MZ_FALSE;
pState = pZip->m_pState;
// no zip64 support yet
if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
return MZ_FALSE;
central_dir_ofs = 0;
central_dir_size = 0;
if (pZip->m_total_files)
{
// Write central directory
central_dir_ofs = pZip->m_archive_size;
central_dir_size = pState->m_central_dir.m_size;
pZip->m_central_directory_file_ofs = central_dir_ofs;
if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size)
return MZ_FALSE;
pZip->m_archive_size += central_dir_size;
}
// Write end of central directory record
MZ_CLEAR_OBJ(hdr);
MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG);
MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files);
MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files);
MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size);
MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs);
if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr))
return MZ_FALSE;
#ifndef MINIZ_NO_STDIO
if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF))
return MZ_FALSE;
#endif // #ifndef MINIZ_NO_STDIO
pZip->m_archive_size += sizeof(hdr);
pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED;
return MZ_TRUE;
}
mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize)
{
if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize))
return MZ_FALSE;
if (pZip->m_pWrite != mz_zip_heap_write_func)
return MZ_FALSE;
if (!mz_zip_writer_finalize_archive(pZip))
return MZ_FALSE;
*pBuf = pZip->m_pState->m_pMem;
*pSize = pZip->m_pState->m_mem_size;
pZip->m_pState->m_pMem = NULL;
pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0;
return MZ_TRUE;
}
mz_bool mz_zip_writer_end(mz_zip_archive *pZip)
{
mz_zip_internal_state *pState;
mz_bool status = MZ_TRUE;
if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)))
return MZ_FALSE;
pState = pZip->m_pState;
pZip->m_pState = NULL;
mz_zip_array_clear(pZip, &pState->m_central_dir);
mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
#ifndef MINIZ_NO_STDIO
if (pState->m_pFile)
{
MZ_FCLOSE(pState->m_pFile);
pState->m_pFile = NULL;
}
#endif // #ifndef MINIZ_NO_STDIO
if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem))
{
pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem);
pState->m_pMem = NULL;
}
pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
return status;
}
#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
{
mz_bool status, created_new_archive = MZ_FALSE;
mz_zip_archive zip_archive;
struct MZ_FILE_STAT_STRUCT file_stat;
MZ_CLEAR_OBJ(zip_archive);
if ((int)level_and_flags < 0)
level_and_flags = MZ_DEFAULT_LEVEL;
if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION))
return MZ_FALSE;
if (!mz_zip_writer_validate_archive_name(pArchive_name))
return MZ_FALSE;
if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0)
{
// Create a new archive.
if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0))
return MZ_FALSE;
created_new_archive = MZ_TRUE;
}
else
{
// Append to an existing archive.
if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY))
return MZ_FALSE;
if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename))
{
mz_zip_reader_end(&zip_archive);
return MZ_FALSE;
}
}
status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0);
// Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.)
if (!mz_zip_writer_finalize_archive(&zip_archive))
status = MZ_FALSE;
if (!mz_zip_writer_end(&zip_archive))
status = MZ_FALSE;
if ((!status) && (created_new_archive))
{
// It's a new archive and something went wrong, so just delete it.
int ignoredStatus = MZ_DELETE_FILE(pZip_filename);
(void)ignoredStatus;
}
return status;
}
void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags)
{
int file_index;
mz_zip_archive zip_archive;
void *p = NULL;
if (pSize)
*pSize = 0;
if ((!pZip_filename) || (!pArchive_name))
return NULL;
MZ_CLEAR_OBJ(zip_archive);
if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY))
return NULL;
if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0)
p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags);
mz_zip_reader_end(&zip_archive);
return p;
}
#endif // #ifndef MINIZ_NO_STDIO
#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
#endif // #ifndef MINIZ_NO_ARCHIVE_APIS
#ifdef __cplusplus
}
#endif
#endif // MINIZ_HEADER_FILE_ONLY
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
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 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.
For more information, please refer to <https://unlicense.org/>
*/
diff --git a/Modules/DICOM/cmdapps/DICOMVolumeDiagnostics.cpp b/Modules/DICOM/cmdapps/DICOMVolumeDiagnostics.cpp
index ae7d47f01d..dc50c4c610 100644
--- a/Modules/DICOM/cmdapps/DICOMVolumeDiagnostics.cpp
+++ b/Modules/DICOM/cmdapps/DICOMVolumeDiagnostics.cpp
@@ -1,199 +1,199 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkCommandLineParser.h>
#include <mitkIOUtil.h>
#include <mitkDICOMEnums.h>
#include <mitkDICOMFilesHelper.h>
#include <mitkDICOMFileReaderSelector.h>
-#include <filesystem>
+#include <mitkFileSystem.h>
#include <nlohmann/json.hpp>
void InitializeCommandLineParser(mitkCommandLineParser& parser)
{
parser.setTitle("DICOM Volume Diagnostics");
parser.setCategory("DICOM");
parser.setDescription("Gives insights how MITK readers would convert a set of DICOM files into image volumes (e.g. number of volumes and the sorting of the files)");
parser.setContributor("German Cancer Research Center (DKFZ)");
parser.setArgumentPrefix("--", "-");
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.addArgument("only-own-series", "s", mitkCommandLineParser::Bool, "Only own series", "Analyze only files in the same directory that have the same DICOM Series UID, if a file is provided as input.", us::Any());
parser.addArgument("check-3d", "d", mitkCommandLineParser::Bool, "Check 3D configs", "Analyze the input by using all known 3D configurations. If flag is not set all configurations (3D and 3D+t) will be used.", us::Any());
parser.addArgument("check-3d+t", "t", mitkCommandLineParser::Bool, "Check 3D+t configs", "Analyze the input by using all known 3D+t configurations (thus dynamic image configurations). If flag is not set all configurations (3D and 3D+t) will be used.", us::Any());
parser.addArgument("input", "i", mitkCommandLineParser::File, "Input file or path", "Input contour(s)", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output", "o", mitkCommandLineParser::File, "Output file", "Output file where the diagnostics results are stored as json.", us::Any());
}
int main(int argc, char* argv[])
{
int returnValue = EXIT_SUCCESS;
mitkCommandLineParser parser;
InitializeCommandLineParser(parser);
auto args = parser.parseArguments(argc, argv);
if (args.empty())
{
std::cout << parser.helpText();
return EXIT_FAILURE;
}
nlohmann::json diagnosticsResult;
try
{
auto inputFilename = us::any_cast<std::string>(args["input"]);
auto outputFilename = args.count("output")==0 ? std::string() : us::any_cast<std::string>(args["output"]);
bool onlyOwnSeries = args.count("only-own-series");
bool check3D = args.count("check-3d");
bool check3DPlusT = args.count("check-3d+t");
if (!check3D && !check3DPlusT)
{ //if no check option is selected all are activated by default.
check3D = true;
check3DPlusT = true;
}
diagnosticsResult["input"] = inputFilename;
diagnosticsResult["only-own-series"] = onlyOwnSeries;
diagnosticsResult["check-3d"] = check3D;
diagnosticsResult["check-3d+t"] = check3DPlusT;
mitk::StringList relevantFiles = mitk::GetDICOMFilesInSameDirectory(inputFilename);
if (relevantFiles.empty())
{
mitkThrow() << "DICOM Volume Diagnostics found no relevant files in specified location. No data is loaded. Location: " << inputFilename;
}
else
{
- bool pathIsDirectory = std::filesystem::is_directory(inputFilename);
+ bool pathIsDirectory = fs::is_directory(inputFilename);
if (!pathIsDirectory && onlyOwnSeries)
{
relevantFiles = mitk::FilterDICOMFilesForSameSeries(inputFilename, relevantFiles);
}
diagnosticsResult["analyzed_files"] = relevantFiles;
auto selector = mitk::DICOMFileReaderSelector::New();
if (check3D) selector->LoadBuiltIn3DConfigs();
if (check3DPlusT) selector->LoadBuiltIn3DnTConfigs();
nlohmann::json readerInfos;
for (const auto& reader : selector->GetAllConfiguredReaders())
{
nlohmann::json readerInfo;
readerInfo["class_name"] = reader->GetNameOfClass();
readerInfo["configuration_label"] = reader->GetConfigurationLabel();
readerInfo["configuration_description"] = reader->GetConfigurationDescription();
readerInfos.push_back(readerInfo);
}
diagnosticsResult["checked_readers"] = readerInfos;
selector->SetInputFiles(relevantFiles);
auto reader = selector->GetFirstReaderWithMinimumNumberOfOutputImages();
if (reader.IsNull())
{
mitkThrow() << "DICOM Volume Diagnostics service found no suitable reader configuration for relevant files.";
}
else
{
nlohmann::json readerInfo;
readerInfo["class_name"] = reader->GetNameOfClass();
readerInfo["configuration_label"] = reader->GetConfigurationLabel();
readerInfo["configuration_description"] = reader->GetConfigurationDescription();
readerInfo["configuration_description"] = reader->GetConfigurationDescription();
std::stringstream config;
reader->PrintConfiguration(config);
readerInfo["config_details"] = config.str();
diagnosticsResult["selected_reader"] = readerInfo;
nlohmann::json outputInfos;
unsigned int relevantOutputCount = 0;
const auto nrOfOutputs = reader->GetNumberOfOutputs();
for (std::remove_const_t<decltype(nrOfOutputs)> outputIndex = 0; outputIndex < nrOfOutputs; ++outputIndex)
{
bool isRelevantOutput = true;
if (!pathIsDirectory)
{
const auto frameList = reader->GetOutput(outputIndex).GetImageFrameList();
auto finding = std::find_if(frameList.begin(), frameList.end(), [&](const mitk::DICOMImageFrameInfo::Pointer& frame)
{
- std::filesystem::path framePath(frame->Filename);
- std::filesystem::path inputPath(inputFilename);
+ fs::path framePath(frame->Filename);
+ fs::path inputPath(inputFilename);
return framePath == inputPath;
});
isRelevantOutput = finding != frameList.end();
}
if (isRelevantOutput)
{
++relevantOutputCount;
nlohmann::json outputInfo;
const auto output = reader->GetOutput(outputIndex);
const auto frameList = output.GetImageFrameList();
mitk::DICOMFilePathList outputFiles;
outputFiles.resize(frameList.size());
std::transform(frameList.begin(), frameList.end(), outputFiles.begin(), [](const mitk::DICOMImageFrameInfo::Pointer& frame) { return frame->Filename; });
outputInfo["files"] = outputFiles;
outputInfo["timesteps"] = output.GetNumberOfTimeSteps();
outputInfo["frames_per_timesteps"] = output.GetNumberOfFramesPerTimeStep();
outputInfos.push_back(outputInfo);
}
}
diagnosticsResult["volume_count"] = relevantOutputCount;
diagnosticsResult["volumes"] = outputInfos;
}
}
std::cout << "\n### DIAGNOSTICS REPORT ###\n" << std::endl;
std::cout << std::setw(2) << diagnosticsResult << std::endl;
if (!outputFilename.empty())
{
std::ofstream fileout(outputFilename);
fileout << diagnosticsResult;
fileout.close();
}
}
catch (const mitk::Exception& e)
{
MITK_ERROR << e.GetDescription();
return EXIT_FAILURE;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "An unknown error occurred!";
return EXIT_FAILURE;
}
return returnValue;
}
diff --git a/Modules/DICOM/include/mitkBaseDICOMReaderService.h b/Modules/DICOM/include/mitkBaseDICOMReaderService.h
index 75133e9064..d4b51c69ab 100644
--- a/Modules/DICOM/include/mitkBaseDICOMReaderService.h
+++ b/Modules/DICOM/include/mitkBaseDICOMReaderService.h
@@ -1,70 +1,70 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkBaseDICOMReaderService_h
#define mitkBaseDICOMReaderService_h
#include <mitkAbstractFileReader.h>
#include <mitkDICOMFileReader.h>
#include "MitkDICOMExports.h"
namespace mitk {
/**
Base class for service wrappers that make DICOMFileReader from
the DICOM module usable.
*/
class MITKDICOM_EXPORT BaseDICOMReaderService : public AbstractFileReader
{
public:
using AbstractFileReader::Read;
IFileReader::ConfidenceLevel GetConfidenceLevel() const override;
protected:
BaseDICOMReaderService(const std::string& description);
BaseDICOMReaderService(const mitk::CustomMimeType& customType, const std::string& description);
/** Uses this->GetRelevantFile() and this->GetReader to load the image.
* data and puts it into base data instances-*/
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
/** Returns the list of all DCM files that are in the same directory
* like this->GetLocalFileName().*/
mitk::StringList GetDICOMFilesInSameDirectory() const;
/** Returns the reader instance that should be used. The decision may be based
* one the passed list of relevant files.*/
virtual mitk::DICOMFileReader::Pointer GetReader(const mitk::StringList& relevantFiles) const = 0;
void SetOnlyRegardOwnSeries(bool);
bool GetOnlyRegardOwnSeries() const;
private:
- /** Flags that constrols if the read() operation should only regard DICOM files of the same series
+ /** Flags that controls if the read() operation should only regard DICOM files of the same series
if the specified GetLocalFileName() is a file. If it is a director, this flag has no impact (it is
assumed false then).
*/
bool m_OnlyRegardOwnSeries = true;
};
class IPropertyProvider;
/** Helper function that generates a name string (e.g. for DataNode names) from the DICOM properties of the passed
provider instance. If the instance is nullptr, or has no dicom properties DataNode::NO_NAME_VALUE() will be returned.*/
std::string MITKDICOM_EXPORT GenerateNameFromDICOMProperties(const mitk::IPropertyProvider* provider);
}
#endif
diff --git a/Modules/DICOM/include/mitkDICOMSortCriterion.h b/Modules/DICOM/include/mitkDICOMSortCriterion.h
index 2a35cd3447..70aaec9949 100644
--- a/Modules/DICOM/include/mitkDICOMSortCriterion.h
+++ b/Modules/DICOM/include/mitkDICOMSortCriterion.h
@@ -1,78 +1,78 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkDICOMSortCriterion_h
#define mitkDICOMSortCriterion_h
#include "itkObjectFactory.h"
#include "mitkCommon.h"
#include "mitkDICOMDatasetAccess.h"
namespace mitk
{
/**
\ingroup DICOMModule
\brief A tag based sorting criterion for use in DICOMTagBasedSorter.
This class is used within std::sort (see DICOMTagBasedSorter::Sort())
and has to answer a simple question by implementing IsLeftBeforeRight().
Each time IsLeftBeforeRight() is called, the method should return whether
the left dataset should be sorted before the right dataset.
Because there are identical tags values quite often, a DICOMSortCriterion
will always hold a secondary DICOMSortCriterion. In cases of equal tag
values, the decision is referred to the secondary criterion.
*/
class MITKDICOM_EXPORT DICOMSortCriterion : public itk::LightObject
{
public:
mitkClassMacroItkParent( DICOMSortCriterion, itk::LightObject );
- /// \brief Tags used for comparison (includes seconary criteria).
+ /// \brief Tags used for comparison (includes secondary criteria).
DICOMTagList GetAllTagsOfInterest() const;
/// \brief Tags used for comparison.
virtual DICOMTagList GetTagsOfInterest() const = 0;
/// \brief Answer the sorting question.
virtual bool IsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const = 0;
/// \brief Calculate a distance between two datasets.
- /// This ansers the question of consecutive datasets.
+ /// This answers the question of consecutive datasets.
virtual double NumericDistance(const mitk::DICOMDatasetAccess* from, const mitk::DICOMDatasetAccess* to) const = 0;
/// \brief The fallback criterion.
DICOMSortCriterion::ConstPointer GetSecondaryCriterion() const;
/// brief describe this class in given stream.
virtual void Print(std::ostream& os) const = 0;
virtual bool operator==(const DICOMSortCriterion& other) const = 0;
protected:
DICOMSortCriterion( DICOMSortCriterion::Pointer secondaryCriterion );
~DICOMSortCriterion() override;
bool NextLevelIsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const;
explicit DICOMSortCriterion(const DICOMSortCriterion& other);
DICOMSortCriterion& operator=(const DICOMSortCriterion& other);
DICOMSortCriterion::Pointer m_SecondaryCriterion;
};
}
#endif
diff --git a/Modules/DICOM/include/mitkEquiDistantBlocksSorter.h b/Modules/DICOM/include/mitkEquiDistantBlocksSorter.h
index 39a2ae15f8..26e1b0da35 100644
--- a/Modules/DICOM/include/mitkEquiDistantBlocksSorter.h
+++ b/Modules/DICOM/include/mitkEquiDistantBlocksSorter.h
@@ -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.
============================================================================*/
#ifndef mitkEquiDistantBlocksSorter_h
#define mitkEquiDistantBlocksSorter_h
#include "mitkDICOMDatasetSorter.h"
#include "mitkDICOMSortCriterion.h"
#include "mitkGantryTiltInformation.h"
#include "mitkVector.h"
namespace mitk
{
/**
\ingroup DICOMModule
\brief Split inputs into blocks of equidistant slices (for use in DICOMITKSeriesGDCMReader).
Since inter-slice distance is not recorded in DICOM tags, we must ensure that blocks are made up of
slices that have equal distances between neighboring slices. This is especially necessary because itk::ImageSeriesReader
is later used for the actual loading, and this class expects (and does nocht verify) equal inter-slice distance (see \ref DICOMITKSeriesGDCMReader_ForcedConfiguration).
To achieve such grouping, the inter-slice distance is calculated from the first two different slice positions of a block.
Following slices are added to a block as long as they can be added by adding the calculated inter-slice distance to the
last slice of the block. Slices that do not fit into the expected distance pattern, are set aside for further analysis.
This grouping is done until each file has been assigned to a group.
Slices that share a position in space are also sorted into separate blocks during this step.
So the result of this step is a set of blocks that contain only slices with equal z spacing
and unique slices at each position.
During sorting, the origins (documented in tag image position patient) are compared
against expected origins (from former origin plus moving direction). As there will
be minor differences in numbers (from both calculations and imprecise tag values),
we must be a bit tolerant here. The default behavior is to expect that an origin is
not further away from the expected position than 30% of the inter-slice distance.
To support a legacy behavior of a former loader (DicomSeriesReader), this default can
be restricted to a constant number of millimeters by calling SetToleratedOriginOffset(mm).
Detailed implementation in AnalyzeFileForITKImageSeriesReaderSpacingAssumption().
*/
class MITKDICOM_EXPORT EquiDistantBlocksSorter : public DICOMDatasetSorter
{
public:
mitkClassMacro( EquiDistantBlocksSorter, DICOMDatasetSorter );
itkNewMacro( EquiDistantBlocksSorter );
DICOMTagList GetTagsOfInterest() override;
/**
\brief Delegates work to AnalyzeFileForITKImageSeriesReaderSpacingAssumption().
AnalyzeFileForITKImageSeriesReaderSpacingAssumption() is called until it does not
create multiple blocks anymore.
*/
void Sort() override;
/**
\brief Whether or not to accept images from a tilted acquisition in a single output group.
*/
void SetAcceptTilt(bool accept);
bool GetAcceptTilt() const;
/**
\brief See class description and SetToleratedOriginOffset().
*/
void SetToleratedOriginOffsetToAdaptive(double fractionOfInterSliceDistanct = 0.3);
/**
\brief See class description and SetToleratedOriginOffsetToAdaptive().
Default value of 0.005 is calculated so that we get a maximum of 1/10mm
error when having a measurement crosses 20 slices in z direction (too strict? we don't know better..).
*/
void SetToleratedOriginOffset(double millimeters = 0.005);
double GetToleratedOriginOffset() const;
bool IsToleratedOriginOffsetAbsolute() const;
void SetAcceptTwoSlicesGroups(bool accept);
bool GetAcceptTwoSlicesGroups() const;
void PrintConfiguration(std::ostream& os, const std::string& indent = "") const override;
bool operator==(const DICOMDatasetSorter& other) const override;
protected:
/**
\brief Return type of AnalyzeFileForITKImageSeriesReaderSpacingAssumption().
Class contains the grouping result of method AnalyzeFileForITKImageSeriesReaderSpacingAssumption(),
which takes as input a number of images, which are all equally oriented and spatially sorted along their normal direction.
The result contains of two blocks: a first one is the grouping result, all of those images can be loaded
into one image block because they have an equal origin-to-origin distance without any gaps in-between.
*/
class SliceGroupingAnalysisResult
{
public:
SliceGroupingAnalysisResult();
/**
\brief Grouping result, all same origin-to-origin distance w/o gaps.
*/
DICOMDatasetList GetBlockDatasets();
void SetFirstFilenameOfBlock(const std::string& filename);
std::string GetFirstFilenameOfBlock() const;
void SetLastFilenameOfBlock(const std::string& filename);
std::string GetLastFilenameOfBlock() const;
/**
\brief Remaining files, which could not be grouped.
*/
DICOMDatasetList GetUnsortedDatasets();
/**
\brief Whether or not the grouped result contain a gantry tilt.
*/
bool ContainsGantryTilt();
/**
\brief Detailed description of gantry tilt.
*/
const GantryTiltInformation& GetTiltInfo() const;
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
*/
void AddFileToSortedBlock(DICOMDatasetAccess* dataset);
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
*/
void AddFileToUnsortedBlock(DICOMDatasetAccess* dataset);
void AddFilesToUnsortedBlock(const DICOMDatasetList& datasets);
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
\todo Could make sense to enhance this with an instance of GantryTiltInformation to store the whole result!
*/
void FlagGantryTilt(const GantryTiltInformation& tiltInfo);
/**
\brief Only meaningful for use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption.
*/
void UndoPrematureGrouping();
protected:
DICOMDatasetList m_GroupedFiles;
DICOMDatasetList m_UnsortedFiles;
GantryTiltInformation m_TiltInfo;
std::string m_FirstFilenameOfBlock;
std::string m_LastFilenameOfBlock;
};
/**
\brief Ensure an equal z-spacing for a group of files.
Takes as input a number of images, which are all equally oriented and spatially sorted along their normal direction.
- Internally used by GetSeries. Returns two lists: the first one contins slices of equal inter-slice spacing.
+ Internally used by GetSeries. Returns two lists: the first one contains slices of equal inter-slice spacing.
The second list contains remaining files, which need to be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption again.
Relevant code that is matched here is in
itkImageSeriesReader.txx (ImageSeriesReader<TOutputImage>::GenerateOutputInformation(void)), lines 176 to 245 (as of ITK 3.20)
*/
SliceGroupingAnalysisResult
AnalyzeFileForITKImageSeriesReaderSpacingAssumption(const DICOMDatasetList& files, bool groupsOfSimilarImages);
/**
\brief Safely convert const char* to std::string.
*/
std::string
ConstCharStarToString(const char* s);
EquiDistantBlocksSorter();
~EquiDistantBlocksSorter() override;
EquiDistantBlocksSorter(const EquiDistantBlocksSorter& other);
EquiDistantBlocksSorter& operator=(const EquiDistantBlocksSorter& other);
bool m_AcceptTilt;
typedef std::vector<SliceGroupingAnalysisResult> ResultsList;
ResultsList m_SliceGroupingResults;
double m_ToleratedOriginOffset;
bool m_ToleratedOriginOffsetIsAbsolute;
bool m_AcceptTwoSlicesGroups;
};
}
#endif
diff --git a/Modules/DICOM/include/mitkITKDICOMSeriesReaderHelper.h b/Modules/DICOM/include/mitkITKDICOMSeriesReaderHelper.h
index c721472f2d..4bcae594c7 100644
--- a/Modules/DICOM/include/mitkITKDICOMSeriesReaderHelper.h
+++ b/Modules/DICOM/include/mitkITKDICOMSeriesReaderHelper.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 mitkITKDICOMSeriesReaderHelper_h
#define mitkITKDICOMSeriesReaderHelper_h
#include "mitkImage.h"
#include "mitkGantryTiltInformation.h"
#include "mitkDICOMTag.h"
#include <itkGDCMImageIO.h>
/* Forward deceleration of an DCMTK class. Used in the txx but part of the interface.*/
class OFDateTime;
namespace mitk
{
class ITKDICOMSeriesReaderHelper
{
public:
static const DICOMTag AcquisitionDateTag;
static const DICOMTag AcquisitionTimeTag;
static const DICOMTag TriggerTimeTag;
typedef std::vector<std::string> StringContainer;
typedef std::list<StringContainer> StringContainerList;
Image::Pointer Load( const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo );
Image::Pointer Load3DnT( const StringContainerList& filenamesLists, bool correctTilt, const GantryTiltInformation& tiltInfo );
static bool CanHandleFile(const std::string& filename);
private:
typedef std::vector<TimeBounds> TimeBoundsList;
typedef itk::FixedArray<OFDateTime,2> DateTimeBounds;
/** Scans the given files for the acquisition time and returns the lowest and
highest acquisition date time as date time bounds via bounds.
@param bounds The acquisition date time bound extracted from the files.
@param triggerBounds Time bounds for trigger information extracted from the files.
If no trigger information was found than it returns trigger == [0.0, 0.0].
@return If no acquisition date times can be found the function return will be false. Otherwise
it returns True.
*/
static bool ExtractDateTimeBoundsAndTriggerOfTimeStep( const StringContainer& filenamesOfTimeStep,
DateTimeBounds& bounds, TimeBounds& triggerBounds);
/* Determine the time bounds in ms respective to the baselineDateTime for the passed
files. Additionally it regards the trigger time tag if set and acquisition date time
carries not enough information.*/
static bool ExtractTimeBoundsOfTimeStep(const StringContainer& filenamesOfTimeStep,
TimeBounds& bounds,
const OFDateTime& baselineDateTime );
/** Returns the list of time bounds of all passed time step containers.
(sa ExtractTimeBoundsOfTimeStep and ExtractDateTimeBoundsOfTimeStep).
Time steps where no time bounds could be extracted
- are indecated by "null" time bounds (both values "0"). The order of the returned
+ are indicated by "null" time bounds (both values "0"). The order of the returned
list equals of passed filenamesOfTimeSteps order.
@remark The function regards acquisition date time tags and trigger time tags.*/
static TimeBoundsList ExtractTimeBoundsOfTimeSteps (const StringContainerList& filenamesOfTimeSteps);
/** Helper function that generates a time geometry using the template and the passed boundslist
(which indicates the number of time steps).
*/
static TimeGeometry::Pointer GenerateTimeGeometry(const BaseGeometry* templateGeometry, const TimeBoundsList& boundsList);
template <typename ImageType>
typename ImageType::Pointer
FixUpTiltedGeometry( ImageType* input, const GantryTiltInformation& tiltInfo );
template <typename PixelType>
Image::Pointer
LoadDICOMByITK( const StringContainer& filenames,
bool correctTilt,
const GantryTiltInformation& tiltInfo,
itk::GDCMImageIO::Pointer& io);
template <typename PixelType>
Image::Pointer
LoadDICOMByITK3DnT( const StringContainerList& filenames,
bool correctTilt,
const GantryTiltInformation& tiltInfo,
itk::GDCMImageIO::Pointer& io);
};
}
#endif
diff --git a/Modules/DICOM/src/legacy/mitkDicomSeriesReader.h b/Modules/DICOM/src/legacy/mitkDicomSeriesReader.h
index 261e638e8a..f18179f78d 100644
--- a/Modules/DICOM/src/legacy/mitkDicomSeriesReader.h
+++ b/Modules/DICOM/src/legacy/mitkDicomSeriesReader.h
@@ -1,1033 +1,1033 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkDicomSeriesReader_h
#define mitkDicomSeriesReader_h
#include "mitkConfig.h"
#include "mitkDataNode.h"
#include <itkGDCMImageIO.h>
#include <itkCommand.h>
#include <itkImageSeriesReader.h>
#ifdef NOMINMAX
#define DEF_NOMINMAX
#undef NOMINMAX
#endif
#include <gdcmConfigure.h>
#ifdef DEF_NOMINMAX
#ifndef NOMINMAX
#define NOMINMAX
#endif
#undef DEF_NOMINMAX
#endif
#include <gdcmDataSet.h>
#include <gdcmScanner.h>
namespace mitk
{
/**
\brief Loading DICOM images as MITK images.
- \ref DicomSeriesReader_purpose
- \ref DicomSeriesReader_limitations
- \ref DicomSeriesReader_usage
- \ref DicomSeriesReader_sorting
- \ref DicomSeriesReader_sorting1
- \ref DicomSeriesReader_sorting2
- \ref DicomSeriesReader_sorting3
- \ref DicomSeriesReader_sorting4
- \ref DicomSeriesReader_gantrytilt
- \ref DicomSeriesReader_pixelspacing
- \ref DicomSeriesReader_nextworkitems
- \ref DicomSeriesReader_whynotinitk
- \ref DicomSeriesReader_tests
\section DicomSeriesReader_purpose Purpose
DicomSeriesReader serves as a central class for loading DICOM images as mitk::Image.
As the term "DICOM image" covers a huge variety of possible modalities and
implementations, and since MITK assumes that 3D images are made up of continuous blocks
of slices without any gaps or changes in orientation, the loading mechanism must
implement a number of decisions and compromises.
<b>The main intention of this implementation is not efficiency but correctness of generated slice positions and pixel
spacings!</b>
\section DicomSeriesReader_limitations Assumptions and limitations
The class is working only with GDCM 2.0.14 (or possibly newer). This version is the
default of an MITK super-build. Support for other versions or ITK's DicomIO was dropped
because of the associated complexity of DicomSeriesReader.
\b Assumptions
- expected to work with certain SOP Classes (mostly CT Image Storage and MR Image Storage)
- see ImageBlockDescriptor.GetReaderImplementationLevel() method for the details
- special treatment for a certain type of Philips 3D ultrasound (recognized by tag 3001,0010 set to "Philips3D")
- loader will always attempt to read multiple single slices as a single 3D image volume (i.e. mitk::Image)
- slices will be grouped by basic properties such as orientation, rows, columns, spacing and grouped into as large
blocks as possible
- images which do NOT report a position or orientation in space (Image Position Patient, Image Orientation) will be
assigned defaults
- image position (0,0,0)
- image orientation (1,0,0), (0,1,0)
- such images will always be grouped separately since spatial grouping / sorting makes no sense for them
\b Options
- images that cover the same piece of space (i.e. position, orientation, and dimensions are equal)
can be interpreted as time-steps of the same image, i.e. a series will be loaded as 3D+t
\b Limitations
- the 3D+t assumption only works if all time-steps have an equal number of slices and if all
have the Acquisition Time attribute set to meaningful values
\section DicomSeriesReader_usage Usage
The starting point for an application is a set of DICOM files that should be loaded.
For convenience, DicomSeriesReader can also parse a whole directory for DICOM files,
but an application should better know exactly what to load.
Loading is then done in two steps:
1. <b>Group the files into spatial blocks</b> by calling GetSeries().
This method will sort all passed files into meaningful blocks that
could fit into an mitk::Image. Sorting for 3D+t loading is optional but default.
The \b return value of this function is a list of descriptors, which
describe a grouped list of files with its most basic properties:
- SOP Class (CT Image Storage, Secondary Capture Image Storage, etc.)
- Modality
- What type of pixel spacing can be read from the provided DICOM tags
- How well DicomSeriesReader is prepared to load this type of data
2. <b>Load a sorted set of files</b> by calling LoadDicomSeries().
This method expects go receive the sorting output of GetSeries().
The method will then invoke ITK methods configured with GDCM-IO
classes to actually load the files into memory and put them into
mitk::Images. Again, loading as 3D+t is optional.
Example:
\code
// only a directory is known at this point: /home/who/dicom
DicomSeriesReader::FileNamesGrouping allImageBlocks = DicomSeriesReader::GetSeries("/home/who/dicom/");
// file now divided into groups of identical image size, orientation, spacing, etc.
// each of these lists should be loadable as an mitk::Image.
DicomSeriesReader::StringContainer seriesToLoad = allImageBlocks[...]; // decide what to load
// final step: load into DataNode (can result in 3D+t image)
DataNode::Pointer node = DicomSeriesReader::LoadDicomSeries( oneBlockSorted );
itk::SmartPointer<Image> image = dynamic_cast<mitk::Image*>( node->GetData() );
\endcode
\section DicomSeriesReader_sorting Logic for sorting 2D slices from DICOM images into 3D+t blocks for mitk::Image
The general sorting mechanism (implemented in GetSeries) groups and sorts a set of DICOM files, each assumed to
contain
a single CT/MR slice.
In the following we refer to those file groups as "blocks", since this is what they are meant to become when loaded
into an mitk::Image.
\subsection DicomSeriesReader_sorting1 Step 1: Avoiding pure non-sense
A first pass separates slices that cannot possibly be loaded together because of restrictions of mitk::Image.
After this steps, each block contains only slices that match in all of the following DICOM tags:
- (0020,000e) Series Instance UID
- (0020,0037) Image Orientation
- (0028,0030) Pixel Spacing
- (0018,1164) Imager Pixel Spacing
- (0018,0050) Slice Thickness
- (0028,0010) Number Of Rows
- (0028,0011) Number Of Columns
- (0028,0008) Number Of Frames
\subsection DicomSeriesReader_sorting2 Step 2: Sort slices spatially
Before slices are further analyzed, they are sorted spatially. As implemented by GdcmSortFunction(),
slices are sorted by
1. distance from origin (calculated using (0020,0032) Image Position Patient and (0020,0037) Image Orientation)
2. when distance is equal, (0020,0012) Acquisition Number, (0008,0032) Acquisition Time and (0018,1060) Trigger Time
are
used as a backup criterions (necessary for meaningful 3D+t sorting)
\subsection DicomSeriesReader_sorting3 Step 3: Ensure equal z spacing
Since inter-slice distance is not recorded in DICOM tags, we must ensure that blocks are made up of
slices that have equal distances between neighboring slices. This is especially necessary because
itk::ImageSeriesReader
is later used for the actual loading, and this class expects (and does nocht verify) equal inter-slice distance (see
\ref DicomSeriesReader_whatweknowaboutitk).
To achieve such grouping, the inter-slice distance is calculated from the first two different slice positions of a
block.
Following slices are added to a block as long as they can be added by adding the calculated inter-slice distance to
the
last slice of the block. Slices that do not fit into the expected distance pattern, are set aside for further
analysis.
This grouping is done until each file has been assigned to a group.
Slices that share a position in space are also sorted into separate blocks during this step.
So the result of this step is a set of blocks that contain only slices with equal z spacing
and unique slices at each position.
\subsection DicomSeriesReader_sorting4 Step 4 (optional): group 3D blocks as 3D+t when possible
This last step depends on an option of GetSeries(). When requested, image blocks from the previous step are merged
again
whenever two blocks occupy the same portion of space (i.e. same origin, number of slices and z-spacing).
\section DicomSeriesReader_gantrytilt Handling of gantry tilt
When CT gantry tilt is used, the gantry plane (= X-Ray source and detector ring) and the vertical plane do not align
anymore. This scanner feature is used for example to reduce metal artifacts (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 in conjunction with mitk::Image, DicomSeriesReader performs a correction for such series
if the groupImagesWithGantryTilt or correctGantryTilt flag in GetSeries and LoadDicomSeries is set (default = on).
The correction algorithms undoes two errors introduced by ITK's ImageSeriesReader:
- the plane shift that is ignored by ITK's reader is recreated by applying a shearing transformation using
itk::ResampleFilter.
- the spacing is corrected (it is calculated by ITK's reader from the distance between two origins, which is NOT the
slice distance in this special case)
Both errors are introduced in
itkImageSeriesReader.txx (ImageSeriesReader<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
\section DicomSeriesReader_whatweknowaboutitk The actual image loading process
When calling LoadDicomSeries(), this method "mainly" uses an instance of itk::ImageSeriesReader,
configured with an itk::GDCMImageIO object. Because DicomSeriesReader works around some of the
behaviors of these classes, the following is a list of features that we find in the code and need to work with:
- itk::ImageSeriesReader::GenerateOutputInformation() does the z-spacing handling
+ spacing is directly determined by comparing (euclidean distance) the origins of the first two slices of a series
* this is GOOD because there is no reliable z-spacing information in DICOM images
* this is bad because it does not work with gantry tilt, in which case the slice distance is SMALLER than the
distance between two origins (see section on tilt)
- origin and spacing are calculated by GDCMImageIO and re-used in itk::ImageSeriesReader
+ the origins are read from appropriate tags, nothing special about that
+ the spacing is read by gdcm::ImageReader, gdcm::ImageHelper::GetSpacingValue() from a tag determined by
gdcm::ImageHelper::GetSpacingTagFromMediaStorage(), which basically determines ONE appropriate pixel spacing tag for
each media storage type (ct image, mr image, secondary capture image, etc.)
* this is fine for modalities such as CT/MR where the "Pixel Spacing" tag is mandatory, but for other modalities
such as CR or Secondary Capture, the tag "Imager Pixel Spacing" is taken, which is no only optional but also has a
more
complicated relation with the "Pixel Spacing" tag. For this reason we check/modify the pixel spacing reported by
itk::ImageSeriesReader after loading the image (see \ref DicomSeriesReader_pixelspacing)
AFTER loading, DicomSeriesReader marks some of its findings as mitk::Properties to the loaded Image and DataNode:
- <b>dicomseriesreader.SOPClass</b> : DICOM SOP Class as readable string (instead of a UID)
- <b>dicomseriesreader.ReaderImplementationLevelString</b> : Confidence /Support level of the reader for this image
as
readable string
- <b>dicomseriesreader.ReaderImplementationLevel</b> : Confidence /Support level of the reader for this image as
enum
value of type ReaderImplementationLevel
- <b>dicomseriesreader.PixelSpacingInterpretationString</b> : Appropriate interpreteation of pixel spacing for this
Image as readable string
- <b>dicomseriesreader.PixelSpacingInterpretation</b> : Appropriate interpreteation of pixel spacing for this Image
as
enum value of type PixelSpacingInterpretation
- <b>dicomseriesreader.MultiFrameImage</b> : bool flag to mark multi-frame images
- <b>dicomseriesreader.GantyTiltCorrected</b> : bool flag to mark images where a gantry tilt was corrected to fit
slices into an mitk::Image
- <b>dicomseriesreader.3D+t</b> : bool flag to mark images with a time dimension (multiple 3D blocks of the same
size
at the same position in space)
\section DicomSeriesReader_pixelspacing Handling of pixel spacing
The reader implements what is described in DICOM Part 3, chapter 10.7 (Basic Pixel Spacing Calibration Macro): Both
tags
- (0028,0030) Pixel Spacing and
- (0018,1164) Imager Pixel Spacing
are evaluated and the pixel spacing is set to the spacing within the patient when tags allow that.
The result of pixel spacing interpretation can be read from a property
"dicomseriesreader.PixelSpacingInterpretation",
which refers to one of the enumerated values of type PixelSpacingInterpretation;
\section DicomSeriesReader_supportedmodalities Limitations for specific modalities
- <b>Enhanced Computed Tomography / Magnetic Resonance Images</b> are currently NOT supported at all, because we
lack
general support for multi-frame images.
- <b>Nuclear Medicine Images</b> are not supported fully supported, only the single-frame variants are loaded
properly.
\section DicomSeriesReader_nextworkitems Possible enhancements
This is a short list of ideas for enhancement:
- Class has historically grown and should be reviewed again. There is probably too many duplicated scanning code
- Multi-frame images don't mix well with the current assumption of "one file - one slice", which is assumed by our
code
- It should be checked how well GDCM and ITK support these files (some load, some don't)
- Specializations such as the Philips 3D code should be handled in a more generic way. The current handling of
Philips 3D images is not nice at all
\section DicomSeriesReader_whynotinitk Why is this not in ITK?
Some of this code would probably be better located in ITK. It is just a matter of resources that this is not the
case yet. Any attempts into this direction are welcome and can be supported. At least the gantry tilt correction
should be a simple addition to itk::ImageSeriesReader.
\section DicomSeriesReader_tests Tests regarding DICOM loading
A number of tests have been implemented to check our assumptions regarding DICOM loading. Please see \ref
DICOMTesting
\todo refactor all the protected helper objects/methods into a separate header so we compile faster
*/
class Image;
class DicomSeriesReader
{
public:
/**
\brief Lists of filenames.
*/
typedef std::vector<std::string> StringContainer;
/**
\brief Interface for the progress callback.
*/
typedef void (*UpdateCallBackMethod)(float);
/**
\brief Describes how well the reader is tested for a certain file type.
Applications should not rely on the outcome for images which are reported
ReaderImplementationLevel_Implemented or ReaderImplementationLevel_Unsupported.
Errors to load images which are reported as ReaderImplementationLevel_Supported
are considered bugs. For ReaderImplementationLevel_PartlySupported please check the appropriate paragraph in \ref
DicomSeriesReader_supportedmodalities
*/
typedef enum {
ReaderImplementationLevel_Supported, /// loader code and tests are established
ReaderImplementationLevel_PartlySupported, /// loader code and tests are established for specific parts of a SOP
/// Class
ReaderImplementationLevel_Implemented, /// loader code is implemented but not accompanied by tests
ReaderImplementationLevel_Unsupported, /// loader code is not working with this SOP Class
} ReaderImplementationLevel;
/**
\brief How the mitk::Image spacing should be interpreted.
Compare DICOM PS 3.3 10.7 (Basic Pixel Spacing Calibration Macro).
*/
typedef enum {
PixelSpacingInterpretation_SpacingInPatient, /// distances are mm within a patient
PixelSpacingInterpretation_SpacingAtDetector, /// distances are mm at detector surface
PixelSpacingInterpretation_SpacingUnknown /// NO spacing information is present, we use (1,1) as default
} PixelSpacingInterpretation;
/**
\brief Return type of GetSeries, describes a logical group of files.
Files grouped into a single 3D or 3D+t block are described by an instance
of this class. Relevant descriptive properties can be used to provide
the application user with meaningful choices.
*/
class ImageBlockDescriptor
{
public:
/// List of files in this group
StringContainer GetFilenames() const;
/// A unique ID describing this bloc (enhanced Series Instance UID).
std::string GetImageBlockUID() const;
/// The Series Instance UID.
std::string GetSeriesInstanceUID() const;
/// Series Modality (CT, MR, etc.)
std::string GetModality() const;
/// SOP Class UID as readable string (Computed Tomography Image Storage, Secondary Capture Image Storage, etc.)
std::string GetSOPClassUIDAsString() const;
/// SOP Class UID as DICOM UID
std::string GetSOPClassUID() const;
/// Confidence of the reader that this block can be read successfully.
ReaderImplementationLevel GetReaderImplementationLevel() const;
/// Whether or not the block contains a gantry tilt which will be "corrected" during loading
bool HasGantryTiltCorrected() const;
/// Whether or not mitk::Image spacing relates to the patient
bool PixelSpacingRelatesToPatient() const;
/// Whether or not mitk::Image spacing relates to the detector surface
bool PixelSpacingRelatesToDetector() const;
/// Whether or not mitk::Image spacing is of unknown origin
bool PixelSpacingIsUnknown() const;
/// How the mitk::Image spacing can meaningfully be interpreted.
PixelSpacingInterpretation GetPixelSpacingType() const;
/// 3D+t or not
bool HasMultipleTimePoints() const;
/// Multi-frame image(s) or not
bool IsMultiFrameImage() const;
ImageBlockDescriptor();
~ImageBlockDescriptor();
private:
friend class DicomSeriesReader;
ImageBlockDescriptor(const StringContainer &files);
void AddFile(const std::string &file);
void AddFiles(const StringContainer &files);
void SetImageBlockUID(const std::string &uid);
void SetSeriesInstanceUID(const std::string &uid);
void SetModality(const std::string &modality);
void SetNumberOfFrames(const std::string &);
void SetSOPClassUID(const std::string &mediaStorageSOPClassUID);
void SetHasGantryTiltCorrected(bool);
void SetPixelSpacingInformation(const std::string &pixelSpacing, const std::string &imagerPixelSpacing);
void SetHasMultipleTimePoints(bool);
void GetDesiredMITKImagePixelSpacing(ScalarType &spacingX, ScalarType &spacingY) const;
StringContainer m_Filenames;
std::string m_ImageBlockUID;
std::string m_SeriesInstanceUID;
std::string m_Modality;
std::string m_SOPClassUID;
bool m_HasGantryTiltCorrected;
std::string m_PixelSpacing;
std::string m_ImagerPixelSpacing;
bool m_HasMultipleTimePoints;
bool m_IsMultiFrameImage;
};
typedef std::map<std::string, ImageBlockDescriptor> FileNamesGrouping;
/**
\brief Provide combination of preprocessor defines that was active during compilation.
Since this class is a combination of several possible implementations, separated only
by ifdef's, calling instances might want to know which flags were active at compile time.
*/
static std::string GetConfigurationString();
/**
\brief Checks if a specific file contains DICOM data.
*/
static bool IsDicom(const std::string &filename);
/**
\brief see other GetSeries().
Find all series (and sub-series -- see details) in a particular directory.
*/
static FileNamesGrouping GetSeries(const std::string &dir,
bool groupImagesWithGantryTilt,
const StringContainer &restrictions = StringContainer());
/**
\brief see other GetSeries().
\warning Untested, could or could not work.
This differs only by having an additional restriction to a single known DICOM series.
Internally, it uses the other GetSeries() method.
*/
static StringContainer GetSeries(const std::string &dir,
const std::string &series_uid,
bool groupImagesWithGantryTilt,
const StringContainer &restrictions = StringContainer());
/**
\brief PREFERRED version of this method - scan and sort DICOM files.
Parse a list of files for images of DICOM series.
For each series, an enumeration of the files contained in it is created.
\return The resulting maps UID-like keys (based on Series Instance UID and slice properties) to sorted lists of
file
names.
SeriesInstanceUID will be enhanced to be unique for each set of file names
that is later loadable as a single mitk::Image. This implies that
Image orientation, slice thickness, pixel spacing, rows, and columns
must be the same for each file (i.e. the image slice contained in the file).
If this separation logic requires that a SeriesInstanceUID must be made more specialized,
it will follow the same logic as itk::GDCMSeriesFileNames to enhance the UID with
more digits and dots.
Optionally, more tags can be used to separate files into different logical series by setting
the restrictions parameter.
\warning Adding restrictions is not yet implemented!
*/
static FileNamesGrouping GetSeries(const StringContainer &files,
bool sortTo3DPlust,
bool groupImagesWithGantryTilt,
const StringContainer &restrictions = StringContainer());
/**
\brief See other GetSeries().
Use GetSeries(const StringContainer& files, bool sortTo3DPlust, const StringContainer &restrictions) instead.
*/
static FileNamesGrouping GetSeries(const StringContainer &files,
bool groupImagesWithGantryTilt,
const StringContainer &restrictions = StringContainer());
/**
Loads a DICOM series composed by the file names enumerated in the file names container.
If a callback method is supplied, it will be called after every progress update with a progress value in [0,1].
\param filenames The filenames to load.
\param sort Whether files should be sorted spatially (true) or not (false - maybe useful if presorted)
\param load4D Whether to load the files as 3D+t (if possible)
\param correctGantryTilt
\param callback
\param preLoadedImageBlock
*/
static DataNode::Pointer LoadDicomSeries(const StringContainer &filenames,
bool sort = true,
bool load4D = true,
bool correctGantryTilt = true,
UpdateCallBackMethod callback = nullptr,
itk::SmartPointer<Image> preLoadedImageBlock = nullptr);
/**
\brief See LoadDicomSeries! Just a slightly different interface.
If \p preLoadedImageBlock is provided, the reader will only "fake" loading and create appropriate mitk::Properties.
\param filenames
\param node
\param sort
\param load4D
\param correctGantryTilt
\param callback
\param preLoadedImageBlock
*/
static bool LoadDicomSeries(const StringContainer &filenames,
DataNode &node,
bool sort = true,
bool load4D = true,
bool correctGantryTilt = true,
UpdateCallBackMethod callback = nullptr,
itk::SmartPointer<Image> preLoadedImageBlock = nullptr);
protected:
/**
\brief Return type of DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption.
Class contains the grouping result of method
DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption,
which takes as input a number of images, which are all equally oriented and spatially sorted along their normal
direction.
The result contains of two blocks: a first one is the grouping result, all of those images can be loaded
into one image block because they have an equal origin-to-origin distance without any gaps in-between.
*/
class SliceGroupingAnalysisResult
{
public:
SliceGroupingAnalysisResult();
/**
\brief Grouping result, all same origin-to-origin distance w/o gaps.
*/
StringContainer GetBlockFilenames();
/**
\brief Remaining files, which could not be grouped.
*/
StringContainer GetUnsortedFilenames();
/**
\brief Whether or not the grouped result contain a gantry tilt.
*/
bool ContainsGantryTilt();
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
*/
void AddFileToSortedBlock(const std::string &filename);
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
*/
void AddFileToUnsortedBlock(const std::string &filename);
void AddFilesToUnsortedBlock(const StringContainer &filenames);
/**
\brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only.
\todo Could make sense to enhance this with an instance of GantryTiltInformation to store the whole result!
*/
void FlagGantryTilt();
/**
\brief Only meaningful for use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption.
*/
void UndoPrematureGrouping();
protected:
StringContainer m_GroupedFiles;
StringContainer m_UnsortedFiles;
bool m_GantryTilt;
};
/**
\brief Gantry tilt analysis result.
Takes geometry information for two slices of a DICOM series and
calculates whether these fit into an orthogonal block or not.
If NOT, they can either be the result of an acquisition with
gantry tilt OR completely broken by some shearing transformation.
Most calculations are done in the constructor, results can then
be read via the remaining methods.
*/
class GantryTiltInformation
{
public:
// two types to avoid any rounding errors
typedef itk::Point<double, 3> Point3Dd;
typedef itk::Vector<double, 3> Vector3Dd;
/**
\brief Just so we can create empty instances for assigning results later.
*/
GantryTiltInformation();
/**
\brief THE constructor, which does all the calculations.
Determining the amount of tilt is done by checking the distances
of origin1 from planes through origin2. Two planes are considered:
- normal vector along normal of slices (right x up): gives the slice distance
- normal vector along orientation vector "up": gives the shift parallel to the plane orientation
The tilt angle can then be calculated from these distances
\param origin1 origin of the first slice
\param origin2 origin of the second slice
\param right right/up describe the orientatation of borth slices
\param up right/up describe the orientatation of borth slices
\param numberOfSlicesApart how many slices are the given origins apart (1 for neighboring slices)
*/
GantryTiltInformation(const Point3D &origin1,
const Point3D &origin2,
const Vector3D &right,
const Vector3D &up,
unsigned int numberOfSlicesApart);
/**
\brief Whether the slices were sheared.
True if any of the shifts along right or up vector are non-zero.
*/
bool IsSheared() const;
/**
\brief Whether the shearing is a gantry tilt or more complicated.
Gantry tilt will only produce shifts in ONE orientation, not in both.
Since the correction code currently only covers one tilt direction
AND we don't know of medical images with two tilt directions, the
loading code wants to check if our assumptions are true.
*/
bool IsRegularGantryTilt() const;
/**
\brief The offset distance in Y direction for each slice in mm (describes the tilt result).
*/
double GetMatrixCoefficientForCorrectionInWorldCoordinates() const;
/**
\brief The z / inter-slice spacing. Needed to correct ImageSeriesReader's result.
*/
double GetRealZSpacing() const;
/**
\brief The shift between first and last slice in mm.
Needed to resize an orthogonal image volume.
*/
double GetTiltCorrectedAdditionalSize() const;
/**
\brief Calculated tilt angle in degrees.
*/
double GetTiltAngleInDegrees() const;
protected:
/**
\brief Projection of point p onto line through lineOrigin in direction of lineDirection.
*/
Point3D projectPointOnLine(Point3Dd p, Point3Dd lineOrigin, Vector3Dd lineDirection);
double m_ShiftUp;
double m_ShiftRight;
double m_ShiftNormal;
double m_ITKAssumedSliceSpacing;
unsigned int m_NumberOfSlicesApart;
};
/**
\brief for internal sorting.
*/
typedef std::pair<StringContainer, StringContainer> TwoStringContainers;
/**
\brief Maps DICOM tags to MITK properties.
*/
typedef std::map<std::string, std::string> TagToPropertyMapType;
/**
\brief Ensure an equal z-spacing for a group of files.
Takes as input a number of images, which are all equally oriented and spatially sorted along their normal
direction.
- Internally used by GetSeries. Returns two lists: the first one contins slices of equal inter-slice spacing.
+ Internally used by GetSeries. Returns two lists: the first one contains slices of equal inter-slice spacing.
The second list contains remaining files, which need to be run through
AnalyzeFileForITKImageSeriesReaderSpacingAssumption again.
Relevant code that is matched here is in
itkImageSeriesReader.txx (ImageSeriesReader<TOutputImage>::GenerateOutputInformation(void)), lines 176 to 245 (as
of
ITK 3.20)
*/
static SliceGroupingAnalysisResult AnalyzeFileForITKImageSeriesReaderSpacingAssumption(
const StringContainer &files, bool groupsOfSimilarImages, const gdcm::Scanner::MappingType &tagValueMappings_);
/**
\brief Safely convert const char* to std::string.
*/
static std::string ConstCharStarToString(const char *s);
/**
\brief Safely convert a string into pixel spacing x and y.
*/
static bool DICOMStringToSpacing(const std::string &s, ScalarType &spacingX, ScalarType &spacingY);
/**
\brief Convert DICOM string describing a point to Point3D.
DICOM tags like ImagePositionPatient contain a position as float numbers separated by backslashes:
\verbatim
42.7131\13.77\0.7
\endverbatim
*/
static Point3D DICOMStringToPoint3D(const std::string &s, bool &successful);
/**
\brief Convert DICOM string describing a point two Vector3D.
DICOM tags like ImageOrientationPatient contain two vectors as float numbers separated by backslashes:
\verbatim
42.7131\13.77\0.7\137.76\0.3
\endverbatim
*/
static void DICOMStringToOrientationVectors(const std::string &s, Vector3D &right, Vector3D &up, bool &successful);
template <typename ImageType>
static typename ImageType::Pointer
// TODO this is NOT inplace!
InPlaceFixUpTiltedGeometry(ImageType *input, const GantryTiltInformation &tiltInfo);
/**
\brief Sort a set of file names in an order that is meaningful for loading them into an mitk::Image.
\warning This method assumes that input files are similar in basic properties such as
slice thickness, image orientation, pixel spacing, rows, columns.
It should always be ok to put the result of a call to GetSeries(..) into this method.
Sorting order is determined by
1. image position along its normal (distance from world origin)
2. acquisition time
If P<n> denotes a position and T<n> denotes a time step, this method will order slices from three timesteps like
this:
\verbatim
P1T1 P1T2 P1T3 P2T1 P2T2 P2T3 P3T1 P3T2 P3T3
\endverbatim
*/
static StringContainer SortSeriesSlices(const StringContainer &unsortedFilenames);
public:
/**
\brief Checks if a specific file is a Philips3D ultrasound DICOM file.
*/
static bool IsPhilips3DDicom(const std::string &filename);
static std::string ReaderImplementationLevelToString(const ReaderImplementationLevel &enumValue);
static std::string PixelSpacingInterpretationToString(const PixelSpacingInterpretation &enumValue);
protected:
/**
\brief Read a Philips3D ultrasound DICOM file and put into an mitk::Image.
*/
static bool ReadPhilips3DDicom(const std::string &filename, itk::SmartPointer<Image> output_image);
/**
\brief Construct a UID that takes into account sorting criteria from GetSeries().
*/
static std::string CreateMoreUniqueSeriesIdentifier(gdcm::Scanner::TagToValue &tagValueMap);
/**
\brief Helper for CreateMoreUniqueSeriesIdentifier
*/
static std::string CreateSeriesIdentifierPart(gdcm::Scanner::TagToValue &tagValueMap, const gdcm::Tag &tag);
/**
\brief Helper for CreateMoreUniqueSeriesIdentifier
*/
static std::string IDifyTagValue(const std::string &value);
typedef itk::GDCMImageIO DcmIoType;
/**
\brief Progress callback for DicomSeriesReader.
*/
class CallbackCommand : public itk::Command
{
public:
CallbackCommand(UpdateCallBackMethod callback) : m_Callback(callback) {}
void Execute(const itk::Object *caller, const itk::EventObject &) override
{
(*this->m_Callback)(static_cast<const itk::ProcessObject *>(caller)->GetProgress());
}
void Execute(itk::Object *caller, const itk::EventObject &) override
{
(*this->m_Callback)(static_cast<itk::ProcessObject *>(caller)->GetProgress());
}
protected:
UpdateCallBackMethod m_Callback;
};
static void FixSpacingInformation(Image *image, const ImageBlockDescriptor &imageBlockDescriptor);
/**
\brief Scan for slice image information
*/
static void ScanForSliceInformation(const StringContainer &filenames, gdcm::Scanner &scanner);
/**
\brief Performs actual loading of a series and creates an image having the specified pixel type.
*/
static void LoadDicom(const StringContainer &filenames,
DataNode &node,
bool sort,
bool check_4d,
bool correctTilt,
UpdateCallBackMethod callback,
itk::SmartPointer<Image> preLoadedImageBlock);
/**
\brief Feed files into itk::ImageSeriesReader and retrieve a 3D MITK image.
\param correctTilt
\param tiltInfo
\param io
\param command can be used for progress reporting
\param preLoadedImageBlock
*/
template <typename PixelType>
static itk::SmartPointer<Image> LoadDICOMByITK(const StringContainer &,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
static itk::SmartPointer<Image> MultiplexLoadDICOMByITK(const StringContainer &,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
static itk::SmartPointer<Image> MultiplexLoadDICOMByITKScalar(const StringContainer &,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
static itk::SmartPointer<Image> MultiplexLoadDICOMByITKRGBPixel(const StringContainer &,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
template <typename PixelType>
static itk::SmartPointer<Image> LoadDICOMByITK4D(std::list<StringContainer> &imageBlocks,
ImageBlockDescriptor imageBlockDescriptor,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
static itk::SmartPointer<Image> MultiplexLoadDICOMByITK4D(std::list<StringContainer> &imageBlocks,
ImageBlockDescriptor imageBlockDescriptor,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
static itk::SmartPointer<Image> MultiplexLoadDICOMByITK4DScalar(std::list<StringContainer> &imageBlocks,
ImageBlockDescriptor imageBlockDescriptor,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
static itk::SmartPointer<Image> MultiplexLoadDICOMByITK4DRGBPixel(std::list<StringContainer> &imageBlocks,
ImageBlockDescriptor imageBlockDescriptor,
bool correctTilt,
const GantryTiltInformation &tiltInfo,
DcmIoType::Pointer &io,
CallbackCommand *command,
itk::SmartPointer<Image> preLoadedImageBlock);
/**
\brief Sort files into time step blocks of a 3D+t image.
Called by LoadDicom. Expects to be fed a single list of filenames that have been sorted by
GetSeries previously (one map entry). This method will check how many timestep can be filled
with given files.
Assumption is that the number of time steps is determined by how often the first position in
space repeats. I.e. if the first three files in the input parameter all describe the same
location in space, we'll construct three lists of files. and sort the remaining files into them.
\todo We can probably remove this method if we somehow transfer 3D+t information from GetSeries to
LoadDicomSeries.
*/
static std::list<StringContainer> SortIntoBlocksFor3DplusT(const StringContainer &presortedFilenames,
const gdcm::Scanner::MappingType &tagValueMappings_,
bool sort,
bool &canLoadAs4D);
/**
\brief Defines spatial sorting for sorting by GDCM 2.
Sorts by image position along image normal (distance from world origin).
In cases of conflict, acquisition time is used as a secondary sort criterium.
*/
static bool GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2);
/**
\brief Copy information about files and DICOM tags from ITK's MetaDataDictionary
and from the list of input files to the PropertyList of mitk::Image.
\todo Tag copy must follow; image level will cause some additional files parsing, probably.
*/
static void CopyMetaDataToImageProperties(StringContainer filenames,
const gdcm::Scanner::MappingType &tagValueMappings_,
DcmIoType *io,
const ImageBlockDescriptor &blockInfo,
Image *image);
static void CopyMetaDataToImageProperties(std::list<StringContainer> imageBlock,
const gdcm::Scanner::MappingType &tagValueMappings_,
DcmIoType *io,
const ImageBlockDescriptor &blockInfo,
Image *image);
/**
\brief Map between DICOM tags and MITK properties.
Uses as a positive list for copying specified DICOM tags (from ITK's ImageIO)
to MITK properties. ITK provides MetaDataDictionary entries of form
"gggg|eeee" (g = group, e = element), e.g. "0028,0109" (Largest Pixel in Series),
which we want to sort as dicom.series.largest_pixel_in_series".
*/
static const TagToPropertyMapType &GetDICOMTagsToMITKPropertyMap();
};
}
#endif
diff --git a/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.h b/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.h
index e39652cd83..12992a1fdc 100644
--- a/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.h
+++ b/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.h
@@ -1,69 +1,69 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkDICOMPMIO_h
#define mitkDICOMPMIO_h
#include <mitkAbstractFileIO.h>
#include <mitkImage.h>
#include <dcmtk/dcmpmap/dpmparametricmapiod.h>
#include <dcmqi/JSONSegmentationMetaInformationHandler.h>
#include <dcmqi/JSONParametricMapMetaInformationHandler.h>
namespace mitk
{
/**
* Read and Writes a Parametric map to a dcm file
* @ingroup Process
*/
class DICOMPMIO : public mitk::AbstractFileIO
{
public:
DICOMPMIO();
// -------------- AbstractFileReader -------------
using AbstractFileReader::Read;
ConfidenceLevel GetReaderConfidenceLevel() const override;
// -------------- AbstractFileWriter -------------
void Write() override;
ConfidenceLevel GetWriterConfidenceLevel() const override;
protected:
/**
* @brief Reads a DICOM parametric map from the file system
* @return an mitk::Image
- * @throws an mitk::Exception if an error ocurrs
+ * @throws an mitk::Exception if an error occurs
*/
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
private:
typedef mitk::Image PMInputType;
typedef itk::Image<double, 3> PMitkInputImageType;
typedef IODFloatingPointImagePixelModule::value_type PMFloatPixelType; // input type required for DCMQI
typedef itk::Image<PMFloatPixelType, 3> PMitkInternalImageType;
DICOMPMIO *IOClone() const override;
// -------------- DICOMPMIO specific functions -------------
const std::string CreateMetaDataJsonFilePM() const;
};
} // end of namespace mitk
#endif
diff --git a/Modules/DICOMTesting/test/mitkDICOMLocaleTest.cpp b/Modules/DICOMTesting/test/mitkDICOMLocaleTest.cpp
index a96055eaef..11b266f752 100644
--- a/Modules/DICOMTesting/test/mitkDICOMLocaleTest.cpp
+++ b/Modules/DICOMTesting/test/mitkDICOMLocaleTest.cpp
@@ -1,132 +1,132 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 test is meant to reproduce the following error:
- The machine or current user has a German locale.
- This esp. means that stream IO expects the decimal separator as a comma: ","
- DICOM files use a point "." as the decimal separator to be locale independent
- The parser used by MITK (ITK's GDCM) seems to use the current locale instead of the "C" or "POSIX" locale
- This leads to spacings (and probably other numbers) being trimmed/rounded,
e.g. the correct spacing of 0.314 is read as 1.0 etc.
*/
#include "mitkStandardFileLocations.h"
#include "mitkTestDICOMLoading.h"
#include "mitkTestingMacros.h"
#include <list>
#include <locale>
#include <locale.h>
bool mitkDICOMLocaleTestChangeLocale(const std::string& locale)
{
try
{
MITK_TEST_OUTPUT(<< " ** Changing locale from " << setlocale(LC_ALL, nullptr) << " to '" << locale << "'");
setlocale(LC_ALL, locale.c_str());
std::locale l( locale.c_str() );
std::cin.imbue(l);
return true;
}
catch(...)
{
MITK_TEST_OUTPUT(<< "Could not activate locale " << locale);
return false;
}
}
void mitkDICOMLocaleTestWithReferenceImage(std::string filename)
{
mitk::TestDICOMLoading loader;
mitk::TestDICOMLoading::ImageList images = loader.LoadFiles({ filename });
MITK_TEST_CONDITION_REQUIRED(images.size() > 0, "file " << filename << " loaded");
mitk::DataNode::Pointer node = factory->GetOutput( 0 );
image = dynamic_cast<mitk::Image*>(node->GetData());
mitk::Image::Pointer image;
if(images.empty())
{
MITK_TEST_FAILED_MSG(<< "File "<< filename << " is not an image - test will not be applied." );
return;
}
else
{
image = images[0];
}
// note importance of minor differences in spacings:
// DICOM has order y-spacing, x-spacing, while in MITK we assume x-spacing, y-spacing (both meant for 0 and 1 index in array)
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(image->GetGeometry()->GetSpacing()[0], 0.3141592), "correct x spacing? found "
<< image->GetGeometry()->GetSpacing()[0]);
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(image->GetGeometry()->GetSpacing()[1], 0.3411592), "correct y spacing? found "
<< image->GetGeometry()->GetSpacing()[1]);
}
int mitkDICOMLocaleTest(int argc, char* argv[])
{
MITK_TEST_BEGIN("DICOMLocaleTest");
MITK_TEST_CONDITION_REQUIRED(argc >= 2, "File to load has been specified on commandline");
MITK_TEST_OUTPUT(<< "Configuration: \n" << mitk::DicomSeriesReader::GetConfigurationString() );
std::string filename = argv[1];
// load a reference DICOM file with the "C" locale being set
mitkDICOMLocaleTestChangeLocale("C");
mitkDICOMLocaleTestWithReferenceImage(filename);
// load a reference DICOM file with German locales being set
typedef std::list<std::string> StringList;
StringList alllocales;
alllocales.push_back("de_DE");
alllocales.push_back("de_DE.utf8");
alllocales.push_back("de_DE.UTF8");
alllocales.push_back("de_DE@euro");
alllocales.push_back("German_Germany");
- // supressing this test to be run on MacOS X
+ // suppressing this test to be run on MacOS X
// See bug #3894
#if defined (__APPLE__) || defined(MACOSX)
alllocales.push_back("C");
#endif
unsigned int numberOfTestedGermanLocales(0);
for (StringList::iterator iter = alllocales.begin();
iter != alllocales.end();
++iter)
{
if ( mitkDICOMLocaleTestChangeLocale(*iter) )
{
++numberOfTestedGermanLocales;
mitkDICOMLocaleTestWithReferenceImage(filename);
}
}
if(numberOfTestedGermanLocales == 0)
{
MITK_TEST_OUTPUT(<< "Warning: No German locale was found on the system.");
}
//MITK_TEST_CONDITION_REQUIRED( numberOfTestedGermanLocales > 0, "Verify that at least one German locale has been tested.");
MITK_TEST_END();
}
diff --git a/Modules/DICOMUI/include/QmitkDicomLocalStorageWidget.h b/Modules/DICOMUI/include/QmitkDicomLocalStorageWidget.h
index c6b3dd0644..4ced1979d2 100644
--- a/Modules/DICOMUI/include/QmitkDicomLocalStorageWidget.h
+++ b/Modules/DICOMUI/include/QmitkDicomLocalStorageWidget.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 QmitkDicomLocalStorageWidget_h
#define QmitkDicomLocalStorageWidget_h
#include "ui_QmitkDicomLocalStorageWidgetControls.h"
#include <MitkDICOMUIExports.h>
// include QT
#include <QHash>
#include <QString>
#include <QStringList>
#include <QVariant>
#include <QWidget>
class QProgressDialog;
class QLabel;
class ctkDICOMDatabase;
class ctkDICOMIndexer;
/**
* \brief QmitkDicomLocalStorageWidget is a QWidget providing functionality for dicom storage and import.
*
* \ingroup Functionalities
*/
class MITKDICOMUI_EXPORT QmitkDicomLocalStorageWidget : public QWidget
{
// this is needed for all Qt objects that should have a Qt meta-object
// (everything that derives from QObject and wants to have signal/slots)
Q_OBJECT
public:
static const std::string Widget_ID;
/**
* \brief QmitkDicomLocalStorageWidget(QWidget *parent) constructor.
*
* \param parent is a pointer to the parent widget
*/
QmitkDicomLocalStorageWidget(QWidget *parent);
/**
* \brief QmitkDicomExternalDataWidget destructor.
*/
~QmitkDicomLocalStorageWidget() override;
/**
* \brief CreateQtPartControl(QWidget *parent) sets the view objects from ui_QmitkDicomExternalDataWidgetControls.h.
*
* \param parent is a pointer to the parent widget
*/
virtual void CreateQtPartControl(QWidget *parent);
/**
* \brief SetDatabaseDirectory sets database directory.
*
- * \param newDatabaseDirectory contains path to new database directoy.
+ * \param newDatabaseDirectory contains path to new database directory.
*/
void SetDatabaseDirectory(QString newDatabaseDirectory);
signals:
/// @brief emitted when import into database is finished.
void SignalFinishedImport();
/**
* @brief emitted when view button is clicked.
* @param _t1 containing dicom UIDs properties.
*/
void SignalDicomToDataManager(QHash<QString, QVariant> _t1);
/// \brief emitted if cancel button is pressed.
void SignalCancelImport();
public slots:
/// @brief Called when view button was clicked.
void OnViewButtonClicked();
/// @brief Called delete button was clicked.
void OnDeleteButtonClicked();
/// @brief Called when adding a dicom directory. Starts a thread adding the directory.
void OnStartDicomImport(const QString &dicomData);
/// @brief Called when adding a list of dicom files. Starts a thread adding the dicom files.
void OnStartDicomImport(const QStringList &dicomData);
/// @brief Called when the selection in the series table has changed
void OnSeriesSelectionChanged(const QStringList &);
protected:
void SetDatabase(QString databaseFile);
bool DeletePatients();
bool DeleteStudies();
bool DeleteSeries();
ctkDICOMDatabase *m_LocalDatabase;
ctkDICOMIndexer *m_LocalIndexer;
Ui::QmitkDicomLocalStorageWidgetControls *m_Controls;
};
#endif
diff --git a/Modules/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h b/Modules/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h
index a1b875db38..b7732f23f7 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)
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().
+ * @brief InitMembers convenience 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/include/mitkApplyDiffImageOperation.h b/Modules/DataTypesExt/include/mitkApplyDiffImageOperation.h
index d4ea0219e7..3c01d9bc53 100644
--- a/Modules/DataTypesExt/include/mitkApplyDiffImageOperation.h
+++ b/Modules/DataTypesExt/include/mitkApplyDiffImageOperation.h
@@ -1,88 +1,88 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkApplyDiffImageOperation_h
#define mitkApplyDiffImageOperation_h
#include "MitkDataTypesExtExports.h"
#include "mitkCompressedImageContainer.h"
#include "mitkOperation.h"
namespace mitk
{
/**
@brief Operation, that holds information about some image difference
This class stores undo information for DiffImageApplier.
Instances of this class are created e.g. by QmitkSlicesInterpolator.
This works only for images with 1 channel (gray scale images, no color images).
ApplyDiffImageOperation of course refers to an image (a segmentation usually).
- The refered image is observed for itk::DeleteEvent, because there is no SmartPointer
+ The referred image is observed for itk::DeleteEvent, because there is no SmartPointer
used to keep the image alive -- the purpose of this class is undo and the undo
stack should not keep things alive forever.
To save memory, compression is used via CompressedImageContainer.
@ingroup Undo
@ingroup ToolManagerEtAl
*/
class MITKDATATYPESEXT_EXPORT ApplyDiffImageOperation : public Operation
{
protected:
void OnImageDeleted();
Image *m_Image;
unsigned int m_SliceIndex;
unsigned int m_SliceDimension;
unsigned int m_TimeStep;
double m_Factor;
bool m_ImageStillValid;
unsigned long m_DeleteTag;
CompressedImageContainer m_CompressedImageContainer;
public:
/**
Pass only 2D images here.
\param operationType
\param image
\param diffImage
\param timeStep
\param sliceIndex brief Which slice to extract (first one has index 0).
\param sliceDimension Number of the dimension which is constant for all pixels of the desired slice (e.g. 0 for
axial)
*/
ApplyDiffImageOperation(OperationType operationType,
Image *image,
Image *diffImage,
unsigned int timeStep = 0,
unsigned int sliceDimension = 2,
unsigned int sliceIndex = 0);
~ApplyDiffImageOperation() override;
// Unfortunately cannot use itkGet/SetMacros here, since Operation does not inherit itk::Object
unsigned int GetSliceIndex() { return m_SliceIndex; }
unsigned int GetSliceDimension() { return m_SliceDimension; }
unsigned int GetTimeStep() { return m_TimeStep; }
void SetFactor(double factor) { m_Factor = factor; }
double GetFactor() { return m_Factor; }
Image *GetImage() { return m_Image; }
Image::Pointer GetDiffImage();
bool IsImageStillValid() { return m_ImageStillValid; }
};
} // namespace mitk
#endif
diff --git a/Modules/DataTypesExt/include/mitkColorSequence.h b/Modules/DataTypesExt/include/mitkColorSequence.h
index 2a8637e35d..37e48cb373 100644
--- a/Modules/DataTypesExt/include/mitkColorSequence.h
+++ b/Modules/DataTypesExt/include/mitkColorSequence.h
@@ -1,43 +1,43 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkColorSequence_h
#define mitkColorSequence_h
#include "MitkDataTypesExtExports.h"
#include <mitkColorProperty.h>
namespace mitk
{
/*!
- \brief Inferface for creating a sequence of nice/matching/appropriate/... colors.
+ \brief Interface for creating a sequence of nice/matching/appropriate/... colors.
See derived classes for implemented sequences.
*/
class MITKDATATYPESEXT_EXPORT ColorSequence
{
public:
ColorSequence();
virtual ~ColorSequence();
/*!
\brief Return another color
*/
virtual Color GetNextColor() = 0;
/*!
\brief Set the color-index to begin again
*/
virtual void GoToBegin() = 0;
};
}
#endif
diff --git a/Modules/DataTypesExt/include/mitkColorSequenceCycleH.h b/Modules/DataTypesExt/include/mitkColorSequenceCycleH.h
index f95bb80f1e..ea3c65acea 100644
--- a/Modules/DataTypesExt/include/mitkColorSequenceCycleH.h
+++ b/Modules/DataTypesExt/include/mitkColorSequenceCycleH.h
@@ -1,79 +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 mitkColorSequenceCycleH_h
#define mitkColorSequenceCycleH_h
#include "MitkDataTypesExtExports.h"
#include <mitkColorSequence.h>
namespace mitk
{
/*!
\brief Creates a list of around 36 different colors, where one is easily distinguished from the preceding one.
The list of colors starts with a fully saturated, full valued red (Hue = 0 = 360).
After that the sequence is generated like this:
- first cycle through fully saturated colors (increase hue by 60)
- then cycle through colors with halfed saturation (increase hue by 60)
- then cycle through colors with halfed value (increase hue by 60)
Finally repeat colors.
*/
class MITKDATATYPESEXT_EXPORT ColorSequenceCycleH : public ColorSequence
{
public:
ColorSequenceCycleH();
~ColorSequenceCycleH() override;
/*!
\brief Return another color
*/
Color GetNextColor() override;
/*!
\brief Rewind to first color
*/
void GoToBegin() override;
/*!
\brief Increase the used Hue value.
This can be done by steps ( = steps * 60 increase of Hue )
or absolute ( 0.0 < Hue < 360.0).
Can also be used to decrease the Hue; Values < 0 are cropped to 0.
Note: This does not change the other values, i.e. the color cycle.
Therefor, the method can just be used to skip steps (i.e. colors) in a cycle.
Use SetColorCycle if you want to change other values.
*/
virtual void ChangeHueValueByCycleSteps(int steps);
virtual void ChangeHueValueByAbsoluteNumber(float number);
/*!
\brief Set the color cycle.
The color cycle has to be an integer value between 0 and 5
(see class description for an explanation). Use this in combination with
- the hue value cahnge to generate your dream colors...
+ the hue value change to generate your dream colors...
*/
virtual void SetColorCycle(unsigned short cycle);
protected:
float color_h; // current hue (0 .. 360)
float color_s; // current saturation (0 .. 1)
float color_v; // current value (0 .. 1)
unsigned short color_cycle;
};
}
#endif
diff --git a/Modules/DataTypesExt/include/mitkLabeledImageLookupTable.h b/Modules/DataTypesExt/include/mitkLabeledImageLookupTable.h
index 0c6fd009c3..b9e004cc75 100644
--- a/Modules/DataTypesExt/include/mitkLabeledImageLookupTable.h
+++ b/Modules/DataTypesExt/include/mitkLabeledImageLookupTable.h
@@ -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.
============================================================================*/
#ifndef mitkLabeledImageLookupTable_h
#define mitkLabeledImageLookupTable_h
#include "MitkDataTypesExtExports.h"
#include "mitkLevelWindow.h"
#include "mitkLookupTable.h"
#include <iostream>
#include <string>
namespace mitk
{
/**
* A lookup table for 2D mapping of labeled images. The lookup table supports
* images with up to 256 unsigned labels. Negative labels are not supported.
* Please use the level/window settings as given by the GetLevelWindow() method
* to make sure, that the colors are rendered correctly.
* The colors are initialized with random colors as default. As background
* the label 0 is assumed. The color for the background is set to fully transparent
* as default.
*/
class MITKDATATYPESEXT_EXPORT LabeledImageLookupTable : public LookupTable
{
public:
/**
* Standard mitk typedefs are generated by the mitkClassMacro
*/
mitkClassMacro(LabeledImageLookupTable, LookupTable);
/**
* Make this object constructable by the ::%New() Method.
*/
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* The data type for a label. Currently only images with labels
* in the range [0,255] are supported.
*/
typedef unsigned char LabelType;
LabeledImageLookupTable &operator=(const LookupTable &other) override;
/**
* Sets the color for a given label
* @param label The pixel value used as a label in the image
- * @param r The red component of the rgba color value. Values sould be given in the range [0,1]
- * @param g The green component of the rgba color value. Values sould be given in the range [0,1]
- * @param b The blue component of the rgba color value. Values sould be given in the range [0,1]
- * @param a The alpha component of the rgba color value. Values sould be given in the range [0,1]. Default is 1.
+ * @param r The red component of the rgba color value. Values should be given in the range [0,1]
+ * @param g The green component of the rgba color value. Values should be given in the range [0,1]
+ * @param b The blue component of the rgba color value. Values should be given in the range [0,1]
+ * @param a The alpha component of the rgba color value. Values should be given in the range [0,1]. Default is 1.
*/
virtual void SetColorForLabel(
const LabelType &label, const double &r, const double &g, const double &b, const double a = 1.0);
/**
* Determines the color which will be used for coloring a given label.
* @param label the label for which the color should be returned
* @returns an rgba array containing the color information for the given label.
* Color components are expressed as [0,1] double values.
*/
virtual double *GetColorForLabel(const LabelType &label);
/**
* Provides access to level window settings, which should be used
* in combination with the LUTs generated by this filter (at lease for
* 2D visualization. If you use other level/window settings, it is not
* guaranteed, that scalar values are mapped to the correct colors.
*/
mitk::LevelWindow GetLevelWindow() { return m_LevelWindow; }
protected:
/**
* Default constructor. Protected to prevent "normal" creation
*/
LabeledImageLookupTable();
LabeledImageLookupTable(const LabeledImageLookupTable &other);
/**
* Virtual destructor
*/
~LabeledImageLookupTable() override;
/**
* Generates a random rgb color value. Values for rgb are in the range
* [0,1]
*/
virtual void GenerateRandomColor(double &r, double &g, double &b);
/**
* Generates a radnom number drawn from a uniform
* distribution in the range [0,1].
*/
virtual double GenerateRandomNumber();
mitk::LevelWindow m_LevelWindow;
private:
itk::LightObject::Pointer InternalClone() const override;
};
}
#endif
diff --git a/Modules/DataTypesExt/include/mitkLookupTableSource.h b/Modules/DataTypesExt/include/mitkLookupTableSource.h
index 04e2021278..35072bdc10 100644
--- a/Modules/DataTypesExt/include/mitkLookupTableSource.h
+++ b/Modules/DataTypesExt/include/mitkLookupTableSource.h
@@ -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.
============================================================================*/
#ifndef mitkLookupTableSource_h
#define mitkLookupTableSource_h
#include "MitkDataTypesExtExports.h"
#include "mitkCommon.h"
#include "mitkLookupTable.h"
#include "itkProcessObject.h"
namespace mitk
{
/**
* @brief Base class for all objects which have an object of type
* mitkLookupTable as output
*
* Base class for all objects which have an object of type mitkLookupTable
* as output. It is assumed, that mitkLookupTableSources do not provide support
* for streaming, that is, that the requested region is always the largest
* possible region.
* @ingroup Process
*/
class MITKDATATYPESEXT_EXPORT LookupTableSource : public itk::ProcessObject
{
public:
mitkClassMacroItkParent(LookupTableSource, itk::ProcessObject);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef mitk::LookupTable OutputType;
typedef OutputType::Pointer OutputTypePointer;
typedef itk::DataObject::Pointer DataObjectPointer;
/**
* 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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
/**
* Generates the input requested region simply by calling the equivalent
* method of the superclass.
*/
void GenerateInputRequestedRegion() override;
/**
- * Replacement of the SetOutput method. I think it is not yet correcly
+ * Replacement of the SetOutput method. I think it is not yet correctly
* implemented, so you should better not use it.
- * @todo provide a more usefule implementation
+ * @todo provide a more useful implementation
* @param output the intended output of the lookup table source.
*/
virtual void GraftOutput(OutputType *output);
virtual OutputType *GetOutput();
virtual const OutputType *GetOutput() const;
virtual OutputType *GetOutput(DataObjectPointerArraySizeType idx);
virtual const OutputType *GetOutput(DataObjectPointerArraySizeType idx) const;
protected:
LookupTableSource();
~LookupTableSource() override;
};
} // namespace mitk
#endif
diff --git a/Modules/DataTypesExt/include/mitkUnstructuredGridSource.h b/Modules/DataTypesExt/include/mitkUnstructuredGridSource.h
index 08828ab4de..0bfa04e070 100644
--- a/Modules/DataTypesExt/include/mitkUnstructuredGridSource.h
+++ b/Modules/DataTypesExt/include/mitkUnstructuredGridSource.h
@@ -1,68 +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 mitkUnstructuredGridSource_h
#define mitkUnstructuredGridSource_h
#include "MitkDataTypesExtExports.h"
#include "mitkBaseDataSource.h"
namespace mitk
{
class UnstructuredGrid;
//##Documentation
//## @brief Superclass of all classes generating unstructured grids (instances of class
//## UnstructuredGrid) 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::UnstructuredGridSource.
//## @ingroup Process
class MITKDATATYPESEXT_EXPORT UnstructuredGridSource : public BaseDataSource
{
public:
mitkClassMacro(UnstructuredGridSource, BaseDataSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef mitk::UnstructuredGrid OutputType;
mitkBaseDataSourceGetOutputDeclarations
/**
* 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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
protected:
UnstructuredGridSource();
~UnstructuredGridSource() override;
};
} // namespace mitk
#endif
diff --git a/Modules/DataTypesExt/include/mitkVideoSource.h b/Modules/DataTypesExt/include/mitkVideoSource.h
index b69375b056..a7149e2074 100644
--- a/Modules/DataTypesExt/include/mitkVideoSource.h
+++ b/Modules/DataTypesExt/include/mitkVideoSource.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 mitkVideoSource_h
#define mitkVideoSource_h
#include "MitkDataTypesExtExports.h"
#include "mitkCommon.h"
#include <itkObject.h>
#include <itkObjectFactory.h>
#include <mitkMessage.h>
namespace mitk
{
///
/// Simple base class for acquiring video data.
///
class MITKDATATYPESEXT_EXPORT VideoSource : virtual public itk::Object
{
public:
///
/// Smart pointer defs
///
mitkClassMacroItkParent(VideoSource, itk::Object);
///
/// assigns the grabbing devices for acquiring the next frame.
/// in this base implementation it does nothing except incrementing
/// m_FrameCount
///
virtual void FetchFrame();
///
/// \return a pointer to the image data array for opengl rendering.
///
virtual unsigned char *GetVideoTexture() = 0;
///
/// advices this class to start the video capturing.
/// in this base implementation: toggles m_CapturingInProcess, resets m_FrameCount
/// *ATTENTION*: this should be also done in subclasses overwriting this method
///
virtual void StartCapturing();
///
/// advices this class to stop the video capturing.
/// in this base implementation: toggles m_CapturingInProcess, resets m_FrameCount
/// *ATTENTION*: this should be also done in subclasses overwriting this method
///
virtual void StopCapturing();
///
/// \return true if video capturing is active.
/// \see m_CapturingInProcess
///
virtual bool IsCapturingEnabled() const;
///
/// \return the current frame width (might be 0 if unknown)
///
virtual int GetImageWidth();
///
/// \return the current frame height (might be 0 if unknown)
///
virtual int GetImageHeight();
///
/// \return the current frame count
///
virtual unsigned long GetFrameCount() const;
///
/// \return true, if capturing is currently paused, false otherwise
///
virtual bool GetCapturePaused() const;
///
/// toggles m_CapturePaused
/// In Subclasses this function can be overwritten to take
/// measurs to provide a pause image, *BUT DO NOT FORGET TO
/// TOGGLE m_CapturePaused*
///
virtual void PauseCapturing();
protected:
///
/// init member
///
VideoSource();
///
/// deletes m_CurrentVideoTexture (if not 0)
///
~VideoSource() override;
protected:
///
/// finally this is what the video source must create: a video texture pointer
/// default: 0
///
unsigned char *m_CurrentVideoTexture;
///
/// should be filled when the first frame is available
/// default: 0
///
int m_CaptureWidth;
///
/// should be filled when the first frame is available
/// default: 0
///
int m_CaptureHeight;
///
/// saves if capturing is in procress
/// default: false
///
bool m_CapturingInProcess;
///
/// Saves the current frame count. Incremented in FetchFrame().
- /// Resetted to 0 when StartCapturing() or StopCapturing() is called.
+ /// Reset to 0 when StartCapturing() or StopCapturing() is called.
/// default: 0
///
unsigned long m_FrameCount;
///
/// Saves if the capturing is currently paused, i.e. this
/// will not fetch any further frames but provide the current
/// frame as long as m_CapturePaused is true
/// default: false
///
bool m_CapturePaused;
};
}
#endif
diff --git a/Modules/DataTypesExt/src/mitkClippingPlaneInteractor3D.cpp b/Modules/DataTypesExt/src/mitkClippingPlaneInteractor3D.cpp
index 2769e718fa..b63d5d4617 100644
--- a/Modules/DataTypesExt/src/mitkClippingPlaneInteractor3D.cpp
+++ b/Modules/DataTypesExt/src/mitkClippingPlaneInteractor3D.cpp
@@ -1,333 +1,333 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkClippingPlaneInteractor3D.h"
#include <mitkInteractionConst.h>
#include <mitkInteractionPositionEvent.h>
#include <mitkRotationOperation.h>
#include <mitkSurface.h>
#include <vtkCamera.h>
#include <vtkInteractorObserver.h>
#include <vtkInteractorStyle.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkRenderWindowInteractor.h>
mitk::ClippingPlaneInteractor3D::ClippingPlaneInteractor3D()
{
m_OriginalGeometry = Geometry3D::New();
// Initialize vector arithmetic
m_ObjectNormal[0] = 0.0;
m_ObjectNormal[1] = 0.0;
m_ObjectNormal[2] = 1.0;
}
mitk::ClippingPlaneInteractor3D::~ClippingPlaneInteractor3D()
{
}
void mitk::ClippingPlaneInteractor3D::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);
}
void mitk::ClippingPlaneInteractor3D::DataNodeChanged()
{
}
bool mitk::ClippingPlaneInteractor3D::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::ClippingPlaneInteractor3D::SelectObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
node->SetColor(1.0, 0.0, 0.0);
- // Colorize surface / wireframe dependend on distance from picked point
+ // Colorize surface / wireframe dependent on distance from picked point
this->ColorizeSurface(interactionEvent->GetSender(), 0.0);
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::ClippingPlaneInteractor3D::DeselectObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
node->SetColor(1.0, 1.0, 1.0);
// Colorize surface / wireframe as inactive
this->ColorizeSurface(interactionEvent->GetSender(), -1.0);
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::ClippingPlaneInteractor3D::InitTranslate(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
m_InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(),
m_InitialPickedDisplayPoint[0],
m_InitialPickedDisplayPoint[1],
0.0, // m_InitialInteractionPickedPoint[2],
m_InitialPickedWorldPoint);
// 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());
}
void mitk::ClippingPlaneInteractor3D::InitRotate(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
m_InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(),
m_InitialPickedDisplayPoint[0],
m_InitialPickedDisplayPoint[1],
0.0, // m_InitialInteractionPickedPoint[2],
m_InitialPickedWorldPoint);
// Get the timestep to also support 3D+t
int 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());
}
void mitk::ClippingPlaneInteractor3D::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
double currentWorldPoint[4];
mitk::Point2D currentDisplayPoint = positionEvent->GetPointerPositionOnScreen();
vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(),
currentDisplayPoint[0],
currentDisplayPoint[1],
0.0, // m_InitialInteractionPickedPoint[2],
currentWorldPoint);
Vector3D interactionMove;
interactionMove[0] = currentWorldPoint[0] - m_InitialPickedWorldPoint[0];
interactionMove[1] = currentWorldPoint[1] - m_InitialPickedWorldPoint[1];
interactionMove[2] = currentWorldPoint[2] - m_InitialPickedWorldPoint[2];
Point3D origin = m_OriginalGeometry->GetOrigin();
// Get the timestep to also support 3D+t
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
// If data is an mitk::Surface, extract it
Surface::Pointer surface = dynamic_cast<Surface *>(this->GetDataNode()->GetData());
vtkPolyData *polyData = nullptr;
if (surface.IsNotNull())
{
polyData = surface->GetVtkPolyData(timeStep);
// Extract surface normal from surface (if existent, otherwise use default)
vtkPointData *pointData = polyData->GetPointData();
if (pointData != nullptr)
{
vtkDataArray *normal = polyData->GetPointData()->GetVectors("planeNormal");
if (normal != nullptr)
{
m_ObjectNormal[0] = normal->GetComponent(0, 0);
m_ObjectNormal[1] = normal->GetComponent(0, 1);
m_ObjectNormal[2] = normal->GetComponent(0, 2);
}
}
}
Vector3D transformedObjectNormal;
this->GetDataNode()->GetData()->GetGeometry(timeStep)->IndexToWorld(m_ObjectNormal, transformedObjectNormal);
this->GetDataNode()->GetData()->GetGeometry(timeStep)->SetOrigin(
origin + transformedObjectNormal * (interactionMove * transformedObjectNormal));
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::ClippingPlaneInteractor3D::RotateObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
double currentWorldPoint[4];
Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(),
currentPickedDisplayPoint[0],
currentPickedDisplayPoint[1],
0.0, // m_InitialInteractionPickedPoint[2],
currentWorldPoint);
vtkCamera *camera = nullptr;
vtkRenderer *currentVtkRenderer = nullptr;
if ((interactionEvent->GetSender()) != nullptr)
{
vtkRenderWindow *renderWindow = interactionEvent->GetSender()->GetRenderWindow();
if (renderWindow != nullptr)
{
vtkRenderWindowInteractor *renderWindowInteractor = renderWindow->GetInteractor();
if (renderWindowInteractor != nullptr)
{
currentVtkRenderer = renderWindowInteractor->GetInteractorStyle()->GetCurrentRenderer();
if (currentVtkRenderer != nullptr)
camera = currentVtkRenderer->GetActiveCamera();
}
}
}
if (camera)
{
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::ClippingPlaneInteractor3D::ColorizeSurface(BaseRenderer::Pointer renderer, double scalar)
{
BaseData::Pointer data = this->GetDataNode()->GetData();
if (data.IsNull())
{
MITK_ERROR << "ClippingPlaneInteractor3D: No data object present!";
return;
}
// Get the timestep to also support 3D+t
int timeStep = 0;
if (renderer.IsNotNull())
timeStep = renderer->GetTimeStep(data);
// If data is an mitk::Surface, extract it
Surface::Pointer surface = dynamic_cast<Surface *>(data.GetPointer());
vtkPolyData *polyData = nullptr;
if (surface.IsNotNull())
polyData = surface->GetVtkPolyData(timeStep);
if (polyData == nullptr)
{
MITK_ERROR << "ClippingPlaneInteractor3D: No poly data present!";
return;
}
vtkPointData *pointData = polyData->GetPointData();
if (pointData == nullptr)
{
MITK_ERROR << "ClippingPlaneInteractor3D: No point data present!";
return;
}
vtkDataArray *scalars = pointData->GetScalars();
if (scalars == nullptr)
{
MITK_ERROR << "ClippingPlaneInteractor3D: No scalars for point data present!";
return;
}
for (vtkIdType i = 0; i < pointData->GetNumberOfTuples(); ++i)
{
scalars->SetComponent(i, 0, scalar);
}
polyData->Modified();
pointData->Update();
}
diff --git a/Modules/DataTypesExt/src/mitkLabeledImageLookupTable.cpp b/Modules/DataTypesExt/src/mitkLabeledImageLookupTable.cpp
index fd6214a6a5..dcb8634714 100644
--- a/Modules/DataTypesExt/src/mitkLabeledImageLookupTable.cpp
+++ b/Modules/DataTypesExt/src/mitkLabeledImageLookupTable.cpp
@@ -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.
============================================================================*/
#include "mitkLabeledImageLookupTable.h"
#include <cstdlib>
#include <vtkLookupTable.h>
/**
* Default constructor. Protected to prevent "normal" creation
*/
mitk::LabeledImageLookupTable::LabeledImageLookupTable() : m_LevelWindow(128, 256)
{
if (m_LookupTable == nullptr)
{
itkWarningMacro(
"LookupTable is nullptr, it should have been initialized by the default constructor of mitk::LookupTable");
m_LookupTable = vtkLookupTable::New();
}
m_LookupTable->SetNumberOfTableValues(256);
// set the background to black and fully transparent
m_LookupTable->SetTableValue(0, 0.0, 0.0, 0.0, 0.0);
// initialize the remaining 255 colors with random values and
// an alpha value of 1.0
double r, g, b;
//
// Initialize the random number generator with an arbitrary seed.
// This way, the generated colors are random, but always the same...
std::srand(2);
for (vtkIdType index = 1; index < 256; ++index)
{
GenerateRandomColor(r, g, b);
m_LookupTable->SetTableValue(index, r, g, b);
}
// initialize the default level/window settings,
// which can be accessed via GetLevelWindow();
m_LevelWindow.SetRangeMinMax(0, 255);
m_LevelWindow.SetWindowBounds(0, 255);
m_LevelWindow.SetFixed(true);
}
mitk::LabeledImageLookupTable::LabeledImageLookupTable(const mitk::LabeledImageLookupTable &other)
: LookupTable(other), m_LevelWindow(other.m_LevelWindow)
{
}
/**
* Virtual destructor
*/
mitk::LabeledImageLookupTable::~LabeledImageLookupTable()
{
}
mitk::LabeledImageLookupTable &mitk::LabeledImageLookupTable::operator=(const mitk::LookupTable &other)
{
LookupTable::operator=(other);
if (const auto *lut = dynamic_cast<const LabeledImageLookupTable *>(&other))
{
this->m_LevelWindow = lut->m_LevelWindow;
}
return *this;
}
/**
* Sets the color for a given label
* @param label The pixel value used as a label in the image
- * @param r The red component of the rgba color value. Values sould be given in the range [0,1]
- * @param g The green component of the rgba color value. Values sould be given in the range [0,1]
- * @param b The blue component of the rgba color value. Values sould be given in the range [0,1]
- * @param a The alpha component of the rgba color value. Values sould be given in the range [0,1]. Default is 1.
+ * @param r The red component of the rgba color value. Values should be given in the range [0,1]
+ * @param g The green component of the rgba color value. Values should be given in the range [0,1]
+ * @param b The blue component of the rgba color value. Values should be given in the range [0,1]
+ * @param a The alpha component of the rgba color value. Values should be given in the range [0,1]. Default is 1.
*/
void mitk::LabeledImageLookupTable::SetColorForLabel(const mitk::LabeledImageLookupTable::LabelType &label,
const double &r,
const double &g,
const double &b,
const double a)
{
if (m_LookupTable == nullptr)
{
itkWarningMacro("LookupTable is nullptr, but it should have been initialized by the constructor");
return;
}
m_LookupTable->SetTableValue(label, r, g, b, a);
}
/**
* Determines the color which will be used for coloring a given label.
* @param label the label for which the color should be returned
* @returns an rgba array containing the color information for the given label
* Color components are expressed as [0,1] double values.
*/
double *mitk::LabeledImageLookupTable::GetColorForLabel(const mitk::LabeledImageLookupTable::LabelType &label)
{
if (m_LookupTable == nullptr)
{
itkWarningMacro("LookupTable is nullptr, but it should have been initialized by the constructor");
return nullptr;
}
return m_LookupTable->GetTableValue(label);
}
/**
* Generates a random rgb color value. Values for rgb are in the range
* [0,1]
*/
void mitk::LabeledImageLookupTable::GenerateRandomColor(double &r, double &g, double &b)
{
r = GenerateRandomNumber();
g = GenerateRandomNumber();
b = GenerateRandomNumber();
}
/**
* Generates a radnom number drawn from a uniform
* distribution in the range [0,1].
*/
double mitk::LabeledImageLookupTable::GenerateRandomNumber()
{
return (((double)(std::rand())) / ((double)(RAND_MAX)));
}
itk::LightObject::Pointer mitk::LabeledImageLookupTable::InternalClone() const
{
itk::LightObject::Pointer result(new Self(*this));
result->UnRegister();
return result;
}
diff --git a/Modules/DataTypesExt/src/mitkSurfaceDeformationDataInteractor3D.cpp b/Modules/DataTypesExt/src/mitkSurfaceDeformationDataInteractor3D.cpp
index 94248cdde3..8470604f00 100644
--- a/Modules/DataTypesExt/src/mitkSurfaceDeformationDataInteractor3D.cpp
+++ b/Modules/DataTypesExt/src/mitkSurfaceDeformationDataInteractor3D.cpp
@@ -1,301 +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.
============================================================================*/
#include "mitkSurfaceDeformationDataInteractor3D.h"
#include "mitkMouseWheelEvent.h"
#include <vtkInteractorObserver.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
mitk::SurfaceDeformationDataInteractor3D::SurfaceDeformationDataInteractor3D() : m_GaussSigma(30.0)
{
m_OriginalPolyData = vtkPolyData::New();
// Initialize vector arithmetic
m_ObjectNormal[0] = 0.0;
m_ObjectNormal[1] = 0.0;
m_ObjectNormal[2] = 1.0;
}
mitk::SurfaceDeformationDataInteractor3D::~SurfaceDeformationDataInteractor3D()
{
m_OriginalPolyData->Delete();
}
void mitk::SurfaceDeformationDataInteractor3D::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("initDeformation", InitDeformation);
CONNECT_FUNCTION("deformObject", DeformObject);
CONNECT_FUNCTION("scaleRadius", ScaleRadius);
}
void mitk::SurfaceDeformationDataInteractor3D::DataNodeChanged()
{
if (this->GetDataNode() != nullptr)
{
m_Surface = dynamic_cast<Surface *>(this->GetDataNode()->GetData());
if (m_Surface == nullptr)
MITK_ERROR << "SurfaceDeformationDataInteractor3D::DataNodeChanged(): DataNode has to contain a surface.";
}
else
m_Surface = nullptr;
}
bool mitk::SurfaceDeformationDataInteractor3D::CheckOverObject(const InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return false;
Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
Point3D currentPickedPoint;
if (interactionEvent->GetSender()->PickObject(currentPickedDisplayPoint, currentPickedPoint) == this->GetDataNode())
{
// Colorized surface at current picked position
m_SurfaceColorizationCenter = currentPickedPoint;
return true;
}
return false;
}
void mitk::SurfaceDeformationDataInteractor3D::SelectObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep);
this->GetDataNode()->SetColor(1.0, 0.0, 0.0);
- // Colorize surface / wireframe dependend on distance from picked point
+ // Colorize surface / wireframe dependent on distance from picked point
this->ColorizeSurface(polyData, timeStep, m_SurfaceColorizationCenter, COLORIZATION_GAUSS);
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::SurfaceDeformationDataInteractor3D::DeselectObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep);
this->GetDataNode()->SetColor(1.0, 1.0, 1.0);
// Colorize surface / wireframe as inactive
this->ColorizeSurface(polyData, timeStep, m_SurfaceColorizationCenter, COLORIZATION_CONSTANT, -1.0);
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::SurfaceDeformationDataInteractor3D::InitDeformation(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep);
// Store current picked point
mitk::Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen();
interactionEvent->GetSender()->PickObject(currentPickedDisplayPoint, m_InitialPickedPoint);
vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(),
currentPickedDisplayPoint[0],
currentPickedDisplayPoint[1],
0.0, // m_InitialInteractionPickedPoint[2],
m_InitialPickedWorldPoint);
// Make deep copy of vtkPolyData interacted on
m_OriginalPolyData->DeepCopy(polyData);
}
void mitk::SurfaceDeformationDataInteractor3D::DeformObject(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (positionEvent == nullptr)
return;
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep);
BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetGeometry(timeStep);
double currentWorldPoint[4];
mitk::Point2D currentDisplayPoint = positionEvent->GetPointerPositionOnScreen();
vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(),
currentDisplayPoint[0],
currentDisplayPoint[1],
0.0, // m_InitialInteractionPickedPoint[2],
currentWorldPoint);
// Calculate mouse move in 3D space
Vector3D interactionMove;
interactionMove[0] = currentWorldPoint[0] - m_InitialPickedWorldPoint[0];
interactionMove[1] = currentWorldPoint[1] - m_InitialPickedWorldPoint[1];
interactionMove[2] = currentWorldPoint[2] - m_InitialPickedWorldPoint[2];
// Transform mouse move into geometry space
this->GetDataNode()->GetData()->UpdateOutputInformation(); // make sure that the Geometry is up-to-date
Vector3D interactionMoveIndex;
geometry->WorldToIndex(interactionMove, interactionMoveIndex);
// Get picked point and transform into local coordinates
Point3D pickedPoint;
geometry->WorldToIndex(m_InitialPickedPoint, pickedPoint);
Vector3D v1 = pickedPoint.GetVectorFromOrigin();
vtkDataArray *normal = polyData->GetPointData()->GetVectors("planeNormal");
if (normal != nullptr)
{
m_ObjectNormal[0] = normal->GetComponent(0, 0);
m_ObjectNormal[1] = normal->GetComponent(0, 1);
m_ObjectNormal[2] = normal->GetComponent(0, 2);
}
Vector3D v2 = m_ObjectNormal * (interactionMoveIndex * m_ObjectNormal);
vtkPoints *originalPoints = m_OriginalPolyData->GetPoints();
vtkPoints *deformedPoints = polyData->GetPoints();
double denom = m_GaussSigma * m_GaussSigma * 2;
double point[3];
for (vtkIdType i = 0; i < deformedPoints->GetNumberOfPoints(); ++i)
{
// Get original point
double *originalPoint = originalPoints->GetPoint(i);
Vector3D v0;
v0[0] = originalPoint[0];
v0[1] = originalPoint[1];
v0[2] = originalPoint[2];
// Calculate distance of this point from line through picked point
double d = itk::CrossProduct(m_ObjectNormal, (v1 - v0)).GetNorm();
Vector3D t = v2 * exp(-d * d / denom);
point[0] = originalPoint[0] + t[0];
point[1] = originalPoint[1] + t[1];
point[2] = originalPoint[2] + t[2];
deformedPoints->SetPoint(i, point);
}
// Make sure that surface is colorized at initial picked position as long as we are in deformation state
m_SurfaceColorizationCenter = m_InitialPickedPoint;
polyData->Modified();
m_Surface->Modified();
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::SurfaceDeformationDataInteractor3D::ScaleRadius(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto *wheelEvent = dynamic_cast<const MouseWheelEvent *>(interactionEvent);
if (wheelEvent == nullptr)
return;
m_GaussSigma += (double)(wheelEvent->GetWheelDelta()) / 20;
if (m_GaussSigma < 10.0)
{
m_GaussSigma = 10.0;
}
else if (m_GaussSigma > 128.0)
{
m_GaussSigma = 128.0;
}
int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData());
vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep);
- // Colorize surface / wireframe dependend on sigma and distance from picked point
+ // Colorize surface / wireframe dependent on sigma and distance from picked point
this->ColorizeSurface(polyData, timeStep, m_SurfaceColorizationCenter, COLORIZATION_GAUSS);
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::SurfaceDeformationDataInteractor3D::ColorizeSurface(
vtkPolyData *polyData, int timeStep, const Point3D &pickedPoint, int mode, double scalar)
{
if (polyData == nullptr)
return;
vtkPoints *points = polyData->GetPoints();
vtkPointData *pointData = polyData->GetPointData();
if (pointData == nullptr)
return;
vtkDataArray *scalars = pointData->GetScalars();
if (scalars == nullptr)
return;
if (mode == COLORIZATION_GAUSS)
{
// Get picked point and transform into local coordinates
Point3D localPickedPoint;
BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetGeometry(timeStep);
geometry->WorldToIndex(pickedPoint, localPickedPoint);
Vector3D v1 = localPickedPoint.GetVectorFromOrigin();
vtkDataArray *normal = polyData->GetPointData()->GetVectors("planeNormal");
if (normal != nullptr)
{
m_ObjectNormal[0] = normal->GetComponent(0, 0);
m_ObjectNormal[1] = normal->GetComponent(0, 1);
m_ObjectNormal[2] = normal->GetComponent(0, 2);
}
double denom = m_GaussSigma * m_GaussSigma * 2;
for (vtkIdType i = 0; i < points->GetNumberOfPoints(); ++i)
{
// Get original point
double *point = points->GetPoint(i);
Vector3D v0;
v0[0] = point[0];
v0[1] = point[1];
v0[2] = point[2];
// Calculate distance of this point from line through picked point
double d = itk::CrossProduct(m_ObjectNormal, (v1 - v0)).GetNorm();
double t = exp(-d * d / denom);
scalars->SetComponent(i, 0, t);
}
}
else if (mode == COLORIZATION_CONSTANT)
{
for (vtkIdType i = 0; i < pointData->GetNumberOfTuples(); ++i)
{
scalars->SetComponent(i, 0, scalar);
}
}
polyData->Modified();
pointData->Update();
}
diff --git a/Modules/DataTypesExt/test/mitkColorSequenceRainbowTest.cpp b/Modules/DataTypesExt/test/mitkColorSequenceRainbowTest.cpp
index 18c69db63b..191544b94a 100644
--- a/Modules/DataTypesExt/test/mitkColorSequenceRainbowTest.cpp
+++ b/Modules/DataTypesExt/test/mitkColorSequenceRainbowTest.cpp
@@ -1,91 +1,91 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <limits>
// MITK includes
#include "mitkColorSequenceRainbow.h"
// VTK includes
#include <vtkDebugLeaks.h>
class mitkColorSequenceRainbowTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkColorSequenceRainbowTestSuite);
MITK_TEST(GetNextColor_ReturnsADifferentColor);
MITK_TEST(GoToBegin_NextCallReturnsSameColorAsFirstCall);
MITK_TEST(GetNextColor_CanWrapAroundWithoutCrashing);
CPPUNIT_TEST_SUITE_END();
public:
/**
- * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used
+ * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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
{
}
void tearDown() override
{
}
void GetNextColor_ReturnsADifferentColor()
{
mitk::ColorSequenceRainbow rainbowColorSequence;
mitk::Color color1 = rainbowColorSequence.GetNextColor();
mitk::Color color2 = rainbowColorSequence.GetNextColor();
CPPUNIT_ASSERT_MESSAGE("Two consecutive colors are not equal.", color1 != color2);
}
void GoToBegin_NextCallReturnsSameColorAsFirstCall()
{
mitk::ColorSequenceRainbow rainbowColorSequence;
mitk::Color color1 = rainbowColorSequence.GetNextColor();
rainbowColorSequence.GoToBegin();
mitk::Color color2 = rainbowColorSequence.GetNextColor();
CPPUNIT_ASSERT_MESSAGE("GoToBegin is identical to beginning.", color1 == color2);
}
void GetNextColor_CanWrapAroundWithoutCrashing()
{
int counter = 0;
try
{
mitk::ColorSequenceRainbow rainbowColorSequence;
mitk::Color color1 = rainbowColorSequence.GetNextColor();
mitk::Color color2 = rainbowColorSequence.GetNextColor();
while (color1 != color2 && counter < std::numeric_limits<int>::max())
{
color2 = rainbowColorSequence.GetNextColor();
++counter;
}
}
catch (...)
{
CPPUNIT_FAIL("Exception during rainbow color sequence color generation");
}
CPPUNIT_ASSERT_MESSAGE("Error free wraparound achieved.", counter < std::numeric_limits<int>::max());
}
};
MITK_TEST_SUITE_REGISTRATION(mitkColorSequenceRainbow)
diff --git a/Modules/Gizmo/include/mitkGizmo.h b/Modules/Gizmo/include/mitkGizmo.h
index 69e2009ee6..2da31558f1 100644
--- a/Modules/Gizmo/include/mitkGizmo.h
+++ b/Modules/Gizmo/include/mitkGizmo.h
@@ -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.
============================================================================*/
#ifndef mitkGizmo_h
#define mitkGizmo_h
#include <mitkDataNode.h>
#include <mitkSurface.h>
#include <MitkGizmoExports.h>
namespace mitk
{
class DataStorage;
class GizmoRemover;
//! A geometry manipulation "gizmo".
//!
//! This class represents the principal axes of some arbitrary BaseGeometry.
//!
//! The visualization shows the three axes x, y, and z along with a orthogonal ring around them.
//! In its center, a small sphere is visualized.
//!
//! The class is intended to be visualized along with another data object that "owns" the followed
//! BaseGeometry. The Gizmo will automatically update itself to all modifications to the
//! followed base geometry. Interactive modifications to the geometry can thus be easily visualized.
//!
- //! The gizmo is definded by
+ //! The gizmo is defined by
//! - a center
//! - three axes for x, y, and z
//! - a radius
//!
//! The radius determines the size of the axes and the rings around them.
//!
//! A nice overview of similar / alternative representations can be found in
//! "Schmidt R, Singh K, and Balakrishnan R. Sketching and Composing Widgets for 3D Manipulation.
//! EUROGRAPHICS 2008"
//!
//! \sa GizmoInteractor3D
class MITKGIZMO_EXPORT Gizmo : public Surface
{
public:
//! Names for the three axes
enum AxisType
{
AxisX,
AxisY,
AxisZ
};
//! Names for the different parts of the gizmo
enum HandleType
{
MoveFreely, //< the central sphere
MoveAlongAxisX,
MoveAlongAxisY,
MoveAlongAxisZ,
RotateAroundAxisX,
RotateAroundAxisY,
RotateAroundAxisZ,
ScaleX,
ScaleY,
ScaleZ,
NoHandle //< to indicate picking failure
};
//! Conversion for any kind of logging/debug/... purposes
static std::string HandleTypeToString(HandleType type);
mitkClassMacro(Gizmo, Surface);
itkNewMacro(Gizmo);
itkGetConstMacro(Center, Point3D);
itkSetMacro(Center, Point3D);
itkGetConstMacro(AxisX, Vector3D);
itkSetMacro(AxisX, Vector3D);
itkGetConstMacro(AxisY, Vector3D);
itkSetMacro(AxisY, Vector3D);
itkGetConstMacro(AxisZ, Vector3D);
itkSetMacro(AxisZ, Vector3D);
itkGetConstMacro(Radius, Vector3D);
itkSetMacro(Radius, Vector3D);
itkGetConstMacro(AllowTranslation, bool);
itkSetMacro(AllowTranslation, bool);
itkBooleanMacro(AllowTranslation);
itkGetConstMacro(AllowRotation, bool);
itkSetMacro(AllowRotation, bool);
itkBooleanMacro(AllowRotation);
itkGetConstMacro(AllowScaling, bool);
itkSetMacro(AllowScaling, bool);
itkBooleanMacro(AllowScaling);
//! Return the longest of the three axes.
double GetLongestRadius() const;
//! Updates the representing surface object after changes to center, axes, or radius.
void UpdateRepresentation();
//! Setup the gizmo to follow any ModifiedEvents of the given geometry.
//! The object will adapt and update itself in function of the geometry's changes.
void FollowGeometry(BaseGeometry *geom);
//! The ITK callback to receive modified events of the followed geometry
void OnFollowedGeometryModified();
//! Determine the nature of the the given vertex id.
//! Can be used after picking a vertex id to determine what part of the
//! gizmo has been picked.
HandleType GetHandleFromPointID(vtkIdType id);
//! Determine the nature of the the given vertex data value.
//! Can be used after picking a vertex data value to determine what part of the
//! gizmo has been picked.
mitk::Gizmo::HandleType GetHandleFromPointDataValue(double value);
//! Convenience creation of a gizmo for given node
//! \param node The node holding the geometry to be visualized
//! \param storage The DataStorage where a node holding the gizmo
//! shall be added to (ignored when nullptr)
//!
- //! \return DataNode::Pointer containing the node used for vizualization of our gizmo
+ //! \return DataNode::Pointer containing the node used for visualization of our gizmo
static DataNode::Pointer AddGizmoToNode(DataNode *node, DataStorage *storage);
//! Convenience removal of gizmo from given node
//! \param node The node being currently manipulated
//! \param storage The DataStorage where the gizmo has been added to
//!
//! \return true if the gizmo has been found and removed successfully
//!
//! Make sure to pass the same parameters here that you provided to a
//! previous call to AddGizmoToNode.
//!
- //! \return DataNode::Pointer containing the node used for vizualization of our gizmo
+ //! \return DataNode::Pointer containing the node used for visualization of our gizmo
static bool RemoveGizmoFromNode(DataNode *node, DataStorage *storage);
//! \return whether given node in given storage has a gizmo attached.
static bool HasGizmoAttached(mitk::DataNode *node, DataStorage *storage);
protected:
Gizmo();
~Gizmo() override;
Gizmo(const Gizmo &); // = delete;
Gizmo &operator=(const Gizmo &); // = delete;
//! Creates a vtkPolyData representing the parameters defining the gizmo.
vtkSmartPointer<vtkPolyData> BuildGizmo();
private:
Point3D m_Center;
Vector3D m_AxisX;
Vector3D m_AxisY;
Vector3D m_AxisZ;
Vector3D m_Radius;
bool m_AllowTranslation;
bool m_AllowRotation;
bool m_AllowScaling;
BaseGeometry::Pointer m_FollowedGeometry;
//! ITK tag for the observing of m_FollowedGeometry
unsigned long m_FollowerTag;
//! Observes a data storage for removal of the manipulated object.
//! Removes gizmo together with the manipulated object
std::unique_ptr<GizmoRemover> m_GizmoRemover;
};
}
#endif
diff --git a/Modules/GraphAlgorithms/itkShortestPathImageFilter.h b/Modules/GraphAlgorithms/itkShortestPathImageFilter.h
index 5d0aa957bc..abf640627f 100644
--- a/Modules/GraphAlgorithms/itkShortestPathImageFilter.h
+++ b/Modules/GraphAlgorithms/itkShortestPathImageFilter.h
@@ -1,235 +1,235 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 __itkShortestPathImageFilter_h
#define __itkShortestPathImageFilter_h
#include "itkImageToImageFilter.h"
#include "itkShortestPathCostFunction.h"
#include "itkShortestPathNode.h"
#include <itkImageRegionIteratorWithIndex.h>
#include <itkMacro.h>
// ------- INFORMATION ----------
/// SET FUNCTIONS
// void SetInput( ItkImage ) // Compulsory
// void SetStartIndex (const IndexType & StartIndex); // Compulsory
// void SetEndIndex(const IndexType & EndIndex); // Compulsory
// void SetFullNeighborsMode(bool) // Optional (default=false), if false N4, if true N26
// void SetActivateTimeOut(bool) // Optional (default=false), for debug issues: after 30s algorithms terminates. You can
// have a look at the VectorOrderImage to see how far it came
// void SetMakeOutputImage(bool) // Optional (default=true), Generate an outputimage of the path. You can also get the
// path directoy with GetVectorPath()
// void SetCalcAllDistances(bool) // Optional (default=false), Calculate Distances over the whole image. CAREFUL,
// algorithm time extends a lot. Necessary for GetDistanceImage
// void SetStoreVectorOrder(bool) // Optional (default=false), Stores in which order the pixels were checked. Necessary
// for GetVectorOrderImage
// void AddEndIndex(const IndexType & EndIndex) //Optional. By calling this function you can add several endpoints! The
-// algorithm will look for several shortest Pathes. From Start to all Endpoints.
+// algorithm will look for several shortest Paths. From Start to all Endpoints.
//
/// GET FUNCTIONS
// std::vector< itk::Index<3> > GetVectorPath(); // returns the shortest path as vector
-// std::vector< std::vector< itk::Index<3> > GetMultipleVectorPathe(); // returns a vector of shortest Pathes (which are
+// std::vector< std::vector< itk::Index<3> > GetMultipleVectorPathe(); // returns a vector of shortest Paths (which are
// vectors of points)
// GetDistanceImage // Returns the distance image
// GetVectorOrderIMage // Returns the Vector Order image
//
// EXAMPLE USE
// pleae see qmitkmitralvalvesegmentation4dtee bundle
namespace itk
{
template <class TInputImageType, class TOutputImageType>
class ShortestPathImageFilter : public ImageToImageFilter<TInputImageType, TOutputImageType>
{
public:
// Standard Typedefs
typedef ShortestPathImageFilter Self;
typedef ImageToImageFilter<TInputImageType, TOutputImageType> Superclass;
typedef SmartPointer<Self> Pointer;
typedef SmartPointer<const Self> ConstPointer;
// Typdefs for metric
typedef ShortestPathCostFunction<TInputImageType> CostFunctionType;
typedef typename CostFunctionType::Pointer CostFunctionTypePointer;
// More typdefs for convenience
typedef TInputImageType InputImageType;
typedef typename TInputImageType::Pointer InputImagePointer;
typedef typename TInputImageType::PixelType InputImagePixelType;
typedef typename TInputImageType::SizeType InputImageSizeType;
typedef typename TInputImageType::IndexType IndexType;
typedef typename itk::ImageRegionIteratorWithIndex<InputImageType> InputImageIteratorType;
typedef TOutputImageType OutputImageType;
typedef typename TOutputImageType::Pointer OutputImagePointer;
typedef typename TOutputImageType::PixelType OutputImagePixelType;
typedef typename TOutputImageType::IndexType OutputImageIndexType;
typedef ImageRegionIteratorWithIndex<OutputImageType> OutputImageIteratorType;
typedef itk::ShapedNeighborhoodIterator<TInputImageType> itkShapedNeighborhoodIteratorType;
// New Macro for smartpointer instantiation
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
// Run-time type information
itkTypeMacro(ShortestPathImageFilter, ImageToImageFilter);
// Display
void PrintSelf(std::ostream &os, Indent indent) const override;
// Compare function for A_STAR
struct CompareNodeStar
{
bool operator()(ShortestPathNode *a, ShortestPathNode *b) { return (a->distAndEst > b->distAndEst); }
};
// \brief Set Starpoint for ShortestPath Calculation
void SetStartIndex(const IndexType &StartIndex);
// \brief Adds Endpoint for multiple ShortestPath Calculation
void AddEndIndex(const IndexType &index);
// \brief Set Endpoint for ShortestPath Calculation
void SetEndIndex(const IndexType &EndIndex);
// \brief Set FullNeighborsMode. false = no diagonal neighbors, in 2D this means N4 Neigborhood. true = would be N8
// in 2D
itkSetMacro(FullNeighborsMode, bool);
itkGetMacro(FullNeighborsMode, bool);
// \brief Set Graph_fullNeighbors. false = no diagonal neighbors, in 2D this means N4 Neigborhood. true = would be
// N8 in 2D
itkSetMacro(Graph_fullNeighbors, bool);
// \brief (default=true), Produce output image, which shows the shortest path. But you can also get the shortest
// Path directly as vector with the function GetVectorPath
itkSetMacro(MakeOutputImage, bool);
itkGetMacro(MakeOutputImage, bool);
// \brief (default=false), Store an Vector of Order, so you can call getVectorOrderImage after update
itkSetMacro(StoreVectorOrder, bool);
itkGetMacro(StoreVectorOrder, bool);
// \brief (default=false), // Calculate all Distances to all pixels, so you can call getDistanceImage after update
// (warning algo will take a long time)
itkSetMacro(CalcAllDistances, bool);
itkGetMacro(CalcAllDistances, bool);
// \brief (default=false), for debug issues: after 30s algorithms terminates. You can have a look at the
// VectorOrderImage to see how far it came
itkSetMacro(ActivateTimeOut, bool);
itkGetMacro(ActivateTimeOut, bool);
// \brief returns shortest Path as vector
std::vector<IndexType> GetVectorPath();
// \brief returns Multiple shortest Paths. You can call this function, when u performed a multiple shortest path
// search (one start, several ends)
std::vector<std::vector<IndexType>> GetMultipleVectorPaths();
// \brief returns the vector order image. It shows in which order the pixels were checked. good for debugging. Be
// sure to have m_StoreVectorOrder=true
OutputImagePointer GetVectorOrderImage();
// \brief returns the distance image. It shows the distances from the startpoint to all other pixels. Be sure to
// have m_CalcAllDistances=true
OutputImagePointer GetDistanceImage();
// \brief Fill m_VectorPath
void MakeShortestPathVector();
// \brief cleans up the filter
void CleanUp();
itkSetObjectMacro(CostFunction,
CostFunctionType); // itkSetObjectMacro = set function that uses pointer as parameter
itkGetObjectMacro(CostFunction, CostFunctionType);
void SetUseCostFunction(bool doUseCostFunction) { m_useCostFunction = doUseCostFunction; };
bool GetUseCostFunction() { return m_useCostFunction; };
protected:
std::vector<IndexType>
m_endPoints; // if you fill this vector, the algo will not rest until all endPoints have been reached
std::vector<IndexType> m_endPointsClosed;
ShortestPathNode *m_Nodes; // main list that contains all nodes
NodeNumType m_Graph_NumberOfNodes;
NodeNumType m_Graph_StartNode;
NodeNumType m_Graph_EndNode;
bool m_Graph_fullNeighbors;
bool m_useCostFunction;
std::vector<ShortestPathNode *> m_Graph_DiscoveredNodeList;
ShortestPathImageFilter(Self &); // intentionally not implemented
void operator=(const Self &); // intentionally not implemented
const static int BACKGROUND = 0;
const static int FOREGROUND = 255;
bool m_FullNeighborsMode;
bool m_MakeOutputImage;
bool m_StoreVectorOrder; // Store an Vector of Order, so you can call getVectorOrderImage after update
bool m_CalcAllDistances; // Calculate all Distances, so you can call getDistanceImage after update (warning algo
// will take a long time)
bool multipleEndPoints;
bool m_ActivateTimeOut; // if true, then i search max. 30 secs. then abort
bool m_Initialized;
CostFunctionTypePointer m_CostFunction;
IndexType m_StartIndex, m_EndIndex;
std::vector<IndexType> m_VectorPath;
std::vector<std::vector<IndexType>> m_MultipleVectorPaths;
std::vector<NodeNumType> m_VectorOrder;
ShortestPathImageFilter();
~ShortestPathImageFilter() override;
// \brief Create all the outputs
void MakeOutputs();
// \brief Generate Data
void GenerateData() override;
// \brief gets the estimate costs from pixel a to target.
double getEstimatedCostsToTarget(const IndexType &a);
typename InputImageType::Pointer m_magnitudeImage;
// \brief Convert a indexnumber of a node in m_Nodes to image coordinates
typename TInputImageType::IndexType NodeToCoord(NodeNumType);
// \brief Convert image coordinate to a indexnumber of a node in m_Nodes
unsigned int CoordToNode(IndexType);
// \brief Returns the neighbors of a node
std::vector<ShortestPathNode *> GetNeighbors(NodeNumType nodeNum, bool FullNeighbors);
// \brief Check if coords are in bounds of image
bool CoordIsInBounds(IndexType);
// \brief Initializes the graph
void InitGraph();
// \brief Start ShortestPathSearch
void StartShortestPathSearch();
};
} // end of namespace itk
#include "itkShortestPathImageFilter.txx"
#endif
diff --git a/Modules/GraphAlgorithms/itkShortestPathImageFilter.txx b/Modules/GraphAlgorithms/itkShortestPathImageFilter.txx
index edaba4950e..6c010a9423 100644
--- a/Modules/GraphAlgorithms/itkShortestPathImageFilter.txx
+++ b/Modules/GraphAlgorithms/itkShortestPathImageFilter.txx
@@ -1,889 +1,889 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 __itkShortestPathImageFilter_txx
#define __itkShortestPathImageFilter_txx
#include "itkShortestPathImageFilter.h"
#include "mitkMemoryUtilities.h"
#include <ctime>
#include <algorithm>
#include <iostream>
#include <vector>
namespace itk
{
// Constructor (initialize standard values)
template <class TInputImageType, class TOutputImageType>
ShortestPathImageFilter<TInputImageType, TOutputImageType>::ShortestPathImageFilter()
: m_Nodes(nullptr),
m_Graph_NumberOfNodes(0),
m_Graph_fullNeighbors(false),
m_useCostFunction(true),
m_FullNeighborsMode(false),
m_MakeOutputImage(true),
m_StoreVectorOrder(false),
m_CalcAllDistances(false),
multipleEndPoints(false),
m_ActivateTimeOut(false),
m_Initialized(false)
{
m_endPoints.clear();
m_endPointsClosed.clear();
if (m_MakeOutputImage)
{
this->SetNumberOfRequiredOutputs(1);
this->SetNthOutput(0, OutputImageType::New());
}
}
template <class TInputImageType, class TOutputImageType>
ShortestPathImageFilter<TInputImageType, TOutputImageType>::~ShortestPathImageFilter()
{
delete[] m_Nodes;
}
template <class TInputImageType, class TOutputImageType>
inline typename ShortestPathImageFilter<TInputImageType, TOutputImageType>::IndexType
ShortestPathImageFilter<TInputImageType, TOutputImageType>::NodeToCoord(NodeNumType node)
{
const InputImageSizeType &size = this->GetInput()->GetRequestedRegion().GetSize();
int dim = InputImageType::ImageDimension;
IndexType coord;
if (dim == 2)
{
coord[1] = node / size[0];
coord[0] = node % size[0];
if (((unsigned long)coord[0] >= size[0]) || ((unsigned long)coord[1] >= size[1]))
{
coord[0] = 0;
coord[1] = 0;
}
}
if (dim == 3)
{
coord[2] = node / (size[0] * size[1]);
coord[1] = (node % (size[0] * size[1])) / size[0];
coord[0] = (node % (size[0] * size[1])) % size[0];
if (((unsigned long)coord[0] >= size[0]) || ((unsigned long)coord[1] >= size[1]) ||
((unsigned long)coord[2] >= size[2]))
{
coord[0] = 0;
coord[1] = 0;
coord[2] = 0;
}
}
return coord;
}
template <class TInputImageType, class TOutputImageType>
inline typename itk::NodeNumType ShortestPathImageFilter<TInputImageType, TOutputImageType>::CoordToNode(
IndexType coord)
{
const InputImageSizeType &size = this->GetInput()->GetRequestedRegion().GetSize();
int dim = InputImageType::ImageDimension;
NodeNumType node = 0;
if (dim == 2)
{
node = (coord[1] * size[0]) + coord[0];
}
if (dim == 3)
{
node = (coord[2] * size[0] * size[1]) + (coord[1] * size[0]) + coord[0];
}
if ((m_Graph_NumberOfNodes > 0) && (node >= m_Graph_NumberOfNodes))
{
/*MITK_INFO << "WARNING! Coordinates outside image!";
MITK_INFO << "Coords = " << coord ;
MITK_INFO << "ImageDim = " << dim ;
MITK_INFO << "RequestedRegionSize = " << size ;*/
node = 0;
}
return node;
}
template <class TInputImageType, class TOutputImageType>
inline bool ShortestPathImageFilter<TInputImageType, TOutputImageType>::CoordIsInBounds(IndexType coord)
{
const InputImageSizeType &size = this->GetInput()->GetRequestedRegion().GetSize();
int dim = InputImageType::ImageDimension;
if (dim == 2)
{
if ((coord[0] >= 0) && ((unsigned long)coord[0] < size[0]) && (coord[1] >= 0) &&
((unsigned long)coord[1] < size[1]))
{
return true;
}
}
if (dim == 3)
{
if ((coord[0] >= 0) && ((unsigned long)coord[0] < size[0]) && (coord[1] >= 0) &&
((unsigned long)coord[1] < size[1]) && (coord[2] >= 0) && ((unsigned long)coord[2] < size[2]))
{
return true;
}
}
return false;
}
template <class TInputImageType, class TOutputImageType>
inline std::vector<ShortestPathNode *> ShortestPathImageFilter<TInputImageType, TOutputImageType>::GetNeighbors(
unsigned int nodeNum, bool FullNeighbors)
{
// returns a vector of nodepointers.. these nodes are the neighbors
int dim = InputImageType::ImageDimension;
IndexType Coord = NodeToCoord(nodeNum);
IndexType NeighborCoord;
std::vector<ShortestPathNode *> nodeList;
int neighborDistance = 1; // if i increase that, i might not hit the endnote
// maybe use itkNeighborhoodIterator here, might be faster
if (dim == 2)
{
// N4
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
if (FullNeighbors)
{
// N8
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
}
}
if (dim == 3)
{
// N6
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
if (FullNeighbors)
{
// N26
// Middle Slice
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2];
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
// BackSlice (Diagonal)
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
// BackSlice (Non-Diag)
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2] - neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
// FrontSlice (Diagonal)
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
// FrontSlice(Non-Diag)
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] - neighborDistance;
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] + neighborDistance;
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0];
NeighborCoord[1] = Coord[1] + neighborDistance;
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
NeighborCoord[0] = Coord[0] - neighborDistance;
NeighborCoord[1] = Coord[1];
NeighborCoord[2] = Coord[2] + neighborDistance;
if (CoordIsInBounds(NeighborCoord))
nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]);
}
}
return nodeList;
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::SetStartIndex(
const typename TInputImageType::IndexType &StartIndex)
{
for (unsigned int i = 0; i < TInputImageType::ImageDimension; ++i)
{
m_StartIndex[i] = StartIndex[i];
}
m_Graph_StartNode = CoordToNode(m_StartIndex);
// MITK_INFO << "StartIndex = " << StartIndex;
// MITK_INFO << "StartNode = " << m_Graph_StartNode;
m_Initialized = false;
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::SetEndIndex(
const typename TInputImageType::IndexType &EndIndex)
{
for (unsigned int i = 0; i < TInputImageType::ImageDimension; ++i)
{
m_EndIndex[i] = EndIndex[i];
}
m_Graph_EndNode = CoordToNode(m_EndIndex);
// MITK_INFO << "EndNode = " << m_Graph_EndNode;
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::AddEndIndex(
const typename TInputImageType::IndexType &index)
{
// ONLY FOR MULTIPLE END POINTS SEARCH
IndexType newEndIndex;
for (unsigned int i = 0; i < TInputImageType::ImageDimension; ++i)
{
newEndIndex[i] = index[i];
}
m_endPoints.push_back(newEndIndex);
SetEndIndex(m_endPoints[0]);
multipleEndPoints = true;
}
template <class TInputImageType, class TOutputImageType>
inline double ShortestPathImageFilter<TInputImageType, TOutputImageType>::getEstimatedCostsToTarget(
const typename TInputImageType::IndexType &a)
{
// Returns the minimal possible costs for a path from "a" to targetnode.
itk::Vector<float, 3> v;
v[0] = m_EndIndex[0] - a[0];
v[1] = m_EndIndex[1] - a[1];
v[2] = m_EndIndex[2] - a[2];
return m_CostFunction->GetMinCost() * v.GetNorm();
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::InitGraph()
{
if (!m_Initialized)
{
// Clean up previous stuff
CleanUp();
// Calc Number of nodes
auto imageDimensions = TInputImageType::ImageDimension;
const InputImageSizeType &size = this->GetInput()->GetRequestedRegion().GetSize();
m_Graph_NumberOfNodes = 1;
for (NodeNumType i = 0; i < imageDimensions; ++i)
m_Graph_NumberOfNodes = m_Graph_NumberOfNodes * size[i];
// Initialize mainNodeList with that number
m_Nodes = new ShortestPathNode[m_Graph_NumberOfNodes];
// Initialize each node in nodelist
for (NodeNumType i = 0; i < m_Graph_NumberOfNodes; i++)
{
m_Nodes[i].distAndEst = -1;
m_Nodes[i].distance = -1;
m_Nodes[i].prevNode = -1;
m_Nodes[i].mainListIndex = i;
m_Nodes[i].closed = false;
}
m_Initialized = true;
}
// In the beginning, the Startnode needs a distance of 0
m_Nodes[m_Graph_StartNode].distance = 0;
m_Nodes[m_Graph_StartNode].distAndEst = 0;
- // initalize cost function
+ // initialize cost function
m_CostFunction->Initialize();
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::StartShortestPathSearch()
{
// Setup Timer
clock_t startAll = clock();
clock_t stopAll = clock();
// init variables
double durationAll = 0;
bool timeout = false;
NodeNumType mainNodeListIndex = 0;
DistanceType curNodeDistance = 0;
// Create Multimap (tree structure for fast searching)
std::multimap<double, ShortestPathNode *> myMap;
std::pair<std::multimap<double, ShortestPathNode *>::iterator, std::multimap<double, ShortestPathNode *>::iterator>
ret;
std::multimap<double, ShortestPathNode *>::iterator it;
// At first, only startNote is discovered.
myMap.insert(
std::pair<double, ShortestPathNode *>(m_Nodes[m_Graph_StartNode].distAndEst, &m_Nodes[m_Graph_StartNode]));
// While there are discovered Nodes, pick the one with lowest distance,
// update its neighbors and eventually delete it from the discovered Nodes list.
while (!myMap.empty())
{
// Get element with lowest score
mainNodeListIndex = myMap.begin()->second->mainListIndex;
curNodeDistance = myMap.begin()->second->distance;
myMap.begin()->second->closed = true; // close it
// Debug:
// MITK_INFO << "INFO: size " << myMap.size();
/*
for (it = myMap.begin(); it != myMap.end(); ++it)
{
MITK_INFO << "(1) " << it->first << "|" << it->second->distAndEst << "|"<<it->second->mainListIndex;
}
*/
// Kicks out element with lowest score
myMap.erase(myMap.begin());
// if wanted, store vector order
if (m_StoreVectorOrder)
{
m_VectorOrder.push_back(mainNodeListIndex);
}
// Check neighbors
std::vector<ShortestPathNode *> neighborNodes = GetNeighbors(mainNodeListIndex, m_Graph_fullNeighbors);
for (NodeNumType i = 0; i < neighborNodes.size(); i++)
{
if (neighborNodes[i]->closed)
continue; // this nodes is already closed, go to next neighbor
IndexType coordCurNode = NodeToCoord(mainNodeListIndex);
IndexType coordNeighborNode = NodeToCoord(neighborNodes[i]->mainListIndex);
// calculate the new Distance to the current neighbor
double newDistance = curNodeDistance + (m_CostFunction->GetCost(coordCurNode, coordNeighborNode));
// if it is shorter than any yet known path to this neighbor, than the current path is better. Save that!
if ((newDistance < neighborNodes[i]->distance) || (neighborNodes[i]->distance == -1))
{
// if that neighbornode is not in discoverednodeList yet, Push it there and update
if (neighborNodes[i]->distance == -1)
{
neighborNodes[i]->distance = newDistance;
neighborNodes[i]->distAndEst = newDistance + getEstimatedCostsToTarget(coordNeighborNode);
neighborNodes[i]->prevNode = mainNodeListIndex;
myMap.insert(std::pair<double, ShortestPathNode *>(m_Nodes[neighborNodes[i]->mainListIndex].distAndEst,
&m_Nodes[neighborNodes[i]->mainListIndex]));
/*
MITK_INFO << "Inserted: " << m_Nodes[neighborNodes[i]->mainListIndex].distAndEst << "|" <<
m_Nodes[neighborNodes[i]->mainListIndex].mainListIndex;
MITK_INFO << "INFO: size " << myMap.size();
for (it = myMap.begin(); it != myMap.end(); ++it)
{
MITK_INFO << "(1) " << it->first << "|" << it->second->distAndEst << "|"<<it->second->mainListIndex;
}
*/
}
// or if is already in discoverednodelist, update
else
{
/*
it = myMap.find(neighborNodes[i]->distAndEst);
if (it == myMap.end() )
{
MITK_INFO << "Nothing!";
// look further
for (it = myMap.begin(); it != myMap.end(); ++it)
{
if ((*it).second->mainListIndex == lookForId)
{
MITK_INFO << "But it is there!!!";
MITK_INFO << "Searched for: " << lookFor << " but had: " << (*it).second->distAndEst;
}
}
}
*/
// 1st : find and delete old element
bool found = false;
ret = myMap.equal_range(neighborNodes[i]->distAndEst);
if ((ret.first == ret.second))
{
/*+++++++++++++ no exact match +++++++++++++*/
// MITK_INFO << "No exact match!"; // if this happens, you are screwed
/*
MITK_INFO << "Was looking for: " << lookFor << " ID: " << lookForId;
if (ret.first != myMap.end() )
{
it = ret.first;
MITK_INFO << "Found: " << it->first << " ID: " << it->second->mainListIndex;
++it;
MITK_INFO << "Found: " << it->first << " ID: " << it->second->mainListIndex;
--it;
--it;
MITK_INFO << "Found: " << it->first << " ID: " << it->second->mainListIndex;
}
// look if that ID is found in the map
for (it = myMap.begin(); it != myMap.end(); ++it)
{
if ((*it).second->mainListIndex == lookForId)
{
MITK_INFO << "But it is there!!!";
MITK_INFO << "Searched dist: " << lookFor << " found dist: " << (*it).second->distAndEst;
}
}
*/
}
else
{
for (it = ret.first; it != ret.second; ++it)
{
if (it->second->mainListIndex == neighborNodes[i]->mainListIndex)
{
found = true;
myMap.erase(it);
/*
MITK_INFO << "INFO: size " << myMap.size();
MITK_INFO << "Erase: " << it->first << "|" << it->second->mainListIndex;
MITK_INFO << "INFO: size " << myMap.size();
for (it = myMap.begin(); it != myMap.end(); ++it)
{
MITK_INFO << "(1) " << it->first << "|" << it->second->distAndEst << "|"<<it->second->mainListIndex;
}
*/
break;
}
}
}
if (!found)
{
// MITK_INFO << "Could not find it! :(";
continue;
}
// 2nd: update and insert new element
neighborNodes[i]->distance = newDistance;
neighborNodes[i]->distAndEst = newDistance + getEstimatedCostsToTarget(coordNeighborNode);
neighborNodes[i]->prevNode = mainNodeListIndex;
// myMap.insert( std::pair<double,ShortestPathNode*> (neighborNodes[i]->distAndEst, neighborNodes[i]));
myMap.insert(std::pair<double, ShortestPathNode *>(m_Nodes[neighborNodes[i]->mainListIndex].distAndEst,
&m_Nodes[neighborNodes[i]->mainListIndex]));
// MITK_INFO << "Re-Inserted: " << m_Nodes[neighborNodes[i]->mainListIndex].distAndEst << "|" <<
// m_Nodes[neighborNodes[i]->mainListIndex].mainListIndex;
// MITK_INFO << "INFO: size " << myMap.size();
/*for (it = myMap.begin(); it != myMap.end(); ++it)
{
MITK_INFO << "(1) " << it->first << "|" << it->second->distAndEst << "|"<<it->second->mainListIndex;
}*/
}
}
}
// finished with checking all neighbors.
// Check Timeout, if activated
if (m_ActivateTimeOut)
{
stopAll = clock();
durationAll = (double)(stopAll - startAll) / CLOCKS_PER_SEC;
if (durationAll >= 30)
{
// MITK_INFO << "TIMEOUT!! Search took over 30 seconds";
timeout = true;
}
}
// Check end criteria:
// For multiple points
if (multipleEndPoints)
{
// super slow, make it faster
for (unsigned int i = 0; i < m_endPoints.size(); i++)
{
if (CoordToNode(m_endPoints[i]) == mainNodeListIndex)
{
m_endPointsClosed.push_back(NodeToCoord(mainNodeListIndex));
m_endPoints.erase(m_endPoints.begin() + i);
if (m_endPoints.empty())
{
// Finished! break
return;
}
if (m_Graph_EndNode == mainNodeListIndex)
{
// set new end
SetEndIndex(m_endPoints[0]);
}
}
}
}
// if single end point, then end, if this one is reached or timeout happened.
else if ((mainNodeListIndex == m_Graph_EndNode || timeout) && !m_CalcAllDistances)
{
/*if (m_StoreVectorOrder)
MITK_INFO << "Number of Nodes checked: " << m_VectorOrder.size() ;*/
return;
}
}
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::MakeOutputs()
{
// MITK_INFO << "Make Output";
if (m_MakeOutputImage)
{
OutputImagePointer output0 = this->GetOutput(0);
output0->SetRegions(this->GetInput()->GetLargestPossibleRegion());
output0->Allocate();
OutputImageIteratorType shortestPathImageIt(output0, output0->GetRequestedRegion());
// Create ShortestPathImage (Output 0)
for (shortestPathImageIt.GoToBegin(); !shortestPathImageIt.IsAtEnd(); ++shortestPathImageIt)
{
- // First intialize with background color
+ // First initialize with background color
shortestPathImageIt.Set(BACKGROUND);
}
if (!multipleEndPoints) // Only one path was calculated
{
for (unsigned int i = 0; i < m_VectorPath.size(); i++)
{
shortestPathImageIt.SetIndex(m_VectorPath[i]);
shortestPathImageIt.Set(FOREGROUND);
}
}
- else // multiple pathes has been calculated, draw all
+ else // multiple paths has been calculated, draw all
{
for (unsigned int i = 0; i < m_MultipleVectorPaths.size(); i++)
{
for (unsigned int j = 0; j < m_MultipleVectorPaths[i].size(); j++)
{
shortestPathImageIt.SetIndex(m_MultipleVectorPaths[i][j]);
shortestPathImageIt.Set(FOREGROUND);
}
}
}
}
}
template <class TInputImageType, class TOutputImageType>
typename ShortestPathImageFilter<TInputImageType, TOutputImageType>::OutputImagePointer
ShortestPathImageFilter<TInputImageType, TOutputImageType>::GetVectorOrderImage()
{
// Create Vector Order Image
// Return it
OutputImagePointer image = OutputImageType::New();
image->SetRegions(this->GetInput()->GetLargestPossibleRegion());
image->Allocate();
OutputImageIteratorType vectorOrderImageIt(image, image->GetRequestedRegion());
// MITK_INFO << "GetVectorOrderImage";
for (vectorOrderImageIt.GoToBegin(); !vectorOrderImageIt.IsAtEnd(); ++vectorOrderImageIt)
{
- // First intialize with background color
+ // First initialize with background color
vectorOrderImageIt.Value() = BACKGROUND;
}
for (int i = 0; i < m_VectorOrder.size(); i++)
{
vectorOrderImageIt.SetIndex(NodeToCoord(m_VectorOrder[i]));
vectorOrderImageIt.Set(BACKGROUND + i);
}
return image;
}
template <class TInputImageType, class TOutputImageType>
typename ShortestPathImageFilter<TInputImageType, TOutputImageType>::OutputImagePointer
ShortestPathImageFilter<TInputImageType, TOutputImageType>::GetDistanceImage()
{
// Create Distance Image
// Return it
OutputImagePointer image = OutputImageType::New();
image->SetRegions(this->GetInput()->GetLargestPossibleRegion());
image->Allocate();
;
OutputImageIteratorType distanceImageIt(image, image->GetRequestedRegion());
// Create Distance Image (Output 1)
NodeNumType myNodeNum;
for (distanceImageIt.GoToBegin(); !distanceImageIt.IsAtEnd(); ++distanceImageIt)
{
IndexType index = distanceImageIt.GetIndex();
myNodeNum = CoordToNode(index);
double newVal = m_Nodes[myNodeNum].distance;
distanceImageIt.Set(newVal);
}
}
template <class TInputImageType, class TOutputImageType>
std::vector<typename ShortestPathImageFilter<TInputImageType, TOutputImageType>::IndexType>
ShortestPathImageFilter<TInputImageType, TOutputImageType>::GetVectorPath()
{
return m_VectorPath;
}
template <class TInputImageType, class TOutputImageType>
std::vector<std::vector<typename ShortestPathImageFilter<TInputImageType, TOutputImageType>::IndexType>>
ShortestPathImageFilter<TInputImageType, TOutputImageType>::GetMultipleVectorPaths()
{
return m_MultipleVectorPaths;
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::MakeShortestPathVector()
{
// MITK_INFO << "Make ShortestPath Vec";
if (m_useCostFunction == false)
{
m_VectorPath.push_back(NodeToCoord(m_Graph_StartNode));
m_VectorPath.push_back(NodeToCoord(m_Graph_EndNode));
return;
}
// single end point
if (!multipleEndPoints)
{
// fill m_VectorPath with the Shortest Path
m_VectorPath.clear();
// Go backwards from endnote to startnode
NodeNumType prevNode = m_Graph_EndNode;
while (prevNode != m_Graph_StartNode)
{
m_VectorPath.push_back(NodeToCoord(prevNode));
prevNode = m_Nodes[prevNode].prevNode;
}
m_VectorPath.push_back(NodeToCoord(prevNode));
// reverse it
std::reverse(m_VectorPath.begin(), m_VectorPath.end());
}
- // Multiple end end points and pathes
+ // Multiple end end points and paths
else
{
for (unsigned int i = 0; i < m_endPointsClosed.size(); i++)
{
m_VectorPath.clear();
// Go backwards from endnote to startnode
NodeNumType prevNode = CoordToNode(m_endPointsClosed[i]);
while (prevNode != m_Graph_StartNode)
{
m_VectorPath.push_back(NodeToCoord(prevNode));
prevNode = m_Nodes[prevNode].prevNode;
}
m_VectorPath.push_back(NodeToCoord(prevNode));
// reverse it
std::reverse(m_VectorPath.begin(), m_VectorPath.end());
// push_back
m_MultipleVectorPaths.push_back(m_VectorPath);
}
}
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::CleanUp()
{
m_VectorOrder.clear();
m_VectorPath.clear();
// TODO: if multiple Path, clear all multiple Paths
if (m_Nodes)
delete[] m_Nodes;
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::GenerateData()
{
// Build Graph
InitGraph();
- // Calc Shortest Parth
+ // Calc Shortest Path
StartShortestPathSearch();
// Fill Shortest Path
MakeShortestPathVector();
// Make Outputs
MakeOutputs();
}
template <class TInputImageType, class TOutputImageType>
void ShortestPathImageFilter<TInputImageType, TOutputImageType>::PrintSelf(std::ostream &os, Indent indent) const
{
Superclass::PrintSelf(os, indent);
}
} /* end namespace itk */
#endif // __itkShortestPathImageFilter_txx
diff --git a/Modules/GraphAlgorithms/itkShortestPathNode.h b/Modules/GraphAlgorithms/itkShortestPathNode.h
index 181444788d..07de4b2f78 100644
--- a/Modules/GraphAlgorithms/itkShortestPathNode.h
+++ b/Modules/GraphAlgorithms/itkShortestPathNode.h
@@ -1,37 +1,37 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 __itkShortestPathNode_h_
#define __itkShortestPathNode_h_
#include "MitkGraphAlgorithmsExports.h"
namespace itk
{
typedef double DistanceType; // Type to declare the costs
typedef unsigned int
NodeNumType; // Type for Node Numeration: unsignend int for up to 4.2 billion pixel in 32Bit system
class MITKGRAPHALGORITHMS_EXPORT ShortestPathNode
{
public:
DistanceType distance; // minimal costs from StartPoint to this pixel
- DistanceType distAndEst; // Distance+Estimated Distnace to target
+ DistanceType distAndEst; // Distance+Estimated Distance to target
NodeNumType prevNode; // previous node. Important to find the Shortest Path
NodeNumType mainListIndex; // Indexnumber of this node in m_Nodes
bool closed; // determines if this node is closes, so its optimal path to startNode is known
};
// bool operator<(const ShortestPathNode &a) const;
// bool operator==(const ShortestPathNode &a) const;
}
#endif
diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp
index 14922b27b8..83891633bf 100644
--- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp
+++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp
@@ -1,477 +1,477 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkExtractDirectedPlaneImageFilter.h"
#include "mitkAbstractTransformGeometry.h"
//#include "mitkImageMapperGL2D.h"
#include "vtkMitkThickSlicesFilter.h"
#include <mitkDataNode.h>
#include <mitkProperties.h>
#include <mitkResliceMethodProperty.h>
#include <vtkGeneralTransform.h>
#include <vtkImageChangeInformation.h>
#include <vtkImageData.h>
#include <vtkPoints.h>
#include <vtkSmartPointer.h>
#include <vtkTransform.h>
#include <vtkTransform.h>
mitk::ExtractDirectedPlaneImageFilter::ExtractDirectedPlaneImageFilter() : m_WorldGeometry(nullptr)
{
MITK_WARN << "Class ExtractDirectedPlaneImageFilter is deprecated! Use ExtractSliceFilter instead.";
m_Reslicer = vtkImageReslice::New();
m_TargetTimestep = 0;
m_InPlaneResampleExtentByGeometry = true;
m_ResliceInterpolationProperty = nullptr; // VtkResliceInterpolationProperty::New(); //TODO initial with value
m_ThickSlicesMode = 0;
m_ThickSlicesNum = 1;
}
mitk::ExtractDirectedPlaneImageFilter::~ExtractDirectedPlaneImageFilter()
{
if (m_ResliceInterpolationProperty != nullptr)
m_ResliceInterpolationProperty->Delete();
m_Reslicer->Delete();
}
void mitk::ExtractDirectedPlaneImageFilter::GenerateData()
{
// A world geometry must be set...
if (m_WorldGeometry == nullptr)
{
itkWarningMacro(<< "No world geometry has been set. Returning.");
return;
}
auto *input = dynamic_cast<ImageToImageFilter::InputImageType *>(this->GetInput());
input->Update();
if (input == nullptr)
{
itkWarningMacro(<< "No input set.");
return;
}
const TimeGeometry *inputTimeGeometry = input->GetTimeGeometry();
if ((inputTimeGeometry == nullptr) || (inputTimeGeometry->CountTimeSteps() == 0))
{
itkWarningMacro(<< "Error reading input image geometry.");
return;
}
// Get the target timestep; if none is set, use the lowest given.
unsigned int timestep = m_TargetTimestep;
if (inputTimeGeometry->IsValidTimeStep(timestep) == false)
{
itkWarningMacro(<< "This is not a valid timestep: " << timestep);
return;
}
// check if there is something to display.
if (!input->IsVolumeSet(timestep))
{
itkWarningMacro(<< "No volume data existent at given timestep " << timestep);
return;
}
Image::RegionType requestedRegion = input->GetLargestPossibleRegion();
requestedRegion.SetIndex(3, timestep);
requestedRegion.SetSize(3, 1);
requestedRegion.SetSize(4, 1);
input->SetRequestedRegion(&requestedRegion);
input->Update();
vtkImageData *inputData = input->GetVtkImageData(timestep);
if (inputData == nullptr)
{
itkWarningMacro(<< "Could not extract vtk image data for given timestep" << timestep);
return;
}
double spacing[3];
inputData->GetSpacing(spacing);
// how big the area is in physical coordinates: widthInMM x heightInMM pixels
mitk::ScalarType widthInMM, heightInMM;
// where we want to sample
Point3D origin;
Vector3D right, bottom, normal;
Vector3D rightInIndex, bottomInIndex;
assert(input->GetTimeGeometry() == inputTimeGeometry);
// take transform of input image into account
BaseGeometry *inputGeometry = inputTimeGeometry->GetGeometryForTimeStep(timestep);
if (inputGeometry == nullptr)
{
itkWarningMacro(<< "There is no Geometry3D at given timestep " << timestep);
return;
}
ScalarType mmPerPixel[2];
// Bounds information for reslicing (only required if reference geometry
// is present)
double bounds[6];
bool boundsInitialized = false;
for (auto &bound : bounds)
{
bound = 0.0;
}
Vector2D extent;
extent.Fill(0.0);
// Do we have a simple PlaneGeometry?
if (dynamic_cast<const PlaneGeometry *>(m_WorldGeometry) != nullptr &&
dynamic_cast<const AbstractTransformGeometry *>(m_WorldGeometry) == nullptr)
{
const auto *planeGeometry = static_cast<const PlaneGeometry *>(m_WorldGeometry);
origin = planeGeometry->GetOrigin();
right = planeGeometry->GetAxisVector(0);
bottom = planeGeometry->GetAxisVector(1);
normal = planeGeometry->GetNormal();
if (m_InPlaneResampleExtentByGeometry)
{
// Resampling grid corresponds to the current world geometry. This
// means that the spacing of the output 2D image depends on the
// currently selected world geometry, and *not* on the image itself.
extent[0] = m_WorldGeometry->GetExtent(0);
extent[1] = m_WorldGeometry->GetExtent(1);
}
else
{
// Resampling grid corresponds to the input geometry. This means that
// the spacing of the output 2D image is directly derived from the
// associated input image, regardless of the currently selected world
// geometry.
inputGeometry->WorldToIndex(right, rightInIndex);
inputGeometry->WorldToIndex(bottom, bottomInIndex);
extent[0] = rightInIndex.GetNorm();
extent[1] = bottomInIndex.GetNorm();
}
// Get the extent of the current world geometry and calculate resampling
// spacing therefrom.
widthInMM = m_WorldGeometry->GetExtentInMM(0);
heightInMM = m_WorldGeometry->GetExtentInMM(1);
mmPerPixel[0] = widthInMM / extent[0];
mmPerPixel[1] = heightInMM / extent[1];
right.Normalize();
bottom.Normalize();
normal.Normalize();
// origin += right * ( mmPerPixel[0] * 0.5 );
// origin += bottom * ( mmPerPixel[1] * 0.5 );
// widthInMM -= mmPerPixel[0];
// heightInMM -= mmPerPixel[1];
// Use inverse transform of the input geometry for reslicing the 3D image
m_Reslicer->SetResliceTransform(inputGeometry->GetVtkTransform()->GetLinearInverse());
// Set background level to TRANSLUCENT (see PlaneGeometryDataVtkMapper3D)
m_Reslicer->SetBackgroundLevel(-32768);
// Check if a reference geometry does exist (as would usually be the case for
// PlaneGeometry).
// Note: this is currently not strictly required, but could facilitate
// correct plane clipping.
if (m_WorldGeometry->GetReferenceGeometry())
{
// 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.
boundsInitialized =
this->CalculateClippedPlaneBounds(m_WorldGeometry->GetReferenceGeometry(), planeGeometry, bounds);
}
}
// Do we have an AbstractTransformGeometry?
else if (dynamic_cast<const AbstractTransformGeometry *>(m_WorldGeometry))
{
const auto *abstractGeometry =
dynamic_cast<const AbstractTransformGeometry *>(m_WorldGeometry);
extent[0] = abstractGeometry->GetParametricExtent(0);
extent[1] = abstractGeometry->GetParametricExtent(1);
widthInMM = abstractGeometry->GetParametricExtentInMM(0);
heightInMM = abstractGeometry->GetParametricExtentInMM(1);
mmPerPixel[0] = widthInMM / extent[0];
mmPerPixel[1] = heightInMM / extent[1];
origin = abstractGeometry->GetPlane()->GetOrigin();
right = abstractGeometry->GetPlane()->GetAxisVector(0);
right.Normalize();
bottom = abstractGeometry->GetPlane()->GetAxisVector(1);
bottom.Normalize();
normal = abstractGeometry->GetPlane()->GetNormal();
normal.Normalize();
// Use a combination of the InputGeometry *and* the possible non-rigid
// AbstractTransformGeometry for reslicing the 3D Image
vtkGeneralTransform *composedResliceTransform = vtkGeneralTransform::New();
composedResliceTransform->Identity();
composedResliceTransform->Concatenate(inputGeometry->GetVtkTransform()->GetLinearInverse());
composedResliceTransform->Concatenate(abstractGeometry->GetVtkAbstractTransform());
m_Reslicer->SetResliceTransform(composedResliceTransform);
// Set background level to BLACK instead of translucent, to avoid
// boundary artifacts (see PlaneGeometryDataVtkMapper3D)
m_Reslicer->SetBackgroundLevel(-1023);
composedResliceTransform->Delete();
}
else
{
itkWarningMacro(<< "World Geometry has to be a PlaneGeometry or an AbstractTransformGeometry.");
return;
}
// Make sure that the image to be resliced has a certain minimum size.
if ((extent[0] <= 2) && (extent[1] <= 2))
{
itkWarningMacro(<< "Image is too small to be resliced...");
return;
}
vtkSmartPointer<vtkImageChangeInformation> unitSpacingImageFilter = vtkImageChangeInformation::New();
unitSpacingImageFilter->SetOutputSpacing(1.0, 1.0, 1.0);
unitSpacingImageFilter->SetInputData(inputData);
m_Reslicer->SetInputConnection(unitSpacingImageFilter->GetOutputPort());
// m_Reslicer->SetInput( inputData );
m_Reslicer->SetOutputDimensionality(2);
m_Reslicer->SetOutputOrigin(0.0, 0.0, 0.0);
Vector2D pixelsPerMM;
pixelsPerMM[0] = 1.0 / mmPerPixel[0];
pixelsPerMM[1] = 1.0 / mmPerPixel[1];
- // calulate the originArray and the orientations for the reslice-filter
+ // calculate the originArray and the orientations for the reslice-filter
double originArray[3];
itk2vtk(origin, originArray);
m_Reslicer->SetResliceAxesOrigin(originArray);
double cosines[9];
// direction of the X-axis of the sampled result
vnl2vtk(right.GetVnlVector(), cosines);
// direction of the Y-axis of the sampled result
vnl2vtk(bottom.GetVnlVector(), cosines + 3);
// normal of the plane
vnl2vtk(normal.GetVnlVector(), cosines + 6);
m_Reslicer->SetResliceAxesDirectionCosines(cosines);
int xMin, xMax, yMin, yMax;
if (boundsInitialized)
{
xMin = static_cast<int>(bounds[0] / mmPerPixel[0]); //+ 0.5 );
xMax = static_cast<int>(bounds[1] / mmPerPixel[0]); //+ 0.5 );
yMin = static_cast<int>(bounds[2] / mmPerPixel[1]); //+ 0.5);
yMax = static_cast<int>(bounds[3] / mmPerPixel[1]); //+ 0.5 );
}
else
{
// If no reference geometry is available, we also don't know about the
// maximum plane size; so the overlap is just ignored
xMin = yMin = 0;
xMax = static_cast<int>(extent[0] - pixelsPerMM[0]); //+ 0.5 );
yMax = static_cast<int>(extent[1] - pixelsPerMM[1]); //+ 0.5 );
}
m_Reslicer->SetOutputSpacing(mmPerPixel[0], mmPerPixel[1], 1.0);
// xMax and yMax are meant exclusive until now, whereas
// SetOutputExtent wants an inclusive bound. Thus, we need
// to subtract 1.
m_Reslicer->SetOutputExtent(xMin, xMax - 1, yMin, yMax - 1, 0, 1);
// 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.
m_Reslicer->Modified();
m_Reslicer->ReleaseDataFlagOn();
m_Reslicer->Update();
// 1. Check the result
vtkImageData *reslicedImage = m_Reslicer->GetOutput();
if ((reslicedImage == nullptr) || (reslicedImage->GetDataDimension() < 1))
{
itkWarningMacro(<< "Reslicer returned empty image");
return;
}
unsigned int dimensions[2];
dimensions[0] = (unsigned int)extent[0];
dimensions[1] = (unsigned int)extent[1];
Vector3D spacingVector;
FillVector3D(spacingVector, mmPerPixel[0], mmPerPixel[1], 1.0);
mitk::Image::Pointer resultImage = this->GetOutput();
resultImage->Initialize(input->GetPixelType(), 2, dimensions);
resultImage->SetSpacing(spacingVector);
}
void mitk::ExtractDirectedPlaneImageFilter::GenerateOutputInformation()
{
Superclass::GenerateOutputInformation();
}
bool mitk::ExtractDirectedPlaneImageFilter::CalculateClippedPlaneBounds(const BaseGeometry *boundingGeometry,
const PlaneGeometry *planeGeometry,
double *bounds)
{
// Clip the plane with the bounding geometry. To do so, the corner points
// of the bounding box are transformed by the inverse transformation
// matrix, and the transformed bounding box edges derived therefrom are
// clipped with the plane z=0. The resulting min/max values are taken as
// bounds for the image reslicer.
const BoundingBox *boundingBox = boundingGeometry->GetBoundingBox();
BoundingBox::PointType bbMin = boundingBox->GetMinimum();
BoundingBox::PointType bbMax = boundingBox->GetMaximum();
vtkPoints *points = vtkPoints::New();
if (boundingGeometry->GetImageGeometry())
{
points->InsertPoint(0, bbMin[0] - 0.5, bbMin[1] - 0.5, bbMin[2] - 0.5);
points->InsertPoint(1, bbMin[0] - 0.5, bbMin[1] - 0.5, bbMax[2] - 0.5);
points->InsertPoint(2, bbMin[0] - 0.5, bbMax[1] - 0.5, bbMax[2] - 0.5);
points->InsertPoint(3, bbMin[0] - 0.5, bbMax[1] - 0.5, bbMin[2] - 0.5);
points->InsertPoint(4, bbMax[0] - 0.5, bbMin[1] - 0.5, bbMin[2] - 0.5);
points->InsertPoint(5, bbMax[0] - 0.5, bbMin[1] - 0.5, bbMax[2] - 0.5);
points->InsertPoint(6, bbMax[0] - 0.5, bbMax[1] - 0.5, bbMax[2] - 0.5);
points->InsertPoint(7, bbMax[0] - 0.5, bbMax[1] - 0.5, bbMin[2] - 0.5);
}
else
{
points->InsertPoint(0, bbMin[0], bbMin[1], bbMin[2]);
points->InsertPoint(1, bbMin[0], bbMin[1], bbMax[2]);
points->InsertPoint(2, bbMin[0], bbMax[1], bbMax[2]);
points->InsertPoint(3, bbMin[0], bbMax[1], bbMin[2]);
points->InsertPoint(4, bbMax[0], bbMin[1], bbMin[2]);
points->InsertPoint(5, bbMax[0], bbMin[1], bbMax[2]);
points->InsertPoint(6, bbMax[0], bbMax[1], bbMax[2]);
points->InsertPoint(7, bbMax[0], bbMax[1], bbMin[2]);
}
vtkPoints *newPoints = vtkPoints::New();
vtkTransform *transform = vtkTransform::New();
transform->Identity();
transform->Concatenate(planeGeometry->GetVtkTransform()->GetLinearInverse());
transform->Concatenate(boundingGeometry->GetVtkTransform());
transform->TransformPoints(points, newPoints);
transform->Delete();
bounds[0] = bounds[2] = 10000000.0;
bounds[1] = bounds[3] = -10000000.0;
bounds[4] = bounds[5] = 0.0;
this->LineIntersectZero(newPoints, 0, 1, bounds);
this->LineIntersectZero(newPoints, 1, 2, bounds);
this->LineIntersectZero(newPoints, 2, 3, bounds);
this->LineIntersectZero(newPoints, 3, 0, bounds);
this->LineIntersectZero(newPoints, 0, 4, bounds);
this->LineIntersectZero(newPoints, 1, 5, bounds);
this->LineIntersectZero(newPoints, 2, 6, bounds);
this->LineIntersectZero(newPoints, 3, 7, bounds);
this->LineIntersectZero(newPoints, 4, 5, bounds);
this->LineIntersectZero(newPoints, 5, 6, bounds);
this->LineIntersectZero(newPoints, 6, 7, bounds);
this->LineIntersectZero(newPoints, 7, 4, bounds);
// clean up vtk data
points->Delete();
newPoints->Delete();
if ((bounds[0] > 9999999.0) || (bounds[2] > 9999999.0) || (bounds[1] < -9999999.0) || (bounds[3] < -9999999.0))
{
return false;
}
else
{
// The resulting bounds must be adjusted by the plane spacing, since we
// we have so far dealt with index coordinates
const mitk::Vector3D planeSpacing = planeGeometry->GetSpacing();
bounds[0] *= planeSpacing[0];
bounds[1] *= planeSpacing[0];
bounds[2] *= planeSpacing[1];
bounds[3] *= planeSpacing[1];
bounds[4] *= planeSpacing[2];
bounds[5] *= planeSpacing[2];
return true;
}
}
bool mitk::ExtractDirectedPlaneImageFilter::LineIntersectZero(vtkPoints *points, int p1, int p2, double *bounds)
{
double point1[3];
double point2[3];
points->GetPoint(p1, point1);
points->GetPoint(p2, point2);
if ((point1[2] * point2[2] <= 0.0) && (point1[2] != point2[2]))
{
double x, y;
x = (point1[0] * point2[2] - point1[2] * point2[0]) / (point2[2] - point1[2]);
y = (point1[1] * point2[2] - point1[2] * point2[1]) / (point2[2] - point1[2]);
if (x < bounds[0])
{
bounds[0] = x;
}
if (x > bounds[1])
{
bounds[1] = x;
}
if (y < bounds[2])
{
bounds[2] = y;
}
if (y > bounds[3])
{
bounds[3] = y;
}
bounds[4] = bounds[5] = 0.0;
return true;
}
return false;
}
diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp
index 6070306cfa..5d291e1da2 100644
--- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp
+++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp
@@ -1,284 +1,284 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkExtractDirectedPlaneImageFilterNew.h"
#include "itkImageRegionIterator.h"
#include "mitkImageCast.h"
#include "mitkImageTimeSelector.h"
#include <mitkImageAccessByItk.h>
mitk::ExtractDirectedPlaneImageFilterNew::ExtractDirectedPlaneImageFilterNew()
: m_CurrentWorldPlaneGeometry(nullptr), m_ImageGeometry(nullptr), m_ActualInputTimestep(0)
{
MITK_WARN << "Class ExtractDirectedPlaneImageFilterNew is deprecated! Use ExtractSliceFilter instead.";
}
mitk::ExtractDirectedPlaneImageFilterNew::~ExtractDirectedPlaneImageFilterNew()
{
}
void mitk::ExtractDirectedPlaneImageFilterNew::GenerateData()
{
mitk::Image::ConstPointer inputImage = ImageToImageFilter::GetInput(0);
if (!inputImage)
{
MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew: No input available. Please set the input!" << std::endl;
itkExceptionMacro("mitk::ExtractDirectedPlaneImageFilterNew: No input available. Please set the input!");
return;
}
m_ImageGeometry = inputImage->GetGeometry();
// If no timestep is set, the lowest given will be selected
// const mitk::TimeGeometry* inputTimeGeometry = this->GetInput()->GetTimeGeometry();
if (inputImage->GetDimension() > 4 || inputImage->GetDimension() < 2)
{
MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew:GenerateData works only with 3D and 3D+t images, sorry."
<< std::endl;
itkExceptionMacro("mitk::ExtractDirectedPlaneImageFilterNew works only with 3D and 3D+t images, sorry.");
return;
}
else if (inputImage->GetDimension() == 4)
{
mitk::ImageTimeSelector::Pointer timeselector = mitk::ImageTimeSelector::New();
timeselector->SetInput(inputImage);
timeselector->SetTimeNr(m_ActualInputTimestep);
timeselector->UpdateLargestPossibleRegion();
inputImage = timeselector->GetOutput();
}
else if (inputImage->GetDimension() == 2)
{
mitk::Image::Pointer resultImage = ImageToImageFilter::GetOutput();
resultImage = const_cast<mitk::Image *>(inputImage.GetPointer());
ImageToImageFilter::SetNthOutput(0, resultImage);
return;
}
if (!m_CurrentWorldPlaneGeometry)
{
MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew::GenerateData has no CurrentWorldPlaneGeometry set"
<< std::endl;
return;
}
AccessFixedDimensionByItk(inputImage, ItkSliceExtraction, 3);
} // Generate Data
void mitk::ExtractDirectedPlaneImageFilterNew::GenerateOutputInformation()
{
Superclass::GenerateOutputInformation();
}
/*
* The desired slice is extracted by filling the image`s corresponding pixel values in an empty 2 dimensional itk::Image
* Therefor the itk image`s extent in pixel (in each direction) is doubled and its spacing (also in each direction) is
* divided by two
* (similar to the shannon theorem).
*/
template <typename TPixel, unsigned int VImageDimension>
void mitk::ExtractDirectedPlaneImageFilterNew::ItkSliceExtraction(const itk::Image<TPixel, VImageDimension> *inputImage)
{
typedef itk::Image<TPixel, VImageDimension> InputImageType;
typedef itk::Image<TPixel, VImageDimension - 1> SliceImageType;
typedef itk::ImageRegionConstIterator<SliceImageType> SliceIterator;
// Creating an itk::Image that represents the sampled slice
typename SliceImageType::Pointer resultSlice = SliceImageType::New();
typename SliceImageType::IndexType start;
start[0] = 0;
start[1] = 0;
Point3D origin = m_CurrentWorldPlaneGeometry->GetOrigin();
Vector3D right = m_CurrentWorldPlaneGeometry->GetAxisVector(0);
Vector3D bottom = m_CurrentWorldPlaneGeometry->GetAxisVector(1);
// Calculation the sample-spacing, i.e the half of the smallest spacing existing in the original image
Vector3D newPixelSpacing = m_ImageGeometry->GetSpacing();
float minSpacing = newPixelSpacing[0];
for (unsigned int i = 1; i < newPixelSpacing.Size(); i++)
{
if (newPixelSpacing[i] < minSpacing)
{
minSpacing = newPixelSpacing[i];
}
}
newPixelSpacing[0] = 0.5 * minSpacing;
newPixelSpacing[1] = 0.5 * minSpacing;
newPixelSpacing[2] = 0.5 * minSpacing;
float pixelSpacing[2];
pixelSpacing[0] = newPixelSpacing[0];
pixelSpacing[1] = newPixelSpacing[1];
// Calculating the size of the sampled slice
typename SliceImageType::SizeType size;
Vector2D extentInMM;
extentInMM[0] = m_CurrentWorldPlaneGeometry->GetExtentInMM(0);
extentInMM[1] = m_CurrentWorldPlaneGeometry->GetExtentInMM(1);
- // The maximum extent is the lenght of the diagonal of the considered plane
+ // The maximum extent is the length of the diagonal of the considered plane
double maxExtent = sqrt(extentInMM[0] * extentInMM[0] + extentInMM[1] * extentInMM[1]);
unsigned int xTranlation = (maxExtent - extentInMM[0]);
unsigned int yTranlation = (maxExtent - extentInMM[1]);
size[0] = (maxExtent + xTranlation) / newPixelSpacing[0];
size[1] = (maxExtent + yTranlation) / newPixelSpacing[1];
// Creating an ImageRegion Object
typename SliceImageType::RegionType region;
region.SetSize(size);
region.SetIndex(start);
// Defining the image`s extent and origin by passing the region to it and allocating memory for it
resultSlice->SetRegions(region);
resultSlice->SetSpacing(pixelSpacing);
resultSlice->Allocate();
/*
* Here we create an new geometry so that the transformations are calculated correctly (our resulting slice has a
* different bounding box and spacing)
* The original current worldgeometry must be cloned because we have to keep the directions of the axis vector which
* represents the rotation
*/
right.Normalize();
bottom.Normalize();
// Here we translate the origin to adapt the new geometry to the previous calculated extent
origin[0] -= xTranlation * right[0] + yTranlation * bottom[0];
origin[1] -= xTranlation * right[1] + yTranlation * bottom[1];
origin[2] -= xTranlation * right[2] + yTranlation * bottom[2];
// Putting it together for the new geometry
mitk::BaseGeometry::Pointer newSliceGeometryTest =
dynamic_cast<BaseGeometry *>(m_CurrentWorldPlaneGeometry->Clone().GetPointer());
newSliceGeometryTest->ChangeImageGeometryConsideringOriginOffset(true);
// Workaround because of BUG (#6505)
newSliceGeometryTest->GetIndexToWorldTransform()->SetMatrix(
m_CurrentWorldPlaneGeometry->GetIndexToWorldTransform()->GetMatrix());
// Workaround end
newSliceGeometryTest->SetOrigin(origin);
ScalarType bounds[6] = {0, static_cast<ScalarType>(size[0]), 0, static_cast<ScalarType>(size[1]), 0, 1};
newSliceGeometryTest->SetBounds(bounds);
newSliceGeometryTest->SetSpacing(newPixelSpacing);
newSliceGeometryTest->Modified();
// Workaround because of BUG (#6505)
itk::MatrixOffsetTransformBase<mitk::ScalarType, 3, 3>::MatrixType tempTransform =
newSliceGeometryTest->GetIndexToWorldTransform()->GetMatrix();
// Workaround end
/*
* Now we iterate over the recently created slice.
* For each slice - pixel we check whether there is an according
* pixel in the input - image which can be set in the slice.
* In this way a slice is sampled out of the input - image regrading to the given PlaneGeometry
*/
Point3D currentSliceIndexPointIn2D;
Point3D currentImageWorldPointIn3D;
typename InputImageType::IndexType inputIndex;
SliceIterator sliceIterator(resultSlice, resultSlice->GetLargestPossibleRegion());
sliceIterator.GoToBegin();
while (!sliceIterator.IsAtEnd())
{
/*
* Here we add 0.5 to to assure that the indices are correctly transformed.
* (Because of the 0.5er Bug)
*/
currentSliceIndexPointIn2D[0] = sliceIterator.GetIndex()[0] + 0.5;
currentSliceIndexPointIn2D[1] = sliceIterator.GetIndex()[1] + 0.5;
currentSliceIndexPointIn2D[2] = 0;
newSliceGeometryTest->IndexToWorld(currentSliceIndexPointIn2D, currentImageWorldPointIn3D);
m_ImageGeometry->WorldToIndex(currentImageWorldPointIn3D, inputIndex);
if (m_ImageGeometry->IsIndexInside(inputIndex))
{
resultSlice->SetPixel(sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex));
}
else
{
resultSlice->SetPixel(sliceIterator.GetIndex(), 0);
}
++sliceIterator;
}
Image::Pointer resultImage = ImageToImageFilter::GetOutput();
GrabItkImageMemory(resultSlice, resultImage, nullptr, false);
resultImage->SetClonedGeometry(newSliceGeometryTest);
// Workaround because of BUG (#6505)
resultImage->GetGeometry()->GetIndexToWorldTransform()->SetMatrix(tempTransform);
// Workaround end
}
///**TEST** May ba a little bit more efficient but doesn`t already work/
// right.Normalize();
// bottom.Normalize();
// Point3D currentImagePointIn3D = origin /*+ bottom*newPixelSpacing*/;
// unsigned int columns ( 0 );
/**ENDE**/
/****TEST***/
// SliceImageType::IndexType index = sliceIterator.GetIndex();
// if ( columns == (extentInPixel[0]) )
//{
// If we are at the end of a row, then we have to go to the beginning of the next row
// currentImagePointIn3D = origin;
// currentImagePointIn3D += newPixelSpacing[1]*bottom*index[1];
// columns = 0;
// m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex);
//}
// else
//{
////
// if ( columns != 0 )
//{
// currentImagePointIn3D += newPixelSpacing[0]*right;
//}
// m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex);
//}
// if ( m_ImageGeometry->IsIndexInside( inputIndex ))
//{
// resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) );
//}
// else if (currentImagePointIn3D == origin)
//{
// Point3D temp;
// temp[0] = bottom[0]*newPixelSpacing[0]*0.5;
// temp[1] = bottom[1]*newPixelSpacing[1]*0.5;
// temp[2] = bottom[2]*newPixelSpacing[2]*0.5;
// origin[0] += temp[0];
// origin[1] += temp[1];
// origin[2] += temp[2];
// currentImagePointIn3D = origin;
// m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex);
// if ( m_ImageGeometry->IsIndexInside( inputIndex ))
//{
// resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) );
//}
//}
/****TEST ENDE****/
diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h
index 4c6e40c549..6897f35cbf 100644
--- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h
+++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h
@@ -1,98 +1,95 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkExtractDirectedPlaneImageFilterNew_h
#define mitkExtractDirectedPlaneImageFilterNew_h
#include "itkImage.h"
#include "mitkITKImageImport.h"
#include "mitkImageToImageFilter.h"
#include <MitkImageExtractionExports.h>
namespace mitk
{
/**
\deprecated This class is deprecated. Use mitk::ExtractSliceFilter instead.
\sa ExtractSliceFilter
\brief A filter that can extract a 2D slice from a 3D or 4D image especially if the image`s axes are rotated
\sa ContourTool
\sa SegTool2D
\sa ExtractImageFilter
\sa OverwriteDirectedPlaneImageFilter
\ingroup Process
\ingroup Reliver
- There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref
- QmitkSegmentationTechnicalPage
-
This class takes an 3D or 4D mitk::Image as input and extracts a slice from it. If you work with a 4D image as input
you have to specify the
desired timestep at which the slice shall be extracted, otherwise the lowest given timestep is selected by default.
The special feature of this filter is, that the planes of the input image can be rotated in any way. To assure a
proper extraction you have to
set the currentWorldPlaneGeometry with you can obtain from the BaseRenderer, respectively the positionEvent send by
the renderer.
The output will not be set if there was a problem with the input image
$Author: fetzer $
*/
class MITKIMAGEEXTRACTION_EXPORT ExtractDirectedPlaneImageFilterNew : public ImageToImageFilter
{
public:
mitkClassMacro(ExtractDirectedPlaneImageFilterNew, ImageToImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
\brief Set macro for the current worldgeometry
\a Parameter The current wordgeometry that describes the position (rotation, translation)
of the plane (and therefore the slice to be extracted) in our 3D(+t) image
*/
itkSetMacro(CurrentWorldPlaneGeometry, BaseGeometry *);
/**
* \deprecatedSince{2014_10} Please use SetCurrentWorldPlaneGeometry
*/
DEPRECATED(void SetCurrentWorldGeometry2D(BaseGeometry *geo)) { SetCurrentWorldPlaneGeometry(geo); };
itkSetMacro(ImageGeometry, BaseGeometry *);
/**
\brief Set macro for the current timestep
\a Parameter The timestep of the image from which the slice shall be extracted
*/
itkSetMacro(ActualInputTimestep, int);
protected:
ExtractDirectedPlaneImageFilterNew();
~ExtractDirectedPlaneImageFilterNew() override;
void GenerateData() override;
void GenerateOutputInformation() override;
private:
const BaseGeometry *m_CurrentWorldPlaneGeometry;
const BaseGeometry *m_ImageGeometry;
int m_ActualInputTimestep;
template <typename TPixel, unsigned int VImageDimension>
void ItkSliceExtraction(const itk::Image<TPixel, VImageDimension> *inputImage);
};
} // namespace
#endif
diff --git a/Modules/ImageExtraction/mitkExtractImageFilter.cpp b/Modules/ImageExtraction/mitkExtractImageFilter.cpp
index 2a754f0045..19d3bbf43d 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;
}
AnatomicalPlane orientation = AnatomicalPlane::Axial;
switch (m_SliceDimension)
{
default:
case 2:
orientation = AnatomicalPlane::Axial;
break;
case 1:
orientation = AnatomicalPlane::Coronal;
break;
case 0:
orientation = AnatomicalPlane::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::DirectionCollapseStrategyEnum collapseStrategy;
switch (m_DirectionCollapseToStrategy)
{
case DIRECTIONCOLLAPSETOUNKOWN:
collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOUNKOWN;
break;
case DIRECTIONCOLLAPSETOIDENTITY:
collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOIDENTITY;
break;
case DIRECTIONCOLLAPSETOSUBMATRIX:
collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOSUBMATRIX;
break;
case DIRECTIONCOLLAPSETOGUESS:
default:
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
+ * Generate the information describing 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 = AnatomicalPlane::Axial;
tmpDimensions[0] = input->GetDimension(0);
tmpDimensions[1] = input->GetDimension(1);
break;
case 1:
// orientation = AnatomicalPlane::Coronal;
tmpDimensions[0] = input->GetDimension(0);
tmpDimensions[1] = input->GetDimension(2);
break;
case 0:
// orientation = AnatomicalPlane::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/ImageExtraction/mitkExtractImageFilter.h b/Modules/ImageExtraction/mitkExtractImageFilter.h
index adff52486c..c7ec1e3e2c 100644
--- a/Modules/ImageExtraction/mitkExtractImageFilter.h
+++ b/Modules/ImageExtraction/mitkExtractImageFilter.h
@@ -1,110 +1,107 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkExtractImageFilter_h
#define mitkExtractImageFilter_h
#include "mitkCommon.h"
#include "mitkImageToImageFilter.h"
#include <MitkImageExtractionExports.h>
#include "itkImage.h"
namespace mitk
{
/**
\deprecated This class is deprecated. Use mitk::ExtractSliceFilter instead.
\sa ExtractSliceFilter
\brief Extracts a 2D slice from a 3D image.
\sa SegTool2D
\ingroup Process
\ingroup ToolManagerEtAl
- There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref
- QmitkSegmentationTechnicalPage
-
This class takes a 3D mitk::Image as input and tries to extract one slice from it.
Two parameters determine which slice is extracted: the "slice dimension" is that one, which is constant for all
points in the plane, e.g. axial would mean 2.
The "slice index" is the slice index in the image direction you specified with "affected dimension". Indices count
from zero.
Output will not be set if there was a problem extracting the desired slice.
Last contributor: $Author$
*/
class MITKIMAGEEXTRACTION_EXPORT ExtractImageFilter : public ImageToImageFilter
{
public:
mitkClassMacro(ExtractImageFilter, ImageToImageFilter);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
\brief Which slice to extract (first one has index 0).
*/
itkSetMacro(SliceIndex, unsigned int);
itkGetConstMacro(SliceIndex, unsigned int);
/**
\brief The orientation of the slice to be extracted.
\a Parameter SliceDimension Number of the dimension which is constant for all pixels of the desired slice (e.g. 2
for axial)
*/
itkSetMacro(SliceDimension, unsigned int);
itkGetConstMacro(SliceDimension, unsigned int);
/**
\brief Time step of the image to be extracted.
*/
itkSetMacro(TimeStep, unsigned int);
itkGetConstMacro(TimeStep, unsigned int);
typedef enum DirectionCollapseStrategyEnum {
DIRECTIONCOLLAPSETOUNKOWN = 0,
DIRECTIONCOLLAPSETOIDENTITY = 1,
DIRECTIONCOLLAPSETOSUBMATRIX = 2,
DIRECTIONCOLLAPSETOGUESS = 3
} DIRECTIONCOLLAPSESTRATEGY;
/**
\brief Collapse strategy to be used.
*/
itkSetMacro(DirectionCollapseToStrategy, DIRECTIONCOLLAPSESTRATEGY);
itkGetConstMacro(DirectionCollapseToStrategy, DIRECTIONCOLLAPSESTRATEGY);
protected:
ExtractImageFilter(); // purposely hidden
~ExtractImageFilter() override;
void GenerateOutputInformation() override;
void GenerateInputRequestedRegion() override;
void GenerateData() override;
template <typename TPixel, unsigned int VImageDimension>
void ItkImageProcessing(const itk::Image<TPixel, VImageDimension> *image);
unsigned int m_SliceIndex;
unsigned int m_SliceDimension;
unsigned int m_TimeStep;
DIRECTIONCOLLAPSESTRATEGY m_DirectionCollapseToStrategy;
};
} // namespace
#endif
diff --git a/Modules/ImageStatistics/Testing/mitkImageStatisticsHotspotTest.cpp b/Modules/ImageStatistics/Testing/mitkImageStatisticsHotspotTest.cpp
index 3114c2b52c..33f285ece8 100644
--- a/Modules/ImageStatistics/Testing/mitkImageStatisticsHotspotTest.cpp
+++ b/Modules/ImageStatistics/Testing/mitkImageStatisticsHotspotTest.cpp
@@ -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.
============================================================================*/
#include "mitkImageStatisticsCalculator.h"
#include "itkMultiGaussianImageSource.h"
#include "mitkTestingMacros.h"
#include <mitkITKImageImport.h>
#include <itkImageRegionIterator.h>
#include <stdexcept>
#include <itkDOMNode.h>
#include <itkDOMReader.h>
#include <mitkHotspotMaskGenerator.h>
#include <mitkImageMaskGenerator.h>
#include <mitkIOUtil.h>
/**
\section hotspotCalculationTestCases Testcases
To see the different Hotspot-Testcases have a look at the \ref hotspottestdoc.
Note from an intensive session of checking the test results:
- itk::MultiGaussianImageSource needs a review
- the test idea is ok, but the combination of XML files for parameters and MultiGaussianImageSource has serious flaws
- the XML file should contain exactly the parameters that MultiGaussianImageSource requires
- in contrast, now the XML file mentions index coordinates for gaussian centers while the MultiGaussianImageSource expects world coordinates
- this requires a transformation (index * spacing assuming no rotation) that was actually broken until recently
*/
struct mitkImageStatisticsHotspotTestClass
{
/**
\brief Test parameters for one test case.
Describes all aspects of a single test case:
- parameters to generate a test image
- parameters of a ROI that describes where to calculate statistics
- expected statistics results
*/
struct Parameters
{
public:
// XML-Tag <testimage>
/** \brief XML-Tag "image-rows": size of x-dimension */
int m_ImageRows;
/** \brief XML-Tag "image-columns": size of y-dimension */
int m_ImageColumns;
/** \brief XML-Tag "image-slices": size of z-dimension */
int m_ImageSlices;
/** \brief XML-Tag "numberOfGaussians": number of used gauss-functions */
int m_NumberOfGaussian;
/** \brief XML-Tags "spacingX", "spacingY", "spacingZ": spacing of image in every direction */
double m_Spacing[3];
/** \brief XML-Tag "entireHotSpotInImage" */
unsigned int m_EntireHotspotInImage;
// XML-Tag <gaussian>
/**
\brief XML-Tag "centerIndexX: gaussian parameter
\warning This parameter READS the centerIndexX parameter from file and is THEN MISUSED to calculate some position in world coordinates, so we require double.
*/
std::vector<double> m_CenterX;
/** \brief XML-Tag "centerIndexY: gaussian parameter
\warning This parameter READS the centerIndexX parameter from file and is THEN MISUSED to calculate some position in world coordinates, so we require double.
*/
std::vector<double> m_CenterY;
/** \brief XML-Tag "centerIndexZ: gaussian parameter
\warning This parameter READS the centerIndexX parameter from file and is THEN MISUSED to calculate some position in world coordinates, so we require double.
*/
std::vector<double> m_CenterZ;
/** \brief XML-Tag "deviationX: gaussian parameter */
std::vector<double> m_SigmaX;
/** \brief XML-Tag "deviationY: gaussian parameter */
std::vector<double> m_SigmaY;
/** \brief XML-Tag "deviationZ: gaussian parameter */
std::vector<double> m_SigmaZ;
/** \brief XML-Tag "altitude: gaussian parameter */
std::vector<double> m_Altitude;
// XML-Tag <segmentation>
/** \brief XML-Tag "numberOfLabels": number of different labels which appear in the mask */
unsigned int m_NumberOfLabels;
/** \brief XML-Tag "hotspotRadiusInMM": radius of hotspot */
double m_HotspotRadiusInMM;
// XML-Tag <roi>
/** \brief XML-Tag "maximumSizeX": maximum position of ROI in x-dimension */
vnl_vector<int> m_MaxIndexX;
/** \brief XML-Tag "minimumSizeX": minimum position of ROI in x-dimension */
vnl_vector<int> m_MinIndexX;
/** \brief XML-Tag "maximumSizeX": maximum position of ROI in y-dimension */
vnl_vector<int> m_MaxIndexY;
/** \brief XML-Tag "minimumSizeX": minimum position of ROI in y-dimension */
vnl_vector<int> m_MinIndexY;
/** \brief XML-Tag "maximumSizeX": maximum position of ROI in z-dimension */
vnl_vector<int> m_MaxIndexZ;
/** \brief XML-Tag "minimumSizeX": minimum position of ROI in z-dimension */
vnl_vector<int> m_MinIndexZ;
/** \brief XML-Tag "label": value of label */
vnl_vector<unsigned int> m_Label;
//XML-Tag <statistic>
/** \brief XML-Tag "minimum": minimum inside hotspot */
vnl_vector<double> m_HotspotMin;
/** \brief XML-Tag "maximum": maximum inside hotspot */
vnl_vector<double> m_HotspotMax;
/** \brief XML-Tag "mean": mean value of hotspot */
vnl_vector<double> m_HotspotMean;
/** \brief XML-Tag "maximumIndexX": x-coordinate of maximum-location inside hotspot */
vnl_vector<int> m_HotspotMaxIndexX;
/** \brief XML-Tag "maximumIndexX": y-coordinate of maximum-location inside hotspot */
vnl_vector<int> m_HotspotMaxIndexY;
/** \brief XML-Tag "maximumIndexX": z-coordinate of maximum-location inside hotspot */
vnl_vector<int> m_HotspotMaxIndexZ;
/** \brief XML-Tag "maximumIndexX": x-coordinate of maximum-location inside hotspot */
vnl_vector<int> m_HotspotMinIndexX;
/** \brief XML-Tag "maximumIndexX": y-coordinate of maximum-location inside hotspot */
vnl_vector<int> m_HotspotMinIndexY;
/** \brief XML-Tag "maximumIndexX": z-coordinate of maximum-location inside hotspot */
vnl_vector<int> m_HotspotMinIndexZ;
/** \brief XML-Tag "maximumIndexX": x-coordinate of hotspot-location */
vnl_vector<int> m_HotspotIndexX;
/** \brief XML-Tag "maximumIndexX": y-coordinate of hotspot-location */
vnl_vector<int> m_HotspotIndexY;
/** \brief XML-Tag "maximumIndexX": z-coordinate of hotspot-location */
vnl_vector<int> m_HotspotIndexZ;
};
/**
\brief Find/Convert integer attribute in itk::DOMNode.
*/
static int GetIntegerAttribute(itk::DOMNode* domNode, const std::string& tag)
{
assert(domNode);
MITK_TEST_CONDITION_REQUIRED( domNode->HasAttribute(tag), "Tag '" << tag << "' is defined in test parameters" );
std::string attributeValue = domNode->GetAttribute(tag);
int resultValue;
try
{
//MITK_TEST_OUTPUT( << "Converting tag value '" << attributeValue << "' for tag '" << tag << "' to integer");
std::stringstream(attributeValue) >> resultValue;
return resultValue;
}
catch(std::exception& /*e*/)
{
MITK_TEST_CONDITION_REQUIRED(false, "Convert tag value '" << attributeValue << "' for tag '" << tag << "' to integer");
return 0; // just to satisfy compiler
}
}
/**
\brief Find/Convert double attribute in itk::DOMNode.
*/
static double GetDoubleAttribute(itk::DOMNode* domNode, const std::string& tag)
{
assert(domNode);
MITK_TEST_CONDITION_REQUIRED( domNode->HasAttribute(tag), "Tag '" << tag << "' is defined in test parameters" );
std::string attributeValue = domNode->GetAttribute(tag);
double resultValue;
try
{
//MITK_TEST_OUTPUT( << "Converting tag value '" << attributeValue << "' for tag '" << tag << "' to double");
std::stringstream(attributeValue) >> resultValue;
return resultValue;
}
catch(std::exception& /*e*/)
{
MITK_TEST_CONDITION_REQUIRED(false, "Convert tag value '" << attributeValue << "' for tag '" << tag << "' to double");
return 0.0; // just to satisfy compiler
}
}
/**
\brief Read XML file describing the test parameters.
Reads XML file given in first commandline parameter in order
to construct a Parameters structure. The XML file should be
- structurs as the following example, i.e. we describe the
+ structured as the following example, i.e. we describe the
three test aspects of Parameters in four different tags,
with all the details described as tag attributes. */
/**
\verbatim
<testcase>
<!--
Test case: multi-label mask
-->
<testimage image-rows="50" image-columns="50" image-slices="20" numberOfGaussians="2" spacingX="1" spacingY="1" spacingZ="1" entireHotSpotInImage="1">
<gaussian centerIndexX="10" centerIndexY="10" centerIndexZ="10" deviationX="5" deviationY="5" deviationZ="5" altitude="200"/>
<gaussian centerIndexX="40" centerIndexY="40" centerIndexZ="10" deviationX="2" deviationY="4" deviationZ="6" altitude="180"/>
</testimage>
<segmentation numberOfLabels="2" hotspotRadiusInMM="6.2035">
<roi label="1" maximumSizeX="20" minimumSizeX="0" maximumSizeY="20" minimumSizeY="0" maximumSizeZ="20" minimumSizeZ="0"/>
<roi label="2" maximumSizeX="50" minimumSizeX="30" maximumSizeY="50" minimumSizeY="30" maximumSizeZ="20" minimumSizeZ="0"/>
</segmentation>
<statistic hotspotIndexX="10" hotspotIndexY="10" hotspotIndexZ="10" mean="122.053" maximumIndexX="10" maximumIndexY="10" maximumIndexZ="10" maximum="200" minimumIndexX="9" minimumIndexY="9" minimumIndexZ="4" minimum="93.5333"/>
<statistic hotspotIndexX="40" hotspotIndexY="40" hotspotIndexZ="10" mean="61.1749" maximumIndexX="40" maximumIndexY="40" maximumIndexZ="10" maximum="180" minimumIndexX="46" minimumIndexY="39" minimumIndexZ="9" minimum="1.91137"/>
</testcase>
\endverbatim
*/
static Parameters ParseParameters(int argc, char* argv[])
{
MITK_TEST_CONDITION_REQUIRED(argc == 2, "Test is invoked with exactly 1 parameter (XML parameters file)");
MITK_INFO << "Reading parameters from file '" << argv[1] << "'";
std::string filename = argv[1];
Parameters result;
itk::DOMNodeXMLReader::Pointer xmlReader = itk::DOMNodeXMLReader::New();
xmlReader->SetFileName( filename );
try
{
xmlReader->Update();
itk::DOMNode::Pointer domRoot = xmlReader->GetOutput();
typedef std::vector<itk::DOMNode*> NodeList;
NodeList testimages;
domRoot->GetChildren("testimage", testimages);
MITK_TEST_CONDITION_REQUIRED( testimages.size() == 1, "One test image defined" )
itk::DOMNode* testimage = testimages[0];
result.m_ImageRows = GetIntegerAttribute( testimage, "image-rows" );
result.m_ImageColumns = GetIntegerAttribute( testimage, "image-columns" );
result.m_ImageSlices = GetIntegerAttribute( testimage, "image-slices" );
result.m_NumberOfGaussian = GetIntegerAttribute( testimage, "numberOfGaussians" );
result.m_Spacing[0] = GetDoubleAttribute(testimage, "spacingX");
result.m_Spacing[1] = GetDoubleAttribute(testimage, "spacingY");
result.m_Spacing[2] = GetDoubleAttribute(testimage, "spacingZ");
result.m_EntireHotspotInImage = GetIntegerAttribute( testimage, "entireHotSpotInImage" );
MITK_TEST_OUTPUT( << "Read size parameters (x,y,z): " << result.m_ImageRows << "," << result.m_ImageColumns << "," << result.m_ImageSlices);
MITK_TEST_OUTPUT( << "Read spacing parameters (x,y,z): " << result.m_Spacing[0] << "," << result.m_Spacing[1] << "," << result.m_Spacing[2]);
NodeList gaussians;
testimage->GetChildren("gaussian", gaussians);
MITK_TEST_CONDITION_REQUIRED( gaussians.size() >= 1, "At least one gaussian is defined" )
result.m_CenterX.resize(result.m_NumberOfGaussian);
result.m_CenterY.resize(result.m_NumberOfGaussian);
result.m_CenterZ.resize(result.m_NumberOfGaussian);
result.m_SigmaX.resize(result.m_NumberOfGaussian);
result.m_SigmaY.resize(result.m_NumberOfGaussian);
result.m_SigmaZ.resize(result.m_NumberOfGaussian);
result.m_Altitude.resize(result.m_NumberOfGaussian);
for(int i = 0; i < result.m_NumberOfGaussian ; ++i)
{
itk::DOMNode* gaussian = gaussians[i];
result.m_CenterX[i] = GetIntegerAttribute(gaussian, "centerIndexX");
result.m_CenterY[i] = GetIntegerAttribute(gaussian, "centerIndexY");
result.m_CenterZ[i] = GetIntegerAttribute(gaussian, "centerIndexZ");
result.m_SigmaX[i] = GetDoubleAttribute(gaussian, "deviationX");
result.m_SigmaY[i] = GetDoubleAttribute(gaussian, "deviationY");
result.m_SigmaZ[i] = GetDoubleAttribute(gaussian, "deviationZ");
result.m_Altitude[i] = GetDoubleAttribute(gaussian, "altitude");
result.m_CenterX[i] = result.m_CenterX[i] * result.m_Spacing[0];
result.m_CenterY[i] = result.m_CenterY[i] * result.m_Spacing[1];
result.m_CenterZ[i] = result.m_CenterZ[i] * result.m_Spacing[2];
result.m_SigmaX[i] = result.m_SigmaX[i] * result.m_Spacing[0];
result.m_SigmaY[i] = result.m_SigmaY[i] * result.m_Spacing[1];
result.m_SigmaZ[i] = result.m_SigmaZ[i] * result.m_Spacing[2];
}
NodeList segmentations;
domRoot->GetChildren("segmentation", segmentations);
MITK_TEST_CONDITION_REQUIRED( segmentations.size() == 1, "One segmentation defined");
itk::DOMNode* segmentation = segmentations[0];
result.m_NumberOfLabels = GetIntegerAttribute(segmentation, "numberOfLabels");
result.m_HotspotRadiusInMM = GetDoubleAttribute(segmentation, "hotspotRadiusInMM");
// read ROI parameters, fill result structure
NodeList rois;
segmentation->GetChildren("roi", rois);
MITK_TEST_CONDITION_REQUIRED( rois.size() >= 1, "At least one ROI defined" )
result.m_MaxIndexX.set_size(result.m_NumberOfLabels);
result.m_MinIndexX.set_size(result.m_NumberOfLabels);
result.m_MaxIndexY.set_size(result.m_NumberOfLabels);
result.m_MinIndexY.set_size(result.m_NumberOfLabels);
result.m_MaxIndexZ.set_size(result.m_NumberOfLabels);
result.m_MinIndexZ.set_size(result.m_NumberOfLabels);
result.m_Label.set_size(result.m_NumberOfLabels);
for(unsigned int i = 0; i < rois.size(); ++i)
{
result.m_MaxIndexX[i] = GetIntegerAttribute(rois[i], "maximumIndexX");
result.m_MinIndexX[i] = GetIntegerAttribute(rois[i], "minimumIndexX");
result.m_MaxIndexY[i] = GetIntegerAttribute(rois[i], "maximumIndexY");
result.m_MinIndexY[i] = GetIntegerAttribute(rois[i], "minimumIndexY");
result.m_MaxIndexZ[i] = GetIntegerAttribute(rois[i], "maximumIndexZ");
result.m_MinIndexZ[i] = GetIntegerAttribute(rois[i], "minimumIndexZ");
result.m_Label[i] = GetIntegerAttribute(rois[i], "label");
}
// read statistic parameters, fill result structure
NodeList statistics;
domRoot->GetChildren("statistic", statistics);
MITK_TEST_CONDITION_REQUIRED( statistics.size() >= 1 , "At least one statistic defined" )
MITK_TEST_CONDITION_REQUIRED( statistics.size() == rois.size(), "Same number of rois and corresponding statistics defined");
result.m_HotspotMin.set_size(statistics.size());
result.m_HotspotMax.set_size(statistics.size());
result.m_HotspotMean.set_size(statistics.size());
result.m_HotspotMinIndexX.set_size(statistics.size());
result.m_HotspotMinIndexY.set_size(statistics.size());
result.m_HotspotMinIndexZ.set_size(statistics.size());
result.m_HotspotMaxIndexX.set_size(statistics.size());
result.m_HotspotMaxIndexY.set_size(statistics.size());
result.m_HotspotMaxIndexZ.set_size(statistics.size());
result.m_HotspotIndexX.set_size(statistics.size());
result.m_HotspotIndexY.set_size(statistics.size());
result.m_HotspotIndexZ.set_size(statistics.size());
for(unsigned int i = 0; i < statistics.size(); ++i)
{
result.m_HotspotMin[i] = GetDoubleAttribute(statistics[i], "minimum");
result.m_HotspotMax[i] = GetDoubleAttribute(statistics[i], "maximum");
result.m_HotspotMean[i] = GetDoubleAttribute(statistics[i], "mean");
result.m_HotspotMinIndexX[i] = GetIntegerAttribute(statistics[i], "minimumIndexX");
result.m_HotspotMinIndexY[i] = GetIntegerAttribute(statistics[i], "minimumIndexY");
result.m_HotspotMinIndexZ[i] = GetIntegerAttribute(statistics[i], "minimumIndexZ");
result.m_HotspotMaxIndexX[i] = GetIntegerAttribute(statistics[i], "maximumIndexX");
result.m_HotspotMaxIndexY[i] = GetIntegerAttribute(statistics[i], "maximumIndexY");
result.m_HotspotMaxIndexZ[i] = GetIntegerAttribute(statistics[i], "maximumIndexZ");
result.m_HotspotIndexX[i] = GetIntegerAttribute(statistics[i], "hotspotIndexX");
result.m_HotspotIndexY[i] = GetIntegerAttribute(statistics[i], "hotspotIndexY");
result.m_HotspotIndexZ[i] = GetIntegerAttribute(statistics[i], "hotspotIndexZ");
}
}
catch (std::exception& e)
{
MITK_TEST_CONDITION_REQUIRED(false, "Reading test parameters from XML file. Error message: " << e.what());
}
return result;
}
/**
\brief Generate an image that contains a couple of 3D gaussian distributions.
Uses the given parameters to produce a test image using class MultiGaussianImageSource.
*/
static mitk::Image::Pointer BuildTestImage(const Parameters& testParameters)
{
typedef double PixelType;
const int Dimension = 3;
typedef itk::Image<PixelType, Dimension> ImageType;
typedef itk::MultiGaussianImageSource< ImageType > MultiGaussianImageSource;
MultiGaussianImageSource::Pointer gaussianGenerator = MultiGaussianImageSource::New();
ImageType::SizeValueType size[3];
size[0] = testParameters.m_ImageColumns;
size[1] = testParameters.m_ImageRows;
size[2] = testParameters.m_ImageSlices;
itk::MultiGaussianImageSource<ImageType>::VectorType centerXVec, centerYVec, centerZVec, sigmaXVec, sigmaYVec, sigmaZVec, altitudeVec;
for(int i = 0; i < testParameters.m_NumberOfGaussian; ++i)
{
centerXVec.push_back(testParameters.m_CenterX[i]);
centerYVec.push_back(testParameters.m_CenterY[i]);
centerZVec.push_back(testParameters.m_CenterZ[i]);
sigmaXVec.push_back(testParameters.m_SigmaX[i]);
sigmaYVec.push_back(testParameters.m_SigmaY[i]);
sigmaZVec.push_back(testParameters.m_SigmaZ[i]);
altitudeVec.push_back(testParameters.m_Altitude[i]);
}
ImageType::SpacingType spacing;
for( int i = 0; i < Dimension; ++i )
spacing[i] = testParameters.m_Spacing[i];
gaussianGenerator->SetSize( size );
gaussianGenerator->SetSpacing( spacing );
gaussianGenerator->SetRadius(testParameters.m_HotspotRadiusInMM);
gaussianGenerator->SetNumberOfGausssians(testParameters.m_NumberOfGaussian);
gaussianGenerator->AddGaussian(centerXVec, centerYVec, centerZVec,
sigmaXVec, sigmaYVec, sigmaZVec, altitudeVec);
gaussianGenerator->Update();
return mitk::GrabItkImageMemory(gaussianGenerator->GetOutput(), nullptr, nullptr, false);
}
/**
\brief Calculates hotspot statistics for given test image and ROI parameters.
Uses ImageStatisticsCalculator to find a hotspot in a defined ROI within the given image.
*/
static mitk::ImageStatisticsContainer::ImageStatisticsObject CalculateStatistics(mitk::Image* image, const Parameters& testParameters, unsigned int label)
{
const unsigned int Dimension = 3;
typedef itk::Image<unsigned short, Dimension> MaskImageType;
MaskImageType::Pointer mask = MaskImageType::New();
MaskImageType::SizeType size;
MaskImageType::SpacingType spacing;
MaskImageType::IndexType start;
mitk::ImageStatisticsCalculator::Pointer statisticsCalculator = mitk::ImageStatisticsCalculator::New();
statisticsCalculator->SetInputImage(image);
if((testParameters.m_MaxIndexX[label] > testParameters.m_MinIndexX[label] && testParameters.m_MinIndexX[label] >= 0) &&
(testParameters.m_MaxIndexY[label] > testParameters.m_MinIndexY[label] && testParameters.m_MinIndexY[label] >= 0) &&
(testParameters.m_MaxIndexZ[label] > testParameters.m_MinIndexZ[label] && testParameters.m_MinIndexZ[label] >= 0))
{
for(unsigned int i = 0; i < Dimension; ++i)
{
start[i] = 0;
spacing[i] = testParameters.m_Spacing[i];
}
size[0] = testParameters.m_ImageColumns;
size[1] = testParameters.m_ImageRows;
size[2] = testParameters.m_ImageSlices;
MaskImageType::RegionType region;
region.SetIndex(start);
region.SetSize(size);
mask->SetSpacing(spacing);
mask->SetRegions(region);
mask->Allocate();
typedef itk::ImageRegionIteratorWithIndex<MaskImageType> MaskImageIteratorType;
MaskImageIteratorType maskIt(mask, region);
for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
{
maskIt.Set(0);
}
for(unsigned int i = 0; i < testParameters.m_NumberOfLabels; ++i)
{
for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt)
{
MaskImageType::IndexType index = maskIt.GetIndex();
if((index[0] >= testParameters.m_MinIndexX[i] && index[0] <= testParameters.m_MaxIndexX[i] ) &&
(index[1] >= testParameters.m_MinIndexY[i] && index[1] <= testParameters.m_MaxIndexY[i] ) &&
(index[2] >= testParameters.m_MinIndexZ[i] && index[2] <= testParameters.m_MaxIndexZ[i] ))
{
maskIt.Set(testParameters.m_Label[i]);
}
}
}
auto mitkMaskImage = mitk::GrabItkImageMemory(mask, nullptr, nullptr, false);
mitk::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New();
imgMaskGen->SetInputImage(image);
imgMaskGen->SetImageMask(mitkMaskImage);
mitk::HotspotMaskGenerator::Pointer hotspotMaskGen = mitk::HotspotMaskGenerator::New();
hotspotMaskGen->SetInputImage(image);
hotspotMaskGen->SetLabel(testParameters.m_Label[label]);
hotspotMaskGen->SetMask(imgMaskGen.GetPointer());
hotspotMaskGen->SetHotspotRadiusInMM(testParameters.m_HotspotRadiusInMM);
if(testParameters.m_EntireHotspotInImage == 1)
{
- MITK_INFO << "Hotspot must be completly inside image";
+ MITK_INFO << "Hotspot must be completely inside image";
hotspotMaskGen->SetHotspotMustBeCompletelyInsideImage(true);
}
else
{
- MITK_INFO << "Hotspot must not be completly inside image";
+ MITK_INFO << "Hotspot must not be completely inside image";
hotspotMaskGen->SetHotspotMustBeCompletelyInsideImage(false);
}
statisticsCalculator->SetMask(hotspotMaskGen.GetPointer());
MITK_DEBUG << "Masking is set to hotspot+image mask";
}
else
{
mitk::HotspotMaskGenerator::Pointer hotspotMaskGen = mitk::HotspotMaskGenerator::New();
hotspotMaskGen->SetInputImage(image);
hotspotMaskGen->SetHotspotRadiusInMM(testParameters.m_HotspotRadiusInMM);
if(testParameters.m_EntireHotspotInImage == 1)
{
- MITK_INFO << "Hotspot must be completly inside image";
+ MITK_INFO << "Hotspot must be completely inside image";
hotspotMaskGen->SetHotspotMustBeCompletelyInsideImage(true);
}
else
{
- MITK_INFO << "Hotspot must not be completly inside image";
+ MITK_INFO << "Hotspot must not be completely inside image";
hotspotMaskGen->SetHotspotMustBeCompletelyInsideImage(false);
}
MITK_DEBUG << "Masking is set to hotspot only";
}
return statisticsCalculator->GetStatistics()->GetStatistics(label,0);
}
static void ValidateStatisticsItem(const std::string& label, double testvalue, double reference, double tolerance)
{
double diff = ::fabs(reference - testvalue);
MITK_TEST_CONDITION( diff < tolerance, "'" << label << "' value close enough to reference value "
"(value=" << testvalue <<
", reference=" << reference <<
", diff=" << diff << ")" );
}
static void ValidateStatisticsItem(const std::string& label, const vnl_vector<int>& testvalue, const vnl_vector<int>& reference)
{
double diffX = ::fabs(double(testvalue[0] - reference[0]));
double diffY = ::fabs(double(testvalue[1] - reference[1]));
double diffZ = ::fabs(double(testvalue[2] - reference[2]));
std::stringstream testPosition;
testPosition << testvalue[0] << "," << testvalue[1] << "," << testvalue[2];
std::stringstream referencePosition;
referencePosition << reference[0] << "," << reference[1] << "," << reference[2];
MITK_TEST_CONDITION( diffX < mitk::eps && diffY < mitk::eps && diffZ < mitk::eps,
"'" << label << "' close enough to reference value " <<
"(value=[" << testPosition.str() << "]," <<
" reference=[" << referencePosition.str() << "]");
}
/**
\brief Compares calculated against actual statistics values.
Checks validness of all statistics aspects. Lets test fail if any aspect is not sufficiently equal.
*/
static void ValidateStatistics(const mitk::ImageStatisticsContainer::ImageStatisticsObject hotspotStatistics, const Parameters& testParameters, unsigned int label)
{
// check all expected test result against actual results
double eps = 0.25; // value above the largest tested difference
auto mean = hotspotStatistics.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MEAN());
auto max = hotspotStatistics.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MAXIMUM());
auto min = hotspotStatistics.GetValueConverted<mitk::ImageStatisticsContainer::RealType>(mitk::ImageStatisticsConstants::MINIMUM());
ValidateStatisticsItem("Hotspot mean", mean, testParameters.m_HotspotMean[label], eps);
ValidateStatisticsItem("Hotspot maximum", max, testParameters.m_HotspotMax[label], eps);
ValidateStatisticsItem("Hotspot minimum", min, testParameters.m_HotspotMin[label], eps);
vnl_vector<int> referenceHotspotCenterIndex; referenceHotspotCenterIndex.set_size(3);
referenceHotspotCenterIndex[0] = testParameters.m_HotspotIndexX[label];
referenceHotspotCenterIndex[1] = testParameters.m_HotspotIndexY[label];
referenceHotspotCenterIndex[2] = testParameters.m_HotspotIndexZ[label];
// ValidateStatisticsItem("Hotspot center position", statistics.GetHotspotStatistics().GetHotspotIndex(), referenceHotspotCenterIndex); TODO: new image statistics calculator does not give hotspot position
// TODO we do not test minimum/maximum positions within the peak/hotspot region, because
// these positions are not unique, i.e. there are multiple valid minima/maxima positions.
- // One solution would be to modify the test cases in order to achive clear positions.
+ // One solution would be to modify the test cases in order to achieve clear positions.
// The BETTER/CORRECT solution would be to change the singular position into a set of positions / a region
}
};
/**
\brief Verifies that hotspot statistics part of ImageStatisticsCalculator.
The test reads parameters from an XML-file to generate a test-image, calculates the hotspot statistics of the image
and checks if the calculated statistics are the same as the specified values of the XML-file.
*/
int mitkImageStatisticsHotspotTest(int argc, char* argv[])
{
MITK_TEST_BEGIN("mitkImageStatisticsHotspotTest")
try {
mitkImageStatisticsHotspotTestClass::Parameters parameters = mitkImageStatisticsHotspotTestClass::ParseParameters(argc,argv);
mitk::Image::Pointer image = mitkImageStatisticsHotspotTestClass::BuildTestImage(parameters);
MITK_TEST_CONDITION_REQUIRED( image.IsNotNull(), "Generate test image" );
for(unsigned int label = 0; label < parameters.m_NumberOfLabels; ++label)
{
mitk::ImageStatisticsContainer::ImageStatisticsObject statistics = mitkImageStatisticsHotspotTestClass::CalculateStatistics(image, parameters, label);
mitkImageStatisticsHotspotTestClass::ValidateStatistics(statistics, parameters, label);
std::cout << std::endl;
}
}
catch (std::exception& e)
{
std::cout << "Error: " << e.what() << std::endl;
MITK_TEST_CONDITION_REQUIRED( false, "Exception occurred during test execution: " << e.what() );
}
catch(...)
{
MITK_TEST_CONDITION_REQUIRED( false, "Exception occurred during test execution." );
}
MITK_TEST_END()
}
diff --git a/Modules/ImageStatistics/Testing/mitkMultiGaussianTest.cpp b/Modules/ImageStatistics/Testing/mitkMultiGaussianTest.cpp
index 26e0c1e563..9d3df1f259 100644
--- a/Modules/ImageStatistics/Testing/mitkMultiGaussianTest.cpp
+++ b/Modules/ImageStatistics/Testing/mitkMultiGaussianTest.cpp
@@ -1,542 +1,542 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "itkMultiGaussianImageSource.h"
#include <string>
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <itkDOMNode.h>
#include <itkDOMNodeXMLWriter.h>
#include <itkDOMNodeXMLReader.h>
#include <itkImage.h>
-// Commandline:(for exmaple) mitkMultiGaussianTest C:/temp/output C:/temp/inputFile.xml
+// Commandline:(for example) mitkMultiGaussianTest C:/temp/output C:/temp/inputFile.xml
//
//For Example: inputFile.xml
//<testcase>
// <testimage image-rows="20" image-columns="20" image-slices="20" numberOfGaussians="1" spacingX="1" spacingY="1" spacingZ="1" entireHotSpotInImage="1">
// <gaussian centerIndexX="10" centerIndexY="10" centerIndexZ="10" deviationX="6" deviationY="6" deviationZ="6" altitude="200"/>
// </testimage>
//<segmentation numberOfLabels="2" hotspotRadiusInMM="6.2035">
// <roi label="1" maximumIndexX="20" minimumIndexX="12" maximumIndexY="20" minimumIndexY="12" maximumIndexZ="20" minimumIndexZ="12"/>
// <roi label="2" maximumIndexX="10" minimumIndexX="0" maximumIndexY="10" minimumIndexY="0" maximumIndexZ="10" minimumIndexZ="0"/>
// </segmentation>
//</testcase>
//
//For Example: output.xml
// <testcase>
// <testimage image-rows="20" image-columns="20" image-slices="20" numberOfGaussians="1" spacingX="1" spacingY="1" spacingZ="1" entireHotSpotInImage="1">
// <gaussian centerIndexX="50" centerIndexY="50" centerIndexZ="50" deviationX="5" deviationY="5" deviationZ="5" altitude="200"/>
// <gaussian centerIndexX="46" centerIndexY="46" centerIndexZ="46" deviationX="40" deviationY="40" deviationZ="40" altitude="170"/>
// </testimage>
// <segmentation numberOfLabels="1" hotspotRadiusInMM="8">
// <roi hotspotRadiusInMM="6.2035" maximumIndexX="122" minimumIndexX="7" maximumIndexY="122" minimumIndexY="7" maximumIndexZ="60" minimumIndexZ="4"/>
// </segmentation>
// <statistic hotspotIndexX="50" hotspotIndexY="50" hotspotIndexZ="25" peak="291.067" mean="291.067" maximumIndexX="50" maximumIndexY="50" maximumIndexZ="25" maximum="367.469" minimumIndexX="55" minimumIndexY="53" minimumIndexZ="26" minimum="254.939"/>
//</testcase>
bool IsInOtherROI(int,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType);
int mitkMultiGaussianTest(int argc, char* argv[])
{
- // Read the parmaeter from a .xml File.
+ // Read the parameter from an .xml File.
// In the inputFile.xml we find the characteristics of the Gaussian and the ROI's. Hier we can have more then one ROI -> we find the hot spot for each of the ROI's; we can set the entire HotSpot to be in the image or just its midpoint, but not necessary the whole HotSpot.
const unsigned int Dimension = 3;
typedef double PixelType;
typedef itk::DOMNode::Pointer DOMNodeType;
typedef itk::Image<PixelType, Dimension> ImageType;
typedef itk::MultiGaussianImageSource< ImageType > MultiGaussianImageSource;
std::string outputFilename = argv[1], name;
int numberOfImages;
double centerX, centerY, centerZ, sigmaX, sigmaY, sigmaZ, altitude, hotSpotRadiusInMM;
unsigned int numberOfGaussians, minWidthOfGaussian, maxWidthOfGaussian, minAltitudeOfGaussian, maxAltitudeOfGaussian, numberOfLabels;
itk::MultiGaussianImageSource< ImageType >::VectorType centerXVec, centerYVec, centerZVec, sigmaXVec, sigmaYVec, sigmaZVec, altitudeVec, ROImaxIndexX, ROIminIndexX, ROImaxIndexY, ROIminIndexY, ROImaxIndexZ, ROIminIndexZ, label;
itk::MultiGaussianImageSource< ImageType >::ItkVectorType regionOfInterestMax, regionOfInterestMin;
itk::MultiGaussianImageSource< ImageType >::IndexType sphereMidpt, maxValueIndexInSphere, minValueIndexInSphere;
MultiGaussianImageSource::Pointer gaussianGenerator;
itk::DOMNodeXMLWriter::Pointer xmlWriter;
itk::MultiGaussianImageSource< ImageType >::SpacingValueArrayType spacing;
DOMNodeType domTestCase, domTestImage, domGaussian, domSegmentation, domStatistics, domROI;
ImageType::SizeValueType size[3];
std::stringstream ss;
double radius = pow(itk::Math::one_over_pi * 0.75 , 1.0 / 3.0) * 10;
char * fileNamePointer;
std::string attributeValue;
double value;
bool entireHotSpotInImage;
int maxIndex, minIndex;
std::string filename = argv[2];
itk::DOMNodeXMLReader::Pointer xmlReader = itk::DOMNodeXMLReader::New();
xmlReader->SetFileName( filename );
xmlReader->Update();
itk::DOMNode::Pointer domRoot = xmlReader->GetOutput();
typedef std::vector<itk::DOMNode*> NodeList;
// read test image parameters, fill result structure
NodeList testimages;
domRoot->GetChildren("testimage", testimages);
MITK_TEST_CONDITION_REQUIRED( testimages.size() == 1, "One test image defined" )
itk::DOMNode* testimage = testimages[0];
attributeValue = testimage->GetAttribute("image-rows");
std::stringstream(attributeValue) >> size[0];
attributeValue = testimage->GetAttribute("image-columns");
std::stringstream(attributeValue) >> size[1];
attributeValue = testimage->GetAttribute("image-slices");
std::stringstream(attributeValue) >> size[2];
attributeValue = testimage->GetAttribute( "numberOfGaussians" );
std::stringstream(attributeValue) >> numberOfGaussians;
attributeValue = testimage->GetAttribute( "spacingX" );
std::stringstream(attributeValue) >> spacing[0];
attributeValue = testimage->GetAttribute( "spacingY" );
std::stringstream(attributeValue) >> spacing[1];
attributeValue = testimage->GetAttribute( "spacingZ" );
std::stringstream(attributeValue) >> spacing[2];
attributeValue = testimage->GetAttribute( "entireHotSpotInImage" );
std::stringstream(attributeValue) >> entireHotSpotInImage;
std::cout << "Read size parameters (x,y,z): " << size[0] << ", " << size[1] << ", " << size[2] << "\n" << std::endl;
std::cout << "Read spacing parameters (x,y,z): " << spacing[0] << ", " << spacing[1] << ", " << spacing[2]<< "\n" << std::endl;
NodeList gaussians;
testimage->GetChildren("gaussian", gaussians);
itk::DOMNode* gaussian;
for(int i = 0; i < numberOfGaussians ; ++i)
{
gaussian = gaussians[i];
//TODO
attributeValue = gaussian->GetAttribute( "centerIndexX" );
std::stringstream(attributeValue) >> value;
centerXVec.push_back(value * spacing[0]);
attributeValue = gaussian->GetAttribute( "centerIndexY" );
std::stringstream(attributeValue) >> value;
centerYVec.push_back(value * spacing[1]);
attributeValue = gaussian->GetAttribute( "centerIndexZ" );
std::stringstream(attributeValue) >> value;
centerZVec.push_back(value * spacing[2]);
std::cout << "Read center of Gaussian (x,y,z) in mm: " << centerXVec[i] << ", " << centerYVec[i] << ", " << centerZVec[i] << "\n" << std::endl;
attributeValue = gaussian->GetAttribute( "deviationX" );
std::stringstream(attributeValue) >> value;
sigmaXVec.push_back(value * spacing[0]);
attributeValue = gaussian->GetAttribute( "deviationY" );
std::stringstream(attributeValue) >> value;
sigmaYVec.push_back(value * spacing[1]);
attributeValue = gaussian->GetAttribute( "deviationZ" );
std::stringstream(attributeValue) >> value;
sigmaZVec.push_back(value * spacing[2]);
std::cout << "Read deviation of Gaussian (x,y,z) in mm: " << sigmaXVec[i] << ", " << sigmaYVec[i] << ", " << sigmaZVec[i] << "\n" << std::endl;
attributeValue = gaussian->GetAttribute( "altitude" );
std::stringstream(attributeValue) >> value;
altitudeVec.push_back(value);
std::cout << "Read altitude: " << altitudeVec[i] << "\n" << std::endl;
}
// read ROI's parameter
NodeList segmentations;
domRoot->GetChildren("segmentation", segmentations);
MITK_TEST_CONDITION_REQUIRED( segmentations.size() == 1, "One ROI image defined" )
itk::DOMNode* segmentation = segmentations[0];
attributeValue = segmentation->GetAttribute("numberOfLabels");
std::stringstream(attributeValue) >> numberOfLabels;
attributeValue = segmentation->GetAttribute("hotspotRadiusInMM");
std::stringstream(attributeValue) >> hotSpotRadiusInMM;
std::cout << "Read number of labels: " << numberOfLabels << std::endl;
std::cout << "Read radius in mm : " << hotSpotRadiusInMM << std::endl;
NodeList rois;
segmentation->GetChildren("roi", rois);
itk::DOMNode* roi;
// for each label i take the ROI and set it to be the i'th element of ROImaxIndex* and ROIminIndex* ( * = X, Y, Z)
for(int i = 0; i < numberOfLabels ; ++i)
{
roi = rois[i];
attributeValue = roi->GetAttribute( "label" );
std::stringstream(attributeValue) >> value;
label.push_back(value);
attributeValue = roi->GetAttribute( "maximumIndexX" );
std::stringstream(attributeValue) >> value;
ROImaxIndexX.push_back(value);
attributeValue = roi->GetAttribute( "minimumIndexX" );
std::stringstream(attributeValue) >> value;
ROIminIndexX.push_back(value);
attributeValue = roi->GetAttribute( "maximumIndexY" );
std::stringstream(attributeValue) >> value;
ROImaxIndexY.push_back(value);
attributeValue = roi->GetAttribute( "minimumIndexY" );
std::stringstream(attributeValue) >> value;
ROIminIndexY.push_back(value);
attributeValue = roi->GetAttribute( "maximumIndexZ" );
std::stringstream(attributeValue) >> value;
ROImaxIndexZ.push_back(value);
attributeValue = roi->GetAttribute( "minimumIndexZ" );
std::stringstream(attributeValue) >> value;
ROIminIndexZ.push_back(value);
std::cout << "Read ROI with label number: " << label[i] << " with min and max values in the x-, y-, z-Achse: [" << ROIminIndexX[i] << " " << ROImaxIndexX[i] <<"], [" << ROIminIndexY[i] << " " << ROImaxIndexY[i] <<"], [" << ROIminIndexZ[i] << " " << ROImaxIndexZ[i] <<"]\n" << std::endl;
}
// Check whether the ROI's are correct defined, i.e. whether the ROI's are disjoint
for(int i = 1; i < numberOfLabels ; ++i)
{
// check whether the edges of the i'th ROI is in another ROI included (when yes -> ERROR)
bool isInOtherROI = IsInOtherROI( i, ROIminIndexX, ROImaxIndexX, ROIminIndexY, ROImaxIndexY, ROIminIndexZ, ROImaxIndexZ );
if( isInOtherROI)
{
std::cout << "The ROI's in the different labels should be disjoint! Please define it correct. " << std::endl;
return 0;
}
}
//write test image parameter
xmlWriter = itk::DOMNodeXMLWriter::New();
domTestCase = itk::DOMNode::New();
domTestCase->SetName("testcase");
domTestImage = itk::DOMNode::New();
domTestImage->SetName("testimage");
ss.str("");
ss << size[0];
domTestImage->SetAttribute("image-rows", ss.str());
ss.str("");
ss << size[1];
domTestImage->SetAttribute("image-columns", ss.str());
ss.str("");
ss << size[2];
domTestImage->SetAttribute("image-slices", ss.str());
ss.str("");
ss << numberOfGaussians;
domTestImage->SetAttribute("numberOfGaussians", ss.str());
ss.str("");
ss << spacing[0];
domTestImage->SetAttribute("spacingX", ss.str());
ss.str("");
ss << spacing[1];
domTestImage->SetAttribute("spacingY", ss.str());
ss.str("");
ss << spacing[2];
domTestImage->SetAttribute("spacingZ", ss.str());
ss.str("");
ss << entireHotSpotInImage;
domTestImage->SetAttribute("entireHotSpotInImage", ss.str());
domTestCase->AddChildAtBegin(domTestImage);
for( unsigned int i = 0; i < numberOfGaussians; ++i)
{
domGaussian = itk::DOMNode::New() ;
domGaussian->SetName("gaussian");
domTestImage->AddChildAtEnd(domGaussian);
// write the midpoint and the daviation in pixel units
centerX = centerXVec[i] / spacing[0];
ss.str("");
ss << centerX; // * spacing[0]; //static_cast<double>( static_cast<int>( centerX / spacing[0] + 0.9999 ) );
domGaussian->SetAttribute("centerIndexX", ss.str());
centerY = centerYVec[i] / spacing[1];
ss.str("");
ss << centerY; // * spacing[1]; //static_cast<double>( static_cast<int>( centerY / spacing[1] + 0.9999 ) );
domGaussian->SetAttribute("centerIndexY", ss.str());
centerZ = centerZVec[i] / spacing[2];
ss.str("");
ss << centerZ; // * spacing[2]; //static_cast<double>( static_cast<int>( centerZ / spacing[2] + 0.9999 ) );
domGaussian->SetAttribute("centerIndexZ", ss.str());
sigmaX = sigmaXVec[i] / spacing[0];
ss.str("");
ss << sigmaX; // * spacing[0]; // static_cast<double>( static_cast<int>( sigmaX / spacing[0] + 0.9999 ) );
domGaussian->SetAttribute("deviationX", ss.str());
sigmaY = sigmaYVec[i] / spacing[1];
ss.str("");
ss << sigmaY; // * spacing[1]; //static_cast<double>( static_cast<int>( sigmaY / spacing[1] + 0.9999 ) );
domGaussian->SetAttribute("deviationY", ss.str());
sigmaZ = sigmaZVec[i] / spacing[2];
ss.str("");
ss << sigmaZ; // * spacing[2]; //static_cast<double>( static_cast<int>( sigmaZ / spacing[2] + 0.9999 ) );
domGaussian->SetAttribute("deviationZ", ss.str());
altitude = altitudeVec[i];
ss.str("");
ss << altitude;
domGaussian->SetAttribute("altitude", ss.str());
}
radius = hotSpotRadiusInMM;
// set the parameter for the gaussianGenerator
gaussianGenerator = MultiGaussianImageSource::New();
gaussianGenerator->SetSize( size );
gaussianGenerator->SetSpacing( spacing );
gaussianGenerator->SetRadius(radius);
gaussianGenerator->SetNumberOfGausssians(numberOfGaussians);
gaussianGenerator->AddGaussian(centerXVec, centerYVec, centerZVec, sigmaXVec, sigmaYVec, sigmaZVec, altitudeVec);
domSegmentation = itk::DOMNode::New();
domSegmentation->SetName("segmentation");
domTestCase->AddChildAtEnd(domSegmentation);
ss.str("");
ss << numberOfLabels;
domSegmentation->SetAttribute("numberOfLabels", ss.str());
ss.str("");
ss << hotSpotRadiusInMM;
domSegmentation->SetAttribute("hotspotRadiusInMM", ss.str());
// set the region of interest for each label i
for (unsigned int i = 0; i < numberOfLabels; ++i)
{
// Set region of interest in index values. The entire HotSpot is in the image.
if(entireHotSpotInImage)
{
// x axis region of interest------------------------------------------------------
minIndex = 0.0 + static_cast<int>((radius)/spacing[0]+ 0.5);
maxIndex = size[0]-1-minIndex;
if( minIndex >= maxIndex )
{
std::cout << "The sphere is larger then the image in the x axis!" << std::endl;
}
// the maximum in the x-Axis
regionOfInterestMax.SetElement( 0, ( ROImaxIndexX[i] < maxIndex ) ? ROImaxIndexX[i] : maxIndex );
// the minimum in the x-Axis
regionOfInterestMin.SetElement( 0, ( ROIminIndexX[i] > minIndex ) ? ROIminIndexX[i] : minIndex );
// y axis region of interest------------------------------------------------------
minIndex = 0.0 + static_cast<int>((radius)/spacing[1]+ 0.5);
maxIndex = size[1]-1-minIndex;
if( minIndex >= maxIndex )
{
std::cout << "The sphere is larger then the image in the y axis!" << std::endl;
}
// the maximum in the y-Axis
regionOfInterestMax.SetElement( 1, ( ROImaxIndexY[i] < maxIndex ) ? ROImaxIndexY[i] : maxIndex );
// the minimum in the y-Axis
regionOfInterestMin.SetElement( 1, ( ROIminIndexY[i] > minIndex ) ? ROIminIndexY[i] : minIndex );
// z axis region of interest------------------------------------------------------
minIndex = 0.0 + static_cast<int>((radius)/spacing[2]+ 0.5); // int(6.2/3.0 + 0.5) = 2
maxIndex = size[2]-1-minIndex;
if( minIndex >= maxIndex )
{
std::cout << "The sphere is larger then the image in the z axis!" << std::endl;
}
// the maximum in the z-Axis
regionOfInterestMax.SetElement( 2, ( ROImaxIndexZ[i] < maxIndex ) ? ROImaxIndexZ[i] : maxIndex );
// the minimum in the z-Axis
regionOfInterestMin.SetElement( 2, ( ROIminIndexZ[i] > minIndex ) ? ROIminIndexZ[i] : minIndex );
}
// Set region of interest in index values. The midpoint of the HotSpot is in the image, but not necessary the whole HotSpot
else
{
// x axis region of interest------------------------------------------------------
regionOfInterestMax.SetElement( 0, ROImaxIndexX[i] );
regionOfInterestMin.SetElement( 0, ROIminIndexX[i] );
// y axis region of interest------------------------------------------------------
regionOfInterestMax.SetElement( 1, ROImaxIndexY[i] );
regionOfInterestMin.SetElement( 1, ROIminIndexY[i] );
// z axis region of interest------------------------------------------------------
regionOfInterestMax.SetElement( 2, ROImaxIndexZ[i] );
regionOfInterestMin.SetElement( 2, ROIminIndexZ[i] );
}
gaussianGenerator->SetRegionOfInterest(regionOfInterestMin, regionOfInterestMax);
gaussianGenerator->Update();
//write region of interest for the .xml file
domROI = itk::DOMNode::New();
domROI->SetName("roi");
domSegmentation->AddChildAtEnd(domROI);
ss.str("");
ss << label[i];
domROI->SetAttribute("label", ss.str());
ss.str("");
ss << ROImaxIndexX[i];
domROI->SetAttribute("maximumIndexX", ss.str());
ss.str("");
ss << ROIminIndexX[i];
domROI->SetAttribute("minimumIndexX", ss.str());
ss.str("");
ss << ROImaxIndexY[i];
domROI->SetAttribute("maximumIndexY", ss.str());
ss.str("");
ss << ROIminIndexY[i];
domROI->SetAttribute("minimumIndexY", ss.str());
ss.str("");
ss << ROImaxIndexZ[i];
domROI->SetAttribute("maximumIndexZ", ss.str());
ss.str("");
ss << ROIminIndexZ[i];
domROI->SetAttribute("minimumIndexZ", ss.str());
// Calculate the mean value and the midpoint of the wanted sphere.
gaussianGenerator->CalculateTheMidpointAndTheMeanValueWithOctree();
//peak and peak coordinate
domStatistics = itk::DOMNode::New();
domStatistics->SetName("statistic");
domTestCase->AddChildAtEnd(domStatistics);
sphereMidpt = gaussianGenerator->GetSphereMidpoint();
ss.str("");
ss << sphereMidpt[0];
domStatistics->SetAttribute("hotspotIndexX", ss.str());
ss.str("");
ss << sphereMidpt[1];
domStatistics->SetAttribute("hotspotIndexY", ss.str());
ss.str("");
ss << sphereMidpt[2];
domStatistics->SetAttribute("hotspotIndexZ", ss.str());
ss.str("");
ss << gaussianGenerator->GetMaxMeanValue();
domStatistics->SetAttribute("mean", ss.str());
//maximum and maximum coordinate
gaussianGenerator->CalculateMaxAndMinInSphere();
maxValueIndexInSphere = gaussianGenerator->GetMaxValueIndexInSphere();
ss.str("");
ss << maxValueIndexInSphere[0];
domStatistics->SetAttribute("maximumIndexX", ss.str());
ss.str("");
ss << maxValueIndexInSphere[1];
domStatistics->SetAttribute("maximumIndexY", ss.str());
ss.str("");
ss << maxValueIndexInSphere[2];
domStatistics->SetAttribute("maximumIndexZ", ss.str());
ss.str("");
ss << gaussianGenerator->GetMaxValueInSphere();
domStatistics->SetAttribute("maximum", ss.str());
//minimum and minimum coordinate
minValueIndexInSphere = gaussianGenerator->GetMinValueIndexInSphere();
ss.str("");
ss << minValueIndexInSphere[0];
domStatistics->SetAttribute("minimumIndexX", ss.str());
ss.str("");
ss << minValueIndexInSphere[1];
domStatistics->SetAttribute("minimumIndexY", ss.str());
ss.str("");
ss << minValueIndexInSphere[2];
domStatistics->SetAttribute("minimumIndexZ", ss.str());
ss.str("");
ss << gaussianGenerator->GetMinValueInSphere();
domStatistics->SetAttribute("minimum", ss.str());
}
// .xml (Data)
ss.str("");
ss << outputFilename << ".xml";
name = ss.str();
fileNamePointer = (char*) name.c_str();
xmlWriter->SetFileName( fileNamePointer);
xmlWriter->SetInput( domTestCase );
xmlWriter->Update();
ImageType::Pointer gaussianImage = gaussianGenerator->GetOutput();
//.nrrd (Image)
typedef itk::ImageFileWriter< ImageType > WriterType;
WriterType::Pointer writer = WriterType::New();
ss.str("");
ss << outputFilename << ".nrrd";
name = ss.str();
fileNamePointer = (char*) name.c_str();
writer->SetFileName( fileNamePointer);
writer->SetInput( gaussianImage );
writer->Update();
}
// check whether the edges of the i'th ROI is in another ROI included
bool IsInOtherROI(int i,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType ROIminIndexX,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType ROImaxIndexX,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType ROIminIndexY,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType ROImaxIndexY,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType ROIminIndexZ,
itk::MultiGaussianImageSource<itk::Image< double, 3> >::VectorType ROImaxIndexZ )
{
bool error = 0;
std::vector<double> xBound, yBound, zBound;
xBound.push_back( ROIminIndexX[i] );
xBound.push_back( ROImaxIndexX[i] );
yBound.push_back( ROIminIndexY[i] );
yBound.push_back( ROImaxIndexY[i] );
zBound.push_back( ROIminIndexZ[i] );
zBound.push_back( ROImaxIndexZ[i] );
//for each ROI
for( unsigned int j = 0; j < i; ++j )
{
for( unsigned int x = 0; x < 2; ++x)
{
for( unsigned int y = 0; y < 2; ++y)
{
for( unsigned int z = 0; z < 2; ++z)
{
double edgeXCoord = xBound[x];
double edgeYCoord = yBound[y];
double edgeZCoord = zBound[z];
// check if the edge with coordinate [edgeXCoord; edgeYCoord; edgeZCoord] is inside the j'th ROI
if ( ROIminIndexX[j] < edgeXCoord && edgeXCoord < ROImaxIndexX[j] &&
ROIminIndexY[j] < edgeYCoord && edgeYCoord < ROImaxIndexY[j] &&
ROIminIndexZ[j] < edgeZCoord && edgeZCoord < ROImaxIndexZ[j])
{
error = 1;
return error;
}
}
}
}
}
return error;
}
diff --git a/Modules/ImageStatistics/Testing/mitkPointSetDifferenceStatisticsCalculatorTest.cpp b/Modules/ImageStatistics/Testing/mitkPointSetDifferenceStatisticsCalculatorTest.cpp
index c1ad7cc852..ed33ae0676 100644
--- a/Modules/ImageStatistics/Testing/mitkPointSetDifferenceStatisticsCalculatorTest.cpp
+++ b/Modules/ImageStatistics/Testing/mitkPointSetDifferenceStatisticsCalculatorTest.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 "mitkTestingMacros.h"
#include "mitkPointSetDifferenceStatisticsCalculator.h"
/**
* \brief Test class for mitkPointSetDifferenceStatisticsCalculator
*/
//Members used for testing purposes
mitk::PointSetDifferenceStatisticsCalculator::Pointer m_myPointSetDifferenceStatisticsCalculator;
mitk::PointSet::Pointer m_myTestPointSet1;
mitk::PointSet::Pointer m_myTestPointSet2;
-//This method should be called before every new sub-test call in order to freshly intialize all relevant classes
+//This method should be called before every new sub-test call in order to freshly initialize all relevant classes
void Setup()
{
// let's create an object of our class
m_myPointSetDifferenceStatisticsCalculator = mitk::PointSetDifferenceStatisticsCalculator::New();
//and some empty test data
m_myTestPointSet1 = mitk::PointSet::New();
m_myTestPointSet2 = mitk::PointSet::New();
}
void PointSetDifferenceStatisticsCalculator_DefaultConstructor_ResultIsNotNull()
{
Setup();
// let's create an object of our class
MITK_TEST_CONDITION_REQUIRED(m_myPointSetDifferenceStatisticsCalculator.IsNotNull(),"Testing instantiation with default constructor.");
}
void PointSetDifferenceStatisticsCalculator_NonDefaultConstructor_ResultIsNotNull()
{
Setup();
m_myPointSetDifferenceStatisticsCalculator = mitk::PointSetDifferenceStatisticsCalculator::New(m_myTestPointSet1,m_myTestPointSet2);
MITK_TEST_CONDITION_REQUIRED(m_myPointSetDifferenceStatisticsCalculator.IsNotNull(),"Testing instantiation with non default constructor.");
}
void PointSetDifferenceStatisticsCalculator_TwoSimplePointSetsOfSizeTwo_ResultsInGroundTruthValues()
{
MITK_TEST_OUTPUT(<< "Starting simple test case...");
mitk::Point3D tmpPoint;
//fill the point sets with simple test data
mitk::FillVector3D(tmpPoint,0,0,0);
m_myTestPointSet1->InsertPoint(0,tmpPoint);
mitk::FillVector3D(tmpPoint,1,1,1);
m_myTestPointSet1->InsertPoint(1,tmpPoint);
mitk::FillVector3D(tmpPoint,0.5,0.5,0.5);
m_myTestPointSet2->InsertPoint(0,tmpPoint);
mitk::FillVector3D(tmpPoint,2,2,2);
m_myTestPointSet2->InsertPoint(1,tmpPoint);
//Ground truth values (No logic in tests! Do not change values! :))
const double mean = 1.299038105676658E+00; // from (sqrt(0.75)+sqrt(3))/2;
const double variance = 0.1875; // from ((sqrt(0.75)-mean)*(sqrt(0.75)-mean)+(sqrt(3)-mean)*(sqrt(3)-mean))/2;
const double sd = 4.330127018922193E-01; //from sqrt(variance);
const double rms = 1.369306393762915E+00; //from sqrt(3.75/2);
const double min = 0.86602540378444; //from sqrt(0.75);
const double max = 1.73205080756888; //from sqrt(3);
const double median = 1.29903810567666; //from (min + max)/2;
m_myPointSetDifferenceStatisticsCalculator->SetPointSets( m_myTestPointSet1, m_myTestPointSet2);
MITK_TEST_CONDITION_REQUIRED((m_myPointSetDifferenceStatisticsCalculator->GetNumberOfPoints()==m_myTestPointSet1->GetSize()),".. Testing GetNumberOfPoints");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMean(),mean, 1E-5),".. Testing GetMean");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetSD(),sd,1E-6),".. Testing GetSD");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetVariance(),variance,1E-4),".. Testing GetVariance");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetRMS(),rms,1E-5),".. Testing GetRMS");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMin(),min,1E-6),".. Testing GetMin");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMax(),max,1E-5),".. Testing GetMax");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMedian(),median,1E-5),".. Testing GetMedian");
}
void PointSetDifferenceStatisticsCalculator_PointSetsOfSameSizeWithDifferentPointIDs_ResultsInGroundTruth()
{
Setup();
mitk::Point3D tmpPoint;
//Fill the point sets with simple test data, but different point IDs
mitk::FillVector3D(tmpPoint,1,1,1);
m_myTestPointSet1->InsertPoint(2,tmpPoint);
m_myTestPointSet2->InsertPoint(0,tmpPoint);
mitk::FillVector3D(tmpPoint,3.5,4.9,2.1); //same point in both pointsets
m_myTestPointSet1->InsertPoint(17,tmpPoint);
m_myTestPointSet2->InsertPoint(522,tmpPoint);
m_myPointSetDifferenceStatisticsCalculator->SetPointSets(m_myTestPointSet1, m_myTestPointSet2);
//Compare results to ground truth which is 0.0, because the sets are identical
MITK_TEST_CONDITION_REQUIRED((m_myPointSetDifferenceStatisticsCalculator->GetNumberOfPoints()==m_myTestPointSet1->GetSize()),".. Testing GetNumberOfPoints");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMean(),0.0),".. Testing GetMean");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetSD(),0.0),".. Testing GetSD");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetVariance(),0.0),".. Testing GetVariance");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetRMS(),0.0),".. Testing GetRMS");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMin(),0.0),".. Testing GetMin");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMax(),0.0),".. Testing GetMax");
MITK_TEST_CONDITION_REQUIRED(mitk::Equal(m_myPointSetDifferenceStatisticsCalculator->GetMedian(),0.0),".. Testing GetMedian");
}
void PointSetDifferenceStatisticsCalculator_TwoPointSetsOfDifferentSize_ThrowsException()
{
Setup();
//One set with 2 points and one set with 1 point
mitk::Point3D tmpPoint;
tmpPoint.Fill(0);
m_myTestPointSet1->InsertPoint(0, tmpPoint);
m_myTestPointSet1->InsertPoint(1, tmpPoint);
m_myTestPointSet2->InsertPoint(0, tmpPoint);
m_myPointSetDifferenceStatisticsCalculator->SetPointSets(m_myTestPointSet1, m_myTestPointSet2);
MITK_TEST_FOR_EXCEPTION(itk::ExceptionObject,m_myPointSetDifferenceStatisticsCalculator->GetMean());
}
void PointSetDifferenceStatisticsCalculator_PointSetWithSizeZero_ThrowsException()
{
Setup();
m_myPointSetDifferenceStatisticsCalculator->SetPointSets(m_myTestPointSet1, m_myTestPointSet2);
MITK_TEST_FOR_EXCEPTION(itk::ExceptionObject,m_myPointSetDifferenceStatisticsCalculator->GetMean());
}
int mitkPointSetDifferenceStatisticsCalculatorTest(int, char* [])
{
MITK_TEST_BEGIN("mitkPointSetDifferenceStatisticsCalculatorTest")
PointSetDifferenceStatisticsCalculator_DefaultConstructor_ResultIsNotNull();
PointSetDifferenceStatisticsCalculator_NonDefaultConstructor_ResultIsNotNull();
PointSetDifferenceStatisticsCalculator_TwoSimplePointSetsOfSizeTwo_ResultsInGroundTruthValues();
PointSetDifferenceStatisticsCalculator_PointSetWithSizeZero_ThrowsException();
PointSetDifferenceStatisticsCalculator_TwoPointSetsOfDifferentSize_ThrowsException();
PointSetDifferenceStatisticsCalculator_PointSetsOfSameSizeWithDifferentPointIDs_ResultsInGroundTruth();
MITK_TEST_END()
}
diff --git a/Modules/ImageStatistics/itkMultiGaussianImageSource.h b/Modules/ImageStatistics/itkMultiGaussianImageSource.h
index c557a6f991..90340f0b7e 100644
--- a/Modules/ImageStatistics/itkMultiGaussianImageSource.h
+++ b/Modules/ImageStatistics/itkMultiGaussianImageSource.h
@@ -1,374 +1,374 @@
/*=========================================================================
*
* 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
*
* https://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_h
#define __itkMultiGaussianImageSource_h
#include "itkImageSource.h"
#include "itkNumericTraits.h"
#include "itkImageRegionIteratorWithIndex.h"
#include "itkImageFileWriter.h"
#include <itkMapContainer.h>
namespace itk
{
/** \class MultiGaussianImageSource
\brief Generate an 3-dimensional multigaussian image.
This class defines an 3-dimensional Image, in which the value at one voxel equals the value of a multigaussian function evaluated at the voxel's coordinates. The multigaussian function is built as a sum of N gaussian function and is defined by the following parameters (\ref Generation-of-a-multigauss-image):
1. CenterX, CenterY, CenterZ - vectors of the size of N determining the expectancy value at the x-, y- and the z-axis. That means: The i-th gaussian bell curve takes its maximal value at the voxel with index [CenterX(i); CenterY(i); Centerz(i)].
2. SigmaX, SigmaY, SigmaZ - vectors of the size of N determining the deviation at the x-, y- and the z-axis. That means: The width of the i-th gaussian bell curve is determined by the deviation in the x-axis, which is SigmaX(i), in the y-axis is SigmaY(i) and in the z-axis is SigmaZ(i).
3. Altitude - vector of the size of N determining the altitude: the i-th gaussian bell curve has a height of Altitude(i).
This class allows by the method CalculateMidpointAndMeanValue() to find a sphere with a specified radius that has a maximal mean value over all sphere with that radius with midpoint inside or on the boundary of the image. Furthermore it can calculate the maximal und minimal pixel intensities and whose indices in the founded sphere.
To serve as a test tool for ImageStatisticsCalculator, esp. the "hotspot search" feature of this class, MultiGaussianImageSource is also able to calculate the position of a sphere that maximizes the mean value of the voxels within the sphere (\ref Algorithm-for-calculating-statistic-in-a-sphere).
\section Generation-of-a-multigauss-image Generation of a multigauss image
A multigauss function consists of the sum of \f$ N \f$ gauss function. The \f$ i \f$-th \n (\f$0 \leq i \leq N \f$) gaussian is described with the following seven parameters (see above):
- \f$ x_0^{(i)} \f$ is the expectancy value in the \f$ x \f$-Axis
- \f$ y_0^{(i)} \f$ is the expectancy value in the \f$ y \f$-Axis
- \f$ z_0^{(i)} \f$ is the expectancy value in the \f$ z \f$-Axis
- \f$ \sigma_x^{(i)} \f$ is the deviation in the \f$ x \f$-Axis
- \f$ \sigma_y^{(i)} \f$ is the deviation in the \f$ y \f$-Axis
- \f$ \sigma_z^{(i)} \f$ is the deviation in the \f$ z \f$-Axis
- \f$ a^{(i)} \f$ is the altitude of the gaussian.
A gauss function has the following form:
\f{eqnarray}{
\nonumber
f^{(i)}(x,y,z) = a^{(i)}
exp \left[ - \left(
\frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} +
\frac{(y - y_0^{(i)})^2}{2 (\sigma_y^{(i)})^2} +
\frac{(z - z_0^{(i)})^2}{2 (\sigma_z^{(i)})^2}
\right) \right].
\f}
A multigauss function has then the form:
\f{align*}{
f_N(x,y,z) =& \sum_{i=0}^{N}f^{(i)}(x,y,z)\\
=&\sum_{0}^{N} a^{(i)}
exp \left[ - \left(
\frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} +
\frac{(y - y_0^{(i)})^2}{2 (\sigma_y^{(i)})^2} +
\frac{(z - z_0^{(i)})^2}{2 (\sigma_z^{(i)})^2}
\right) \right].
\f}
The multigauss function \f$f_N\f$ will be evaluated at each voxel coordinate to become the voxel intensity.
\section Algorithm-for-calculating-statistic-in-a-sphere Algorithm for calculating statistic in a sphere
This section explains how we can find a sphere region which has a maximal mean value over all sphere regions with a fixed radius. Furthermore we want to calculate the maximal and minimal value in the wanted sphere.
To calculate the mean value in a sphere we integrate the gaussians over the whole sphere. The antiderivative is unknown as an explicit function, but we have a value table for the distribution function of the normal distribution \f$ \Phi(x) \f$ for \f$ x \f$ between \f$ -3.99 \f$ and \f$ 3.99 \f$ with step size \f$ 0.01 \f$. The only problem is that we cannot integrate over a spherical region, because we have an 3-dim integral and therefore are the integral limits dependent from each other and we cannot evaluate \f$ \Phi \f$. So we approximate the sphere with cuboids inside the sphere and prisms on the boundary of the sphere. We calculate these cuboids with the octree recursive method: We start by subdividing the wrapping box of the sphere in eight cuboids. Further we subdivide each cuboid in eight cuboids and check for each of them, whether it is inside or outside the sphere or whether it intersects the sphere surface. We save those of them, which are inside the sphere and continue to subdivide the cuboids that intersect the sphere until the recursion breaks. In the last step we take the half of the cuboids on the boundary and this are the prisms. Now we can calculate and sum the integrals over the cuboids and divide through the volume of the body to obtain the mean value.
For each cuboid \f$ Q = [a_1, b_1]\times[a_2, b_2]\times[a_3, b_3] \f$ we apply Fubini's theorem for integrable functions and become for the integral the following:
\f{align*}{
m_k =& \sum_{i=0}^{N} \int_{Q} f^{(i)}(x,y,z)dx\\
=&\sum_{i=0}^{N} a^{(i)} \int_{Q}
exp \left[ - \left(
\frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} +
\frac{(y - y_0^{(i)})^2}{2 (\sigma_y^{(i)})^2} +
\frac{(z - z_0^{(i)})^2}{2 (\sigma_z^{(i)})^2}
\right) \right] dx \\
=& \sum_{i=0}^{N} a^{(i)} \int_{a_1}^{b_1} exp \left[ - \frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} \right] dx
\int_{a_2}^{b_2}exp \left[ -\frac{(y - y_0^{(i)})^2}{2 (\sigma_y^{(i)})^2} \right]dx
\int_{a_3}^{b_3}exp \left[ -\frac{(z - z_0^{(i)})^2}{2 (\sigma_z^{(i)})^2} \right]dx.
\f}
So we calculate three one dimensional integrals:
\f{align*}{
\int_{a}^{b} & exp \left[ - \frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} \right] dx \\
=&\int_{-\infty}^{b} exp \left[ - \frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} \right] dx - \int_{-\infty}^{a} exp \left[ - \frac{(x - x_0^{(i)})^2}{2 (\sigma_x^{(i)})^2} \right] dx \\
=& \sigma_x^{(i)} \left[\int_{-\infty}^{(a - x_0^{(i)})/ \sigma_x^{(i)}} e^{-\frac{t^2}{2}} dt
- \int_{-\infty}^{(b - x_0^{(i)})/ \sigma_x^{(i)}} e^{-\frac{t^2}{2}}dt \right] \\
=&\sigma_x^{(i)} \sqrt{(\pi)} \left[ \Phi \left( \frac{(a - x_0^{(i)})^2}{\sigma_x^{(i)}} \right) - \Phi \left ( \frac{(b - x_0^{(i)})^2}{\sigma_x^{(i)}} \right) \right].
\f}
and become for the integral over \f$ Q \f$:
\f{align*}{
m_k =& \sum_{i=0}^{N} \sigma_x^{(i)} \sigma_y^{(i)} \sigma_z^{(i)} \pi^{1.5}
\left[ \Phi \left( \frac{(a_1 - x_0^{(i)})^2}{\sigma_x^{(i)}} \right) - \Phi \left ( \frac{(b_1 - x_0^{(i)})^2}{\sigma_x^{(i)}} \right) \right]\times \\
&\left[ \Phi \left( \frac{(a_2 - y_0^{(i)})^2}{\sigma_y^{(i)}} \right) - \Phi \left ( \frac{(b_2 - y_0^{(i)})^2}{\sigma_y^{(i)}} \right) \right]\times
\left[ \Phi \left( \frac{(a_3 - z_0^{(i)})^2}{\sigma_z^{(i)}} \right) - \Phi \left ( \frac{(b_3 - z_0^{(i)})^2}{\sigma_z^{(i)}} \right) \right].
\f}
For the integral over the prism we take the half of the integral over the corresponding cuboid.
Altogether we find the mean value in the sphere as:
\f{align*}{
\left( \sum_{Q_k \text{ Cuboid}} m_k + \sum_{P_l \text{ Prism}} 0.5 m_l \right )/Volume(B),
\f}
where Volume(B) is the volume of the body that approximate the sphere.
Now we know how to calculate the mean value in a sphere for given midpoint and radius. So we assume each voxel in the given image to be the sphere midpoint and we calculate the mean value as described above. If the new mean value is greater than the "maximum-until-now", we take the new value to be the "maximum-until-now". Then we go to the next voxel and make the same calculation and so on. At the same time we save the coordinates of the midpoint voxel.
After we found the midpoint and the maximal mean value, we can calculate the maximum and the minimum in the sphere: we just traverse all the voxels in the region and take the maximum and minimum value and the respective coordinates.
\section Input-and-output Input and output
An example for an input in the command-line is:
\verbatim
mitkMultiGaussianTest C:/temp/outputFile C:/temp/inputFile.xml
\endverbatim
Here is outputFile the name of the gaussian image with extension .nrrd and at the same time the name of the output file with extension .xml, which is the same as the inputFile, only added the calculated mean value, max and min and the corresponding indexes in the statistic tag. Here we see an example for the input and output .xml file:
\verbatim
INPUT:
<testcase>
<testimage image-rows="20" image-columns="20" image-slices="20" numberOfGaussians="2" spacingX="1" spacingY="1" spacingZ="1" entireHotSpotInImage="1">
<gaussian centerIndexX="4" centerIndexY="16" centerIndexZ="10" deviationX="7" deviationY="7" deviationZ="7" altitude="200"/>
<gaussian centerIndexX="18" centerIndexY="2" centerIndexZ="10" deviationX="1" deviationY="1" deviationZ="1" altitude="210"/>
</testimage>
<segmentation numberOfLabels="1" hotspotRadiusInMM="6.2035">
<roi label="1" maximumSizeX="20" minimumSizeX="0" maximumSizeY="20" minimumSizeY="0" maximumSizeZ="20" minimumSizeZ="0"/>
</segmentation>
</testcase>
\endverbatim
\verbatim
OUTPUT:
<testcase>
<testimage image-rows="20" image-columns="20" image-slices="20" numberOfGaussians="2" spacingX="1" spacingY="1" spacingZ="1" entireHotSpotInImage="1">
<gaussian centerIndexX="4" centerIndexY="16" centerIndexZ="10" deviationX="7" deviationY="7" deviationZ="7" altitude="200"/>
<gaussian centerIndexX="18" centerIndexY="2" centerIndexZ="10" deviationX="1" deviationY="1" deviationZ="1" altitude="210"/>
</testimage>
<segmentation numberOfLabels="1" hotspotRadiusInMM="6.2035">
<roi label="1" maximumSizeX="20" minimumSizeX="0" maximumSizeY="20" minimumSizeY="0" maximumSizeZ="20" minimumSizeZ="0"/>
</segmentation>
<statistic hotspotIndexX="6" hotspotIndexY="13" hotspotIndexZ="10" peak="141.544" mean="141.544" maximumIndexX="4" maximumIndexY="16" maximumIndexZ="10" maximum="200" minimumIndexX="9" minimumIndexY="8" minimumIndexZ="8" minimum="77.4272"/>
</testcase>
\endverbatim
\subsection Parameter-for-the-input Parameter for the input
In the tag \a testimage we describe the image that we generate. Image rows/columns/slices gives the number of rows/columns/slices of the image; \a numberOfGaussians is the number of gauss functions (\f$ N \f$); spacing defines the extend of one voxel for each direction. The parameter \a entireHotSpotInImage determines whether the whole sphere is in the image included (\f$ = 1 \f$) or only the midpoint of the sphere is inside the image.
NOTE: When the \a entireHotSpotInImage \f$ = 0 \f$ it is possible that we find the midpoint of the sphere on the border of the image. In this case we cut the approximation of the sphere, so that we become a body, which is completely inside the image, but not a "sphere" anymore. To that effect is the volume of the body decreased and that could lead to unexpected results.
In the subtag \a gaussian we describe each gauss function as mentioned in the second section.
In the tag \a segmentation we define the radius of the wanted sphere in mm (\a hotspotRadiusInMM ). We can also set the number of labels (\a numberOfLabels ) to be an positive number and this determines the number of regions of interest (ROI). In each ROI we find the sphere with the wanted properties and midpoint inside the ROI, but not necessarily the whole sphere. In the subtag \a roi we set label number and the index coordinates for the borders of the roi.
*/
template< typename TOutputImage >
class ITK_EXPORT MultiGaussianImageSource:public ImageSource< TOutputImage >
{
public:
/** Standard class typedefs. */
typedef MultiGaussianImageSource Self;
typedef ImageSource< TOutputImage > Superclass;
typedef SmartPointer< Self > Pointer;
typedef SmartPointer< const Self > ConstPointer;
/** Typedef for the output image PixelType. */
typedef typename TOutputImage::PixelType OutputImagePixelType;
/** Typedef to describe the output image region type. */
typedef typename TOutputImage::RegionType OutputImageRegionType;
/** Method for creation through the object factory. */
itkNewMacro(Self);
/** Basic types from the OutputImageType */
typedef typename TOutputImage::SizeType SizeType;
typedef typename TOutputImage::IndexType IndexType;
typedef typename TOutputImage::SpacingType SpacingType;
typedef typename TOutputImage::PointType PointType;
typedef typename SizeType::SizeValueType SizeValueType;
typedef SizeValueType SizeValueArrayType[TOutputImage::ImageDimension];
typedef typename TOutputImage::SpacingValueType SpacingValueType;
typedef SpacingValueType SpacingValueArrayType[TOutputImage::ImageDimension];
typedef typename TOutputImage::PointValueType PointValueType;
typedef PointValueType PointValueArrayType[TOutputImage::ImageDimension];
typedef typename itk::ImageRegion<3> ::SizeValueType SizeRegionType;
/** Typedef to describe the sphere radius type. */
typedef double RadiusType;
/** Typedef to describe the standard vector type. */
typedef std::vector<double> VectorType;
/** Typedef to describe the itk vector type. */
typedef Vector<double, TOutputImage::ImageDimension> ItkVectorType;
/** Typedef to describe the ImageRegionIteratorWithIndex type. */
typedef ImageRegionIteratorWithIndex<TOutputImage> IteratorType;
- /** Typedef to describe the Poiner type at the output image. */
+ /** Typedef to describe the Pointer type at the output image. */
typedef typename TOutputImage::Pointer ImageType;
typedef MapContainer<unsigned int, PointType> MapContainerPoints;
typedef MapContainer<unsigned int, double> MapContainerRadius;
/** Set/Get size of the output image. */
itkSetMacro(Size, SizeType);
virtual void SetSize(SizeValueArrayType sizeArray);
virtual const SizeValueType * GetSize() const;
/** Set/Get spacing of the output image. */
itkSetMacro(Spacing, SpacingType);
virtual void SetSpacing(SpacingValueArrayType spacingArray);
virtual const SpacingValueType * GetSpacing() const;
- /** Set/Get origin of the output image. This programm works proper only with origin [0.0, 0.0, 0.0] */
+ /** Set/Get origin of the output image. This program works proper only with origin [0.0, 0.0, 0.0] */
itkSetMacro(Origin, PointType);
virtual void SetOrigin(PointValueArrayType originArray);
virtual const PointValueType * GetOrigin() const;
/** Get the number of gaussian functions in the output image. */
virtual unsigned int GetNumberOfGaussians() const;
/** Set the number of gaussian function. */
virtual void SetNumberOfGausssians( unsigned int );
/** Set/Get the radius of the sphere. */
virtual RadiusType GetRadius() const;
virtual void SetRadius( RadiusType radius );
/** Get the maximal mean value in a sphere over all possible spheres with midpoint in the image. */
virtual const OutputImagePixelType GetMaxMeanValue() const;
/** Get the index of the midpoint of a sphere with the maximal mean value.*/
virtual const IndexType GetSphereMidpoint() const;
/** Calculates the value of the multigaussian function at a Point given by its coordinates [x, y, z]. */
virtual double MultiGaussianFunctionValueAtPoint(double , double, double);
/** Adds a multigaussian defined by the parameter: CenterX, CenterY, CenterZ, SigmaX, SigmaY, SigmaZ, Altitude.
All parameters should have the same size, which determinates the number of the gaussian added. */
virtual void AddGaussian( VectorType centerX, VectorType centerY, VectorType centerZ, VectorType sigmaX, VectorType sigmaY, VectorType sigmaZ, VectorType altitude);
/** Calculates and set the index of the midpoint of the sphere with the maximal mean value as well as the mean value. */
virtual void CalculateTheMidpointAndTheMeanValueWithOctree();
/** Calculates and set the index an the value of maximulm and minimum in the wanted sphere. */
virtual void CalculateMaxAndMinInSphere();
/** Get the index in the sphere with maximal value. */
virtual const IndexType GetMaxValueIndexInSphere() const;
/** Get the maximal value in the sphere. */
virtual const OutputImagePixelType GetMaxValueInSphere() const;
/** Get the index in the sphere with minimal value. */
virtual const IndexType GetMinValueIndexInSphere() const;
/** Get the minimal value in the sphere. */
virtual const OutputImagePixelType GetMinValueInSphere() const;
/** Set the region of interest. */
virtual void SetRegionOfInterest(ItkVectorType, ItkVectorType);
/** Write a .mps file to visualise the point in the sphere. */
virtual void WriteXMLToTestTheCuboidInsideTheSphere();
/**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. */
virtual void CalculateEdgesInSphere( PointType globalCoordinateMidpointCuboid, PointType globalCoordinateMidpointSphere, double cuboidRadius, int level);
/**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. */
virtual double MultiGaussianFunctionValueAtCuboid(double xMin, double xMax, double yMin, double yMax, double zMin, double zMax);
/** Inseret the midpoints of cuboid in a vector m_Midpoints, so that we can visualise it. */
virtual void InsertPoints( PointType globalCoordinateMidpointCuboid, double cuboidRadius);
- /** Start the octree recursion in eigth directions for the sphere with midpoint globalCoordinateMidpointSphere. */
+ /** Start the octree recursion in eight directions for the sphere with midpoint globalCoordinateMidpointSphere. */
virtual void GenerateCuboidSegmentationInSphere( PointType globalCoordinateMidpointSphere );
- /** Get the the values of the cumulative distribution function of the normal distribution. */
+ /** Get the values of the cumulative distribution function of the normal distribution. */
virtual double FunctionPhi(double value);
/** Check if a cuboid with midpoint globalCoordinateMidpointCuboid and side length sideLength intersect the sphere with midpoint globalCoordinateMidpointSphere boundary. */
virtual unsigned int IntersectTheSphere( PointType globalCoordinateMidpointCuboid, PointType globalCoordinateMidpointSphere, double sideLength);
- /** Set the tabel values of the distribution function of the normal distribution. */
+ /** Set the table values of the distribution function of the normal distribution. */
void SetNormalDistributionValues();
/** Set the minimum possible pixel value. By default, it is
* NumericTraits<TOutputImage::PixelType>::min(). */
itkSetClampMacro( Min, OutputImagePixelType,
NumericTraits< OutputImagePixelType >::NonpositiveMin(),
NumericTraits< OutputImagePixelType >::max() );
/** Check if a index is inside the image*/
bool IsInImage(IndexType index);
/** Get the minimum possible pixel value. */
itkGetConstMacro(Min, OutputImagePixelType);
/** Set the maximum possible pixel value. By default, it is
* NumericTraits<TOutputImage::PixelType>::max(). */
itkSetClampMacro( Max, OutputImagePixelType,
NumericTraits< OutputImagePixelType >::NonpositiveMin(),
NumericTraits< OutputImagePixelType >::max() );
/** Get the maximum possible pixel value. */
itkGetConstMacro(Max, OutputImagePixelType);
protected:
MultiGaussianImageSource();
~MultiGaussianImageSource() override;
void PrintSelf(std::ostream & os, Indent indent) const override;
void GenerateData() override;
void GenerateOutputInformation() override;
private:
MultiGaussianImageSource(const MultiGaussianImageSource &); //purposely not implemented
void operator=(const MultiGaussianImageSource &); //purposely not implemented
SizeType m_Size; //size of the output image
SpacingType m_Spacing; //spacing
PointType m_Origin; //origin
OutputImagePixelType m_MaxValueInSphere; //maximal value in the wanted sphere
IndexType m_MaxValueIndexInSphere; //index of the maximal value in the wanted sphere
OutputImagePixelType m_MinValueInSphere; //minimal value in the wanted sphere
IndexType m_MinValueIndexInSphere; //index of the minimal value in the wanted sphere
unsigned int m_NumberOfGaussians; //number of Gaussians
RadiusType m_Radius; //radius of the sphere
unsigned int m_RadiusStepNumber; //number of steps to traverse the sphere radius
OutputImagePixelType m_MeanValue; //mean value in the wanted sphere
OutputImagePixelType m_ValueAtMidpoint; //value at the midpoint of the wanted sphere
IndexType m_SphereMidpoint; //midpoint of the wanted sphere
VectorType m_SigmaX; //deviation in the x-axis
VectorType m_SigmaY; //deviation in the y-axis
VectorType m_SigmaZ; //deviation in the z-axis
VectorType m_CenterX; //x-coordinate of the mean value of Gaussians
VectorType m_CenterY; //y-coordinate of the mean value of Gaussians
VectorType m_CenterZ; //z-coordinate of the mean value of Gaussians
VectorType m_Altitude; //amplitude
ItkVectorType m_RegionOfInterestMax; //maximal values for the coordinates in the region of interest
ItkVectorType m_RegionOfInterestMin; //minimal values for the coordinates in the region of interest
typename TOutputImage::PixelType m_Min; //minimum possible value
typename TOutputImage::PixelType m_Max; //maximum possible value
- PointType m_GlobalCoordinate; //physical coordiante of the sphere midpoint
+ PointType m_GlobalCoordinate; //physical coordinate of the sphere midpoint
bool m_WriteMPS; //1 = write a MPS File to visualise the cuboid midpoints of one approximation of the sphere
MapContainerPoints m_Midpoints; //the midpoints of the cuboids
MapContainerRadius m_RadiusCuboid; //the radius ( = 0.5 * side length) of the cuboids (in the same order as the midpoints in m_Midpoints)
double m_Volume; //the volume of the body, that approximize the sphere
double m_NormalDistValues [410];//normal distribution values
double m_meanValueTemp; //= m_Volume * meanValue in each sphere
// The following variables are deprecated, and provided here just for
// backward compatibility. It use is discouraged.
mutable PointValueArrayType m_OriginArray;
mutable SpacingValueArrayType m_SpacingArray;
};
} // end namespace itk
#ifndef ITK_MANUAL_INSTANTIATION
#include "itkMultiGaussianImageSource.hxx"
#endif
#endif
diff --git a/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx b/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx
index 03086b02e9..bd5fc55cb2 100644
--- a/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx
+++ b/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx
@@ -1,998 +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
*
* https://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. */
+ /* Start the octree recursion in eight 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;
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
+ //this calculates 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/mitkHotspotMaskGenerator.cpp b/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp
index b587208387..09a04be161 100644
--- a/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp
+++ b/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp
@@ -1,542 +1,542 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <itkVnlFFTImageFilterInitFactory.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_InternalMask = mitk::Image::New();
m_InternalMaskUpdateTime = 0;
}
HotspotMaskGenerator::~HotspotMaskGenerator()
{
}
unsigned int HotspotMaskGenerator::GetNumberOfMasks() const
{
return 1;
}
mitk::Image::ConstPointer HotspotMaskGenerator::DoGetMask(unsigned int)
{
if (IsUpdateRequired())
{
if ( m_InputImage.IsNull() )
{
throw std::runtime_error( "Error: image empty!" );
}
if ( m_InputImage->GetTimeGeometry()->IsValidTimePoint(m_TimePoint) )
{
throw std::runtime_error( "Error: invalid time point!" );
}
auto timeSliceImage = SelectImageByTimePoint(m_InputImage, m_TimePoint);
m_internalMask2D = nullptr; // is this correct when this variable holds a smart pointer?
m_internalMask3D = nullptr;
if ( m_Mask != nullptr )
{
m_Mask->SetTimePoint(m_TimePoint);
mitk::Image::ConstPointer timeSliceMask = m_Mask->GetMask(0);
if ( timeSliceImage->GetDimension() == 3 )
{
- itk::Image<unsigned short, 3>::Pointer noneConstMaskImage; //needed to work arround the fact that CastToItkImage currently does not support const itk images.
+ itk::Image<unsigned short, 3>::Pointer noneConstMaskImage; //needed to work around the fact that CastToItkImage currently does not support const itk images.
CastToItkImage(timeSliceMask, noneConstMaskImage);
m_internalMask3D = noneConstMaskImage;
AccessFixedDimensionByItk_2(timeSliceImage, CalculateHotspotMask, 3, m_internalMask3D.GetPointer(), m_Label);
}
else if ( timeSliceImage->GetDimension() == 2 )
{
- itk::Image<unsigned short, 2>::Pointer noneConstMaskImage; //needed to work arround the fact that CastToItkImage currently does not support const itk images.
+ itk::Image<unsigned short, 2>::Pointer noneConstMaskImage; //needed to work around the fact that CastToItkImage currently does not support const itk images.
CastToItkImage(timeSliceMask, noneConstMaskImage);
m_internalMask2D = noneConstMaskImage;
AccessFixedDimensionByItk_2(timeSliceImage, CalculateHotspotMask, 2, m_internalMask2D.GetPointer(), m_Label);
}
else
{
throw std::runtime_error( "Error: invalid image dimension" );
}
}
else
{
if ( timeSliceImage->GetDimension() == 3 )
{
AccessFixedDimensionByItk_2(timeSliceImage, CalculateHotspotMask, 3, m_internalMask3D.GetPointer(), m_Label);
}
else if ( timeSliceImage->GetDimension() == 2 )
{
AccessFixedDimensionByItk_2(timeSliceImage, CalculateHotspotMask, 2, m_internalMask2D.GetPointer(), m_Label);
}
else
{
throw std::runtime_error( "Error: invalid image dimension" );
}
}
this->Modified();
}
m_InternalMaskUpdateTime = m_InternalMask->GetMTime();
return m_InternalMask;
}
template <typename TPixel, unsigned int VImageDimension >
HotspotMaskGenerator::ImageExtrema
HotspotMaskGenerator::CalculateExtremaWorld( const itk::Image<TPixel, VImageDimension>* inputImage,
const itk::Image<unsigned short, VImageDimension>* maskImage,
- double neccessaryDistanceToImageBorderInMM,
+ double necessaryDistanceToImageBorderInMM,
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 );
+ bool keepDistanceToImageBorders( necessaryDistanceToImageBorderInMM > 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);
+ distanceInPixels[dimension] = int( necessaryDistanceToImageBorderInMM / 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::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)
+ subVoxelIndexPosition = voxelPosition + subVoxelOffset; // this COULD be integrated into the for-loops if necessary (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;
itk::VnlFFTImageFilterInitFactory::RegisterFactories();
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(const itk::Image<TPixel, VImageDimension>* inputImage,
const itk::Image<unsigned short, VImageDimension>* 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()");
}
typename MaskImageType::ConstPointer usedMask = maskImage;
// 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)
{
auto defaultMask = 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();
defaultMask->SetRegions(maskRegion);
defaultMask->Allocate();
defaultMask->SetOrigin(maskOrigin);
defaultMask->SetSpacing(maskSpacing);
defaultMask->SetDirection(maskDirection);
defaultMask->FillBuffer(1);
usedMask = defaultMask;
label = 1;
}
// find maximum in convolution image, given the current mask
double requiredDistanceToBorder = m_HotspotMustBeCompletelyInsideImage ? m_HotspotRadiusInMM : -1.0;
ImageExtrema convolutionImageInformation = CalculateExtremaWorld(convolutionImage.GetPointer(), usedMask.GetPointer(), 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/mitkHotspotMaskGenerator.h b/Modules/ImageStatistics/mitkHotspotMaskGenerator.h
index ba40564049..996242cfc9 100644
--- a/Modules/ImageStatistics/mitkHotspotMaskGenerator.h
+++ b/Modules/ImageStatistics/mitkHotspotMaskGenerator.h
@@ -1,155 +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.
============================================================================*/
#ifndef mitkHotspotMaskGenerator_h
#define mitkHotspotMaskGenerator_h
#include <itkObject.h>
#include <mitkImage.h>
#include <itkImage.h>
#include <itkTimeStamp.h>
#include <stdexcept>
#include <MitkImageStatisticsExports.h>
#include <mitkImageTimeSelector.h>
#include <mitkMaskGenerator.h>
namespace mitk
{
/**
* @warning Until T30375 is not clarified the class should be deemed deprecated/erroneous and should not
* be used
* @brief The HotspotMaskGenerator class is used when a hotspot has to be found in an image. A hotspot is
* the region of the image where the mean intensity is maximal (=brightest spot). It is usually used in PET scans.
* The identification of the hotspot is done as follows: First a cubic (or circular, if image is 2d)
* mask of predefined size is generated. This mask is then convolved with the input image (in fourier domain).
* The maximum value of the convolved image then corresponds to the hotspot.
* If a maskGenerator is set, only the pixels of the convolved image where the corresponding mask is == @a label
* are searched for the maximum value.
*/
class MITKIMAGESTATISTICS_EXPORT HotspotMaskGenerator: public MaskGenerator
{
public:
/** Standard Self typedef */
typedef HotspotMaskGenerator Self;
typedef MaskGenerator Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
/** Method for creation through the object factory. */
itkNewMacro(Self); /** Runtime information support. */
itkTypeMacro(HotspotMaskGenerator, MaskGenerator);
unsigned int GetNumberOfMasks() const override;
/**
@brief Set a mask (can be nullptr if no mask is desired)
*/
itkSetObjectMacro(Mask, MaskGenerator);
/**
@brief Set the radius of the hotspot (in MM)
*/
itkGetConstMacro(HotspotRadiusInMM, double);
itkSetMacro(HotspotRadiusInMM, double);
/**
@brief Define whether the hotspot must be completely inside the image. Default is true
*/
itkGetConstMacro(HotspotMustBeCompletelyInsideImage, bool);
itkSetMacro(HotspotMustBeCompletelyInsideImage, bool);
/**
- @brief If a maskGenerator is set, this detemines which mask value is used
+ @brief If a maskGenerator is set, this determines which mask value is used
*/
itkSetMacro(Label, unsigned short);
protected:
HotspotMaskGenerator();
~HotspotMaskGenerator() override;
Image::ConstPointer DoGetMask(unsigned int) override;
class ImageExtrema
{
public:
bool Defined;
double Max;
double Min;
vnl_vector<int> MaxIndex;
vnl_vector<int> MinIndex;
ImageExtrema()
:Defined(false)
,Max(itk::NumericTraits<double>::min())
,Min(itk::NumericTraits<double>::max())
{
}
};
private:
/** \brief Returns size of convolution kernel depending on spacing and radius. */
template <unsigned int VImageDimension>
itk::Size<VImageDimension>
CalculateConvolutionKernelSize(double spacing[VImageDimension], double radiusInMM);
/** \brief Generates image of kernel which is needed for convolution. */
template <unsigned int VImageDimension>
itk::SmartPointer< itk::Image<float, VImageDimension> >
GenerateHotspotSearchConvolutionKernel(double spacing[VImageDimension], double radiusInMM);
/** \brief Convolves image with spherical kernel image. Used for hotspot calculation. */
template <typename TPixel, unsigned int VImageDimension>
itk::SmartPointer< itk::Image<TPixel, VImageDimension> >
GenerateConvolutionImage( const itk::Image<TPixel, VImageDimension>* inputImage );
/** \brief Fills pixels of the spherical hotspot mask. */
template < typename TPixel, unsigned int VImageDimension>
void
FillHotspotMaskPixels( itk::Image<TPixel, VImageDimension>* maskImage,
itk::Point<double, VImageDimension> sphereCenter,
double sphereRadiusInMM);
/** \brief */
template <typename TPixel, unsigned int VImageDimension>
void
CalculateHotspotMask(const itk::Image<TPixel, VImageDimension>* inputImage,
const itk::Image<unsigned short, VImageDimension>* maskImage,
unsigned int label);
template <typename TPixel, unsigned int VImageDimension >
ImageExtrema CalculateExtremaWorld( const itk::Image<TPixel, VImageDimension>* inputImage,
const itk::Image<unsigned short, VImageDimension>* maskImage,
- double neccessaryDistanceToImageBorderInMM,
+ double necessaryDistanceToImageBorderInMM,
unsigned int label);
bool IsUpdateRequired() const;
HotspotMaskGenerator(const HotspotMaskGenerator &);
HotspotMaskGenerator & operator=(const HotspotMaskGenerator &);
MaskGenerator::Pointer m_Mask;
mitk::Image::Pointer m_InternalMask;
itk::Image<unsigned short, 2>::ConstPointer m_internalMask2D;
itk::Image<unsigned short, 3>::ConstPointer m_internalMask3D;
double m_HotspotRadiusInMM;
bool m_HotspotMustBeCompletelyInsideImage;
unsigned short m_Label;
vnl_vector<int> m_ConvolutionImageMinIndex, m_ConvolutionImageMaxIndex;
unsigned long m_InternalMaskUpdateTime;
};
}
#endif
diff --git a/Modules/ImageStatistics/mitkImageMaskGenerator.h b/Modules/ImageStatistics/mitkImageMaskGenerator.h
index e97bc76855..e9db4ad457 100644
--- a/Modules/ImageStatistics/mitkImageMaskGenerator.h
+++ b/Modules/ImageStatistics/mitkImageMaskGenerator.h
@@ -1,61 +1,61 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkImageMaskGenerator_h
#define mitkImageMaskGenerator_h
#include <mitkImage.h>
#include <MitkImageStatisticsExports.h>
#include <mitkMaskGenerator.h>
#include <itkObject.h>
#include <itkSmartPointer.h>
namespace mitk
{
class MITKIMAGESTATISTICS_EXPORT ImageMaskGenerator: public MaskGenerator
{
public:
/** Standard Self typedef */
typedef ImageMaskGenerator Self;
typedef MaskGenerator Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
/** Method for creation through the object factory. */
itkNewMacro(Self); /** Runtime information support. */
- itkTypeMacro(BinaryImageMaskGenerator, MaskGenerator);
+ itkTypeMacro(ImageMaskGenerator, MaskGenerator);
unsigned int GetNumberOfMasks() const override;
itkSetConstObjectMacro(ImageMask, Image)
protected:
ImageMaskGenerator():Superclass(){
m_InternalMaskUpdateTime = 0;
}
Image::ConstPointer DoGetMask(unsigned int) override;
private:
bool IsUpdateRequired() const;
void UpdateInternalMask();
mitk::Image::ConstPointer m_ImageMask;
mitk::Image::ConstPointer m_InternalMask;
unsigned long m_InternalMaskUpdateTime;
};
}
#endif
diff --git a/Modules/ImageStatistics/mitkImageStatisticsCalculator.h b/Modules/ImageStatistics/mitkImageStatisticsCalculator.h
index 50ab83f181..f29b365329 100644
--- a/Modules/ImageStatistics/mitkImageStatisticsCalculator.h
+++ b/Modules/ImageStatistics/mitkImageStatisticsCalculator.h
@@ -1,117 +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.
============================================================================*/
#ifndef mitkImageStatisticsCalculator_h
#define mitkImageStatisticsCalculator_h
#include <MitkImageStatisticsExports.h>
#include <mitkImage.h>
#include <mitkMaskGenerator.h>
#include <mitkImageStatisticsContainer.h>
namespace mitk
{
class MITKIMAGESTATISTICS_EXPORT ImageStatisticsCalculator: public itk::Object
{
public:
/** Standard Self typedef */
typedef ImageStatisticsCalculator Self;
typedef itk::Object Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
/** Method for creation through the object factory. */
itkNewMacro(Self); /** Runtime information support. */
- itkTypeMacro(ImageStatisticsCalculator_v2, itk::Object);
+ itkTypeMacro(ImageStatisticsCalculator, itk::Object);
typedef double statisticsValueType;
typedef std::map<std::string, statisticsValueType> statisticsMapType;
typedef itk::Statistics::Histogram<double> HistogramType;
typedef unsigned short MaskPixelType;
using LabelIndex = ImageStatisticsContainer::LabelValueType;
/**Documentation
@brief Set the image for which the statistics are to be computed.*/
void SetInputImage(const mitk::Image* image);
/**Documentation
@brief Set the mask generator that creates the mask which is to be used to calculate statistics. If no more mask is desired simply set @param mask to nullptr*/
void SetMask(mitk::MaskGenerator* mask);
/**Documentation
@brief Set this if more than one mask should be applied (for instance if a IgnorePixelValueMask were to be used alongside with a segmentation).
Both masks are combined using pixel wise AND operation. The secondary mask does not have to be the same size than the primary but they need to have some overlap*/
void SetSecondaryMask(mitk::MaskGenerator* mask);
/**Documentation
@brief Set number of bins to be used for histogram statistics. If Bin size is set after number of bins, bin size will be used instead!*/
void SetNBinsForHistogramStatistics(unsigned int nBins);
/**Documentation
@brief Retrieve the number of bins used for histogram statistics. Careful: The return value does not indicate whether NBins or BinSize is used.
That solely depends on which parameter has been set last.*/
unsigned int GetNBinsForHistogramStatistics() const;
/**Documentation
@brief Set bin size to be used for histogram statistics. If nbins is set after bin size, nbins will be used instead!*/
void SetBinSizeForHistogramStatistics(double binSize);
/**Documentation
@brief Retrieve the bin size for histogram statistics. Careful: The return value does not indicate whether NBins or BinSize is used.
That solely depends on which parameter has been set last.*/
double GetBinSizeForHistogramStatistics() const;
/**Documentation
@brief Returns the statistics. If these requested statistics are not computed yet the computation is done as well.
*/
ImageStatisticsContainer* GetStatistics();
protected:
ImageStatisticsCalculator(){
m_nBinsForHistogramStatistics = 100;
m_binSizeForHistogramStatistics = 10;
m_UseBinSizeOverNBins = false;
};
private:
//Calculates statistics for each timestep for image
template < typename TPixel, unsigned int VImageDimension >
void InternalCalculateStatisticsUnmasked(const itk::Image< TPixel, VImageDimension >* image, TimeStepType timeStep);
template < typename TPixel, unsigned int VImageDimension >
void InternalCalculateStatisticsMasked(const itk::Image< TPixel, VImageDimension >* image, TimeStepType timeStep);
template < typename TPixel, unsigned int VImageDimension >
double GetVoxelVolume(const itk::Image<TPixel, VImageDimension>* image) const;
bool IsUpdateRequired() const;
mitk::Image::ConstPointer m_Image;
mitk::Image::ConstPointer m_ImageTimeSlice;
mitk::Image::ConstPointer m_InternalImageForStatistics;
mitk::MaskGenerator::Pointer m_MaskGenerator;
mitk::Image::ConstPointer m_InternalMask;
mitk::MaskGenerator::Pointer m_SecondaryMaskGenerator;
mitk::Image::ConstPointer m_SecondaryMask;
unsigned int m_nBinsForHistogramStatistics;
double m_binSizeForHistogramStatistics;
bool m_UseBinSizeOverNBins;
ImageStatisticsContainer::Pointer m_StatisticContainer;
};
}
#endif
diff --git a/Modules/ImageStatistics/mitkMaskGenerator.h b/Modules/ImageStatistics/mitkMaskGenerator.h
index 2e5cf11c1c..7725639e63 100644
--- a/Modules/ImageStatistics/mitkMaskGenerator.h
+++ b/Modules/ImageStatistics/mitkMaskGenerator.h
@@ -1,73 +1,73 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkMaskGenerator_h
#define mitkMaskGenerator_h
#include <MitkImageStatisticsExports.h>
#include <mitkImage.h>
#include <itkRegion.h>
#include <itkObject.h>
#include <itkSmartPointer.h>
namespace mitk
{
/**
* \class MaskGenerator
* \brief Base Class for all Mask Generators. Mask generators are classes that provide functionality for the
-* creation of binary (or unsigned short) masks that can be applied to an image. See dervied classes for more
+* creation of binary (or unsigned short) masks that can be applied to an image. See derived classes for more
* information.
*/
class MITKIMAGESTATISTICS_EXPORT MaskGenerator: public itk::Object
{
public:
mitkClassMacroItkParent(MaskGenerator, itk::Object);
virtual unsigned int GetNumberOfMasks() const = 0;
/**
* @brief GetMask returns the requested (multi) label mask.
* @param maskID Parameter indicating which mask should be returned.
* @return mitk::Image::Pointer of generated mask requested
*/
mitk::Image::ConstPointer GetMask(unsigned int maskID);
/**
* @brief GetReferenceImage per default returns the inputImage (as set by SetInputImage). If no input image is set it will return a nullptr.
*/
virtual mitk::Image::ConstPointer GetReferenceImage();
/**
* @brief SetInputImage is used to set the input image to the mask generator. Some subclasses require an input image, others don't. See the documentation of the specific Mask Generator for more information.
*/
itkSetConstObjectMacro(InputImage, mitk::Image);
itkSetMacro(TimePoint, TimePointType);
protected:
MaskGenerator();
/**
* @brief DoGetMask must be overridden by derived classes.
* @param maskID Parameter indicating which mask should be returned.
* @return mitk::Image::Pointer of generated mask requested
*/
virtual mitk::Image::ConstPointer DoGetMask(unsigned int maskID) = 0;
TimePointType m_TimePoint;
mitk::Image::ConstPointer m_InputImage;
private:
};
}
#endif
diff --git a/Modules/ImageStatistics/mitkMaskUtilities.h b/Modules/ImageStatistics/mitkMaskUtilities.h
index df08a30099..2acd180df1 100644
--- a/Modules/ImageStatistics/mitkMaskUtilities.h
+++ b/Modules/ImageStatistics/mitkMaskUtilities.h
@@ -1,88 +1,88 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkMaskUtilities_h
#define mitkMaskUtilities_h
#include <MitkImageStatisticsExports.h>
#include <mitkImage.h>
#include <mitkNodePredicateGeometry.h>
#include <itkImage.h>
namespace mitk
{
/**
* @brief Utility class for mask operations. It checks whether an image and a mask are compatible (spacing, orientation, etc...)
* and it can also crop an image to the LargestPossibleRegion of the Mask
*/
template <class TPixel, unsigned int VImageDimension>
class MaskUtilities: public itk::Object
{
public:
/** Standard Self typedef */
typedef MaskUtilities Self;
typedef itk::Object Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
/** Method for creation through the object factory. */
itkNewMacro(Self); /** Runtime information support. */
itkTypeMacro(MaskUtilities, itk::Object);
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::Image<unsigned short, VImageDimension> MaskType;
/**
* @brief Set image
*/
void SetImage(const ImageType* image);
/**
* @brief Set mask
*/
void SetMask(const MaskType* mask);
/**
* @brief Checks whether mask and image are compatible for joint access (as via iterators).
* Spacing and direction must be the same between the two and they must be aligned. Also, the mask must be completely inside the image
*/
bool CheckMaskSanity();
/**
* @brief Crops the image to the LargestPossibleRegion of the mask
*/
typename ImageType::ConstPointer ExtractMaskImageRegion();
protected:
MaskUtilities(): m_Image(nullptr), m_Mask(nullptr){}
~MaskUtilities() override{}
private:
const ImageType* m_Image;
const MaskType* m_Mask;
};
/** Tolerance used to check if the mask and input image are compatible for
- * coordinate aspects (orgin, size, grid alignment).*/
+ * coordinate aspects (origin, size, grid alignment).*/
constexpr double MASK_SUITABILITY_TOLERANCE_COORDINATE = NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION;
/** Tolerance used to check if the mask and input image are compatible for
* direction aspects (orientation of mask and image).*/
constexpr double MASK_SUITABILITY_TOLERANCE_DIRECTION = NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION;
}
#ifndef ITK_MANUAL_INSTANTIATION
#include <mitkMaskUtilities.tpp>
#endif
#endif
diff --git a/Modules/ImageStatistics/mitkPointSetStatisticsCalculator.h b/Modules/ImageStatistics/mitkPointSetStatisticsCalculator.h
index 9a2db4c055..0b52bb757d 100644
--- a/Modules/ImageStatistics/mitkPointSetStatisticsCalculator.h
+++ b/Modules/ImageStatistics/mitkPointSetStatisticsCalculator.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 mitkPointSetStatisticsCalculator_h
#define mitkPointSetStatisticsCalculator_h
#include <itkObject.h>
#include <MitkImageStatisticsExports.h>
#include <mitkPointSet.h>
namespace mitk
{
/**
* \brief Class for calculating statistics (like standard derivation, RMS, mean, etc.) for a PointSet.
*/
class MITKIMAGESTATISTICS_EXPORT PointSetStatisticsCalculator : public itk::Object
{
public:
mitkClassMacroItkParent( PointSetStatisticsCalculator, itk::Object );
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
mitkNewMacro1Param(PointSetStatisticsCalculator,mitk::PointSet::Pointer);
/** @brief Sets the point set which will be analysed. */
void SetPointSet(mitk::PointSet::Pointer pSet);
/** @return Returns the mean position of the analysed point set (only valid navigation data). Returns [0;0;0] if there is no valid navigation data.*/
mitk::Point3D GetPositionMean();
/** @return Returns the standard derivation of each component (x, y and z) of the analysed point set (only valid navigation data). Returns [0;0;0] if there is no valid navigation data.*/
mitk::Vector3D GetPositionStandardDeviation();
/** @return Returns the sample standard derivation of each component (x, y and z) of the analysed point set (only valid navigation data). Returns [0;0;0] if there is no valid navigation data.*/
mitk::Vector3D GetPositionSampleStandardDeviation();
- /** @return Returns the mean distance to the mean postion (=mean error) of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
+ /** @return Returns the mean distance to the mean position (=mean error) of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorMean();
/** @return Returns the standard derivation of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorStandardDeviation();
/** @return Returns the sample standard derivation of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorSampleStandardDeviation();
/** @return Returns the RMS of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorRMS();
/** @return Returns the median of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorMedian();
/** @return Returns the maximum of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorMax();
/** @return Returns the minimum of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */
double GetPositionErrorMin();
//#####################################################################################################
//this both methods are used by another class an so they are public... perhaps we want to move them
//out of this class because they have nothing to do with point sets.
/** @return returns the standard derivation of the given list (NOT of the point set).*/
double GetStabw(std::vector<double> list);
/** @return returns the sample standard derivation of the given list (NOT of the point set).*/
double GetSampleStabw(std::vector<double> list);
//#####################################################################################################
protected:
PointSetStatisticsCalculator();
explicit PointSetStatisticsCalculator(mitk::PointSet::Pointer);
~PointSetStatisticsCalculator() override;
// TODO: Remove the std::vector<mitk::Point3D> data structure and use mitk::PointSet everywhere
/** @return Returns a list with the distances to the mean of the list */
std::vector<double> GetErrorList(std::vector<mitk::Point3D> list);
/** @return Returns the mean of the point list. Returns [0;0;0] if the list is empty. */
mitk::Point3D GetMean(std::vector<mitk::Point3D> list);
/** @brief Converts a point set to a vector of Point3D. */
std::vector<mitk::Point3D> PointSetToVector(mitk::PointSet::Pointer pSet);
/** @return Returns true if all positions in the evaluated points set are equal. False if not. */
bool CheckIfAllPositionsAreEqual();
mitk::PointSet::Pointer m_PointSet;
double GetMean(std::vector<double> list);
double GetMedian(std::vector<double> list);
double GetMax(std::vector<double> list);
double GetMin(std::vector<double> list);
};
}
#endif
diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkDataGenerationJobBase.h b/Modules/ImageStatisticsUI/Qmitk/QmitkDataGenerationJobBase.h
index 4ba799839f..777bbd1bd0 100644
--- a/Modules/ImageStatisticsUI/Qmitk/QmitkDataGenerationJobBase.h
+++ b/Modules/ImageStatisticsUI/Qmitk/QmitkDataGenerationJobBase.h
@@ -1,77 +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.
============================================================================*/
#ifndef QmitkDataGenerationJobBase_h
#define QmitkDataGenerationJobBase_h
//QT
#include <QRunnable>
#include <QObject>
#include <QMetaType>
//MITK
#include <mitkBaseData.h>
#include <MitkImageStatisticsUIExports.h>
/*!
\brief QmitkDataGenerationJobBase
Base class for generation jobs used by QmitkDataGenerationBase and derived classes.
*/
class MITKIMAGESTATISTICSUI_EXPORT QmitkDataGenerationJobBase : public QObject, public QRunnable
{
Q_OBJECT
public:
/** Result map that indicates all results generated by the job.
The key is a job specific label for the results.*/
using ResultMapType = std::map<std::string, mitk::BaseData::Pointer>;
virtual ResultMapType GetResults() const = 0;
/** Calls RunComputation() and takes care of the error handling and result signalling.*/
void run() final;
/*!
- /brief Returns a flag the indicates if the job computation was successfull.*/
+ /brief Returns a flag the indicates if the job computation was successful.*/
bool GetComputationSuccessFlag() const;
std::string GetLastErrorMessage() const;
bool IsRunning() const;
signals:
void Error(QString err, const QmitkDataGenerationJobBase* job);
/*! @brief Signal is emitted when results are available.
@param results produced by the job and ready to be used.
@param job the job that produced the data
*/
void ResultsAvailable(ResultMapType results, const QmitkDataGenerationJobBase* job);
protected:
QmitkDataGenerationJobBase() = default;
virtual ~QmitkDataGenerationJobBase() = default;
/**Does the real computation. Returns true if there where results produced.*/
virtual bool RunComputation() = 0;
std::string m_LastErrorMessage;
private:
bool m_ComputationSuccessful = false;
bool m_IsRunning = false;
};
#endif
diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp
index b82ed8585e..8969b03af1 100644
--- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp
+++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp
@@ -1,521 +1,535 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkImageStatisticsContainerManager.h"
#include "mitkProportionalTimeGeometry.h"
#include "mitkStatisticsToImageRelationRule.h"
#include "mitkStatisticsToMaskRelationRule.h"
#include "QmitkStyleManager.h"
QmitkImageStatisticsTreeModel::QmitkImageStatisticsTreeModel(QObject *parent) : QmitkAbstractDataStorageModel(parent)
{
m_RootItem = std::make_unique<QmitkImageStatisticsTreeItem>();
}
QmitkImageStatisticsTreeModel ::~QmitkImageStatisticsTreeModel()
{
// set data storage to nullptr so that the event listener gets removed
this->SetDataStorage(nullptr);
};
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.get();
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)
{
if (item->isWIP() && item->childCount() == 0)
return QVariant(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/hourglass-half-solid.svg")));
else if (!item->isWIP())
{
auto label = item->GetLabelInstance();
if (label.IsNotNull())
{
QPixmap pixmap(QSize(20,20));
QColor color(label->GetColor().GetRed() * 255, label->GetColor().GetGreen() * 255, label->GetColor().GetBlue() * 255);
pixmap.fill(color);
return QVariant(QIcon(pixmap));
}
}
}
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.get();
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.get())
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
Qt::ItemFlags QmitkImageStatisticsTreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return {};
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 time step 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();
}
}
{
std::lock_guard<std::mutex> locked(m_Mutex);
m_Statistics = newStatistics;
m_StatisticNames = mitk::GetAllStatisticNames(m_Statistics);
BuildHierarchicalModel();
m_BuildTime.Modified();
}
}
void AddTimeStepTreeItems(const mitk::ImageStatisticsContainer* statistic, const mitk::DataNode* imageNode, const mitk::DataNode* maskNode, mitk::ImageStatisticsContainer::LabelValueType labelValue, const std::vector<std::string>& statisticNames, bool isWIP, QmitkImageStatisticsTreeItem* parentItem, bool& hasMultipleTimesteps)
{
// 4. hierarchy level: time steps (optional, only if >1 time step)
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->StatisticsExist(labelValue, i))
{
auto statisticsItem = new QmitkImageStatisticsTreeItem(
statistic->GetStatistics(labelValue,i), statisticNames, timeStepLabel, isWIP, parentItem, imageNode, maskNode);
parentItem->appendChild(statisticsItem);
}
else
{
auto statisticsItem = new QmitkImageStatisticsTreeItem(statisticNames, timeStepLabel, isWIP, true, parentItem, imageNode, maskNode);
parentItem->appendChild(statisticsItem);
}
}
}
hasMultipleTimesteps = hasMultipleTimesteps || (statistic->GetTimeSteps() > 1);
}
void AddLabelTreeItems(const mitk::ImageStatisticsContainer* statistic, const mitk::DataNode* imageNode, const mitk::DataNode* maskNode, mitk::ImageStatisticsContainer::LabelValueVectorType labelValues, const std::vector<std::string>& statisticNames, bool isWIP, QmitkImageStatisticsTreeItem* parentItem, bool& hasMultipleTimesteps)
{
// 3. hierarchy level: labels (optional, only if labels >1)
for (const auto labelValue : labelValues)
{
if (labelValue != mitk::ImageStatisticsContainer::NO_MASK_LABEL_VALUE)
{
//currently we only show statistics of the labeled pixel if a mask is provided
QString labelLabel = QStringLiteral("unnamed label");
const auto multiLabelSeg = dynamic_cast<mitk::LabelSetImage*>(maskNode->GetData());
- const mitk::Label* labelInstance = nullptr;
+ mitk::Label::ConstPointer labelInstance;
if (nullptr != multiLabelSeg)
{
labelInstance = multiLabelSeg->GetLabel(labelValue);
- labelLabel = QString::fromStdString(labelInstance->GetName() + " [" + labelInstance->GetTrackingID() + "]");
+ if (labelInstance.IsNotNull())
+ {
+ labelLabel = QString::fromStdString(labelInstance->GetName() + " [" + labelInstance->GetTrackingID() + "]");
+ }
+ else
+ {
+ labelLabel = QString("Unknown label ID ") + QString::number(labelValue);
+ }
}
QmitkImageStatisticsTreeItem* labelItem = nullptr;
- if (statistic->GetTimeSteps() == 1)
+ if (labelInstance.IsNotNull())
{
- // add statistical values directly in this hierarchy level
- auto statisticsObject = statistic->GetStatistics(labelValue, 0);
- labelItem = new QmitkImageStatisticsTreeItem(statisticsObject, statisticNames, labelLabel, isWIP, parentItem, imageNode, maskNode, labelInstance);
+ if (statistic->GetTimeSteps() == 1)
+ {
+ // add statistical values directly in this hierarchy level
+ auto statisticsObject = statistic->GetStatistics(labelValue, 0);
+ labelItem = new QmitkImageStatisticsTreeItem(statisticsObject, statisticNames, labelLabel, isWIP, parentItem, imageNode, maskNode, labelInstance);
+ }
+ else
+ {
+ labelItem = new QmitkImageStatisticsTreeItem(statisticNames, labelLabel, isWIP, false, parentItem, imageNode, maskNode, labelInstance);
+ AddTimeStepTreeItems(statistic, imageNode, maskNode, labelValue, statisticNames, isWIP, labelItem, hasMultipleTimesteps);
+ }
}
else
{
- labelItem = new QmitkImageStatisticsTreeItem(statisticNames, labelLabel, isWIP, false, parentItem, imageNode, maskNode, labelInstance);
- AddTimeStepTreeItems(statistic, imageNode, maskNode, labelValue, statisticNames, isWIP, labelItem, hasMultipleTimesteps);
+ labelItem = new QmitkImageStatisticsTreeItem(statisticNames, labelLabel, isWIP, true, parentItem, imageNode, maskNode);
}
parentItem->appendChild(labelItem);
}
}
}
void QmitkImageStatisticsTreeModel::BuildHierarchicalModel()
{
// reset old model
m_RootItem.reset(new QmitkImageStatisticsTreeItem());
bool hasMask = false;
bool hasMultipleTimesteps = false;
std::map<mitk::DataNode::ConstPointer, QmitkImageStatisticsTreeItem *> dataNodeToTreeItem;
for (const auto &statistic : m_Statistics)
{
bool isWIP = statistic->IsWIP();
// 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);
if (search != dataNodeToTreeItem.end())
{
// the tree item was created previously
imageItem = search->second;
}
else
{
QString imageLabel = QString::fromStdString(image->GetName());
if (statistic->GetTimeSteps() == 1 && maskFinding == m_MaskNodes.end())
{
auto labelValue = isWIP ? mitk::ImageStatisticsContainer::NO_MASK_LABEL_VALUE : statistic->GetExistingLabelValues().front();
auto statisticsObject = isWIP ? mitk::ImageStatisticsContainer::ImageStatisticsObject() : statistic->GetStatistics(labelValue, 0);
// create the final statistics tree item
imageItem = new QmitkImageStatisticsTreeItem(statisticsObject, m_StatisticNames, imageLabel, isWIP, m_RootItem.get(), image);
}
else
{
imageItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, imageLabel, isWIP, false, m_RootItem.get(), image);
}
m_RootItem->appendChild(imageItem);
dataNodeToTreeItem.emplace(image, imageItem);
}
if (maskFinding != m_MaskNodes.end())
{
const auto labelValues = statistic->GetExistingLabelValues(); //currently we not support showing the statistics for unlabeled pixels if a mask exist
// mask: 2. hierarchy level exists
auto& mask = *maskFinding;
QString maskLabel = QString::fromStdString(mask->GetName());
QmitkImageStatisticsTreeItem* maskItem;
if (statistic->GetTimeSteps() == 1 && labelValues.size() == 1)
{
// add statistical values directly in this hierarchy level
auto statisticsObject = isWIP ? mitk::ImageStatisticsContainer::ImageStatisticsObject() : statistic->GetStatistics(labelValues.front(), 0);
maskItem = new QmitkImageStatisticsTreeItem(statisticsObject, m_StatisticNames, maskLabel, isWIP, imageItem, image, mask);
}
else if(labelValues.empty())
{
//all labels are empty -> no stats are computed
maskItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, maskLabel, isWIP, true, imageItem, image, mask);
}
else
{
maskItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, maskLabel, isWIP, false, imageItem, image, mask);
// 3. hierarchy level: labels (optional, only if more then one label in statistic)
if (labelValues.size() > 1)
{
AddLabelTreeItems(statistic, image, mask, labelValues, m_StatisticNames, isWIP, maskItem, hasMultipleTimesteps);
}
else if (!labelValues.empty())
{
mitk::Label::PixelType labelValue = isWIP ? 0 : labelValues.front();
AddTimeStepTreeItems(statistic, image, mask, labelValue, m_StatisticNames, isWIP, maskItem, hasMultipleTimesteps);
}
}
imageItem->appendChild(maskItem);
hasMask = true;
}
else
{
//no mask -> but multi time step
auto labelValue = isWIP ? mitk::ImageStatisticsContainer::NO_MASK_LABEL_VALUE : statistic->GetExistingLabelValues().front();
AddTimeStepTreeItems(statistic, image, nullptr, labelValue, m_StatisticNames, isWIP, imageItem, hasMultipleTimesteps);
}
}
QString headerString = "Images";
if (hasMask)
{
headerString += "/Masks";
}
if (hasMultipleTimesteps)
{
headerString += "/Timesteps";
}
m_HeaderFirstColumn = headerString;
}
void QmitkImageStatisticsTreeModel::NodeRemoved(const mitk::DataNode* changedNode)
{
bool isRelevantNode = (nullptr != dynamic_cast<const mitk::ImageStatisticsContainer*>(changedNode->GetData()));
if (isRelevantNode)
{
emit beginResetModel();
UpdateByDataStorage();
emit endResetModel();
emit modelChanged();
}
}
void QmitkImageStatisticsTreeModel::NodeAdded(const mitk::DataNode * changedNode)
{
bool isRelevantNode = (nullptr != dynamic_cast<const mitk::ImageStatisticsContainer*>(changedNode->GetData()));
if (isRelevantNode)
{
emit beginResetModel();
UpdateByDataStorage();
emit endResetModel();
emit modelChanged();
}
}
void QmitkImageStatisticsTreeModel::NodeChanged(const mitk::DataNode * changedNode)
{
bool isRelevantNode = m_ImageNodes.end() != std::find(m_ImageNodes.begin(), m_ImageNodes.end(), changedNode);
isRelevantNode = isRelevantNode || (m_MaskNodes.end() != std::find(m_MaskNodes.begin(), m_MaskNodes.end(), changedNode));
isRelevantNode = isRelevantNode || (nullptr != dynamic_cast<const mitk::ImageStatisticsContainer*>(changedNode->GetData()));
if (isRelevantNode)
{
if (m_BuildTime.GetMTime() < changedNode->GetData()->GetMTime())
{
emit beginResetModel();
UpdateByDataStorage();
emit endResetModel();
emit modelChanged();
}
}
}
diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h
index 7797c328cb..9f1b214f0f 100644
--- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h
+++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.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 QmitkImageStatisticsTreeModel_h
#define QmitkImageStatisticsTreeModel_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
+In addition you may specified the statistic computation property HistogramNBins 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: Label instances [if Mask has more then one label]
--> 4. 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;
std::mutex m_Mutex;
std::unique_ptr<QmitkImageStatisticsTreeItem> m_RootItem;
QVariant m_HeaderFirstColumn;
itk::TimeStamp m_BuildTime;
bool m_IgnoreZeroValueVoxel = false;
unsigned int m_HistogramNBins = 100;
};
#endif
diff --git a/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp b/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp
index 4562aed61a..d1875debb6 100644
--- a/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp
+++ b/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.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 "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(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(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(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());
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(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(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(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(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(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());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but input does not really change.", 1, generator.m_DataGenerationStartedEmited);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but input does not really change.", 1, generator.m_GenerationFinishedEmited);
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but input does not really change.", generator.m_JobErrorEmited_error.empty());
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but input does not really change.", 1 == generator.m_NewDataAvailable.size());
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ m_MaskImageNode };
generator.SetAutoUpdate(true);
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(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());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but input does not really change.", 4, generator.m_DataGenerationStartedEmited);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but input does not really change.", 4, generator.m_GenerationFinishedEmited);
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but input does not really change.", generator.m_JobErrorEmited_error.empty());
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but input does not really change.", 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());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but input does not really change.", 5, generator.m_DataGenerationStartedEmited);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but input does not really change.", 5, generator.m_GenerationFinishedEmited);
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but input does not really change.", generator.m_JobErrorEmited_error.empty());
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but input does not really change.", 5 == generator.m_NewDataAvailable.size());
}
void DataStorageModificationTest()
{
TestQmitkImageStatisticsDataGenerator generator(m_DataStorage);
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode1 };
QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ 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());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", 3, generator.m_DataGenerationStartedEmited);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", 3, generator.m_GenerationFinishedEmited);
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", generator.m_JobErrorEmited_error.empty());
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, 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());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", 3, generator.m_DataGenerationStartedEmited);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", 3, generator.m_GenerationFinishedEmited);
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", generator.m_JobErrorEmited_error.empty());
+ CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggered, but only irrelevant node was added.", 3 == generator.m_NewDataAvailable.size());
}
};
MITK_TEST_SUITE_REGISTRATION(QmitkImageStatisticsDataGenerator)
diff --git a/Modules/LegacyGL/Documentation/doxygen/LegacyGL.dox b/Modules/LegacyGL/Documentation/doxygen/LegacyGL.dox
index 26d7678b89..5b4ce38903 100644
--- a/Modules/LegacyGL/Documentation/doxygen/LegacyGL.dox
+++ b/Modules/LegacyGL/Documentation/doxygen/LegacyGL.dox
@@ -1,26 +1,26 @@
/**
\page LegacyGLModule OpenGL Legacy Module
\tableofcontents
\section LegacyGLModuleOverview Reasons for this module
The new legacy GL module provides support of pure OpenGL rendering for Mitk. It contains all mitkGL.h related classes from the MitkCore. Modules, which used this classes in the past, now have a dependency to LegacyGL. This module is deprecated and should only be used for a short period until all mappers and props are migrated to the VTK-based rendering pipeline.
Before the integration of this module, there was a lot of code in the Mitk rendering pipeline to enable mitkGLMapper and mitkVtkMapper in parallel. In fact, both mappers render with OpenGL but vtkMapper are compatible to each other. It was not clear for developers how to implement mappers, as there were many negative examples in the form of mitkGLMapper.
-Removing direct rendering of OpenGL makes a lot of code obsolete (e.g. Enable/DisableOpenGL() in VtkPropRenderer) and prevents side effects such as that the level window was sometimes applied to the crosshair etc. Furthermore, the software architecture and design becomes clear. There is now just one way to implement a Mapper in Mitk - the mitkVtkMapper. If you are a developper and want to write GL code, you can simply write a vtkMapper in VTK code and use it inside the mitkVtkMapper.
+Removing direct rendering of OpenGL makes a lot of code obsolete (e.g. Enable/DisableOpenGL() in VtkPropRenderer) and prevents side effects such as that the level window was sometimes applied to the crosshair etc. Furthermore, the software architecture and design becomes clear. There is now just one way to implement a Mapper in Mitk - the mitkVtkMapper. If you are a developer and want to write GL code, you can simply write a vtkMapper in VTK code and use it inside the mitkVtkMapper.
\section PortExamples Examples how to port classes from GL to VTK-based mappers
The following core classes were ported from GL to VTK and may give orientation how to port an existing mapper or prop to the VTK-based rendering pipeline:
\li mitkSurfaceGLMapper2D -> mitkSurfaceVtkMapper2D
\li mitkPointSetGLMapper2D -> mitkPointSetVtkMapper2D
\li mitkPlaneGeometryGLMapper -> mitkPlaneGeometryMapper
\li vtkMitkRectangleProp -> vtkMitkRectangleProp
\section mitkVtkGLMapperWrapperDocu The mitkVtkGLMapperWrappper
LegacyGL also provides a new base class, the mitkVtkGLMapperWrapper, which can be used to wrap existing GLMappers and pretend they are common vtkProps. Examples can be found in the Modules ContourModel and PlanarFigure.
*/
\ No newline at end of file
diff --git a/Modules/LegacyIO/mitkFileSeriesReader.cpp b/Modules/LegacyIO/mitkFileSeriesReader.cpp
index a9e8a5b719..a2a4624ca5 100644
--- a/Modules/LegacyIO/mitkFileSeriesReader.cpp
+++ b/Modules/LegacyIO/mitkFileSeriesReader.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 "mitkFileSeriesReader.h"
#include <itkImageFileReader.h>
#include <itksys/Directory.hxx>
#include <itksys/SystemTools.hxx>
#include <map>
bool mitk::FileSeriesReader::GenerateFileList()
{
typedef std::vector<std::string> StringContainer;
typedef std::map<unsigned int, std::string> SortedStringContainer;
if (m_FileName == "")
{
throw itk::ImageFileReaderException(__FILE__, __LINE__, "FileName must be non-empty");
}
// MITK_INFO << "FileName: "<< m_FileName <<", FilePrefix: "<< m_FilePrefix << ", FilePattern: "<< m_FilePattern <<
// std::endl;
// determine begin and end idexes of the last digit sequence in the
// filename from the sample file name
// Therefore, walk backwards from the end of the filename until
// a number is found. The string in front of the number is the prefix,
// the string after the number is the extension.
std::string basename, path;
path = itksys::SystemTools::GetFilenamePath(m_FileName);
basename = itksys::SystemTools::GetFilenameName(m_FileName);
unsigned int digitBegin = 0;
unsigned int digitEnd = 0;
bool digitFound = false;
for (unsigned int i = basename.length() - 1;; --i)
{
char character = basename[i];
if (character >= '0' && character <= '9')
{
if (!digitFound)
{
digitEnd = i;
digitBegin = i;
digitFound = true;
}
else
digitBegin = i;
}
else
{
// end of digit series found, jump out of loop!
if (digitFound)
break;
}
if (i == 0)
break;
}
//
// if there is no digit in the filename, then we have a problem
// no matching filenames can be identified!
//
if (!digitFound)
{
itkWarningMacro("Filename contains no digit!");
return false;
}
//
// determine prefix and extension start and length
//
unsigned int prefixBegin = 0;
unsigned int prefixLength = digitBegin;
unsigned int extensionBegin = digitEnd + 1;
unsigned int extensionLength = (digitEnd == basename.length() - 1 ? 0 : basename.length() - 1 - digitEnd);
unsigned int numberLength = digitEnd - digitBegin + 1;
//
// extract prefix and extension
//
std::string prefix = "";
if (prefixLength != 0)
prefix = basename.substr(prefixBegin, prefixLength);
std::string extension = "";
if (extensionLength != 0)
extension = basename.substr(extensionBegin, extensionLength);
//
// print debug information
//
/*
MITK_INFO << "digitBegin : " << digitBegin << std::endl;
MITK_INFO << "digitEnd : " << digitEnd << std::endl;
MITK_INFO << "number of digits: " << numberLength << std::endl;
MITK_INFO << "prefixBegin : " << prefixBegin << std::endl;
MITK_INFO << "prefixLength : " << prefixLength << std::endl;
MITK_INFO << "prefix : " << prefix << std::endl;
MITK_INFO << "extensionBegin : " << extensionBegin << std::endl;
MITK_INFO << "extensionLength : " << extensionLength << std::endl;
MITK_INFO << "extension : " << extension << std::endl;
*/
if ((prefixLength + extensionLength + numberLength) != basename.length())
{
throw itk::ImageFileReaderException(
__FILE__, __LINE__, "prefixLength + extensionLength + numberLength != basenameLength");
}
//
// Load Directory
//
std::string directory = itksys::SystemTools::GetFilenamePath(m_FileName);
itksys::Directory itkDir;
if (!itkDir.Load(directory.c_str()))
{
itkWarningMacro(<< "Directory " << directory << " cannot be read!");
return false;
}
//
// Get a list of all files in the directory
//
StringContainer unmatchedFiles;
// unsigned long i;
for (unsigned long i = 0; i < itkDir.GetNumberOfFiles(); i++)
{
// Only read files
std::string filename = directory + "/" + itkDir.GetFile(i);
if (itksys::SystemTools::FileIsDirectory(filename.c_str()))
continue;
// store the filenames without path
unmatchedFiles.push_back(itkDir.GetFile(i));
}
//
// Match the file list against the file prefix and extension,
// the result should be only the files that should be read
//
StringContainer matchedFiles;
for (auto it = unmatchedFiles.begin(); it != unmatchedFiles.end(); ++it)
{
bool prefixMatch = false;
bool extensionMatch = false;
// check if the file prefix matches the current file
if (prefixLength != 0)
prefixMatch = (it->find(prefix) == prefixBegin); // check if prefix is found
else
prefixMatch = (((*it)[0] >= '0') && ((*it)[0] <= '9')); // check if filename begins with digit
// check if the file extension matches the current file
if (extensionLength != 0)
extensionMatch = (it->find(extension) == it->length() - extensionLength); // check if prefix is found
else
extensionMatch =
(((*it)[it->length() - 1] >= '0') && ((*it)[it->length() - 1] <= '9')); // check if filename ends with digit
if (prefixMatch && extensionMatch)
{
matchedFiles.push_back(*it);
}
}
if (matchedFiles.size() == 0)
{
itkWarningMacro(<< "Sorry, none of the files matched the prefix!");
return false;
}
//
// parse the file names from back to front for digits
// and convert them to a number. Store the filename and number
// in a SortedStringContainer
//
SortedStringContainer sortedFiles;
for (auto it = matchedFiles.begin(); it != matchedFiles.end(); ++it)
{
// parse the filename starting from pos digitBegin until we reach a non-digit
// or the end of filename
std::string number = "";
std::string currentFilename(*it);
for (unsigned int i = digitBegin; i < currentFilename.length(); ++i)
{
char character = currentFilename[i];
// do we have a digit?
if (character >= '0' && character <= '9')
number += character;
else
break; // end of digit series found, jump out of loop!
}
if (number.length() == 0)
{
// The file is not numbered, this is an error!
// Nevertheless, we try the next files.
itkWarningMacro(<< "The filename " << *it
<< "does not contain a valid digit sequence but matches prefix and extension. Skipping file!");
}
else
{
if ((number.length() + prefix.length() + extension.length()) != it->length())
{
itkWarningMacro(
"The file "
<< *it
- << " matches prefix and extension, but the string in beteen is not a single digit-sequence. Skipping file!");
+ << " matches prefix and extension, but the string in between is not a single digit-sequence. Skipping file!");
}
else
{
// convert the number string into an integer and
- // insert the filname (including directory) into the SortedStringContainer
+ // insert the filename (including directory) into the SortedStringContainer
unsigned int num = atoi(number.c_str());
sortedFiles.insert(std::make_pair(num, directory + "/" + *it));
}
}
}
if (sortedFiles.size() == 0)
{
itkWarningMacro(<< "Sorry, no numbered files found, I can't load anything...");
return false;
}
//
// Convert the sorted string container in a plain sorted vector of strings;
//
m_MatchedFileNames.clear();
m_MatchedFileNames.resize(sortedFiles.size());
unsigned long index = 0;
for (auto it = sortedFiles.begin(); it != sortedFiles.end(); ++it, ++index)
{
m_MatchedFileNames[index] = it->second;
MITK_INFO << "Added " << it->second << " to the set of matched files!" << std::endl;
}
return true;
}
mitk::FileSeriesReader::MatchedFileNames mitk::FileSeriesReader::GetMatchedFileNames()
{
return m_MatchedFileNames;
}
mitk::FileSeriesReader::FileSeriesReader() : m_FileName(""), m_FilePrefix(""), m_FilePattern("")
{
}
mitk::FileSeriesReader::~FileSeriesReader()
{
}
diff --git a/Modules/LegacyIO/mitkItkPictureWrite.cpp b/Modules/LegacyIO/mitkItkPictureWrite.cpp
index 96f77c9ca2..bab59f0f0c 100644
--- a/Modules/LegacyIO/mitkItkPictureWrite.cpp
+++ b/Modules/LegacyIO/mitkItkPictureWrite.cpp
@@ -1,177 +1,177 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <MitkLegacyIOExports.h>
#include "mitkItkPictureWrite.h"
#include <mitkInstantiateAccessFunctions.h>
#include <itkImageSeriesWriter.h>
#include <itkNumericSeriesFileNames.h>
#include <itkRescaleIntensityImageFilter.h>
#include <itkRGBAPixel.h>
/** Set the filenames to the specified writer in dependace on the number of images passed in */
template <class WriterType>
void SetOutputNames(typename WriterType::Pointer writer, const std::string &baseFileName, unsigned int numberOfImages)
{
if (numberOfImages > 1)
{
itk::NumericSeriesFileNames::Pointer numericFileNameWriter = itk::NumericSeriesFileNames::New();
std::string finalFileName = baseFileName;
std::string::size_type pos = baseFileName.find_last_of(".", baseFileName.length() - 1);
if (pos == std::string::npos)
finalFileName.append(".%d.png");
else
finalFileName.insert(pos, ".%d");
MITK_DEBUG << "Filename: " << finalFileName;
numericFileNameWriter->SetEndIndex(numberOfImages);
numericFileNameWriter->SetSeriesFormat(finalFileName.c_str());
numericFileNameWriter->Modified();
writer->SetFileNames(numericFileNameWriter->GetFileNames());
}
// if the given image is an 2D-png image, do not use the numericFileNameWriter
// to generate the name, since it alters the fileName given as parameter
else
{
writer->SetFileName(baseFileName.c_str());
}
}
template <typename TPixel, unsigned int VImageDimension>
void _mitkItkPictureWrite(itk::Image<TPixel, VImageDimension> *itkImage, const std::string &fileName)
{
typedef itk::Image<TPixel, VImageDimension> TImageType;
typedef itk::Image<unsigned char, 3> UCharOutputImage3DType;
typedef itk::Image<unsigned short, 3> ShortOutputImage3DType;
typedef itk::Image<unsigned char, 2> OutputImage2D_8bitType;
typedef itk::Image<unsigned short, 2> OutputImage2D_16bitType;
typedef itk::ImageSeriesWriter<UCharOutputImage3DType, OutputImage2D_8bitType> UCharWriterType;
typedef itk::ImageSeriesWriter<ShortOutputImage3DType, OutputImage2D_16bitType> ShortWriterType;
typedef itk::RescaleIntensityImageFilter<TImageType, UCharOutputImage3DType> UCharRescalerFilterType;
typedef itk::RescaleIntensityImageFilter<TImageType, ShortOutputImage3DType> ShortRescalerFilterType;
// get the size info
size_t inputTypeSize = sizeof(TPixel);
size_t supportedOutputMaxSize = 1; // default value 8bit
// the PNG and TIFF formats can handle up-to 16-bit images
if (fileName.find(".png") != std::string::npos || fileName.find(".tif") != std::string::npos)
{
supportedOutputMaxSize = 2;
}
// get the dimension info
unsigned int numberOfImages = 1;
if (itkImage->GetImageDimension() > 2)
numberOfImages = itkImage->GetLargestPossibleRegion().GetSize()[2];
typename ShortRescalerFilterType::Pointer sh_rescaler = ShortRescalerFilterType::New();
sh_rescaler->SetInput(itkImage);
sh_rescaler->SetOutputMinimum(0);
sh_rescaler->SetOutputMaximum(65535);
typename UCharRescalerFilterType::Pointer rescaler = UCharRescalerFilterType::New();
rescaler->SetInput(itkImage);
rescaler->SetOutputMinimum(0);
rescaler->SetOutputMaximum(255);
try
{
// input is 8 bit
if (inputTypeSize == 1)
{
UCharWriterType::Pointer writer = UCharWriterType::New();
SetOutputNames<UCharWriterType>(writer, fileName, numberOfImages);
writer->SetInput(rescaler->GetOutput());
writer->Update();
}
// input pixel type is 16bit -> writer can handle 16bit images
else if (inputTypeSize == supportedOutputMaxSize && supportedOutputMaxSize == 2)
{
ShortWriterType::Pointer writer = ShortWriterType::New();
SetOutputNames<ShortWriterType>(writer, fileName, numberOfImages);
writer->SetInput(sh_rescaler->GetOutput());
writer->Update();
}
// rescaling input to maximum of supported format
else
{
if (supportedOutputMaxSize == 2)
{
typename ShortWriterType::Pointer writer = ShortWriterType::New();
SetOutputNames<ShortWriterType>(writer, fileName, numberOfImages);
writer->SetInput(sh_rescaler->GetOutput());
writer->Update();
}
else
{
typename UCharWriterType::Pointer writer = UCharWriterType::New();
SetOutputNames<UCharWriterType>(writer, fileName, numberOfImages);
writer->SetInput(rescaler->GetOutput());
writer->Update();
}
}
}
catch (const itk::ExceptionObject &e)
{
- MITK_ERROR << "ITK Exception occured: " << e.what();
+ MITK_ERROR << "ITK Exception occurred: " << e.what();
mitkThrow() << "Caught ITK exception while writing image with scalar type \n" << e.what();
}
}
template <typename TPixel, unsigned int VImageDimension>
void _mitkItkPictureWriteComposite(itk::Image<TPixel, VImageDimension> *itkImage, const std::string &fileName)
{
typedef itk::Image<TPixel, VImageDimension> TImageType;
typedef itk::Image<TPixel, 2> TImageType2D;
typedef itk::ImageSeriesWriter<TImageType, TImageType2D> WriterType;
typename WriterType::Pointer writer = WriterType::New();
// get the dimension info
unsigned int numberOfImages = 1;
if (itkImage->GetImageDimension() > 2)
numberOfImages = itkImage->GetLargestPossibleRegion().GetSize()[2];
// create output name(s)
SetOutputNames<WriterType>(writer, fileName, numberOfImages);
writer->SetInput(itkImage);
try
{
writer->Update();
}
catch (const itk::ExceptionObject &e)
{
- MITK_ERROR << "ITK Exception occured: " << e.what();
+ MITK_ERROR << "ITK Exception occurred: " << e.what();
mitkThrow() << "Caught ITK exception while writing image with composite type \n" << e.what();
}
}
#define InstantiateAccessFunction__mitkItkPictureWrite(pixelType, dim) \
template MITKLEGACYIO_EXPORT void _mitkItkPictureWrite(itk::Image<pixelType, dim> *, const std::string &);
#define InstantiateAccessFunction__mitkItkPictureWriteComposite(pixelType, dim) \
template MITKLEGACYIO_EXPORT void _mitkItkPictureWriteComposite(itk::Image<pixelType, dim> *, const std::string &);
InstantiateAccessFunction(_mitkItkPictureWrite)
InstantiateAccessFunctionForFixedPixelType(
_mitkItkPictureWriteComposite, MITK_ACCESSBYITK_PIXEL_TYPES_SEQ MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES_SEQ)
diff --git a/Modules/LegacyIO/mitkPointSetReader.h b/Modules/LegacyIO/mitkPointSetReader.h
index 2ecb17ed7c..e0e139f774 100644
--- a/Modules/LegacyIO/mitkPointSetReader.h
+++ b/Modules/LegacyIO/mitkPointSetReader.h
@@ -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.
============================================================================*/
#ifndef mitkPointSetReader_h
#define mitkPointSetReader_h
#include <MitkLegacyIOExports.h>
#include <mitkFileReader.h>
#include <mitkPointSetSource.h>
#include <stack>
#include <string>
#include <vtkXMLParser.h>
namespace tinyxml2
{
class XMLElement;
}
namespace mitk
{
/**
* @brief reads xml representations of mitk::PointSets from a file
*
- * Reader for xml files containing one or multiple xml represenations of
+ * Reader for xml files containing one or multiple xml representations of
* mitk::PointSets. If multiple mitk::PointSets are stored in one file,
* these are assigned to multiple outputs of the filter. The number of point
* sets which have be read can be retrieven by a call to GetNumberOfOutputs()
* after the pipeline update().
* The reader is able to read the old 3D Pointsets without the "specification" and "timeseries" tags and the new 4D
* Pointsets.
* @note loading point sets from multiple files according to a given file pattern
* is not yet supported!
*
* @ingroup MitkLegacyIOModule
*
* @deprecatedSince{2014_10} Use mitk::IOUtils or mitk::FileReaderRegistry instead.
*/
class MITKLEGACYIO_EXPORT PointSetReader : public PointSetSource, public FileReader
{
public:
mitkClassMacro(PointSetReader, FileReader);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* @brief Sets the filename of the file to be read
* @param _arg the filename of the point set xml-file
*/
itkSetStringMacro(FileName);
/**
* @brief Returns the filename of the point set xml-file.
* @returns the filename of the point set xml-file.
*/
itkGetStringMacro(FileName);
/**
* @warning multiple load not (yet) supported
*/
itkSetStringMacro(FilePrefix);
/**
* @warning multiple load not (yet) supported
*/
itkGetStringMacro(FilePrefix);
/**
* @warning multiple load not (yet) supported
*/
itkSetStringMacro(FilePattern);
/**
* @warning multiple load not (yet) supported
*/
itkGetStringMacro(FilePattern);
static bool CanReadFile(const std::string filename, const std::string filePrefix, const std::string filePattern);
/**
* @returns whether the last read attempt was successful or not.
*/
bool GetSuccess() const;
protected:
/**
* Constructor
*/
PointSetReader();
/**
* Virtual destructor
*/
~PointSetReader() override;
/**
* Actually reads the point sets from the given file
*/
void GenerateData() override;
virtual mitk::PointSet::Pointer ReadPoint(mitk::PointSet::Pointer newPointSet,
const tinyxml2::XMLElement *currentTimeSeries,
unsigned int currentTimeStep);
/**
* Does nothing in the current implementation
*/
void GenerateOutputInformation() override;
/**
* Resizes the output-objects according to the given number.
* @param num the new number of output objects.
*/
virtual void ResizeOutputs(const unsigned int &num);
/**
* Checks if the given file has appropriate
* read access.
* @returns true if the file exists and may be read
* or false otherwise.
*/
virtual int CanReadFile(const char *name);
std::string m_FileName;
std::string m_FilePrefix;
std::string m_FilePattern;
bool m_Success;
};
}
#endif
diff --git a/Modules/LegacyIO/vtkPointSetXMLParser.h b/Modules/LegacyIO/vtkPointSetXMLParser.h
index e18b85e9b5..089049a2ca 100644
--- a/Modules/LegacyIO/vtkPointSetXMLParser.h
+++ b/Modules/LegacyIO/vtkPointSetXMLParser.h
@@ -1,132 +1,132 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 _VTK_POINT_SET_XML_READER__H_
#define _VTK_POINT_SET_XML_READER__H_
#include <MitkLegacyIOExports.h>
#include <list>
#include <mitkPointSet.h>
#include <stack>
#include <string>
#include <vtkXMLParser.h>
namespace mitk
{
/**
* @brief Implementation of the vtkXMLParser interface for reading mitk::PointSets.
*
* This class implements the XMLParser interface of the vtkXMLParser which is based
* on expat. It is used by the mitk::PointSetReader and is NOT INTENDED TO BE USED
* FROM THE END-USER. If you want to read point sets, use the mitk::PointSetReader.
* @ingroup MitkLegacyIOModule
*
* @deprecatedSince{2014_10} Use mitk::IOUtils or mitk::FileReaderRegistry instead.
*/
class DEPRECATED() MITKLEGACYIO_EXPORT vtkPointSetXMLParser : public vtkXMLParser
{
public:
vtkTypeMacro(vtkPointSetXMLParser, vtkXMLParser);
static vtkPointSetXMLParser *New();
typedef mitk::PointSet PointSetType;
typedef std::stack<std::string> ParseStack;
typedef std::list<PointSetType::Pointer> PointSetList;
typedef PointSetType::DataType::PointIdentifier PointIdentifier;
typedef PointSetType::PointType PointType;
int InitializeParser() override;
int CleanupParser() override;
/**
* Handler function which is called, when a new xml start-tag
* has been parsed.
*/
void StartElement(const char *name, const char **atts) override;
/**
* Handler function which is called, when a xml end-tag
* has been parsed.
*/
void EndElement(const char *name) override;
/**
- * Handler function which is called, if characted data has been
+ * Handler function which is called, if character data has been
* parsed by expat.
* @param inData a char array containing the parsed string data
* @param inLength the length of the parsed data string.
*/
void CharacterDataHandler(const char *inData, int inLength) override;
/**
* Converts the given data to mitk::ScalarType.
*/
virtual mitk::ScalarType ParseScalarType(const std::string &data);
/**
* Converts the given data to an PointIdentifier
*/
virtual PointIdentifier ParsePointIdentifier(const std::string &data);
/**
* @returns the list of point sets which have been read from file.
* NOTE: your have to call the Parse() function, before this function.
*/
virtual PointSetList GetParsedPointSets();
protected:
vtkPointSetXMLParser();
~vtkPointSetXMLParser() override;
/**
* A stack containing the parsed start-tags.
* If an end tag is encountered, it is matched with the
* top element of the stack.
*/
ParseStack m_ParseStack;
/**
* Contains the parsed point sets.
*/
PointSetList m_PointSetList;
/**
* The current point set which is processed
* by the parser.
*/
PointSetType::Pointer m_CurrentPointSet;
/**
* The current point which is processed
* by the parser.
*/
PointType m_CurrentPoint;
std::string m_CurId;
std::string m_CurXString;
std::string m_CurYString;
std::string m_CurZString;
/**
* The current point id which is processed
* by the parser.
*/
PointIdentifier m_CurrentPointId;
std::locale m_PreviousLocale;
};
}
#endif // _VTK_POINT_SET_XML_READER__H_
diff --git a/Modules/Log/include/mitkLog.h b/Modules/Log/include/mitkLog.h
index 37b44c2ca7..3f2a485589 100644
--- a/Modules/Log/include/mitkLog.h
+++ b/Modules/Log/include/mitkLog.h
@@ -1,220 +1,220 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkLog_h
#define mitkLog_h
#include <mitkLogBackendBase.h>
#include <sstream>
#include <MitkLogExports.h>
#ifndef MITKLOG_MODULENAME
# if defined(US_MODULE_NAME)
# define MITKLOG_STR_(x) #x
# define MITKLOG_STR(x) MITKLOG_STR_(x)
# define MITKLOG_MODULENAME MITKLOG_STR(US_MODULE_NAME)
# else
# define MITKLOG_MODULENAME "n/a"
# endif
#endif
namespace mitk
{
/** \brief Register a backend in the MITK log mechanism.
*
* If a backend is registered here, all log messages are relayed to this backend through the method ProcessMessage.
* If no backend is registered, the default backend is used.
*/
void MITKLOG_EXPORT RegisterBackend(LogBackendBase* backend);
/** \brief Unregister a backend.
*/
void MITKLOG_EXPORT UnregisterBackend(LogBackendBase* backend);
/** \brief Distribute the given message to all registered backends.
*
* Should only be called by PseudoLogStream objects.
*/
void MITKLOG_EXPORT DistributeToBackends(LogMessage& message);
/** \brief Enable the output of a backend.
*/
void MITKLOG_EXPORT EnableBackends(LogBackendBase::OutputType type);
/** \brief Disable the output of a backend.
*/
void MITKLOG_EXPORT DisableBackends(LogBackendBase::OutputType type);
- /** \brief Check wether the output of this backend is enabled.
+ /** \brief Check whether the output of this backend is enabled.
*/
bool MITKLOG_EXPORT IsBackendEnabled(LogBackendBase::OutputType type);
/** \brief Simulates a std::cout stream.
*
* Should only be used by the macros defined in the file mitkLog.h.
*/
class MITKLOG_EXPORT PseudoLogStream
{
public:
PseudoLogStream(LogLevel level, const std::string& filePath, int lineNumber, const std::string& functionName)
: m_Disabled(false),
m_Message(level, filePath, lineNumber, functionName),
m_Stream(std::stringstream::out)
{
}
/** \brief The encapsulated message is written to the backend.
*/
~PseudoLogStream()
{
if (!m_Disabled)
{
m_Message.Message = m_Stream.str();
m_Message.ModuleName = MITKLOG_MODULENAME;
DistributeToBackends(m_Message);
}
}
template <class T>
PseudoLogStream& operator<<(const T& data)
{
if (!m_Disabled)
{
std::locale C("C");
std::locale originalLocale = m_Stream.getloc();
m_Stream.imbue(C);
m_Stream << data;
m_Stream.imbue(originalLocale);
}
return *this;
}
template <class T>
PseudoLogStream& operator<<(T& data)
{
if (!m_Disabled)
{
std::locale C("C");
std::locale originalLocale = m_Stream.getloc();
m_Stream.imbue(C);
m_Stream << data;
m_Stream.imbue(originalLocale);
}
return *this;
}
PseudoLogStream& operator<<(std::ostream& (*func)(std::ostream&))
{
if (!m_Disabled)
{
std::locale C("C");
std::locale originalLocale = m_Stream.getloc();
m_Stream.imbue(C);
m_Stream << func;
m_Stream.imbue(originalLocale);
}
return *this;
}
/** \brief Sets the category of this PseudoLogStream object.
*
- * If there is already a category it is appended, seperated by a dot.
+ * If there is already a category it is appended, separated by a dot.
*/
PseudoLogStream& operator()(const std::string& category)
{
if (!m_Disabled)
{
if (m_Message.Category.length())
m_Message.Category += ".";
m_Message.Category += category;
}
return *this;
}
/** \brief Enables/disables the PseudoLogStream.
*
* If set to false, parsing and output is suppressed.
*/
PseudoLogStream& operator()(bool enabled)
{
m_Disabled |= !enabled;
return *this;
}
protected:
bool m_Disabled;
LogMessage m_Message;
std::stringstream m_Stream;
};
/**
* \brief Simulates a std::cout stream but does nothing.
*
* Should only be used by the macros defined in the file mitkLog.h.
*/
class MITKLOG_EXPORT NullLogStream
{
public:
template <class T>
NullLogStream& operator<<(const T&)
{
return *this;
}
template <class T>
NullLogStream& operator<<(T&)
{
return *this;
}
NullLogStream& operator<<(std::ostream &(*)(std::ostream &))
{
return *this;
}
NullLogStream& operator()(const char*)
{
return *this;
}
NullLogStream& operator()(bool)
{
return *this;
}
};
}
#define MITK_INFO mitk::PseudoLogStream(mitk::LogLevel::Info, __FILE__, __LINE__, __FUNCTION__)
#define MITK_WARN mitk::PseudoLogStream(mitk::LogLevel::Warn, __FILE__, __LINE__, __FUNCTION__)
#define MITK_ERROR mitk::PseudoLogStream(mitk::LogLevel::Error, __FILE__, __LINE__, __FUNCTION__)
#define MITK_FATAL mitk::PseudoLogStream(mitk::LogLevel::Fatal, __FILE__, __LINE__, __FUNCTION__)
#ifdef MITK_ENABLE_DEBUG_MESSAGES
#define MITK_DEBUG mitk::PseudoLogStream(mitk::LogLevel::Debug, __FILE__, __LINE__, __FUNCTION__)
#else
#define MITK_DEBUG true ? mitk::NullLogStream() : mitk::NullLogStream()
#endif
#endif
diff --git a/Modules/MapperExt/include/vtkMaskedGlyph2D.h b/Modules/MapperExt/include/vtkMaskedGlyph2D.h
index 56b9439c45..a79506c5bd 100644
--- a/Modules/MapperExt/include/vtkMaskedGlyph2D.h
+++ b/Modules/MapperExt/include/vtkMaskedGlyph2D.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 __vtkMaskedGlyph2D_h
#define __vtkMaskedGlyph2D_h
#include "MitkMapperExtExports.h"
#include "mitkCommon.h"
#include "vtkGlyph2D.h"
class vtkMaskPoints;
/**
* This class masked points of the input data set and glyphs
- * only the selected poitns. Points may be selected either by
+ * only the selected points. Points may be selected either by
* random or by ratio.
* Additionally, this class allows to set the InputScalars,
* InputVectors and InputNormals by their field name in the
* input dataset.
*/
class MITKMAPPEREXT_EXPORT vtkMaskedGlyph2D : public vtkGlyph2D
{
public:
vtkTypeMacro(vtkMaskedGlyph2D, vtkGlyph2D);
void PrintSelf(ostream &os, vtkIndent indent) override;
/**
* Constructor
*/
static vtkMaskedGlyph2D *New();
/**
* Limit the number of points to glyph
*/
vtkSetMacro(MaximumNumberOfPoints, int);
vtkGetMacro(MaximumNumberOfPoints, int);
/**
* Set the input to this filter.
*/
virtual void SetInput(vtkDataSet *input);
/**
* Set/get whether to mask points
*/
vtkSetMacro(UseMaskPoints, int);
vtkGetMacro(UseMaskPoints, int);
/**
* Set/get flag to cause randomization of which points to mask.
*/
void SetRandomMode(int mode);
int GetRandomMode();
///**
// * If you want to use an arbitrary scalars array, then set its name here.
// * By default this in nullptr and the filter will use the active scalar array.
// */
// vtkGetStringMacro(InputScalarsSelection);
// void SelectInputScalars(const char *fieldName)
// {this->SetInputScalarsSelection(fieldName);}
///**
// * If you want to use an arbitrary vectors array, then set its name here.
// * By default this in nullptr and the filter will use the active vector array.
// */
// vtkGetStringMacro(InputVectorsSelection);
// void SelectInputVectors(const char *fieldName)
// {this->SetInputVectorsSelection(fieldName);}
///**
// * If you want to use an arbitrary normals array, then set its name here.
// * By default this in nullptr and the filter will use the active normal array.
// */
// vtkGetStringMacro(InputNormalsSelection);
// void SelectInputNormals(const char *fieldName)
// {this->SetInputNormalsSelection(fieldName);}
protected:
vtkMaskedGlyph2D();
~vtkMaskedGlyph2D() override;
int RequestData(vtkInformation *info,
vtkInformationVector **inInfoVec,
vtkInformationVector *outInfoVec) override;
vtkMaskPoints *MaskPoints;
int MaximumNumberOfPoints;
int UseMaskPoints;
private:
vtkMaskedGlyph2D(const vtkMaskedGlyph2D &); // Not implemented.
void operator=(const vtkMaskedGlyph2D &); // Not implemented.
};
#endif
diff --git a/Modules/MapperExt/include/vtkMaskedGlyph3D.h b/Modules/MapperExt/include/vtkMaskedGlyph3D.h
index c9ea2d6a5f..8d77dd08be 100644
--- a/Modules/MapperExt/include/vtkMaskedGlyph3D.h
+++ b/Modules/MapperExt/include/vtkMaskedGlyph3D.h
@@ -1,84 +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.
============================================================================*/
#ifndef __vtkMaskedGlyph3D_h
#define __vtkMaskedGlyph3D_h
#include "MitkMapperExtExports.h"
#include "vtkGlyph3D.h"
#include "mitkCommon.h"
class vtkMaskPoints;
/**
* This class masked points of the input data set and glyphs
- * only the selected poitns. Points may be selected either by
+ * only the selected points. Points may be selected either by
* random or by ratio.
* Additionally, this class allows to set the InputScalars,
* InputVectors and InputNormals by their field name in the
* input dataset.
*/
class MITKMAPPEREXT_EXPORT vtkMaskedGlyph3D : public vtkGlyph3D
{
public:
vtkTypeMacro(vtkMaskedGlyph3D, vtkGlyph3D);
void PrintSelf(ostream &os, vtkIndent indent) override;
/**
* Constructor
*/
static vtkMaskedGlyph3D *New();
/**
* Limit the number of points to glyph
*/
vtkSetMacro(MaximumNumberOfPoints, int);
vtkGetMacro(MaximumNumberOfPoints, int);
/**
* Set the input to this filter.
*/
virtual void SetInput(vtkDataSet *input);
/**
* Set/get whether to mask points
*/
vtkSetMacro(UseMaskPoints, int);
vtkGetMacro(UseMaskPoints, int);
/**
* Set/get flag to cause randomization of which points to mask.
*/
void SetRandomMode(int mode);
int GetRandomMode();
void SetInputConnection(vtkAlgorithmOutput *input) override;
using vtkGlyph3D::SetInputConnection;
protected:
vtkMaskedGlyph3D();
~vtkMaskedGlyph3D() override;
int RequestData(vtkInformation *, vtkInformationVector **, vtkInformationVector *) override;
vtkMaskPoints *MaskPoints;
int MaximumNumberOfPoints;
int UseMaskPoints;
private:
vtkMaskedGlyph3D(const vtkMaskedGlyph3D &); // Not implemented.
void operator=(const vtkMaskedGlyph3D &); // Not implemented.
};
#endif
diff --git a/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp b/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp
index a4db90ea37..c4f97e2d1d 100644
--- a/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp
+++ b/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp
@@ -1,555 +1,555 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkUnstructuredGridMapper2D.h"
#include <mitkGL.h>
#include "mitkAbstractTransformGeometry.h"
#include "mitkBaseRenderer.h"
#include "mitkColorProperty.h"
#include "mitkPlaneGeometry.h"
#include "mitkProperties.h"
#include "mitkTransferFunction.h"
#include "mitkTransferFunctionProperty.h"
#include "mitkUnstructuredGrid.h"
#include "mitkVtkMapper3D.h"
#include "mitkVtkScalarModeProperty.h"
#include <vtkAbstractMapper3D.h>
#include <vtkAbstractVolumeMapper.h>
#include <vtkAssembly.h>
#include <vtkCellArray.h>
#include <vtkCellData.h>
#include <vtkColorTransferFunction.h>
#include <vtkLinearTransform.h>
#include <vtkLookupTable.h>
#include <vtkPiecewiseFunction.h>
#include <vtkPlane.h>
#include <vtkPointData.h>
#include <vtkProp3DCollection.h>
#include <vtkScalarsToColors.h>
#include <vtkUnstructuredGrid.h>
#include <vtkVolume.h>
#include <vtkVolumeProperty.h>
#include "vtkPointSetSlicer.h"
void mitk::UnstructuredGridMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer);
bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode());
if (needGenerateData)
{
ls->UpdateGenerateDataTime();
mitk::DataNode::ConstPointer node = this->GetDataNode();
if (node.IsNull())
return;
if (!node->GetProperty(m_ScalarMode, "scalar mode"))
{
m_ScalarMode = mitk::VtkScalarModeProperty::New(0);
}
if (!node->GetProperty(m_ScalarVisibility, "scalar visibility"))
{
m_ScalarVisibility = mitk::BoolProperty::New(true);
}
if (!node->GetProperty(m_Outline, "outline polygons"))
{
m_Outline = mitk::BoolProperty::New(false);
}
if (!node->GetProperty(m_Color, "color"))
{
m_Color = mitk::ColorProperty::New(1.0f, 1.0f, 1.0f);
}
if (!node->GetProperty(m_LineWidth, "line width"))
{
m_LineWidth = mitk::IntProperty::New(1);
}
}
mitk::BaseData::Pointer input = GetDataNode()->GetData();
assert(input);
input->Update();
if (m_VtkPointSet)
m_VtkPointSet->UnRegister(nullptr);
m_VtkPointSet = this->GetVtkPointSet(renderer, this->GetTimestep());
assert(m_VtkPointSet);
m_VtkPointSet->Register(nullptr);
if (m_ScalarVisibility->GetValue())
{
mitk::DataNode::ConstPointer node = this->GetDataNode();
mitk::TransferFunctionProperty::Pointer transferFuncProp;
node->GetProperty(transferFuncProp, "TransferFunction", renderer);
if (transferFuncProp.IsNotNull())
{
mitk::TransferFunction::Pointer tf = transferFuncProp->GetValue();
if (m_ScalarsToColors)
m_ScalarsToColors->UnRegister(nullptr);
m_ScalarsToColors = static_cast<vtkScalarsToColors *>(tf->GetColorTransferFunction());
m_ScalarsToColors->Register(nullptr);
if (m_ScalarsToOpacity)
m_ScalarsToOpacity->UnRegister(nullptr);
m_ScalarsToOpacity = tf->GetScalarOpacityFunction();
m_ScalarsToOpacity->Register(nullptr);
}
else
{
if (m_ScalarsToColors)
m_ScalarsToColors->UnRegister(nullptr);
m_ScalarsToColors = this->GetVtkLUT(renderer);
assert(m_ScalarsToColors);
m_ScalarsToColors->Register(nullptr);
float opacity;
node->GetOpacity(opacity, renderer);
if (m_ScalarsToOpacity)
m_ScalarsToOpacity->UnRegister(nullptr);
m_ScalarsToOpacity = vtkPiecewiseFunction::New();
double range[2];
m_VtkPointSet->GetScalarRange(range);
m_ScalarsToOpacity->AddSegment(range[0], opacity, range[1], opacity);
}
}
}
void mitk::UnstructuredGridMapper2D::Paint(mitk::BaseRenderer *renderer)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if (!visible)
return;
vtkLinearTransform *vtktransform = GetDataNode()->GetVtkTransform();
vtkLinearTransform *inversetransform = vtktransform->GetLinearInverse();
PlaneGeometry::ConstPointer worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
PlaneGeometry::ConstPointer worldPlaneGeometry = dynamic_cast<const PlaneGeometry *>(worldGeometry.GetPointer());
Point3D point;
Vector3D normal;
if (worldPlaneGeometry.IsNotNull())
{
// set up vtkPlane according to worldGeometry
point = worldPlaneGeometry->GetOrigin();
normal = worldPlaneGeometry->GetNormal();
normal.Normalize();
m_Plane->SetTransform((vtkAbstractTransform *)nullptr);
}
else
{
//@FIXME: does not work correctly. Does m_Plane->SetTransform really transforms a "plane plane" into a "curved
//plane"?
return;
AbstractTransformGeometry::ConstPointer worldAbstractGeometry =
dynamic_cast<const AbstractTransformGeometry *>(renderer->GetCurrentWorldPlaneGeometry());
if (worldAbstractGeometry.IsNotNull())
{
// set up vtkPlane according to worldGeometry
point = worldAbstractGeometry->GetParametricBoundingBox()->GetMinimum();
FillVector3D(normal, 0, 0, 1);
m_Plane->SetTransform(worldAbstractGeometry->GetVtkAbstractTransform()->GetInverse());
}
else
return;
}
double vp[3], vnormal[3];
vnl2vtk(point.GetVnlVector(), vp);
vnl2vtk(normal.GetVnlVector(), vnormal);
// normally, we would need to transform the surface and cut the transformed surface with the cutter.
// This might be quite slow. Thus, the idea is, to perform an inverse transform of the plane instead.
//@todo It probably does not work for scaling operations yet:scaling operations have to be
- // dealed with after the cut is performed by scaling the contour.
+ // dealt with after the cut is performed by scaling the contour.
inversetransform->TransformPoint(vp, vp);
inversetransform->TransformNormalAtPoint(vp, vnormal, vnormal);
m_Plane->SetOrigin(vp);
m_Plane->SetNormal(vnormal);
// set data into cutter
m_Slicer->SetInputData(m_VtkPointSet);
// m_Cutter->GenerateCutScalarsOff();
// m_Cutter->SetSortByToSortByCell();
// calculate the cut
m_Slicer->Update();
// apply color and opacity read from the PropertyList
ApplyColorAndOpacityProperties(renderer);
// traverse the cut contour
vtkPolyData *contour = m_Slicer->GetOutput();
vtkPoints *vpoints = contour->GetPoints();
vtkCellArray *vlines = contour->GetLines();
vtkCellArray *vpolys = contour->GetPolys();
vtkPointData *vpointdata = contour->GetPointData();
vtkDataArray *vscalars = vpointdata->GetScalars();
vtkCellData *vcelldata = contour->GetCellData();
vtkDataArray *vcellscalars = vcelldata->GetScalars();
const int numberOfLines = contour->GetNumberOfLines();
const int numberOfPolys = contour->GetNumberOfPolys();
const bool useCellData = m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_DEFAULT ||
m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_CELL_DATA;
const bool usePointData = m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_POINT_DATA;
Point3D p;
Point2D p2d;
vlines->InitTraversal();
vpolys->InitTraversal();
mitk::Color outlineColor = m_Color->GetColor();
glLineWidth((float)m_LineWidth->GetValue());
for (int i = 0; i < numberOfLines; ++i)
{
const vtkIdType *cell(nullptr);
vtkIdType cellSize(0);
vlines->GetNextCell(cellSize, cell);
float rgba[4] = {outlineColor[0], outlineColor[1], outlineColor[2], 1.0f};
if (m_ScalarVisibility->GetValue() && vcellscalars)
{
if (useCellData)
{ // color each cell according to cell data
double scalar = vcellscalars->GetComponent(i, 0);
double rgb[3] = {1.0f, 1.0f, 1.0f};
m_ScalarsToColors->GetColor(scalar, rgb);
rgba[0] = (float)rgb[0];
rgba[1] = (float)rgb[1];
rgba[2] = (float)rgb[2];
rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar);
}
else if (usePointData)
{
double scalar = vscalars->GetComponent(i, 0);
double rgb[3] = {1.0f, 1.0f, 1.0f};
m_ScalarsToColors->GetColor(scalar, rgb);
rgba[0] = (float)rgb[0];
rgba[1] = (float)rgb[1];
rgba[2] = (float)rgb[2];
rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar);
}
}
glColor4fv(rgba);
glBegin(GL_LINE_LOOP);
for (int j = 0; j < cellSize; ++j)
{
vpoints->GetPoint(cell[j], vp);
// take transformation via vtktransform into account
vtktransform->TransformPoint(vp, vp);
vtk2itk(vp, p);
// convert 3D point (in mm) to display coordinates (units )
renderer->WorldToDisplay(p, p2d);
// convert display coordinates ( (0,0) is top-left ) in GL coordinates ( (0,0) is bottom-left )
// p2d[1]=toGL-p2d[1];
// add the current vertex to the line
glVertex2f(p2d[0], p2d[1]);
}
glEnd();
}
bool polyOutline = m_Outline->GetValue();
bool scalarVisibility = m_ScalarVisibility->GetValue();
// cache the transformed points
// a fixed size array is way faster than 'new'
// slices through 3d cells usually do not generated
// polygons with more than 6 vertices
const int maxPolySize = 10;
auto *cachedPoints = new Point2D[maxPolySize * numberOfPolys];
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// only draw polygons if there are cell scalars
// or the outline property is set to true
if (scalarVisibility && vcellscalars)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
for (int i = 0; i < numberOfPolys; ++i)
{
const vtkIdType *cell(nullptr);
vtkIdType cellSize(0);
vpolys->GetNextCell(cellSize, cell);
float rgba[4] = {1.0f, 1.0f, 1.0f, 0};
if (scalarVisibility && vcellscalars)
{
if (useCellData)
{ // color each cell according to cell data
double scalar = vcellscalars->GetComponent(i + numberOfLines, 0);
double rgb[3] = {1.0f, 1.0f, 1.0f};
m_ScalarsToColors->GetColor(scalar, rgb);
rgba[0] = (float)rgb[0];
rgba[1] = (float)rgb[1];
rgba[2] = (float)rgb[2];
rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar);
}
else if (usePointData)
{
double scalar = vscalars->GetComponent(i, 0);
double rgb[3] = {1.0f, 1.0f, 1.0f};
m_ScalarsToColors->GetColor(scalar, rgb);
rgba[0] = (float)rgb[0];
rgba[1] = (float)rgb[1];
rgba[2] = (float)rgb[2];
rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar);
}
}
glColor4fv(rgba);
glBegin(GL_POLYGON);
for (int j = 0; j < cellSize; ++j)
{
vpoints->GetPoint(cell[j], vp);
// take transformation via vtktransform into account
vtktransform->TransformPoint(vp, vp);
vtk2itk(vp, p);
// convert 3D point (in mm) to display coordinates (units )
renderer->WorldToDisplay(p, p2d);
// convert display coordinates ( (0,0) is top-left ) in GL coordinates ( (0,0) is bottom-left )
// p2d[1]=toGL-p2d[1];
cachedPoints[i * 10 + j][0] = p2d[0];
cachedPoints[i * 10 + j][1] = p2d[1];
// add the current vertex to the line
glVertex2f(p2d[0], p2d[1]);
}
glEnd();
}
if (polyOutline)
{
vpolys->InitTraversal();
glColor4f(outlineColor[0], outlineColor[1], outlineColor[2], 1.0f);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
for (int i = 0; i < numberOfPolys; ++i)
{
const vtkIdType *cell(nullptr);
vtkIdType cellSize(0);
vpolys->GetNextCell(cellSize, cell);
glBegin(GL_POLYGON);
// glPolygonOffset(1.0, 1.0);
for (int j = 0; j < cellSize; ++j)
{
// add the current vertex to the line
glVertex2f(cachedPoints[i * 10 + j][0], cachedPoints[i * 10 + j][1]);
}
glEnd();
}
}
}
glDisable(GL_BLEND);
delete[] cachedPoints;
}
vtkAbstractMapper3D *mitk::UnstructuredGridMapper2D::GetVtkAbstractMapper3D(mitk::BaseRenderer *renderer)
{
// MITK_INFO << "GETVTKABSTRACTMAPPER3D\n";
mitk::DataNode::ConstPointer node = this->GetDataNode();
if (node.IsNull())
return nullptr;
mitk::VtkMapper::Pointer mitkMapper = dynamic_cast<mitk::VtkMapper *>(node->GetMapper(2));
if (mitkMapper.IsNull())
{
return nullptr;
}
mitkMapper->Update(renderer);
auto *assembly = dynamic_cast<vtkAssembly *>(mitkMapper->GetVtkProp(renderer));
if (assembly)
{
vtkProp3DCollection *collection = assembly->GetParts();
collection->InitTraversal();
vtkProp3D *prop3d = nullptr;
do
{
prop3d = collection->GetNextProp3D();
auto *actor = dynamic_cast<vtkActor *>(prop3d);
if (actor)
{
return dynamic_cast<vtkAbstractMapper3D *>(actor->GetMapper());
}
auto *volume = dynamic_cast<vtkVolume *>(prop3d);
if (volume)
{
return dynamic_cast<vtkAbstractMapper3D *>(volume->GetMapper());
}
} while (prop3d != collection->GetLastProp3D());
}
else
{
auto *actor = dynamic_cast<vtkActor *>(mitkMapper->GetVtkProp(renderer));
if (actor)
{
return dynamic_cast<vtkAbstractMapper3D *>(actor->GetMapper());
}
auto *volume = dynamic_cast<vtkVolume *>(mitkMapper->GetVtkProp(renderer));
if (volume)
{
return dynamic_cast<vtkAbstractMapper3D *>(volume->GetMapper());
}
}
return nullptr;
}
vtkPointSet *mitk::UnstructuredGridMapper2D::GetVtkPointSet(mitk::BaseRenderer *renderer, int time)
{
// MITK_INFO << "GETVTKPOINTSET\n";
vtkAbstractMapper3D *abstractMapper = GetVtkAbstractMapper3D(renderer);
if (abstractMapper == nullptr)
{
// try to get data from the node
mitk::DataNode::ConstPointer node = this->GetDataNode();
if (node.IsNull())
return nullptr;
mitk::BaseData::Pointer data = node->GetData();
mitk::UnstructuredGrid::Pointer grid = dynamic_cast<mitk::UnstructuredGrid *>(data.GetPointer());
if (!grid.IsNull())
return static_cast<vtkPointSet *>(grid->GetVtkUnstructuredGrid(time));
return nullptr;
}
else
{
auto *mapper = dynamic_cast<vtkMapper *>(abstractMapper);
if (mapper)
{
return dynamic_cast<vtkPointSet *>(mapper->GetInput());
}
auto *volMapper = dynamic_cast<vtkAbstractVolumeMapper *>(abstractMapper);
if (volMapper)
{
return dynamic_cast<vtkPointSet *>(volMapper->GetDataSetInput());
}
}
return nullptr;
}
vtkScalarsToColors *mitk::UnstructuredGridMapper2D::GetVtkLUT(mitk::BaseRenderer *renderer)
{
// MITK_INFO << "GETVTKLUT\n";
auto *mapper = dynamic_cast<vtkMapper *>(GetVtkAbstractMapper3D(renderer));
if (mapper)
return mapper->GetLookupTable();
else
{
mitk::DataNode::ConstPointer node = this->GetDataNode();
if (node.IsNull())
return nullptr;
mitk::VtkMapper::Pointer mitkMapper = dynamic_cast<mitk::VtkMapper *>(node->GetMapper(2));
if (mitkMapper.IsNull())
{
// MITK_INFO << "mitkMapper is null\n";
return nullptr;
}
mitkMapper->Update(renderer);
auto *volume = dynamic_cast<vtkVolume *>(mitkMapper->GetVtkProp(renderer));
if (volume)
{
// MITK_INFO << "found volume prop\n";
return static_cast<vtkScalarsToColors *>(volume->GetProperty()->GetRGBTransferFunction());
}
auto *assembly = dynamic_cast<vtkAssembly *>(mitkMapper->GetVtkProp(renderer));
if (assembly)
{
// MITK_INFO << "found assembly prop\n";
mitk::TransferFunctionProperty::Pointer transferFuncProp;
node->GetProperty(transferFuncProp, "TransferFunction", nullptr);
if (transferFuncProp.IsNotNull())
{
MITK_INFO << "return colortransferfunction\n";
return static_cast<vtkScalarsToColors *>(transferFuncProp->GetValue()->GetColorTransferFunction());
}
}
return nullptr;
}
}
bool mitk::UnstructuredGridMapper2D::IsConvertibleToVtkPointSet(mitk::BaseRenderer *renderer)
{
return (GetVtkPointSet(renderer, this->GetTimestep()) != nullptr);
}
mitk::UnstructuredGridMapper2D::UnstructuredGridMapper2D()
{
m_Plane = vtkPlane::New();
m_Slicer = vtkPointSetSlicer::New();
m_Slicer->SetSlicePlane(m_Plane);
m_ScalarsToColors = nullptr;
m_ScalarsToOpacity = nullptr;
m_VtkPointSet = nullptr;
// m_LUT = vtkLookupTable::New();
// m_LUT->SetTableRange( 0, 255 );
// m_LUT->SetNumberOfColors( 255 );
// m_LUT->SetRampToLinear ();
// m_LUT->Build();
}
mitk::UnstructuredGridMapper2D::~UnstructuredGridMapper2D()
{
m_Slicer->Delete();
m_Plane->Delete();
if (m_ScalarsToOpacity != nullptr)
m_ScalarsToOpacity->UnRegister(nullptr);
if (m_ScalarsToColors != nullptr)
m_ScalarsToColors->UnRegister(nullptr);
if (m_VtkPointSet != nullptr)
m_VtkPointSet->UnRegister(nullptr);
}
diff --git a/Modules/MapperExt/src/mitkVectorImageMapper2D.cpp b/Modules/MapperExt/src/mitkVectorImageMapper2D.cpp
index 0d13d2960b..e4e8a0aba7 100644
--- a/Modules/MapperExt/src/mitkVectorImageMapper2D.cpp
+++ b/Modules/MapperExt/src/mitkVectorImageMapper2D.cpp
@@ -1,527 +1,527 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkVectorImageMapper2D.h"
// vtk related includes
#include <vtkCellArray.h>
#include <vtkCellData.h>
#include <vtkCutter.h>
#include <vtkDataArray.h>
#include <vtkDataObject.h>
#include <vtkDataSetWriter.h>
#include <vtkFloatArray.h>
#include <vtkGlyph2D.h>
#include <vtkGlyphSource2D.h>
#include <vtkImageData.h>
#include <vtkImageReslice.h>
#include <vtkIndent.h>
#include <vtkLinearTransform.h>
#include <vtkLookupTable.h>
#include <vtkLookupTable.h>
#include <vtkMaskedGlyph2D.h>
#include <vtkMaskedGlyph3D.h>
#include <vtkMath.h>
#include <vtkMatrix4x4.h>
#include <vtkMatrixToLinearTransform.h>
#include <vtkPlane.h>
#include <vtkPlane.h>
#include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkScalarsToColors.h>
#include <vtkScalarsToColors.h>
#include <vtkTransform.h>
#include <fstream>
// mitk related includes
#include "mitkAbstractTransformGeometry.h"
#include "mitkBaseRenderer.h"
#include "mitkColorProperty.h"
#include "mitkGL.h"
#include "mitkProperties.h"
#include <mitkLookupTableProperty.h>
const mitk::Image *mitk::VectorImageMapper2D::GetInput(void)
{
if (m_Image.IsNotNull())
return m_Image;
else
return dynamic_cast<const mitk::Image *>(GetDataNode()->GetData());
}
void mitk::VectorImageMapper2D::Paint(mitk::BaseRenderer *renderer)
{
// std::cout << "2d vector mapping..." << std::endl;
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if (!visible)
return;
mitk::Image::Pointer input = const_cast<mitk::Image *>(this->GetInput());
if (input.IsNull())
return;
vtkImageData *vtkImage = input->GetVtkImageData(this->GetCurrentTimeStep(input, renderer));
//
// set up the cutter orientation according to the current geometry of
// the renderers plane
//
Point3D point;
Vector3D normal;
PlaneGeometry::ConstPointer worldPlaneGeometry = renderer->GetCurrentWorldPlaneGeometry();
if (worldPlaneGeometry.IsNotNull())
{
// set up vtkPlane according to worldGeometry
point = worldPlaneGeometry->GetOrigin();
normal = worldPlaneGeometry->GetNormal();
normal.Normalize();
m_Plane->SetTransform((vtkAbstractTransform *)nullptr);
}
else
{
itkWarningMacro(<< "worldPlaneGeometry is nullptr!");
return;
}
double vp[3], vp_slice[3], vnormal[3];
vnl2vtk(point.GetVnlVector(), vp);
vnl2vtk(normal.GetVnlVector(), vnormal);
// std::cout << "Origin: " << vp[0] <<" "<< vp[1] <<" "<< vp[2] << std::endl;
// std::cout << "Normal: " << vnormal[0] <<" "<< vnormal[1] <<" "<< vnormal[2] << std::endl;
// normally, we would need to transform the surface and cut the transformed surface with the cutter.
// This might be quite slow. Thus, the idea is, to perform an inverse transform of the plane instead.
//@todo It probably does not work for scaling operations yet:scaling operations have to be
- // dealed with after the cut is performed by scaling the contour.
+ // dealt with after the cut is performed by scaling the contour.
vtkLinearTransform *vtktransform = GetDataNode()->GetVtkTransform();
vtkTransform *world2vtk = vtkTransform::New();
world2vtk->Identity();
world2vtk->Concatenate(vtktransform->GetLinearInverse());
double myscale[3];
world2vtk->GetScale(myscale);
world2vtk->PostMultiply();
world2vtk->Scale(1 / myscale[0], 1 / myscale[1], 1 / myscale[2]);
world2vtk->TransformPoint(vp, vp);
world2vtk->TransformNormalAtPoint(vp, vnormal, vnormal);
world2vtk->Delete();
// vtk works in axis align coords
// thus the normal also must be axis align, since
// we do not allow arbitrary cutting through volume
//
// vnormal should already be axis align, but in order
// to get rid of precision effects, we set the two smaller
// components to zero here
int dims[3];
vtkImage->GetDimensions(dims);
double spac[3];
vtkImage->GetSpacing(spac);
vp_slice[0] = vp[0];
vp_slice[1] = vp[1];
vp_slice[2] = vp[2];
if (fabs(vnormal[0]) > fabs(vnormal[1]) && fabs(vnormal[0]) > fabs(vnormal[2]))
{
if (fabs(vp_slice[0] / spac[0]) < 0.4)
vp_slice[0] = 0.4 * spac[0];
if (fabs(vp_slice[0] / spac[0]) > (dims[0] - 1) - 0.4)
vp_slice[0] = ((dims[0] - 1) - 0.4) * spac[0];
vnormal[1] = 0;
vnormal[2] = 0;
}
if (fabs(vnormal[1]) > fabs(vnormal[0]) && fabs(vnormal[1]) > fabs(vnormal[2]))
{
if (fabs(vp_slice[1] / spac[1]) < 0.4)
vp_slice[1] = 0.4 * spac[1];
if (fabs(vp_slice[1] / spac[1]) > (dims[1] - 1) - 0.4)
vp_slice[1] = ((dims[1] - 1) - 0.4) * spac[1];
vnormal[0] = 0;
vnormal[2] = 0;
}
if (fabs(vnormal[2]) > fabs(vnormal[1]) && fabs(vnormal[2]) > fabs(vnormal[0]))
{
if (fabs(vp_slice[2] / spac[2]) < 0.4)
vp_slice[2] = 0.4 * spac[2];
if (fabs(vp_slice[2] / spac[2]) > (dims[2] - 1) - 0.4)
vp_slice[2] = ((dims[2] - 1) - 0.4) * spac[2];
vnormal[0] = 0;
vnormal[1] = 0;
}
m_Plane->SetOrigin(vp_slice);
m_Plane->SetNormal(vnormal);
vtkPolyData *cuttedPlane;
if (!((dims[0] == 1 && vnormal[0] != 0) || (dims[1] == 1 && vnormal[1] != 0) || (dims[2] == 1 && vnormal[2] != 0)))
{
m_Cutter->SetCutFunction(m_Plane);
m_Cutter->SetInputData(vtkImage);
m_Cutter->GenerateCutScalarsOff(); //!
m_Cutter->Update();
cuttedPlane = m_Cutter->GetOutput();
}
else
{
// cutting of a 2D-Volume does not work,
// so we have to build up our own polydata object
cuttedPlane = vtkPolyData::New();
vtkPoints *points = vtkPoints::New();
points->SetNumberOfPoints(vtkImage->GetNumberOfPoints());
for (int i = 0; i < vtkImage->GetNumberOfPoints(); i++)
points->SetPoint(i, vtkImage->GetPoint(i));
cuttedPlane->SetPoints(points);
vtkFloatArray *pointdata = vtkFloatArray::New();
int comps = vtkImage->GetPointData()->GetScalars()->GetNumberOfComponents();
pointdata->SetNumberOfComponents(comps);
int tuples = vtkImage->GetPointData()->GetScalars()->GetNumberOfTuples();
pointdata->SetNumberOfTuples(tuples);
for (int i = 0; i < tuples; i++)
pointdata->SetTuple(i, vtkImage->GetPointData()->GetScalars()->GetTuple(i));
pointdata->SetName("vector");
cuttedPlane->GetPointData()->AddArray(pointdata);
}
if (cuttedPlane->GetNumberOfPoints() != 0)
{
//
// make sure, that we have point data with more than 1 component (as vectors)
//
vtkPointData *pointData = cuttedPlane->GetPointData();
if (pointData == nullptr)
{
itkWarningMacro(<< "no point data associated with cutters result!");
return;
}
if (pointData->GetNumberOfArrays() == 0)
{
itkWarningMacro(<< "point data returned by cutter doesn't have any arrays associated!");
return;
}
else if (pointData->GetArray(0)->GetNumberOfComponents() <= 1)
{
itkWarningMacro(<< "number of components <= 1!");
return;
}
else if (pointData->GetArrayName(0) == nullptr)
{
pointData->GetArray(0)->SetName("vector");
// std::cout << "array name = vectors now" << std::endl;
}
// std::cout << " projecting..."<< std::endl;
//
// constrain the vectors to lie on the plane, which means to remove the vector component,
// which is orthogonal to the plane.
//
vtkIdType numPoints, pointId;
numPoints = cuttedPlane->GetNumberOfPoints();
vtkDataArray *inVectors = cuttedPlane->GetPointData()->GetVectors("vector");
assert(inVectors != nullptr);
vtkFloatArray *vectorMagnitudes = vtkFloatArray::New();
vectorMagnitudes->SetName("vectorMagnitudes");
vectorMagnitudes->SetNumberOfComponents(1);
vectorMagnitudes->SetNumberOfValues(numPoints);
vectorMagnitudes->SetNumberOfTuples(numPoints);
double inVector[3], outVector[3], wnormal[3]; //, tmpVector[ 3 ], outVector[ 3 ];
double k = 0.0;
vnl2vtk(normal.GetVnlVector(), wnormal);
vtkMath::Normalize(wnormal);
bool normalizeVecs;
m_DataNode->GetBoolProperty("NormalizeVecs", normalizeVecs);
for (pointId = 0; pointId < numPoints; ++pointId)
{
inVectors->GetTuple(pointId, inVector);
if (normalizeVecs)
{
vnl_vector<double> tmp(3);
vtk2vnl(inVector, tmp);
tmp.normalize();
vnl2vtk(tmp, inVector);
}
k = vtkMath::Dot(wnormal, inVector);
// Remove non orthogonal component.
outVector[0] = inVector[0] - (wnormal[0] * k);
outVector[1] = inVector[1] - (wnormal[1] * k);
outVector[2] = inVector[2] - (wnormal[2] * k);
inVectors->SetTuple(pointId, outVector);
// ?? this was set to norm(inVector) before, but outVector made more sense to me
vectorMagnitudes->SetValue(pointId, vtkMath::Norm(outVector));
// std::cout << "method old: " << inVector[0] <<", " << inVector[1] << ", "<<inVector[2] << ", method new: " <<
// outVector[0] << ", "<< outVector[1] << ", "<< outVector[2] << std::endl;
}
pointData->AddArray(vectorMagnitudes);
pointData->CopyAllOn();
// pointData->PrintSelf(std::cout, vtkIndent(4));
// std::cout << " ...done!"<< std::endl;
// std::cout << " glyphing..."<< std::endl;
// call glyph2D to generate 2D glyphs for each of the
// vectors
vtkGlyphSource2D *glyphSource = vtkGlyphSource2D::New();
// glyphSource->SetGlyphTypeToDash();
glyphSource->DashOn();
// glyphSource->SetScale( 0.1 );
// glyphSource->SetScale2( .5 );
// glyphSource->SetCenter( 0.5, 0.5, 0.5 );
glyphSource->CrossOff();
// glyphSource->FilledOff();
// glyphSource->Update();
double spacing[3];
vtkImage->GetSpacing(spacing);
double min = spacing[0];
min = min > spacing[1] ? spacing[1] : min;
min = min > spacing[2] ? spacing[2] : min;
float scale = 1;
mitk::FloatProperty::Pointer mitkScaleProp =
dynamic_cast<mitk::FloatProperty *>(GetDataNode()->GetProperty("Scale"));
if (mitkScaleProp.IsNotNull())
{
scale = mitkScaleProp->GetValue();
}
vtkMaskedGlyph3D *glyphGenerator = vtkMaskedGlyph3D::New();
glyphGenerator->SetSourceData(glyphSource->GetOutput());
glyphGenerator->SetInput(cuttedPlane);
glyphGenerator->SetInputArrayToProcess(1, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "vector");
glyphGenerator->SetVectorModeToUseVector();
glyphGenerator->OrientOn();
glyphGenerator->SetScaleFactor(min * scale);
glyphGenerator->SetUseMaskPoints(true);
glyphGenerator->SetRandomMode(true);
glyphGenerator->SetMaximumNumberOfPoints(128 * 128);
glyphGenerator->Update();
/*
vtkLookupTable* vtkLut = nullptr;
mitk::LookupTableProperty::Pointer mitkLutProp =
dynamic_cast<mitk::LookupTableProperty*>(GetDataNode()->GetProperty("LookupTable"));
if (mitkLutProp.IsNotNull())
{
vtkLut = mitkLutProp->GetLookupTable()->GetVtkLookupTable();
}
*/
mitk::Color color;
mitk::ColorProperty::Pointer mitkColorProp =
dynamic_cast<mitk::ColorProperty *>(GetDataNode()->GetProperty("color"));
if (mitkColorProp.IsNotNull())
{
color = mitkColorProp->GetColor();
}
else
{
color.SetRed(0);
color.SetBlue(1);
color.SetGreen(0);
}
float lwidth = 1;
mitk::FloatProperty::Pointer mitkLWidthProp =
dynamic_cast<mitk::FloatProperty *>(GetDataNode()->GetProperty("LineWidth"));
if (mitkLWidthProp.IsNotNull())
{
lwidth = mitkLWidthProp->GetValue();
}
vtkTransform *trafo = vtkTransform::New();
trafo->Identity();
trafo->Concatenate(vtktransform);
trafo->PreMultiply();
double myscale[3];
trafo->GetScale(myscale);
trafo->Scale(1 / myscale[0], 1 / myscale[1], 1 / myscale[2]);
this->PaintCells(glyphGenerator->GetOutput(),
renderer->GetCurrentWorldPlaneGeometry(),
trafo,
renderer,
nullptr /*vtkLut*/,
color,
lwidth,
spacing);
vectorMagnitudes->Delete();
glyphSource->Delete();
glyphGenerator->Delete();
trafo->Delete();
}
else
{
std::cout << " no points cutted!" << std::endl;
}
// std::cout << "...done!" << std::endl;
}
void mitk::VectorImageMapper2D::PaintCells(vtkPolyData *glyphs,
const PlaneGeometry * /*worldGeometry*/,
vtkLinearTransform *vtktransform,
mitk::BaseRenderer *renderer,
vtkScalarsToColors *lut,
mitk::Color color,
float lwidth,
double *spacing)
{
vtkPoints *points = glyphs->GetPoints();
vtkPointData *vpointdata = glyphs->GetPointData();
vtkDataArray *vpointscalars = vpointdata->GetArray("vectorMagnitudes");
// vtkDataArray* vpointpositions = vpointdata->GetArray("pointPositions");
assert(vpointscalars != nullptr);
// std::cout << " Scalars range 2d:" << vpointscalars->GetRange()[0] << " " << vpointscalars->GetRange()[0] <<
// std::endl;
Point3D p;
Point2D p2d;
vtkIdList *idList;
vtkCell *cell;
double offset[3];
for (auto &elem : offset)
{
elem = 0;
}
vtkIdType numCells = glyphs->GetNumberOfCells();
for (vtkIdType cellId = 0; cellId < numCells; ++cellId)
{
double vp[3];
cell = glyphs->GetCell(cellId);
idList = cell->GetPointIds();
int numPoints = idList->GetNumberOfIds();
if (numPoints == 1)
{
// take transformation via vtktransform into account
double pos[3], vp_raster[3];
points->GetPoint(idList->GetId(0), vp);
vp_raster[0] = vtkMath::Round(vp[0] / spacing[0]) * spacing[0];
vp_raster[1] = vtkMath::Round(vp[1] / spacing[1]) * spacing[1];
vp_raster[2] = vtkMath::Round(vp[2] / spacing[2]) * spacing[2];
vtktransform->TransformPoint(vp_raster, pos);
offset[0] = pos[0] - vp[0];
offset[1] = pos[1] - vp[1];
offset[2] = pos[2] - vp[2];
}
else
{
glLineWidth(lwidth);
glBegin(GL_LINE_LOOP);
for (int pointNr = 0; pointNr < numPoints; ++pointNr)
{
points->GetPoint(idList->GetId(pointNr), vp);
vp[0] = vp[0] + offset[0];
vp[1] = vp[1] + offset[1];
vp[2] = vp[2] + offset[2];
double tmp[3];
vtktransform->TransformPoint(vp, tmp);
vtk2itk(vp, p);
// convert 3D point (in mm) to display coordinates (units )
renderer->WorldToDisplay(p, p2d);
if (lut != nullptr)
{
// color each point according to point data
double *color;
if (vpointscalars != nullptr)
{
vpointscalars->GetComponent(pointNr, 0);
color = lut->GetColor(vpointscalars->GetComponent(idList->GetId(pointNr), 0));
glColor3f(color[0], color[1], color[2]);
}
}
else
{
glColor3f(color.GetRed(), color.GetGreen(), color.GetBlue());
}
// std::cout << idList->GetId( pointNr )<< ": " << p2d[0]<< " "<< p2d[1] << std::endl;
// draw the line
glVertex2f(p2d[0], p2d[1]);
}
glEnd();
}
}
}
mitk::VectorImageMapper2D::VectorImageMapper2D()
{
m_LUT = nullptr;
m_Plane = vtkPlane::New();
m_Cutter = vtkCutter::New();
m_Cutter->SetCutFunction(m_Plane);
m_Cutter->GenerateValues(1, 0, 1);
}
mitk::VectorImageMapper2D::~VectorImageMapper2D()
{
if (m_LUT != nullptr)
m_LUT->Delete();
if (m_Plane != nullptr)
m_Plane->Delete();
if (m_Cutter != nullptr)
m_Cutter->Delete();
}
int mitk::VectorImageMapper2D::GetCurrentTimeStep(mitk::BaseData *data, mitk::BaseRenderer *renderer)
{
//
// get the TimeGeometry of the input object
//
const TimeGeometry *dataTimeGeometry = data->GetUpdatedTimeGeometry();
if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0))
{
itkWarningMacro(<< "The given object is missing a mitk::TimeGeometry, or the number of time steps is 0!");
return 0;
}
//
// get the world time
//
ScalarType time = renderer->GetTime();
//
// convert the world time to time steps of the input object
//
int timestep = 0;
if (time > itk::NumericTraits<mitk::ScalarType>::NonpositiveMin())
timestep = dataTimeGeometry->TimePointToTimeStep(time);
if (dataTimeGeometry->IsValidTimeStep(timestep) == false)
{
itkWarningMacro(<< timestep << " is not a valid time of the given data object!");
return 0;
}
return timestep;
}
diff --git a/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp b/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp
index c5cf757609..e1a36f4c10 100644
--- a/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp
+++ b/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp
@@ -1,279 +1,279 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <iostream>
#include <fstream>
#include <clocale>
#include "mapRegistration.h"
#include "mapRegistrationFileWriter.h"
#include "mapRegistrationFileReader.h"
#include "mapLazyFileFieldKernelLoader.h"
#include <mitkCustomMimeType.h>
#include <mitkIOMimeTypes.h>
#include <mitkLocaleSwitch.h>
#include "mitkMAPRegistrationWrapperIO.h"
#include "mitkMAPRegistrationWrapper.h"
namespace mitk
{
/** Helper class that allows to use an functor in multiple combinations of
* moving and target dimensions on a passed MAPRegistrationWrapper instance.\n
* DimHelperSub is used DimHelper to iterate in a row of the dimension
* combination matrix.
*/
template< unsigned int i, unsigned int j, template < unsigned int, unsigned int> class TFunctor>
class DimHelperSub
{
public:
static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& data)
{
if (TFunctor<i,j>::Execute(obj, data))
{
return true;
}
return DimHelperSub<i,j-1,TFunctor>::Execute(obj, data);
}
};
/** Specialized template version of DimSubHelper that indicates the end
* of the row in the dimension combination matrix, thus does nothing.
*/
template< unsigned int i, template < unsigned int, unsigned int> class TFunctor>
class DimHelperSub<i,1,TFunctor >
{
public:
static bool Execute(const mitk::MAPRegistrationWrapper*, const map::core::String&)
{
//just unwind. Go to the next "row" with DimHelper
return false;
}
};
/** Helper class that allows to use an functor in multiple combinations of
* moving and target dimensions on a passed MAPRegistrationWrapper instance.\n
* It is helpful if you want to ensure that all combinations are checked/touched
* (e.g. 3D 3D, 3D 2D, 2D 3D, 2D 2D) without generating a large switch yard.
- * Think of n*m matrix (indicating the posible combinations). DimHelper walks from
+ * Think of n*m matrix (indicating the possible combinations). DimHelper walks from
* one row to the next and uses DimHelperSub to iterate in a row.\n
* For every element of the matrix the functor is executed on the passed object.
*/
template< unsigned int i, unsigned int j, template < unsigned int, unsigned int> class TFunctor>
class DimHelper{
public:
static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& data = "")
{
if (DimHelperSub<i,j,TFunctor>::Execute(obj, data))
{
return true;
}
return DimHelper<i-1,j,TFunctor>::Execute(obj, data);
}
};
/** Specialized template version of DimHelper that indicates the end
* of the dimension combination matrix, thus does nothing.
*/
template< unsigned int j, template < unsigned int, unsigned int> class TFunctor>
class DimHelper<1,j, TFunctor >
{
public:
static bool Execute(const mitk::MAPRegistrationWrapper*, const map::core::String&)
{
//just unwind. We are done.
return false;
}
};
/** Functor that checks of the dimension of the registration is supported and can
* be written.
*/
template<unsigned int i, unsigned int j>
class CanWrite
{
public:
static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& = "")
{
bool result = false;
result = dynamic_cast<const map::core::Registration<i,j> *>(obj->GetRegistration()) != nullptr;
return result;
}
};
/** Functor that writes the registration to a file if it has the right dimensionality.
*/
template<unsigned int i, unsigned int j>
class WriteReg
{
public:
static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& data)
{
const map::core::Registration<i,j>* pReg = dynamic_cast<const map::core::Registration<i,j>*>(obj->GetRegistration());
if (pReg == nullptr)
{
return false;
}
typedef map::io::RegistrationFileWriter<i,j> WriterType;
typename WriterType::Pointer writer = WriterType::New();
writer->setExpandLazyKernels(false);
try
{
writer->write(pReg,data);
}
catch (const itk::ExceptionObject& e)
{
std::cout << e.what() << std::endl;
throw;
}
return true;
}
};
MAPRegistrationWrapperIO::MAPRegistrationWrapperIO(const MAPRegistrationWrapperIO& other)
: AbstractFileIO(other)
{
}
MAPRegistrationWrapperIO::MAPRegistrationWrapperIO() : AbstractFileIO(mitk::MAPRegistrationWrapper::GetStaticNameOfClass())
{
std::string category = "MatchPoint Registration File";
CustomMimeType customMimeType;
customMimeType.SetCategory(category);
customMimeType.AddExtension("mapr");
this->AbstractFileIOWriter::SetMimeType(customMimeType);
this->AbstractFileIOWriter::SetDescription(category);
customMimeType.AddExtension("mapr.xml");
customMimeType.AddExtension("MAPR");
customMimeType.AddExtension("MAPR.XML");
this->AbstractFileIOReader::SetMimeType(customMimeType);
this->AbstractFileIOReader::SetDescription(category);
this->RegisterService();
}
void MAPRegistrationWrapperIO::Write()
{
bool success = false;
const BaseData* input = this->GetInput();
if (input == nullptr)
{
mitkThrow() << "Cannot write data. Data pointer is nullptr.";
}
const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast<const mitk::MAPRegistrationWrapper*>(input);
if (wrapper == nullptr)
{
mitkThrow() << "Cannot write data. Data pointer is not a Registration wrapper.";
}
std::ostream* writeStream = this->GetOutputStream();
std::string fileName = this->GetOutputLocation();
if (writeStream)
{
fileName = this->GetLocalFileName();
}
// Switch the current locale to "C"
LocaleSwitch localeSwitch("C");
try
{
success = DimHelper<3,3,WriteReg>::Execute(wrapper, fileName);
}
catch (const std::exception& e)
{
mitkThrow() << e.what();
}
if (!success)
{
mitkThrow() << "Cannot write registration. Currently only registrations up to 4D are supported.";
}
}
AbstractFileIO::ConfidenceLevel MAPRegistrationWrapperIO::GetWriterConfidenceLevel() const
{
const mitk::MAPRegistrationWrapper* regWrapper = dynamic_cast<const mitk::MAPRegistrationWrapper*>(this->GetInput());
if (regWrapper == nullptr)
{
return IFileWriter::Unsupported;
}
// Check if the registration dimension is supported
if (! DimHelper<3,3,CanWrite>::Execute(regWrapper))
{
return IFileWriter::Unsupported;
};
return IFileWriter::Supported;
}
std::vector<BaseData::Pointer > MAPRegistrationWrapperIO::DoRead()
{
std::vector<BaseData::Pointer > result;
LocaleSwitch("C");
std::string fileName = this->GetLocalFileName();
if ( fileName.empty() )
{
mitkThrow() << "Cannot read file. Filename has not been set!";
}
/* Remove the following kernel loader provider because in MITK no lazy file loading should be used
due to conflicts with session loading (end there usage of temporary directories)*/
map::io::RegistrationFileReader::LoaderStackType::unregisterProvider(map::io::LazyFileFieldKernelLoader<2,2>::getStaticProviderName());
map::io::RegistrationFileReader::LoaderStackType::unregisterProvider(map::io::LazyFileFieldKernelLoader<3,3>::getStaticProviderName());
map::io::RegistrationFileReader::Pointer spReader = map::io::RegistrationFileReader::New();
spReader->setPreferLazyLoading(true);
map::core::RegistrationBase::Pointer spReg = spReader->read(fileName);
auto spRegWrapper = mitk::MAPRegistrationWrapper::New(spReg);
result.push_back(spRegWrapper.GetPointer());
return result;
}
AbstractFileIO::ConfidenceLevel MAPRegistrationWrapperIO::GetReaderConfidenceLevel() const
{
AbstractFileIO::ConfidenceLevel result = IFileReader::Unsupported;
std::string fileName = this->GetLocalFileName();
std::ifstream in( fileName.c_str() );
if ( in.good() )
{
result = IFileReader::Supported;
}
in.close();
return result;
}
MAPRegistrationWrapperIO* MAPRegistrationWrapperIO::IOClone() const
{
return new MAPRegistrationWrapperIO(*this);
}
}
diff --git a/Modules/MatchPointRegistration/cmdapps/MapImage.cpp b/Modules/MatchPointRegistration/cmdapps/MapImage.cpp
index 3b57cb5023..f8b1c9d56b 100644
--- a/Modules/MatchPointRegistration/cmdapps/MapImage.cpp
+++ b/Modules/MatchPointRegistration/cmdapps/MapImage.cpp
@@ -1,286 +1,286 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// std includes
#include <string>
#include <numeric>
// itk includes
//#include <itksys/SystemTools.hxx>
// CTK includes
#include <mitkCommandLineParser.h>
// MITK includes
#include <mitkIOUtil.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
#include <mitkMAPRegistrationWrapper.h>
#include <mitkMAPAlgorithmHelper.h>
#include <mitkImageStitchingHelper.h>
struct Settings
{
std::string inFileName = "";
std::string regFileName = "";
std::string outFileName = "";
std::string refGeometryFileName = "";
mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear;
double paddingValue = 0;
std::vector<unsigned int> superSamplingFactors;
};
void setupParser(mitkCommandLineParser& parser)
{
// set general information about your MiniApp
parser.setCategory("Registration Tools");
parser.setTitle("Map Image");
parser.setDescription("MiniApp that allows to map a image into a given output geometry by using a given registration.");
parser.setContributor("MIC, German Cancer Research Center (DKFZ)");
//! [create parser]
//! [add arguments]
// how should arguments be prefixed
parser.setArgumentPrefix("--", "-");
// add each argument, unless specified otherwise each argument is optional
// see mitkCommandLineParser::addArgument for more information
parser.beginGroup("Required I/O parameters");
parser.addArgument(
"input", "i", mitkCommandLineParser::File, "Input image", "Path to the input images that should be mapped", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file path",
- "Path to the maped image.",
+ "Path to the mapped image.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument(
"registration", "r", mitkCommandLineParser::File, "Registration filee", "Path to the registration that should be used. If no registration is specified, an identity transform is assumed.", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument("template",
"t",
mitkCommandLineParser::File,
"Output template image.",
"File path to an image that serves as template for the output geometry. If no template is specified, the geometry of the input image will be used.",
us::Any(),
false, false, false, mitkCommandLineParser::Input);
parser.addArgument(
- "registrations", "r", mitkCommandLineParser::StringList, "Registration files", "Pathes to the registrations that should be used to map the input images. If this parameter is not set, identity transforms are assumed. If this parameter is set, it must have the same number of entries then the parameter inputs. If you want to use and identity transform for a specific input, specify an empty string. The application assumes that inputs and registrations have the same order, so the n-th input should use thr n-th registration.", us::Any(), true, false, false, mitkCommandLineParser::Input);
+ "registrations", "r", mitkCommandLineParser::StringList, "Registration files", "Paths to the registrations that should be used to map the input images. If this parameter is not set, identity transforms are assumed. If this parameter is set, it must have the same number of entries then the parameter inputs. If you want to use and identity transform for a specific input, specify an empty string. The application assumes that inputs and registrations have the same order, so the n-th input should use thr n-th registration.", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument("interpolator", "n", mitkCommandLineParser::Int, "Interpolator type", "Interpolator used for mapping the images. Default: 2; allowed values: 1: Nearest Neighbour, 2: Linear, 3: BSpline 3, 4: WSinc Hamming, 5: WSinc Welch", us::Any(2), true);
parser.addArgument("padding", "p", mitkCommandLineParser::Float, "Padding value", "Value used for output voxels that are not covered by any input image.", us::Any(0.), true);
parser.addArgument("super-sampling", "s", mitkCommandLineParser::StringList, "Super sampling factor", "Value used for super sampling of the result. E.g. factor 2 will lead to a doubled resolution compared to the used template. If not specified, no super sampling will be done.", us::Any(), true);
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
//! [add arguments]
}
bool configureApplicationSettings(std::map<std::string, us::Any> parsedArgs, Settings& settings)
{
try
{
if (parsedArgs.size() == 0)
return false;
settings.inFileName = us::any_cast<std::string>(parsedArgs["input"]);
settings.outFileName = us::any_cast<std::string>(parsedArgs["output"]);
if (parsedArgs.count("template"))
{
settings.refGeometryFileName = us::any_cast<std::string>(parsedArgs["template"]);
}
if (parsedArgs.count("registration"))
{
settings.regFileName = us::any_cast<std::string>(parsedArgs["registration"]);
}
if (parsedArgs.count("interpolator"))
{
auto interpolator = us::any_cast<int>(parsedArgs["interpolator"]);
settings.interpolatorType = static_cast<mitk::ImageMappingInterpolator::Type>(interpolator);
}
if (parsedArgs.count("padding"))
{
settings.paddingValue = us::any_cast<float>(parsedArgs["padding"]);
}
settings.superSamplingFactors.clear();
if (parsedArgs.count("super-sampling"))
{
try
{
auto samplingStrings = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["super-sampling"]);
if (samplingStrings.size() != 1 && samplingStrings.size() != 3)
{
std::cerr << "Error. Invalid number of super sampling parameters provided. Either give one (for isometric super sampling) or 3.";
return false;
}
for (const auto& samplingstr : samplingStrings)
{
settings.superSamplingFactors.push_back(std::stoul(samplingstr));
}
if (settings.superSamplingFactors.size() == 1)
{
settings.superSamplingFactors.push_back(settings.superSamplingFactors[0]);
settings.superSamplingFactors.push_back(settings.superSamplingFactors[0]);
}
}
catch (...)
{
std::cerr << "Error. Invalid super sampling parameter provided.";
throw;
}
}
}
catch (...)
{
return false;
}
return true;
}
int main(int argc, char* argv[])
{
mitk::Image::ConstPointer inputImage;
mitk::MAPRegistrationWrapper::ConstPointer registration;
mitk::BaseGeometry::Pointer refGeometry;
Settings settings;
mitkCommandLineParser parser;
setupParser(parser);
mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" });
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
if (!configureApplicationSettings(parsedArgs, settings))
{
MITK_ERROR << "Command line arguments are invalid. To see the correct usage please call with -h or --help to show the help information.";
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
std::cout << std::endl << "*******************************************" << std::endl;
std::cout << "Input file: " << settings.inFileName << std::endl;
std::cout << "Output file: " << settings.outFileName << std::endl;
std::cout << "Registration: ";
if (settings.regFileName.empty())
std::cout << "None (Identity)" << std::endl;
else
std::cout << settings.regFileName << std::endl;
std::cout << "Template: ";
if (settings.refGeometryFileName.empty())
std::cout << "None (is input geometry)" << std::endl;
else
std::cout << settings.refGeometryFileName << std::endl;
std::cout << "Padding value: " << settings.paddingValue << std::endl;
std::cout << "Interpolation type: " << settings.interpolatorType << std::endl;
//check for super/sub sampling
if (!settings.superSamplingFactors.empty() && (settings.superSamplingFactors.size() != 1 || settings.superSamplingFactors[0] != 1))
{
std::cout << "Super sampling:";
for (auto value : settings.superSamplingFactors)
{
std::cout << " " << value;
}
std::cout << std::endl;
}
//! [do processing]
try
{
std::cout << "Load input data..." << std::endl;
inputImage = mitk::IOUtil::Load<mitk::Image>(settings.inFileName);
if (inputImage.IsNull())
{
MITK_ERROR << "Cannot load input image.";
return EXIT_FAILURE;
}
std::cout << "Load registration..." << std::endl;
if (settings.regFileName.empty())
{
std::cout << " associated registration: identity" << std::endl;
registration = mitk::GenerateIdentityRegistration3D().GetPointer();
}
else
{
registration = mitk::IOUtil::Load<mitk::MAPRegistrationWrapper>(settings.regFileName);
}
if (registration.IsNull())
{
MITK_ERROR << "Cannot load registration.";
return EXIT_FAILURE;
}
if (settings.refGeometryFileName != "")
{
std::cout << "Load reference image..." << std::endl;
auto refImage = mitk::IOUtil::Load<mitk::Image>(settings.refGeometryFileName, &readerFilterFunctor);
if (refImage.IsNotNull())
{
refGeometry = refImage->GetGeometry();
}
else
{
MITK_ERROR << "Cannot load reference geometry image.";
return EXIT_FAILURE;
}
}
else
{
refGeometry = inputImage->GetGeometry();
}
//check for super/sub sampling
if (!settings.superSamplingFactors.empty() && (settings.superSamplingFactors.size()!=1 || settings.superSamplingFactors[0]!=1))
{
refGeometry = mitk::ImageMappingHelper::GenerateSuperSampledGeometry(refGeometry,
settings.superSamplingFactors[0],
settings.superSamplingFactors[1],
settings.superSamplingFactors[2]);
}
std::cout << "Map the images ..." << std::endl;
auto output = mitk::ImageMappingHelper::map(inputImage, registration, false, settings.paddingValue, refGeometry, true, 0, settings.interpolatorType);
std::cout << "Save output image: " << settings.outFileName << std::endl;
mitk::IOUtil::Save(output, settings.outFileName);
std::cout << "Processing finished." << std::endl;
return EXIT_SUCCESS;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
}
diff --git a/Modules/MatchPointRegistration/cmdapps/MatchImage.cpp b/Modules/MatchPointRegistration/cmdapps/MatchImage.cpp
index 17e05af7f5..7e32fa9b6a 100644
--- a/Modules/MatchPointRegistration/cmdapps/MatchImage.cpp
+++ b/Modules/MatchPointRegistration/cmdapps/MatchImage.cpp
@@ -1,482 +1,482 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkCommandLineParser.h"
#include <mitkIOUtil.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
#include <mitkMAPRegistrationWrapper.h>
#include <mitkMAPAlgorithmHelper.h>
#include <mitkPointSet.h>
#include <mitkImageTimeSelector.h>
#include <itkStdStreamLogOutput.h>
// MatchPoint
#include <mapRegistrationAlgorithmInterface.h>
#include <mapAlgorithmEvents.h>
#include <mapAlgorithmWrapperEvent.h>
#include <mapExceptionObjectMacros.h>
#include <mapImageRegistrationAlgorithmInterface.h>
#include <mapPointSetRegistrationAlgorithmInterface.h>
#include <mapMaskedRegistrationAlgorithmInterface.h>
#include <mapMetaPropertyAlgorithmInterface.h>
#include <mapMetaProperty.h>
#include <mapConvert.h>
#include <mapDeploymentDLLAccess.h>
#include <mapDeploymentDLLHandle.h>
#include <mapRegistrationBase.h>
#include <nlohmann/json.hpp>
struct Settings
{
std::string movingFileName = "";
std::string targetFileName = "";
std::string outFileName = "";
std::string algFileName = "";
std::string parameters = "";
};
void SetupParser(mitkCommandLineParser& parser)
{
parser.setTitle("Match Image");
parser.setCategory("Registration Tools");
parser.setDescription("");
parser.setContributor("MIC, German Cancer Research Center (DKFZ)");
parser.setArgumentPrefix("--", "-");
// Add command line argument names
parser.beginGroup("Required I/O parameters");
parser.addArgument(
"moving", "m",
mitkCommandLineParser::File,
- "Moving image files", "Path to the data that should be registred into the target space.",
+ "Moving image files", "Path to the data that should be registered into the target space.",
us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument(
"target", "t",
mitkCommandLineParser::File,
"Tareget image files", "Path to the data that should be the target data on which the moving data should be registered.",
us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument(
"algorithm", "a",
mitkCommandLineParser::File,
"Registration algorithm", "Path to the registration algorithm that should be used for registration.",
us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file path",
"Path to the generated registration.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument(
"parameters", "p", mitkCommandLineParser::String, "Parameters", "Json string containing a json object that contains the parameters that should be passed to the algorithm as key value pairs.");
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
}
bool ConfigureApplicationSettings(std::map<std::string, us::Any> parsedArgs, Settings& settings)
{
try
{
if (parsedArgs.size() == 0)
return false;
settings.movingFileName = us::any_cast<std::string>(parsedArgs["moving"]);
settings.targetFileName = us::any_cast<std::string>(parsedArgs["target"]);
settings.outFileName = us::any_cast<std::string>(parsedArgs["output"]);
settings.algFileName = us::any_cast<std::string>(parsedArgs["algorithm"]);
if (parsedArgs.count("parameters") > 0)
{
settings.parameters = us::any_cast<std::string>(parsedArgs["parameters"]);
}
}
catch (...)
{
return false;
}
return true;
}
map::deployment::RegistrationAlgorithmBasePointer loadAlgorithm(const Settings& settings)
{
map::deployment::RegistrationAlgorithmBasePointer spAlgorithmBase = nullptr;
std::cout << std::endl << "Load registration algorithm..." << std::endl;
map::deployment::DLLHandle::Pointer spHandle = nullptr;
spHandle = map::deployment::openDeploymentDLL(settings.algFileName);
if (spHandle.IsNull())
{
mapDefaultExceptionStaticMacro(<<
"Cannot open deployed registration algorithm file.");
}
- std::cout << "... libary opened..." << std::endl;
+ std::cout << "... library opened..." << std::endl;
std::cout << "Algorithm information: " << std::endl;
spHandle->getAlgorithmUID().Print(std::cout, 2);
std::cout << std::endl;
- //Now load the algorthm from DLL
+ //Now load the algorithm from DLL
spAlgorithmBase = map::deployment::getRegistrationAlgorithm(spHandle);
if (spAlgorithmBase.IsNotNull())
{
std::cout << "... done" << std::endl << std::endl;
}
else
{
mapDefaultExceptionStaticMacro(<< "Cannot create algorithm instance");
}
return spAlgorithmBase;
};
mitk::Image::Pointer ExtractFirstFrame(const mitk::Image* dynamicImage)
{
mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput(dynamicImage);
imageTimeSelector->SetTimeNr(0);
imageTimeSelector->UpdateLargestPossibleRegion();
return imageTimeSelector->GetOutput();
}
template <typename TValueType>
map::core::MetaPropertyBase::Pointer
CheckCastAndSetProp(const nlohmann::json& value)
{
map::core::MetaPropertyBase::Pointer prop;
try
{
const auto castedValue = value.get<TValueType>();
prop = map::core::MetaProperty<TValueType>::New(castedValue).GetPointer();
}
catch (const std::exception& e)
{
MITK_ERROR << "Cannot convert value \"" << value << "\" into type: " << typeid(TValueType).name() << ". Details: " << e.what();
}
catch (...)
{
MITK_ERROR << "Unkown error. Cannot convert value \"" << value << "\" into type: " << typeid(TValueType).name();
}
return prop;
};
template <typename TValueType>
map::core::MetaPropertyBase::Pointer
CheckCastAndSetItkArrayProp(const nlohmann::json& valueSequence)
{
using ArrayType = ::itk::Array<TValueType>;
ArrayType castedValue;
map::core::MetaPropertyBase::Pointer prop;
try
{
castedValue.SetSize(valueSequence.size());
typename ::itk::Array<TValueType>::SizeValueType index = 0;
for (const auto& element : valueSequence)
{
const auto castedElement = element.template get<TValueType>();
castedValue[index] = castedElement;
}
prop = map::core::MetaProperty<::itk::Array<TValueType>>::New(castedValue).GetPointer();
}
catch (const std::exception& e)
{
MITK_ERROR << "Cannot convert value \"" << valueSequence << "\" into type: " << typeid(ArrayType).name() << ". Details: " << e.what();
}
catch (...)
{
MITK_ERROR << "Unkown error. Cannot convert value \"" << valueSequence << "\" into type: " << typeid(ArrayType).name();
}
return prop;
};
::map::core::MetaPropertyBase::Pointer
WrapIntoMetaProperty(const ::map::algorithm::MetaPropertyInfo* pInfo, const nlohmann::json& value)
{
map::core::MetaPropertyBase::Pointer metaProp;
if (pInfo == nullptr)
{
return metaProp;
}
if (pInfo->getTypeInfo() == typeid(int)) {
metaProp = CheckCastAndSetProp<int>(value);
}
else if (pInfo->getTypeInfo() == typeid(unsigned int)) {
metaProp = CheckCastAndSetProp<unsigned int>(value);
}
else if (pInfo->getTypeInfo() == typeid(long)) {
metaProp = CheckCastAndSetProp<long>(value);
}
else if (pInfo->getTypeInfo() == typeid(unsigned long)) {
metaProp = CheckCastAndSetProp<unsigned long>(value);
}
else if (pInfo->getTypeInfo() == typeid(float)) {
metaProp = CheckCastAndSetProp<float>(value);
}
else if (pInfo->getTypeInfo() == typeid(double)) {
metaProp = CheckCastAndSetProp<double>(value);
}
else if (pInfo->getTypeInfo() == typeid(::itk::Array<double>)) {
metaProp = CheckCastAndSetItkArrayProp< double >(value);
}
else if (pInfo->getTypeInfo() == typeid(bool)) {
metaProp = CheckCastAndSetProp< bool >(value);
}
else if (pInfo->getTypeInfo() == typeid(::map::core::String))
{
metaProp = map::core::MetaProperty<map::core::String>::New(value).GetPointer();
}
return metaProp;
};
void OnMapAlgorithmEvent(::itk::Object*, const itk::EventObject& event, void*)
{
const map::events::AlgorithmEvent* pAlgEvent = dynamic_cast<const map::events::AlgorithmEvent*>(&event);
const map::events::AlgorithmWrapperEvent* pWrapEvent =
dynamic_cast<const map::events::AlgorithmWrapperEvent*>(&event);
const map::events::InitializingAlgorithmEvent* pInitEvent =
dynamic_cast<const map::events::InitializingAlgorithmEvent*>(&event);
const map::events::StartingAlgorithmEvent* pStartEvent =
dynamic_cast<const map::events::StartingAlgorithmEvent*>(&event);
const map::events::StoppingAlgorithmEvent* pStoppingEvent =
dynamic_cast<const map::events::StoppingAlgorithmEvent*>(&event);
const map::events::StoppedAlgorithmEvent* pStoppedEvent =
dynamic_cast<const map::events::StoppedAlgorithmEvent*>(&event);
const map::events::FinalizingAlgorithmEvent* pFinalizingEvent =
dynamic_cast<const map::events::FinalizingAlgorithmEvent*>(&event);
const map::events::FinalizedAlgorithmEvent* pFinalizedEvent =
dynamic_cast<const map::events::FinalizedAlgorithmEvent*>(&event);
if (pInitEvent)
{
std::cout <<"Initializing algorithm ..." << std::endl;
}
else if (pStartEvent)
{
std::cout <<"Starting algorithm ..." << std::endl;
}
else if (pStoppingEvent)
{
std::cout <<"Stopping algorithm ..." << std::endl;
}
else if (pStoppedEvent)
{
std::cout <<"Stopped algorithm ..." << std::endl;
if (!pStoppedEvent->getComment().empty())
{
std::cout <<"Stopping condition: " << pStoppedEvent->getComment() << std::endl;
}
}
else if (pFinalizingEvent)
{
std::cout <<"Finalizing algorithm and results ..." << std::endl;
}
else if (pFinalizedEvent)
{
std::cout <<"Finalized algorithm ..." << std::endl;
}
else if (pAlgEvent && !pWrapEvent)
{
std::cout << pAlgEvent->getComment() << std::endl;
}
}
int main(int argc, char* argv[])
{
std::cout << "MitkMatchImage - Generic light weight image registration tool based on MatchPoint." << std::endl;
Settings settings;
mitkCommandLineParser parser;
SetupParser(parser);
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
if (!ConfigureApplicationSettings(parsedArgs, settings))
{
MITK_ERROR << "Command line arguments are invalid. To see the correct usage please call with -h or --help to show the help information.";
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
std::cout << std::endl << "*******************************************" << std::endl;
std::cout << "Moving file: " << settings.movingFileName << std::endl;
std::cout << "Target file: " << settings.targetFileName << std::endl;
std::cout << "Output file: " << settings.outFileName << std::endl;
std::cout << "Algorithm location: " << settings.algFileName << std::endl;
try
{
auto algorithm = loadAlgorithm(settings);
auto command = ::itk::CStyleCommand::New();
command->SetCallback(OnMapAlgorithmEvent);
algorithm->AddObserver(::map::events::AlgorithmEvent(), command);
auto metaPropInterface = dynamic_cast<map::algorithm::facet::MetaPropertyAlgorithmInterface*>(algorithm.GetPointer());
if (!settings.parameters.empty())
{
if (nullptr == metaPropInterface)
{
MITK_WARN << "loaded algorithm does not support custom parameterization. Passed user parameters are ignored.";
}
else
{
nlohmann::json paramMap;
std::string parseError = "";
try
{
paramMap = nlohmann::json::parse(settings.parameters);
}
catch (const std::exception& e)
{
parseError = e.what();
}
if (!parseError.empty())
{
mitkThrow() << "Cannot parametrize algorithm. Passed JSON parameter string seems to be invalid. Passed string: \"" << settings.parameters << "\". Error details: " << parseError;
}
std::cout << "Configuring algorithm with user specified parameters ..." << std::endl;
for (const auto& [key, val] : paramMap.items())
{
const auto info = metaPropInterface->getPropertyInfo(key);
if (info.IsNotNull())
{
if (info->isWritable())
{
std::cout << "Set meta property: " << key << " = " << val << std::endl;
::map::core::MetaPropertyBase::Pointer prop = WrapIntoMetaProperty(info, val);
if (prop.IsNull())
{
mitkThrow() << "Error. Cannot set specified meta property. Type conversion is not supported or value cannot be converted into type. Property name: " << info->getName() << "; property type: " << info->getTypeName();
}
else
{
metaPropInterface->setProperty(key, prop);
}
}
else
{
mitkThrow() << "Cannot parametrize algorithm. A passed parameter is not writable for the algorithm. Violating parameter: \"" << key << "\".";
}
}
else
{
auto knownProps = metaPropInterface->getPropertyInfos();
std::ostringstream knownPropsNameString;
for (const auto& knownProp : knownProps)
{
knownPropsNameString << knownProp->getName() << "; ";
}
mitkThrow() << "Cannot parametrize algorithm. A parameter is unkown to algorithm. Unkown passed parameter: \"" << key << "\". Known parameters: " << knownPropsNameString.str();
}
}
}
}
std::cout << "Load moving data..." << std::endl;
auto movingImage = mitk::IOUtil::Load<mitk::Image>(settings.movingFileName);
if (movingImage.IsNull())
{
MITK_ERROR << "Cannot load moving image.";
return EXIT_FAILURE;
}
if (movingImage->GetTimeSteps() > 1)
{
movingImage = mitk::SelectImageByTimeStep(movingImage, 0)->Clone(); //we have to clone because SelectImageByTimeStep
//only generates as new view of the data and we
//are overwriting the only smartpointer to the source.
- std::cout << "Moving image has multiple time steps. Use first time step for registartion." << std::endl;
+ std::cout << "Moving image has multiple time steps. Use first time step for registration." << std::endl;
}
std::cout << "Load target data..." << std::endl;
auto targetImage = mitk::IOUtil::Load<mitk::Image>(settings.targetFileName);
if (targetImage.IsNull())
{
MITK_ERROR << "Cannot load target image.";
return EXIT_FAILURE;
}
if (targetImage->GetTimeSteps() > 1)
{
targetImage = mitk::SelectImageByTimeStep(targetImage, 0)->Clone(); //we have to clone because SelectImageByTimeStep
//only generates as new view of the data and we
//are overwriting the only smartpointer to the source.
- std::cout << "Target image has multiple time steps. Use first time step for registartion." << std::endl;
+ std::cout << "Target image has multiple time steps. Use first time step for registration." << std::endl;
}
std::cout << "Start registration...." << std::endl;
mitk::MAPAlgorithmHelper helper(algorithm);
helper.SetData(movingImage, targetImage);
::itk::StdStreamLogOutput::Pointer spStreamLogOutput = ::itk::StdStreamLogOutput::New();
spStreamLogOutput->SetStream(std::cout);
map::core::Logbook::addAdditionalLogOutput(spStreamLogOutput);
auto registration = helper.GetRegistration();
// wrap the registration in a data node
if (registration.IsNull())
{
MITK_ERROR << "No valid registration generated";
return EXIT_FAILURE;
}
auto regWrapper = mitk::MAPRegistrationWrapper::New(registration);
std::cout << "Store registration...." << std::endl;
mitk::IOUtil::Save(regWrapper, settings.outFileName);
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
diff --git a/Modules/MatchPointRegistration/cmdapps/StitchImages.cpp b/Modules/MatchPointRegistration/cmdapps/StitchImages.cpp
index 159d96a34f..e486a7a744 100644
--- a/Modules/MatchPointRegistration/cmdapps/StitchImages.cpp
+++ b/Modules/MatchPointRegistration/cmdapps/StitchImages.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.
============================================================================*/
// std includes
#include <string>
#include <numeric>
// itk includes
#include "itksys/SystemTools.hxx"
// CTK includes
#include "mitkCommandLineParser.h"
// MITK includes
#include <mitkIOUtil.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
#include <mitkMAPRegistrationWrapper.h>
#include <mitkMAPAlgorithmHelper.h>
#include <mitkImageStitchingHelper.h>
mitkCommandLineParser::StringContainerType inFilenames;
mitkCommandLineParser::StringContainerType regFilenames;
std::string outFileName;
std::string refGeometryFileName;
std::vector<mitk::Image::ConstPointer> images;
std::vector<mitk::MAPRegistrationWrapper::ConstPointer> registrations;
mitk::BaseGeometry::Pointer refGeometry;
mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear;
double paddingValue = 0;
itk::StitchStrategy stitchStratgy = itk::StitchStrategy::Mean;
void setupParser(mitkCommandLineParser& parser)
{
// set general information about your MiniApp
parser.setCategory("Registration Tools");
parser.setTitle("Stitch 3D Images");
parser.setDescription("MiniApp that allows to map and stitch 3D images into a given output geometry.");
parser.setContributor("MIC, German Cancer Research Center (DKFZ)");
//! [create parser]
//! [add arguments]
// how should arguments be prefixed
parser.setArgumentPrefix("--", "-");
// add each argument, unless specified otherwise each argument is optional
// see mitkCommandLineParser::addArgument for more information
parser.beginGroup("Required I/O parameters");
parser.addArgument(
- "inputs", "i", mitkCommandLineParser::StringList, "Input files", "Pathes to the input images that should be mapped and stitched", us::Any(), false, false, false, mitkCommandLineParser::Input);
+ "inputs", "i", mitkCommandLineParser::StringList, "Input files", "Paths to the input images that should be mapped and stitched", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file path",
"Path to the fused 3D+t image.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument("template",
"t",
mitkCommandLineParser::File,
"Output template image.",
"File path to an image that serves as template for the output geometry.",
us::Any(),
false, false, false, mitkCommandLineParser::Input);
parser.addArgument(
- "registrations", "r", mitkCommandLineParser::StringList, "Registration files", "Pathes to the registrations that should be used to map the input images. If this parameter is not set, identity transforms are assumed. If this parameter is set, it must have the same number of entries then the parameter inputs. If you want to use and identity transform for a specific input, specify an empty string. The application assumes that inputs and registrations have the same order, so the n-th input should use thr n-th registration.", us::Any(), true, false, false, mitkCommandLineParser::Input);
+ "registrations", "r", mitkCommandLineParser::StringList, "Registration files", "Paths to the registrations that should be used to map the input images. If this parameter is not set, identity transforms are assumed. If this parameter is set, it must have the same number of entries then the parameter inputs. If you want to use and identity transform for a specific input, specify an empty string. The application assumes that inputs and registrations have the same order, so the n-th input should use thr n-th registration.", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument("interpolator", "n", mitkCommandLineParser::Int, "Interpolator type", "Interpolator used for mapping the images. Default: 2; allowed values: 1: Nearest Neighbour, 2: Linear, 3: BSpline 3, 4: WSinc Hamming, 5: WSinc Welch", us::Any(2), true);
parser.addArgument("strategy", "s", mitkCommandLineParser::Int, "Stitch strategy", "Strategy used for stitching the images. 0: Mean -> computes the mean value of all input images that cover an output pixel (default strategy). 1: BorderDistance -> Uses the input pixel that has the largest minimal distance to its image borders", us::Any(2), true);
parser.addArgument("padding", "p", mitkCommandLineParser::Float, "Padding value", "Value used for output voxels that are not covered by any input image.", us::Any(0.), true);
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
//! [add arguments]
}
bool configureApplicationSettings(std::map<std::string, us::Any> parsedArgs)
{
try
{
if (parsedArgs.size() == 0)
return false;
inFilenames = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["inputs"]);
outFileName = us::any_cast<std::string>(parsedArgs["output"]);
if (parsedArgs.count("template"))
{
refGeometryFileName = us::any_cast<std::string>(parsedArgs["template"]);
}
if (parsedArgs.count("registrations"))
{
regFilenames = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["registrations"]);
}
else
{
regFilenames.resize(inFilenames.size());
std::fill(regFilenames.begin(), regFilenames.end(), "");
}
if (parsedArgs.count("interpolator"))
{
auto interpolator = us::any_cast<int>(parsedArgs["interpolator"]);
interpolatorType = static_cast<mitk::ImageMappingInterpolator::Type>(interpolator);
}
if (parsedArgs.count("padding"))
{
paddingValue = us::any_cast<float>(parsedArgs["padding"]);
}
if (parsedArgs.count("strategy"))
{
auto temp = us::any_cast<int>(parsedArgs["strategy"]);
stitchStratgy = static_cast<itk::StitchStrategy>(temp);
}
}
catch (...)
{
return false;
}
return true;
}
int main(int argc, char* argv[])
{
mitkCommandLineParser parser;
setupParser(parser);
mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" });
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
if (!configureApplicationSettings(parsedArgs))
{
MITK_ERROR << "Command line arguments are invalid. To see the correct usage please call with -h or --help to show the help information.";
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
if(regFilenames.size() != inFilenames.size())
{
MITK_ERROR << "Cannot stitch inputs. The number of specified registrations does not match the number of inputs.";
return EXIT_FAILURE;
}
//! [do processing]
try
{
std::cout << "Load images:" << std::endl;
unsigned int index = 0;
for (auto path : inFilenames)
{
std::cout << "#"<<index<<" " << path << std::endl;
auto image = mitk::IOUtil::Load<mitk::Image>(path, &readerFilterFunctor);
images.push_back(image.GetPointer());
if (regFilenames[index].empty())
{
std::cout << " associated registration: identity" << std::endl;
registrations.push_back(mitk::GenerateIdentityRegistration3D().GetPointer());
}
else
{
std::cout << " associated registration: " << regFilenames[index] << std::endl;
auto reg = mitk::IOUtil::Load<mitk::MAPRegistrationWrapper>(regFilenames[index]);
registrations.push_back(reg.GetPointer());
}
++index;
}
std::cout << "Reference image: " << refGeometryFileName << std::endl << std::endl;
auto refImage = mitk::IOUtil::Load<mitk::Image>(refGeometryFileName, &readerFilterFunctor);
if (refImage.IsNotNull())
{
refGeometry = refImage->GetGeometry();
}
std::cout << "Padding value: " << paddingValue << std::endl;
std::cout << "Stitch strategy: ";
if (itk::StitchStrategy::Mean == stitchStratgy)
{
std::cout << "Mean " << std::endl;
}
else
{
std::cout << "BorderDistance" << std::endl;
}
std::cout << "Stitch the images ..." << std::endl;
auto output = mitk::StitchImages(images, registrations, refGeometry,paddingValue,stitchStratgy,interpolatorType);
std::cout << "Save output image: " << outFileName << std::endl;
mitk::IOUtil::Save(output, outFileName);
std::cout << "Processing finished." << std::endl;
return EXIT_SUCCESS;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
}
diff --git a/Modules/MatchPointRegistration/include/itkStitchImageFilter.h b/Modules/MatchPointRegistration/include/itkStitchImageFilter.h
index c72836aeca..05e5b6e145 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)
+ BorderDistance = 1 //use the value that is largest minimal distance to its image borders (use e.g. if values 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";
+ os << "unknown";
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.
+ * - BorderDistance: the voxels will be chosen 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
+ /** Convenience 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. */
+ /** Set the output direction 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
+ * By default, output information is specified 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() 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/mitkImageStitchingHelper.h b/Modules/MatchPointRegistration/include/mitkImageStitchingHelper.h
index ff4e573d68..1f1a3b6e17 100644
--- a/Modules/MatchPointRegistration/include/mitkImageStitchingHelper.h
+++ b/Modules/MatchPointRegistration/include/mitkImageStitchingHelper.h
@@ -1,67 +1,67 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkImageStitchingHelper_h
#define mitkImageStitchingHelper_h
#include "mapRegistrationBase.h"
#include "mitkImage.h"
#include "mitkGeometry3D.h"
#include "mitkMAPRegistrationWrapper.h"
#include "mitkImageMappingHelper.h"
#include <itkStitchImageFilter.h>
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
/**Helper that stitches a given vector of input images
* @param inputs vector of input images that should be stitched.
* @param registrations vector of registrations that should be used for mapping of the inputs before stitching.
* the method assumes that order of registrations is the same as the order of inputs, thus for the n-th input
* the n-th registration will be used.
* @param resultGeometry Pointer to the Geometry object that specifies the grid of the result image.
* @param paddingValue Indicates the value that should be used if an out of input error occurs (and throwOnOutOfInputAreaError is false).
* @param interpolatorType Indicates the type of interpolation strategy that should be used.
* @param stitchStrategy Strategy used if more than one input can contribute. for more details see the documentation of itk::StitchStrategy.
* @pre inputs must not be empty and contain valid instances
* @pre registration must have same size as inputs and contain valid instances.
* @pre Dimensionality of the registrations must match with the inputs
* @pre resultGeometry must be valid.
* @remark The helper currently only supports 3D images.
* @result Pointer to the resulting mapped image.h*/
MITKMATCHPOINTREGISTRATION_EXPORT Image::Pointer StitchImages(std::vector<Image::ConstPointer> inputs,
std::vector<::map::core::RegistrationBase::ConstPointer> registrations,
const BaseGeometry* resultGeometry,
const double& paddingValue = 0, itk::StitchStrategy stitchStrategy = itk::StitchStrategy::Mean,
mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear);
MITKMATCHPOINTREGISTRATION_EXPORT Image::Pointer StitchImages(std::vector<Image::ConstPointer> inputs,
std::vector<MAPRegistrationWrapper::ConstPointer> registrations,
const BaseGeometry* resultGeometry,
const double& paddingValue = 0, itk::StitchStrategy stitchStrategy = itk::StitchStrategy::Mean,
mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear);
/**@overload
- * Convinience version that uses identity transforms form the registrations.
+ * Convenience version that uses identity transforms form the registrations.
*/
MITKMATCHPOINTREGISTRATION_EXPORT Image::Pointer StitchImages(std::vector<Image::ConstPointer> inputs,
const BaseGeometry* resultGeometry,
const double& paddingValue = 0, itk::StitchStrategy stitchStrategy = itk::StitchStrategy::Mean,
mitk::ImageMappingInterpolator::Type interpolatorType = mitk::ImageMappingInterpolator::Linear);
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkMAPRegistrationWrapper.h b/Modules/MatchPointRegistration/include/mitkMAPRegistrationWrapper.h
index b3460d25c7..19d56bd003 100644
--- a/Modules/MatchPointRegistration/include/mitkMAPRegistrationWrapper.h
+++ b/Modules/MatchPointRegistration/include/mitkMAPRegistrationWrapper.h
@@ -1,273 +1,273 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkMAPRegistrationWrapper_h
#define mitkMAPRegistrationWrapper_h
//MITK
#include <mitkBaseData.h>
#include <mitkGeometry3D.h>
//MatchPoint
#include <mapRegistrationBase.h>
#include <mapRegistration.h>
#include <mapExceptionObjectMacros.h>
#include <mapContinuousElements.h>
//MITK
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
/*!
\brief MAPRegistrationWrapper
Wrapper class to allow the handling of MatchPoint registration objects as mitk data (e.g. in the data explorer).
*/
class MITKMATCHPOINTREGISTRATION_EXPORT MAPRegistrationWrapper: public mitk::BaseData
{
public:
mitkClassMacro( MAPRegistrationWrapper, BaseData );
mitkNewMacro1Param( Self, ::map::core::RegistrationBase*);
Identifiable::UIDType GetUID() const override;
bool IsEmptyTimeStep(unsigned int t) const override;
bool IsEmpty() const override;
/**
* Empty implementation, since the MAPRegistrationWrapper doesn't
* support the requested region concept
*/
void SetRequestedRegionToLargestPossibleRegion() override;
/**
* Empty implementation, since the MAPRegistrationWrapper doesn't
* support the requested region concept
*/
bool RequestedRegionIsOutsideOfTheBufferedRegion() override;
/**
* Empty implementation, since the MAPRegistrationWrapper doesn't
* support the requested region concept
*/
bool VerifyRequestedRegion() override;
/**
* Empty implementation, since the MAPRegistrationWrapper doesn't
* support the requested region concept
*/
void SetRequestedRegion(const itk::DataObject*) override;
/*! @brief Gets the number of moving dimensions
@pre valid registration instance must be set.
*/
virtual unsigned int GetMovingDimensions() const;
/*! @brief Gets the number of target dimensions
@pre valid registration instance must be set.
*/
virtual unsigned int GetTargetDimensions() const;
/*! typedefs used for the TagMap
*/
typedef ::map::core::RegistrationBase::TagType TagType;
typedef ::map::core::RegistrationBase::ValueType ValueType;
typedef ::map::core::RegistrationBase::TagMapType TagMapType;
/*! @brief returns the tags associated with this registration
@pre valid registration instance must be set.
@return a TagMapType containing tags
*/
const TagMapType& GetTags() const;
/*! @brief returns the tag value for a specific tag
@pre valid registration instance must be set.
@return the success of the operation
*/
bool GetTagValue(const TagType & tag, ValueType & value) const;
/*! Indicates
@pre valid registration instance must be set.
@return is the target representation limited
@retval true if target representation is limited. Thus it is not guaranteed that all inverse mapping operations
will succeed. Transformation(inverse kernel) covers only a part of the target space).
@retval false if target representation is not limited. Thus it is guaranteed that all inverse mapping operations
will succeed.
*/
bool HasLimitedTargetRepresentation() const;
/*!
@pre valid registration instance must be set.
@return is the moving representation limited
@retval true if moving representation is limited. Thus it is not guaranteed that all direct mapping operations
will succeed. Transformation(direct kernel) covers only a part of the moving space).
@retval false if moving representation is not limited. Thus it is guaranteed that all direct mapping operations
will succeed.
*/
bool HasLimitedMovingRepresentation() const;
/*! Helper function that maps a mitk point (of arbitrary dimension) from moving space to target space.
@remarks The operation might fail, if the moving and target dimension of the registration
is not equal to the dimensionality of the passed points.
@pre valid registration instance must be set.
@param inPoint Reference pointer to a MovingPointType
@param outPoint pointer to a TargetPointType
@return success of operation.
@pre direct mapping kernel must be defined
*/
template <unsigned int VMovingDim, unsigned int VTargetDim>
bool MapPoint(const ::itk::Point<mitk::ScalarType,VMovingDim>& inPoint, ::itk::Point<mitk::ScalarType,VTargetDim>& outPoint) const
{
typedef typename ::map::core::continuous::Elements<VMovingDim>::PointType MAPMovingPointType;
typedef typename ::map::core::continuous::Elements<VTargetDim>::PointType MAPTargetPointType;
if (m_spRegistration.IsNull())
{
mapDefaultExceptionMacro(<< "Error. Cannot map point. Wrapper points to invalid registration (nullptr). Point: " << inPoint);
}
bool result = false;
if ((this->GetMovingDimensions() == VMovingDim)&&(this->GetTargetDimensions() == VTargetDim))
{
MAPMovingPointType tempInP;
MAPTargetPointType tempOutP;
tempInP.CastFrom(inPoint);
typedef ::map::core::Registration<VMovingDim,VTargetDim> CastedRegType;
const CastedRegType* pCastedReg = dynamic_cast<const CastedRegType*>(m_spRegistration.GetPointer());
if (!pCastedReg)
{
mapDefaultExceptionMacro(<< "Error. Cannot map point. Registration has invalid dimension. Point: " << inPoint);
}
result = pCastedReg->mapPoint(tempInP,tempOutP);
if (result)
{
outPoint.CastFrom(tempOutP);
}
}
return result;
};
/*! Helper function that maps a mitk point (of arbitrary dimension) from target space to moving space
@remarks The operation might faile, if the moving and target dimension of the registration
is not equal to the dimensionalities of the passed points.
@pre valid registration instance must be set.
@param inPoint pointer to a TargetPointType
@param outPoint pointer to a MovingPointType
@return success of operation
*/
template <unsigned int VMovingDim, unsigned int VTargetDim>
bool MapPointInverse(const ::itk::Point<mitk::ScalarType,VTargetDim> & inPoint, ::itk::Point<mitk::ScalarType,VMovingDim> & outPoint) const
{
typedef typename ::map::core::continuous::Elements<VMovingDim>::PointType MAPMovingPointType;
typedef typename ::map::core::continuous::Elements<VTargetDim>::PointType MAPTargetPointType;
if (m_spRegistration.IsNull())
{
mapDefaultExceptionMacro(<< "Error. Cannot map point. Wrapper points to invalid registration (nullptr). Point: " << inPoint);
}
bool result = false;
if ((this->GetMovingDimensions() == VMovingDim)&&(this->GetTargetDimensions() == VTargetDim))
{
MAPTargetPointType tempInP;
MAPMovingPointType tempOutP;
tempInP.CastFrom(inPoint);
typedef ::map::core::Registration<VMovingDim,VTargetDim> CastedRegType;
const CastedRegType* pCastedReg = dynamic_cast<const CastedRegType*>(m_spRegistration.GetPointer());
if (!pCastedReg)
{
mapDefaultExceptionMacro(<< "Error. Cannot map point. Registration has invalid dimension. Point: " << inPoint);
}
result = pCastedReg->mapPointInverse(tempInP,tempOutP);
if (result)
{
outPoint.CastFrom(tempOutP);
}
}
return result;
};
/*! returns the direct FieldRepresentationDescriptor which defines the part
of the moving space that is guaranteed to be mapped by the direct mapping kernel.
This member converts the internal MatchPoint type into a mitk::Geometry3D.
@pre valid registration instance must be set.
@return smart pointer to a FieldRepresentationDescriptor for the supported registration space in the moving domain.
May be null if the direct registration kernel is global and thus not limited.
- If there is a limitation, the retun value is not nullptr.
+ If there is a limitation, the return value is not nullptr.
@retval nullptr no field representation set/requested by the creating registration algorithm.
*/
mitk::Geometry3D GetDirectFieldRepresentation() const;
/*! returns the inverse FieldRepresentationDescriptor which defines the part
of the target space that is guaranteed to be mapped by the inverse mapping kernel.
This member converts the internal MatchPoint type into a mitk::Geometry3D.
@pre valid registration instance must be set.
@return a const FieldRepresentationDescriptor for the supported registration space in the target domain.
May be null if the inverse registration kernel is global and thus not limited.
- If there is a limitation, the retun value is not nullptr.
+ If there is a limitation, the return value is not nullptr.
@retval nullptr no field representation set/requested by the creating registration algorithm.
*/
mitk::Geometry3D GetInverseFieldRepresentation() const;
/*! forces kernel to precompute, even if it is a LazyFieldKernel
@pre valid registration instance must be set.
@todo der LazyFieldBasedRegistrationKernel muss dann die stong guarantee erfllen beim erzeugen des feldes ansonsten
ist die garantie dieser methode nicht erfllbar. noch berprfen
*/
void PrecomputeDirectMapping();
/*! forces kernel to precompute, even if it is a LazyFieldKernel
@pre valid registration instance must be set.
@todo der LazyFieldBasedRegistrationKernel muss dann die stong guarantee erfllen beim erzeugen des feldes ansonsten
ist die garantie dieser methode nicht erfllbar. noch berprfen
*/
void PrecomputeInverseMapping();
::map::core::RegistrationBase* GetRegistration();
const ::map::core::RegistrationBase* GetRegistration() const;
protected:
void PrintSelf (std::ostream &os, itk::Indent indent) const override;
MAPRegistrationWrapper(::map::core::RegistrationBase* registration);
~MAPRegistrationWrapper() override;
void SetUID(const UIDType& uid) override;
::map::core::RegistrationBase::Pointer m_spRegistration;
private:
MAPRegistrationWrapper& operator = (const MAPRegistrationWrapper&);
MAPRegistrationWrapper(const MAPRegistrationWrapper&);
};
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkMaskedAlgorithmHelper.h b/Modules/MatchPointRegistration/include/mitkMaskedAlgorithmHelper.h
index e3613516a1..01a44a6b4f 100644
--- a/Modules/MatchPointRegistration/include/mitkMaskedAlgorithmHelper.h
+++ b/Modules/MatchPointRegistration/include/mitkMaskedAlgorithmHelper.h
@@ -1,85 +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.
============================================================================*/
#ifndef mitkMaskedAlgorithmHelper_h
#define mitkMaskedAlgorithmHelper_h
#include "itkSpatialObject.h"
//MatchPoint
#include "mapRegistrationAlgorithmBase.h"
//MITK
#include <mitkImage.h>
//MITK
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
/*!
\brief MaskedAlgorithmHelper
Helper class as an easy bridge to set mitk images as masks for registration algorithms. It is assumed that the
Image indicates the mask by pixel values != 0.
\remark Currently only 2D-2D and 3D-3D algorithms are supported.
\remark Current implementation is not thread-save. Just use one Helper class per registration task.
*/
class MITKMATCHPOINTREGISTRATION_EXPORT MaskedAlgorithmHelper
{
public:
MaskedAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase* algorithm);
/** Set one or both masks to an algorithm.
* If the algorithm does not support masks it will be ignored.
* @remark Set a mask to nullptr if you don't want to set it.
* @return Indicates if the masks could be set/was supported by algorithm.*/
bool SetMasks(const mitk::Image* movingMask, const mitk::Image* targetMask);
/** Checks if the algorithm supports masks of the passed type.*/
bool CheckSupport(const mitk::Image* movingMask, const mitk::Image* targetMask) const;
static bool HasMaskedRegistrationAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm);
~MaskedAlgorithmHelper() {}
private:
using MaskPixelType = unsigned char;
MaskedAlgorithmHelper& operator = (const MaskedAlgorithmHelper&);
MaskedAlgorithmHelper(const MaskedAlgorithmHelper&);
/**Internal helper that is used by SetMasks if the data are images to set them properly.*/
template<unsigned int VImageDimension1, unsigned int VImageDimension2>
bool DoSetMasks(const mitk::Image* movingMask, const mitk::Image* targetMask);
/**Internal helper that is used by SetData if the data are images to cast and set them properly.*/
template<typename TPixelType, unsigned int VImageDimension>
void DoConvertMask(const itk::Image<TPixelType, VImageDimension>* mask);
/**Internal helper that is used by SetData if the data are images to set them properly.*/
template<unsigned int VImageDimension>
void DoConvertMask(const itk::Image<MaskPixelType, VImageDimension>* mask);
/**Internal helper that is used to pack the mask image into a spatial object.*/
template<unsigned int VImageDimension>
typename itk::SpatialObject<VImageDimension>::Pointer ConvertMaskSO(const itk::Image<MaskPixelType, VImageDimension>* mask) const;
- /**Helper member that containes the result of the last call of DoConvertMask().*/
+ /**Helper member that contains the result of the last call of DoConvertMask().*/
itk::DataObject::Pointer m_convertResult;
map::algorithm::RegistrationAlgorithmBase::Pointer m_AlgorithmBase;
};
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkMatchPointPropertyTags.h b/Modules/MatchPointRegistration/include/mitkMatchPointPropertyTags.h
index 97205e05fd..9354f61372 100644
--- a/Modules/MatchPointRegistration/include/mitkMatchPointPropertyTags.h
+++ b/Modules/MatchPointRegistration/include/mitkMatchPointPropertyTags.h
@@ -1,44 +1,44 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkMatchPointPropertyTags_h
#define mitkMatchPointPropertyTags_h
// MITK
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
- /**UID of the algorithm that was used to determin a registration.*/
+ /**UID of the algorithm that was used to determine a registration.*/
const char* const Prop_RegAlgUsed = "matchpoint.Registration.Algorithm.UID";
/**UID(s) of the data object(s) used as target for determining the registration.*/
const char* const Prop_RegAlgTargetData = "matchpoint.Registration.Algorithm.UsedData.target";
/**UID(s) of the data object(s) used as moving objects for determining the registration.*/
const char* const Prop_RegAlgMovingData = "matchpoint.Registration.Algorithm.UsedData.moving";
/**UID of the registration instance.*/
const char* const Prop_RegUID = "matchpoint.Registration.UID";
/**Input "section" that specifies what wwas mapped.*/
const char* const Prop_MappingInput = "matchpoint.Mapping.Input";
/**UID of the data object that was mapped (so the source) by the specified registration to generate the current instance.*/
const char* const Prop_MappingInputData = "matchpoint.Mapping.Input.Data";
/**Type of the interpolation strategy that was used to map the object. If not set, no interpolation was needed for mapping.*/
const char* const Prop_MappingInterpolator = "matchpoint.Mapping.Interpolator";
/**Indicates that the data was not mapped (in termes of resampled), but "just" the geometry was refined.*/
const char* const Prop_MappingRefinedGeometry = "matchpoint.Mapping.RefinedGeometry";
/**MatchPoint UID to uniquely identify an data object.*/
const char* const Prop_UID = "data.UID";
/**MatchPoint UID to uniquely identify an node.*/
const char* const nodeProp_UID = "matchpoint.UID";
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkPointSetMappingHelper.h b/Modules/MatchPointRegistration/include/mitkPointSetMappingHelper.h
index ddd8ca2093..84f8480984 100644
--- a/Modules/MatchPointRegistration/include/mitkPointSetMappingHelper.h
+++ b/Modules/MatchPointRegistration/include/mitkPointSetMappingHelper.h
@@ -1,60 +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 mitkPointSetMappingHelper_h
#define mitkPointSetMappingHelper_h
#include "mapRegistrationBase.h"
#include <mitkPointSet.h>
#include "mitkMAPRegistrationWrapper.h"
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
namespace PointSetMappingHelper
{
typedef ::map::core::RegistrationBase RegistrationType;
typedef ::mitk::MAPRegistrationWrapper MITKRegistrationType;
/**Helper that converts the data of an mitk point set into the default point set type of matchpoint.*/
MITKMATCHPOINTREGISTRATION_EXPORT ::map::core::continuous::Elements<3>::InternalPointSetType::Pointer ConvertPointSetMITKtoMAP(const mitk::PointSet::DataType* mitkSet);
/**Helper that maps a given input point set
* @param input Point set that should be mapped.
* @param registration Pointer to the registration instance that should be used for mapping
* @param timeStep Indicates which time step of the point set should be mapped (the rest will just be copied). -1 (default) indicates that all time steps should be mapped.
* @param throwOnMappingError Indicates if mapping should fail with an exception (true), if the registration does not cover/support the whole requested region for mapping into the result image.
- * if set to false, points that cause an mapping error will be transfered without mapping but get the passed errorPointValue as data to indicate unmappable points;
+ * if set to false, points that cause an mapping error will be transferred without mapping but get the passed errorPointValue as data to indicate unmappable points;
* @param errorPointValue Indicates the point data that should be used if an mapping error occurs (and throwOnMappingError is false).
* @pre input must be valid
* @pre registration must be valid
* @pre timeStep must be a valid time step of input or -1
* @pre Dimensionality of the registration must match with the input imageinput must be valid
* @remark Depending in the settings of throwOnMappingError it may also throw
* due to inconsistencies in the mapping process. See parameter description.
* @result Pointer to the resulting mapped point set*/
MITKMATCHPOINTREGISTRATION_EXPORT ::mitk::PointSet::Pointer map(const ::mitk::PointSet* input, const RegistrationType* registration, int timeStep = -1,
bool throwOnMappingError = true, const ::mitk::PointSet::PointDataType& errorPointValue = ::mitk::PointSet::PointDataType());
/**Helper that maps a given input point set
* @overload*/
MITKMATCHPOINTREGISTRATION_EXPORT ::mitk::PointSet::Pointer map(const ::mitk::PointSet* input, const MITKRegistrationType* registration, int timeStep = -1,
bool throwOnMappingError = true, const ::mitk::PointSet::PointDataType& errorPointValue = ::mitk::PointSet::PointDataType());
}
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkQMAPAlgorithmModel.h b/Modules/MatchPointRegistration/include/mitkQMAPAlgorithmModel.h
index dad1a40a4b..376bb10367 100644
--- a/Modules/MatchPointRegistration/include/mitkQMAPAlgorithmModel.h
+++ b/Modules/MatchPointRegistration/include/mitkQMAPAlgorithmModel.h
@@ -1,75 +1,75 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkQMAPAlgorithmModel_h
#define mitkQMAPAlgorithmModel_h
#include <QAbstractTableModel>
#include <QStringList>
//MITK
#include "MitkMatchPointRegistrationExports.h"
// MatchPoint
#include <mapRegistrationAlgorithmBase.h>
#include <mapMetaPropertyAlgorithmInterface.h>
namespace mitk
{
/*!
\class QMAPAlgorithmModel
Helper class that implements a model to handle the MetaProperty interface of a MatchPoint algorithm
- in contect of the QT view-model-concept. A algorithm can be set as data source for the model.
+ in context of the QT view-model-concept. A algorithm can be set as data source for the model.
The model retrieves all information through the MetaPropertyInterface. Changes in the view will
be propagated by the model into the algorithm.
\remarks The model only keep a simple pointer to the MetaPropertyInterface of the algorithm.
You have to ensure to reset the algorithm if the pointer goes invalid.
\warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation.
*/
class MITKMATCHPOINTREGISTRATION_EXPORT QMAPAlgorithmModel : public QAbstractTableModel
{
Q_OBJECT
public:
QMAPAlgorithmModel(QObject *parent = nullptr);
virtual ~QMAPAlgorithmModel() {};
void SetAlgorithm(map::algorithm::RegistrationAlgorithmBase *pAlgorithm);
void SetAlgorithm(map::algorithm::facet::MetaPropertyAlgorithmInterface *pMetaInterface);
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
virtual QVariant data(const QModelIndex &index, int role) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
private:
void UpdateMetaProperties() const ;
/** Method uses m_pMetaInterface to retrieve the MetaProperty and unwraps it into an
* suitable QVariant depending on the passed QT role. If the MetaProperty type is not supported, the QVariant is invalid.
*/
QVariant GetPropertyValue(const map::algorithm::MetaPropertyInfo* pInfo, int role) const;
template <typename TValueType> bool CheckCastAndSetProp(const map::algorithm::MetaPropertyInfo* pInfo, const QVariant& value);
bool SetPropertyValue(const map::algorithm::MetaPropertyInfo* pInfo, const QVariant& value);
map::algorithm::facet::MetaPropertyAlgorithmInterface *m_pMetaInterface;
mutable map::algorithm::facet::MetaPropertyAlgorithmInterface::MetaPropertyVectorType m_MetaProperties;
};
};
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkRegEvaluationMapper2D.h b/Modules/MatchPointRegistration/include/mitkRegEvaluationMapper2D.h
index b977c4d5be..978cfdaab3 100644
--- a/Modules/MatchPointRegistration/include/mitkRegEvaluationMapper2D.h
+++ b/Modules/MatchPointRegistration/include/mitkRegEvaluationMapper2D.h
@@ -1,248 +1,248 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkRegEvaluationMapper2D_h
#define mitkRegEvaluationMapper2D_h
//MatchPoint
#include <mapRegistration.h>
#include "mitkRegEvaluationObject.h"
//MITK
#include <mitkCommon.h>
//MITK Rendering
#include "mitkBaseRenderer.h"
#include "mitkVtkMapper.h"
#include "mitkExtractSliceFilter.h"
//VTK
#include <vtkSmartPointer.h>
#include <vtkPropAssembly.h>
//MITK
#include "MitkMatchPointRegistrationExports.h"
class vtkActor;
class vtkPolyDataMapper;
class vtkPlaneSource;
class vtkImageData;
class vtkLookupTable;
class vtkImageExtractComponents;
class vtkImageReslice;
class vtkImageChangeInformation;
class vtkPoints;
class vtkMitkThickSlicesFilter;
class vtkPolyData;
class vtkMitkApplyLevelWindowToRGBFilter;
class vtkMitkLevelWindowFilter;
namespace mitk {
/** \brief Mapper to resample and display 2D slices of registration evaluation visualization.
* \ingroup Mapper
*/
class MITKMATCHPOINTREGISTRATION_EXPORT RegEvaluationMapper2D : public VtkMapper
{
public:
/** Standard class typedefs. */
mitkClassMacro( RegEvaluationMapper2D,VtkMapper );
/** Method for creation through the object factory. */
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
const mitk::DataNode* GetTargetNode(void);
const mitk::DataNode* GetMovingNode(void);
/** \brief Get the target image to map */
const mitk::Image *GetTargetImage(void);
/** \brief Get the moving image to map */
const mitk::Image *GetMovingImage(void);
/** \brief Get the target image to map */
const mitk::MAPRegistrationWrapper *GetRegistration(void);
/** \brief Checks whether this mapper needs to update itself and generate
* data. */
void Update(mitk::BaseRenderer * renderer) override;
//### methods of MITK-VTK rendering pipeline
vtkProp* GetVtkProp(mitk::BaseRenderer* renderer) override;
//### end of methods of MITK-VTK rendering pipeline
/** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */
/**
* To render axial, coronal, and sagittal, the mapper is called three times.
* For performance reasons, the corresponding data for each view is saved in the
* internal helper class LocalStorage. This allows rendering n views with just
* 1 mitkMapper using n vtkMapper.
* */
class MITKMATCHPOINTREGISTRATION_EXPORT LocalStorage : public mitk::Mapper::BaseLocalStorage
{
public:
/** \brief Actor of a 2D render window. */
vtkSmartPointer<vtkActor> m_Actor;
vtkSmartPointer<vtkPropAssembly> m_Actors;
/** \brief Mapper of a 2D render window. */
vtkSmartPointer<vtkPolyDataMapper> m_Mapper;
/** \brief Current slice of a 2D render window.*/
vtkSmartPointer<vtkImageData> m_EvaluationImage;
/** \brief Empty vtkPolyData that is set when rendering geometry does not
* intersect the image geometry.
* \warning This member variable is set to nullptr,
* if no image geometry is inside the plane geometry
* of the respective render window. Any user of this
* slice has to check whether it is set to nullptr!
*/
vtkSmartPointer<vtkPolyData> m_EmptyPolyData;
/** \brief Plane on which the slice is rendered as texture. */
vtkSmartPointer<vtkPlaneSource> m_Plane;
/** \brief The texture which is used to render the current slice. */
vtkSmartPointer<vtkTexture> m_Texture;
/** \brief The lookuptables for colors and level window */
vtkSmartPointer<vtkLookupTable> m_ColorLookupTable;
vtkSmartPointer<vtkLookupTable> m_DefaultLookupTable;
/** \brief The actual reslicer (one per renderer) */
mitk::ExtractSliceFilter::Pointer m_Reslicer;
/** part of the target image that is relevant for the rendering*/
mitk::Image::Pointer m_slicedTargetImage;
/** part of the moving image mapped into the slicedTargetImage
geometry*/
mitk::Image::Pointer m_slicedMappedImage;
/** \brief Timestamp of last update of stored data. */
itk::TimeStamp m_LastUpdateTime;
/** \brief mmPerPixel relation between pixel and mm. (World spacing).*/
mitk::ScalarType* m_mmPerPixel;
/** \brief This filter is used to apply the level window to target image. */
vtkSmartPointer<vtkMitkLevelWindowFilter> m_TargetLevelWindowFilter;
/** \brief This filter is used to apply the level window to moving image. */
vtkSmartPointer<vtkMitkLevelWindowFilter> m_MappedLevelWindowFilter;
vtkSmartPointer<vtkImageExtractComponents> m_TargetExtractFilter;
vtkSmartPointer<vtkImageExtractComponents> m_MappedExtractFilter;
/** \brief Default constructor of the local storage. */
LocalStorage();
/** \brief Default deconstructor of the local storage. */
~LocalStorage() override;
};
/** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */
mitk::LocalStorageHandler<LocalStorage> m_LSH;
/** \brief Get the LocalStorage corresponding to the current renderer. */
LocalStorage* GetLocalStorage(mitk::BaseRenderer* renderer);
/** \brief Set the default properties for general image rendering. */
static void SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer = nullptr, bool overwrite = false);
protected:
/** \brief Transforms the actor to the actual position in 3D.
* \param renderer The current renderer corresponding to the render window.
*/
void TransformActor(mitk::BaseRenderer* renderer);
/** \brief Generates a plane according to the size of the resliced image in milimeters.
*
* In VTK a vtkPlaneSource is defined through three points. The origin and two
* points defining the axes of the plane (see VTK documentation). The origin is
* set to (xMin; yMin; Z), where xMin and yMin are the minimal bounds of the
* resliced image in space. Z is relevant for blending and the layer property.
* The center of the plane (C) is also the center of the view plane (cf. the image above).
*
* \note For the standard MITK view with three 2D render windows showing three
* different slices, three such planes are generated. All these planes are generated
* in the XY-plane (even if they depict a YZ-slice of the volume).
*
*/
void GeneratePlane(mitk::BaseRenderer* renderer, double planeBounds[6]);
/** Default constructor */
RegEvaluationMapper2D();
/** Default deconstructor */
~RegEvaluationMapper2D() override;
/** \brief Does the actual resampling, without rendering the image yet.
* All the data is generated inside this method. The vtkProp (or Actor)
* is filled with content (i.e. the resliced image).
*
* After generation, a 4x4 transformation matrix(t) of the current slice is obtained
* from the vtkResliceImage object via GetReslicesAxis(). This matrix is
* applied to each textured plane (actor->SetUserTransform(t)) to transform everything
* to the actual 3D position (cf. the following image).
*
* \image html cameraPositioning3D.png
*
*/
void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override;
void PrepareContour( mitk::DataNode* datanode, LocalStorage * localStorage );
void PrepareDifference( LocalStorage * localStorage );
void PrepareWipe(mitk::DataNode* datanode, LocalStorage * localStorage, const Point2D& currentIndex2D);
void PrepareCheckerBoard( mitk::DataNode* datanode, LocalStorage * localStorage );
void PrepareColorBlend( LocalStorage * localStorage );
void PrepareBlend( mitk::DataNode* datanode, LocalStorage * localStorage );
/** \brief This method uses the vtkCamera clipping range and the layer property
- * to calcualte the depth of the object (e.g. image or contour). The depth is used
+ * to calculate the depth of the object (e.g. image or contour). The depth is used
* to keep the correct order for the final VTK rendering.*/
float CalculateLayerDepth(mitk::BaseRenderer* renderer);
/** \brief This method applies (or modifies) the lookuptable for all types of images.
* \warning To use the lookup table, the property 'Lookup Table' must be set and a 'Image Rendering.Mode'
* which uses the lookup table must be set.
*/
void ApplyLookuptable(mitk::BaseRenderer* renderer, const mitk::DataNode* dataNode, vtkMitkLevelWindowFilter* levelFilter);
/**
* @brief ApplyLevelWindow Apply the level window for the given renderer.
* \warning To use the level window, the property 'LevelWindow' must be set and a 'Image Rendering.Mode' which uses the level window must be set.
* @param renderer Level window for which renderer?
* @param dataNode
* @param levelFilter
*/
void ApplyLevelWindow(mitk::BaseRenderer *renderer, const mitk::DataNode* dataNode, vtkMitkLevelWindowFilter* levelFilter);
/** \brief Set the opacity of the actor. */
void ApplyOpacity( mitk::BaseRenderer* renderer );
/**
* \brief Calculates whether the given rendering geometry intersects the
* given SlicedGeometry3D.
*
* This method checks if the given PlaneGeometry intersects the given
* SlicedGeometry3D. It calculates the distance of the PlaneGeometry to all
* 8 cornerpoints of the SlicedGeometry3D. If all distances have the same
* sign (all positive or all negative) there is no intersection.
* If the distances have different sign, there is an intersection.
*
* \param renderingGeometry
* \param imageGeometry
**/
bool RenderingGeometryIntersectsImage( const PlaneGeometry* renderingGeometry, SlicedGeometry3D* imageGeometry );
};
} // namespace mitk
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkRegEvaluationObject.h b/Modules/MatchPointRegistration/include/mitkRegEvaluationObject.h
index d7a4f59488..1e4a2c1b74 100644
--- a/Modules/MatchPointRegistration/include/mitkRegEvaluationObject.h
+++ b/Modules/MatchPointRegistration/include/mitkRegEvaluationObject.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 mitkRegEvaluationObject_h
#define mitkRegEvaluationObject_h
//MITK
#include <mitkImage.h>
#include <mitkDataNode.h>
//MatchPoint
#include <mapRegistrationBase.h>
#include <mapRegistration.h>
#include <mapExceptionObjectMacros.h>
#include <mapContinuousElements.h>
//MITK
#include "mitkMAPRegistrationWrapper.h"
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
/*!
\brief RegEvaluationObject
- Class that containes all data to realize an evaluation of registrations via images.
+ Class that contains all data to realize an evaluation of registrations via images.
*/
class MITKMATCHPOINTREGISTRATION_EXPORT RegEvaluationObject: public mitk::BaseData
{
public:
mitkClassMacro( RegEvaluationObject, BaseData );
itkNewMacro( Self );
/**
* Pass through to the target image that defines the region
*/
void SetRequestedRegionToLargestPossibleRegion() override;
/**
* Pass through to the target image that defines the region
*/
bool RequestedRegionIsOutsideOfTheBufferedRegion() override;
/**
* Pass through to the target image that defines the region
*/
bool VerifyRequestedRegion() override;
/**
* Pass through to the target image that defines the region
*/
void SetRequestedRegion(const itk::DataObject*) override;
itkSetObjectMacro(Registration, mitk::MAPRegistrationWrapper);
/**takes the input image, rescales it and converts it to pixel type int to be used for visualization as target image*/
void SetTargetImage(const mitk::Image* tImg);
/**takes the input image, rescales it and converts it to pixel type int to be used for visualization as mapped moving*/
void SetMovingImage(const mitk::Image* mImg);
itkGetObjectMacro(Registration, mitk::MAPRegistrationWrapper);
itkGetObjectMacro(TargetImage, mitk::Image);
itkGetObjectMacro(MovingImage, mitk::Image);
itkGetConstObjectMacro(Registration, mitk::MAPRegistrationWrapper);
itkGetConstObjectMacro(TargetImage, mitk::Image);
itkGetConstObjectMacro(MovingImage, mitk::Image);
/**takes the input image, rescales it and converts it to pixel type int to be used for visualization as target image*/
void SetTargetNode(const mitk::DataNode* tNode);
/**takes the input image, rescales it and converts it to pixel type int to be used for visualization as mapped moving*/
void SetMovingNode(const mitk::DataNode* mNode);
itkGetConstObjectMacro(TargetNode, mitk::DataNode);
itkGetConstObjectMacro(MovingNode, mitk::DataNode);
protected:
typedef ::itk::Image<unsigned char, 3> InternalImageType;
template <typename TPixelType, unsigned int VImageDimension >
void doConversion(const ::itk::Image<TPixelType,VImageDimension>* input, mitk::Image::Pointer& result) const;
void PrintSelf (std::ostream &os, itk::Indent indent) const override;
RegEvaluationObject();
~RegEvaluationObject() override;
mitk::MAPRegistrationWrapper::Pointer m_Registration;
mitk::Image::Pointer m_TargetImage;
mitk::Image::Pointer m_MovingImage;
mitk::DataNode::ConstPointer m_TargetNode;
mitk::DataNode::ConstPointer m_MovingNode;
private:
RegEvaluationObject& operator = (const RegEvaluationObject&);
RegEvaluationObject(const RegEvaluationObject&);
};
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkRegVisHelper.h b/Modules/MatchPointRegistration/include/mitkRegVisHelper.h
index 7fecb3d09f..3a38098422 100644
--- a/Modules/MatchPointRegistration/include/mitkRegVisHelper.h
+++ b/Modules/MatchPointRegistration/include/mitkRegVisHelper.h
@@ -1,77 +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.
============================================================================*/
#ifndef mitkRegVisHelper_h
#define mitkRegVisHelper_h
//VTK
#include <vtkSmartPointer.h>
#include <vtkPolyData.h>
//MITK
#include <mitkDataNode.h>
#include <mitkGeometry3D.h>
//MatchPoint
#include <mapRegistrationKernelBase.h>
// MITK
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
/** Generates the geometry info used to visualized a registration based on the properties
* of the data node containing the registration.\n
* @pre regNode is a correctly initialized data node of a registration
* @param regNode Pointer to the data node of the registration.
* @param [out] gridDesc Smartpointer to the extracted grid geometry.
* @param [out] gridFrequ Grid frequency stored in the regNode.
*/
void MITKMATCHPOINTREGISTRATION_EXPORT GetGridGeometryFromNode(const mitk::DataNode* regNode, mitk::Geometry3D::Pointer& gridDesc, unsigned int& gridFrequ);
/**
- * Generates a 3D defomration gird according to a passed Geometry3D info. It is the basis
+ * Generates a 3D defomration grid according to a passed Geometry3D info. It is the basis
* for most of the visualizations of a MatchPoint registration.
*/
vtkSmartPointer<vtkPolyData> MITKMATCHPOINTREGISTRATION_EXPORT Generate3DDeformationGrid(const mitk::BaseGeometry* gridDesc, unsigned int gridFrequence, const map::core::RegistrationKernelBase<3,3>* regKernel = nullptr);
/**
* Generates a 3D glyph representation of the given regKernel in the FOV defined by gridDesc.
*/
vtkSmartPointer<vtkPolyData> MITKMATCHPOINTREGISTRATION_EXPORT Generate3DDeformationGlyph(const mitk::BaseGeometry* gridDesc, const map::core::RegistrationKernelBase<3,3>* regKernel);
/**
* Checks if the grid relevant node properties are outdated regarding the passed time stamp
* reference*/
bool MITKMATCHPOINTREGISTRATION_EXPORT GridIsOutdated(const mitk::DataNode* regNode, const itk::TimeStamp& reference);
/**
* Checks if the property of the passed node is outdated regarding the passed time stamp
* reference
* If the property does not exist the return value indicates if the node is outdated.*/
bool MITKMATCHPOINTREGISTRATION_EXPORT PropertyIsOutdated(const mitk::DataNode* regNode, const std::string& propName, const itk::TimeStamp& reference);
/**
* Gets the relevant kernel for visualization of a registration node. The kernel is determined
* by the direction property of the node.
* @return Pointer to the relevant kernel. Method may return nullptr if data node is not valid, node
* contains no registration or has no direction property.*/
MITKMATCHPOINTREGISTRATION_EXPORT const map::core::RegistrationKernelBase<3,3>* GetRelevantRegKernelOfNode(const mitk::DataNode* regNode);
}
#endif
diff --git a/Modules/MatchPointRegistration/include/mitkTimeFramesRegistrationHelper.h b/Modules/MatchPointRegistration/include/mitkTimeFramesRegistrationHelper.h
index 197424070a..4d400dbd33 100644
--- a/Modules/MatchPointRegistration/include/mitkTimeFramesRegistrationHelper.h
+++ b/Modules/MatchPointRegistration/include/mitkTimeFramesRegistrationHelper.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 mitkTimeFramesRegistrationHelper_h
#define mitkTimeFramesRegistrationHelper_h
#include <mitkImage.h>
#include <mitkTimeGeometry.h>
#include <mitkImageMappingHelper.h>
#include <mapRegistrationAlgorithmBase.h>
#include <mapRegistrationBase.h>
#include <mapEvents.h>
#include "MitkMatchPointRegistrationExports.h"
namespace mitk
{
mapEventMacro(FrameRegistrationEvent, ::map::events::TaskBatchEvent, MITKMATCHPOINTREGISTRATION_EXPORT);
mapEventMacro(FrameMappingEvent, ::map::events::TaskBatchEvent, MITKMATCHPOINTREGISTRATION_EXPORT);
- /** Helper class that assumes that registeres time frames of an passed image and returns the resulting new image.
+ /** Helper class that assumes that registers time frames of an passed image and returns the resulting new image.
* A typical use case for the helper class is motion correction in 3D+t images. Each selected frame will be registered
* to the first frame of the image. The user can define frames that may be not registered. These frames will be copied directly.
* Per default all frames will be registered.
- * The user may set a mask for the target frame (1st frame). If this mask image has mulitple time steps, the first time step will be used.
+ * The user may set a mask for the target frame (1st frame). If this mask image has multiple time steps, the first time step will be used.
* The helper class invokes three eventtypes: \n
* - mitk::FrameRegistrationEvent: when ever a frame was registered.
* - mitk::FrameMappingEvent: when ever a frame was mapped registered.
* - itk::ProgressEvent: when ever a new frame was added to the result image.
*/
class MITKMATCHPOINTREGISTRATION_EXPORT TimeFramesRegistrationHelper : public itk::Object
{
public:
mitkClassMacroItkParent(TimeFramesRegistrationHelper, itk::Object);
itkNewMacro(Self);
typedef ::map::algorithm::RegistrationAlgorithmBase RegistrationAlgorithmBaseType;
typedef RegistrationAlgorithmBaseType::Pointer RegistrationAlgorithmPointer;
typedef ::map::core::RegistrationBase RegistrationType;
typedef RegistrationType::Pointer RegistrationPointer;
typedef std::vector<mitk::TimeStepType> IgnoreListType;
itkSetConstObjectMacro(4DImage, Image);
itkGetConstObjectMacro(4DImage, Image);
itkSetConstObjectMacro(TargetMask, Image);
itkGetConstObjectMacro(TargetMask, Image);
itkSetObjectMacro(Algorithm, RegistrationAlgorithmBaseType);
itkGetObjectMacro(Algorithm, RegistrationAlgorithmBaseType);
itkSetMacro(AllowUndefPixels, bool);
itkGetConstMacro(AllowUndefPixels, bool);
itkSetMacro(PaddingValue, double);
itkGetConstMacro(PaddingValue, double);
itkSetMacro(AllowUnregPixels, bool);
itkGetConstMacro(AllowUnregPixels, bool);
itkSetMacro(ErrorValue, double);
itkGetConstMacro(ErrorValue, double);
itkSetMacro(InterpolatorType, mitk::ImageMappingInterpolator::Type);
itkGetConstMacro(InterpolatorType, mitk::ImageMappingInterpolator::Type);
- /** cleares the ignore list. Therefore all frames will be processed.*/
+ /** Clears the ignore list. Therefore all frames will be processed.*/
void ClearIgnoreList();
void SetIgnoreList(const IgnoreListType& il);
itkGetConstMacro(IgnoreList, IgnoreListType);
virtual double GetProgress() const;
/** Commences the generation of the registered 4D image. Stores the result internally.
* After this method call is finished the result can be retrieved via
* GetRegisteredImage.
* @pre 4D image must be set
* @pre 4D image must has more then one frame
* @pre Reg algorithm must be set
* @pre Ignore list values must be within the time geometry of the image
* @post Result image was generated.*/
void Generate();
/** Returns the generated images. Triggers Generate() if result is outdated.
* @pre 4D image must be set
* @pre 4D image must has more then one frame
* @pre Reg algorithm must be set
* @pre Ignore list values must be within the time geometry of the image
*/
Image::Pointer GetRegisteredImage();
protected:
TimeFramesRegistrationHelper() :
m_AllowUndefPixels(true),
m_PaddingValue(0),
m_AllowUnregPixels(true),
m_ErrorValue(0),
m_InterpolatorType(mitk::ImageMappingInterpolator::Linear),
m_Progress(0)
{
m_4DImage = nullptr;
m_TargetMask = nullptr;
m_Registered4DImage = nullptr;
};
~TimeFramesRegistrationHelper() override {};
RegistrationPointer DoFrameRegistration(const mitk::Image* movingFrame,
const mitk::Image* targetFrame, const mitk::Image* targetMask) const;
mitk::Image::Pointer DoFrameMapping(const mitk::Image* movingFrame, const RegistrationType* reg,
const mitk::Image* targetFrame) const;
bool HasOutdatedResult() const;
/** Check if the fit can be generated and all needed inputs are valid.
* Throw an exception for a non valid or missing input.*/
void CheckValidInputs() const;
mitk::Image::Pointer GetFrameImage(const mitk::Image* image, mitk::TimePointType timePoint) const;
RegistrationAlgorithmPointer m_Algorithm;
private:
Image::ConstPointer m_4DImage;
Image::ConstPointer m_TargetMask;
Image::Pointer m_Registered4DImage;
IgnoreListType m_IgnoreList;
/**Indicates if the mapper should allow undefined pixels (true) or mapping should fail (false)*/
bool m_AllowUndefPixels;
/** Value of undefined pixels. Only relevant if m_allowUndefPixels is true. */
double m_PaddingValue;
/**Indicates if the mapper should allow pixels that are not covered by the registration (true) or mapping should fail (false)*/
bool m_AllowUnregPixels;
/** Value of unreged pixels. Only relevant if m_allowUnregPixels is true. */
double m_ErrorValue;
/** Type of interpolator. Only relevant for images and if m_doGeometryRefinement is false. */
mitk::ImageMappingInterpolator::Type m_InterpolatorType;
double m_Progress;
};
}
#endif
diff --git a/Modules/MatchPointRegistration/src/Helper/mitkImageMappingHelper.cpp b/Modules/MatchPointRegistration/src/Helper/mitkImageMappingHelper.cpp
index 58035fabbf..f2f311aa51 100644
--- a/Modules/MatchPointRegistration/src/Helper/mitkImageMappingHelper.cpp
+++ b/Modules/MatchPointRegistration/src/Helper/mitkImageMappingHelper.cpp
@@ -1,482 +1,482 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <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 <mitkImageTimeSelector.h>
#include <mitkLabelSetImage.h>
#include "mapRegistration.h"
#include "mitkImageMappingHelper.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 doMITKMap(const ::itk::Image<TPixelType,VImageDimension>* input, mitk::ImageMappingHelper::ResultImageType::Pointer& result, const mitk::ImageMappingHelper::RegistrationType*& registration,
bool throwOnOutOfInputAreaError, const double& paddingValue, const mitk::ImageMappingHelper::ResultImageGeometryType*& resultGeometry,
bool throwOnMappingError, const double& errorValue, mitk::ImageMappingInterpolator::Type interpolatorType)
{
typedef ::map::core::Registration<VImageDimension,VImageDimension> ConcreteRegistrationType;
typedef ::map::core::ImageMappingTask<ConcreteRegistrationType, ::itk::Image<TPixelType,VImageDimension>, ::itk::Image<TPixelType,VImageDimension> > MappingTaskType;
typename MappingTaskType::Pointer spTask = MappingTaskType::New();
typedef typename MappingTaskType::ResultImageDescriptorType ResultImageDescriptorType;
typename ResultImageDescriptorType::Pointer resultDescriptor;
//check if image and result geometry fits the passed registration
/////////////////////////////////////////////////////////////////
if (registration->getMovingDimensions()!=VImageDimension)
{
map::core::OStringStream str;
str << "Dimension of MITK image ("<<VImageDimension<<") does not equal the moving dimension of the registration object ("<<registration->getMovingDimensions()<<").";
throw mitk::AccessByItkException(str.str());
}
if (registration->getTargetDimensions()!=VImageDimension)
{
map::core::OStringStream str;
str << "Dimension of MITK image ("<<VImageDimension<<") does not equal the target dimension of the registration object ("<<registration->getTargetDimensions()<<").";
throw mitk::AccessByItkException(str.str());
}
const ConcreteRegistrationType* castedReg = dynamic_cast<const ConcreteRegistrationType*>(registration);
if (registration->getTargetDimensions()==2 && resultGeometry)
{
mitk::ImageMappingHelper::ResultImageGeometryType::BoundsArrayType bounds = resultGeometry->GetBounds();
if (bounds[4]!=0 || bounds[5]!=0)
{
//array "bounds" is constructed as [min Dim1, max Dim1, min Dim2, max Dim2, min Dim3, max Dim3]
- //therfore [4] and [5] must be 0
+ //therefore [4] and [5] must be 0
map::core::OStringStream str;
str << "Dimension of defined result geometry does not equal the target dimension of the registration object ("<<registration->getTargetDimensions()<<").";
throw mitk::AccessByItkException(str.str());
}
}
//check/create resultDescriptor
/////////////////////////
if (resultGeometry)
{
resultDescriptor = ResultImageDescriptorType::New();
typename ResultImageDescriptorType::PointType origin;
typename ResultImageDescriptorType::SizeType size;
typename ResultImageDescriptorType::SpacingType fieldSpacing;
typename ResultImageDescriptorType::DirectionType matrix;
mitk::ImageMappingHelper::ResultImageGeometryType::BoundsArrayType geoBounds = resultGeometry->GetBounds();
mitk::Vector3D geoSpacing = resultGeometry->GetSpacing();
mitk::Point3D geoOrigin = resultGeometry->GetOrigin();
mitk::AffineTransform3D::MatrixType geoMatrix = resultGeometry->GetIndexToWorldTransform()->GetMatrix();
for (unsigned int i = 0; i<VImageDimension; ++i)
{
origin[i] = static_cast<typename ResultImageDescriptorType::PointType::ValueType>(geoOrigin[i]);
fieldSpacing[i] = static_cast<typename ResultImageDescriptorType::SpacingType::ValueType>(geoSpacing[i]);
size[i] = static_cast<typename ResultImageDescriptorType::SizeType::SizeValueType>(geoBounds[(2*i)+1]-geoBounds[2*i])*fieldSpacing[i];
}
//Matrix extraction
matrix.SetIdentity();
unsigned int i;
unsigned int j;
/// \warning 2D MITK images could have a 3D rotation, since they have a 3x3 geometry matrix.
/// If it is only a rotation around the transversal 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.
+ // the following loop divides by spacing now to normalize columns.
// counterpart of InitializeByItk in mitkImage.h line 372 of revision 15092.
// Check if information is lost
if ( VImageDimension == 2)
{
if ( ( geoMatrix[0][2] != 0) ||
( geoMatrix[1][2] != 0) ||
( geoMatrix[2][0] != 0) ||
( geoMatrix[2][1] != 0) ||
(( geoMatrix[2][2] != 1) && ( geoMatrix[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 < 2; ++i)
{
for( j=0; j < 2; ++j )
{
matrix[i][j] = geoMatrix[i][j]/fieldSpacing[j];
}
}
}
}
else if (VImageDimension == 3)
{
// Normal 3D image. Conversion possible without problem!
for ( i=0; i < 3; ++i)
{
for( j=0; j < 3; ++j )
{
matrix[i][j] = geoMatrix[i][j]/fieldSpacing[j];
}
}
}
else
{
assert(0);
throw mitk::AccessByItkException("Usage of resultGeometry for 2D images is not yet implemented.");
/**@TODO Implement extraction of 2D-Rotation-Matrix out of 3D-Rotation-Matrix
* to cover this case as well.
* matrix = extract2DRotationMatrix(resultGeometry)*/
}
resultDescriptor->setOrigin(origin);
resultDescriptor->setSize(size);
resultDescriptor->setSpacing(fieldSpacing);
resultDescriptor->setDirection(matrix);
}
//do the mapping
/////////////////////////
typedef ::itk::InterpolateImageFunction< ::itk::Image<TPixelType,VImageDimension> > BaseInterpolatorType;
typename BaseInterpolatorType::Pointer interpolator = generateInterpolator< ::itk::Image<TPixelType,VImageDimension> >(interpolatorType);
assert(interpolator.IsNotNull());
spTask->setImageInterpolator(interpolator);
spTask->setInputImage(input);
spTask->setRegistration(castedReg);
spTask->setResultImageDescriptor(resultDescriptor);
spTask->setThrowOnMappingError(throwOnMappingError);
spTask->setErrorValue(errorValue);
spTask->setThrowOnPaddingError(throwOnOutOfInputAreaError);
spTask->setPaddingValue(paddingValue);
spTask->execute();
mitk::CastToMitkImage<>(spTask->getResultImage(),result);
}
/**Helper function to ensure the mapping of all time steps of an image.*/
void doMapTimesteps(const mitk::ImageMappingHelper::InputImageType* input, mitk::Image* result, const mitk::ImageMappingHelper::RegistrationType* registration, bool throwOnOutOfInputAreaError,double paddingValue, const mitk::ImageMappingHelper::ResultImageGeometryType* resultGeometry, bool throwOnMappingError, double errorValue, mitk::ImageMappingInterpolator::Type interpolatorType)
{
for (unsigned int i = 0; i<input->GetTimeSteps(); ++i)
{
mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput(input);
imageTimeSelector->SetTimeNr(i);
imageTimeSelector->UpdateLargestPossibleRegion();
mitk::ImageMappingHelper::InputImageType::Pointer timeStepInput = imageTimeSelector->GetOutput();
mitk::ImageMappingHelper::ResultImageType::Pointer timeStepResult;
AccessByItk_n(timeStepInput, doMITKMap, (timeStepResult, registration, throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue, interpolatorType));
mitk::ImageReadAccessor readAccess(timeStepResult);
result->SetVolume(readAccess.GetData(), i);
}
}
mitk::TimeGeometry::Pointer CreateResultTimeGeometry(const mitk::ImageMappingHelper::InputImageType* input, const mitk::ImageMappingHelper::ResultImageGeometryType* resultGeometry)
{
mitk::TimeGeometry::ConstPointer timeGeometry = input->GetTimeGeometry();
mitk::TimeGeometry::Pointer mappedTimeGeometry = timeGeometry->Clone();
for (unsigned int i = 0; i < input->GetTimeSteps(); ++i)
{
mitk::ImageMappingHelper::ResultImageGeometryType::Pointer mappedGeometry = resultGeometry->Clone();
mappedTimeGeometry->SetTimeStepGeometry(mappedGeometry, i);
}
return mappedTimeGeometry;
}
mitk::ImageMappingHelper::ResultImageType::Pointer
mitk::ImageMappingHelper::map(const InputImageType* input, const RegistrationType* registration,
bool throwOnOutOfInputAreaError, const double& paddingValue, const ResultImageGeometryType* resultGeometry,
bool throwOnMappingError, const double& errorValue, mitk::ImageMappingInterpolator::Type interpolatorType)
{
if (!registration)
{
mitkThrow() << "Cannot map image. Passed registration wrapper pointer is nullptr.";
}
if (!input)
{
mitkThrow() << "Cannot map image. Passed image pointer is nullptr.";
}
ResultImageType::Pointer result;
auto inputLabelSetImage = dynamic_cast<const LabelSetImage*>(input);
if (nullptr == inputLabelSetImage)
{
if (input->GetTimeSteps() == 1)
{ //map the image and done
AccessByItk_n(input, doMITKMap, (result, registration, throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue, interpolatorType));
}
else
{ //map every time step and compose
auto mappedTimeGeometry = CreateResultTimeGeometry(input, resultGeometry);
result = mitk::Image::New();
result->Initialize(input->GetPixelType(), *mappedTimeGeometry, 1, input->GetTimeSteps());
doMapTimesteps(input, result, registration, throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue, interpolatorType);
}
}
else
{
auto resultLabelSetImage = LabelSetImage::New();
auto mappedTimeGeometry = CreateResultTimeGeometry(input, resultGeometry);
auto resultTemplate = mitk::Image::New();
resultTemplate->Initialize(input->GetPixelType(), *mappedTimeGeometry, 1, input->GetTimeSteps());
resultLabelSetImage->Initialize(resultTemplate);
auto cloneInput = inputLabelSetImage->Clone();
//We need to clone the LabelSetImage due to its illposed design. It is state full
//and we have to iterate through all layers as active layers to ensure the content
//was really stored (directly working with the layer images does not work with the
- //active layer). The clone wastes rescources but is the easiest and safest way to
+ //active layer). The clone wastes resources but is the easiest and safest way to
//ensure 1) correct mapping 2) avoid race conditions with other parts of the
//application because we would change the state of the input.
//This whole code block should be reworked as soon as T28525 is done.
for (unsigned int layerID = 0; layerID < inputLabelSetImage->GetNumberOfLayers(); ++layerID)
{
if (resultLabelSetImage->GetNumberOfLayers() <= layerID)
{
resultLabelSetImage->AddLayer();
}
resultLabelSetImage->ReplaceGroupLabels(layerID, inputLabelSetImage->GetConstLabelsByValue(inputLabelSetImage->GetLabelValuesByGroup(layerID)));
cloneInput->SetActiveLayer(layerID);
resultLabelSetImage->SetActiveLayer(layerID);
doMapTimesteps(cloneInput, resultLabelSetImage, registration, throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue, mitk::ImageMappingInterpolator::Linear);
}
resultLabelSetImage->SetActiveLayer(inputLabelSetImage->GetActiveLayer());
resultLabelSetImage->SetActiveLabel(inputLabelSetImage->GetActiveLabel()->GetValue());
result = resultLabelSetImage;
}
return result;
}
mitk::ImageMappingHelper::ResultImageType::Pointer
mitk::ImageMappingHelper::map(const InputImageType* input, const MITKRegistrationType* registration,
bool throwOnOutOfInputAreaError, const double& paddingValue, const ResultImageGeometryType* resultGeometry,
bool throwOnMappingError, const double& errorValue, mitk::ImageMappingInterpolator::Type)
{
if (!registration)
{
mitkThrow() << "Cannot map image. Passed registration wrapper pointer is nullptr.";
}
if (!registration->GetRegistration())
{
- mitkThrow() << "Cannot map image. Passed registration wrapper containes no registration.";
+ mitkThrow() << "Cannot map image. Passed registration wrapper contains no registration.";
}
if (!input)
{
mitkThrow() << "Cannot map image. Passed image pointer is nullptr.";
}
ResultImageType::Pointer result = map(input, registration->GetRegistration(), throwOnOutOfInputAreaError, paddingValue, resultGeometry, throwOnMappingError, errorValue);
return result;
}
mitk::ImageMappingHelper::ResultImageGeometryType::Pointer
mitk::ImageMappingHelper::GenerateSuperSampledGeometry(const ResultImageGeometryType* inputGeometry, double xScaling, double yScaling, double zScaling)
{
auto resultGeometry = inputGeometry->Clone();
//change the pixel count and spacing of the geometry
mitk::BaseGeometry::BoundsArrayType geoBounds = inputGeometry->GetBounds();
auto oldSpacing = inputGeometry->GetSpacing();
mitk::Vector3D geoSpacing;
geoSpacing[0] = oldSpacing[0] / xScaling;
geoSpacing[1] = oldSpacing[1] / yScaling;
geoSpacing[2] = oldSpacing[2] / zScaling;
geoBounds[1] = geoBounds[1] * xScaling;
geoBounds[3] = geoBounds[3] * yScaling;
geoBounds[5] = geoBounds[5] * zScaling;
resultGeometry->SetBounds(geoBounds);
resultGeometry->SetSpacing(geoSpacing);
auto oldOrigin = inputGeometry->GetOrigin();
//if we change the spacing we must also correct the origin to ensure
//that the voxel matrix still covers the same space. This is due the fact
//that the origin is not in the corner of the voxel matrix, but in the center
// of the voxel that is in the corner.
mitk::Point3D newOrigin;
for (mitk::Point3D::SizeType i = 0; i < 3; ++i)
{
newOrigin[i] = 0.5 * (geoSpacing[i] - oldSpacing[i]) + oldOrigin[i];
}
return resultGeometry;
}
mitk::ImageMappingHelper::ResultImageType::Pointer
mitk::ImageMappingHelper::
refineGeometry(const InputImageType * input, const RegistrationType * registration,
bool throwOnError)
{
mitk::ImageMappingHelper::ResultImageType::Pointer result = nullptr;
if (!registration)
{
mitkThrow() << "Cannot refine image geometry. Passed registration pointer is nullptr.";
}
if (!input)
{
mitkThrow() << "Cannot refine image geometry. Passed image pointer is nullptr.";
}
mitk::MITKRegistrationHelper::Affine3DTransformType::Pointer spTransform = mitk::MITKRegistrationHelper::getAffineMatrix(registration, false);
if (spTransform.IsNull() && throwOnError)
{
mitkThrow() << "Cannot refine image geometry. Registration does not contain a suitable direct mapping kernel (3D affine transformation or compatible required).";
}
if (spTransform.IsNotNull())
{
//copy input image
result = input->Clone();
//refine geometries
for (unsigned int i = 0; i < result->GetTimeSteps(); ++i)
{ //refine every time step
result->GetGeometry(i)->Compose(spTransform);
}
result->GetTimeGeometry()->Update();
}
return result;
}
mitk::ImageMappingHelper::ResultImageType::Pointer
mitk::ImageMappingHelper::
refineGeometry(const InputImageType* input, const MITKRegistrationType* registration,
bool throwOnError)
{
if (!registration)
{
mitkThrow() << "Cannot refine image geometry. Passed registration wrapper pointer is nullptr.";
}
if (!registration->GetRegistration())
{
- mitkThrow() << "Cannot refine image geometry. Passed registration wrapper containes no registration.";
+ mitkThrow() << "Cannot refine image geometry. Passed registration wrapper contains no registration.";
}
if (!input)
{
mitkThrow() << "Cannot refine image geometry. Passed image pointer is nullptr.";
}
ResultImageType::Pointer result = refineGeometry(input, registration->GetRegistration(), throwOnError);
return result;
}
bool
mitk::ImageMappingHelper::
canRefineGeometry(const RegistrationType* registration)
{
bool result = true;
if (!registration)
{
mitkThrow() << "Cannot check refine capability of registration. Passed registration pointer is nullptr.";
}
//if the helper does not return null, we can refine the geometry.
result = mitk::MITKRegistrationHelper::getAffineMatrix(registration,false).IsNotNull();
return result;
}
bool
mitk::ImageMappingHelper::
canRefineGeometry(const MITKRegistrationType* registration)
{
if (!registration)
{
mitkThrow() << "Cannot check refine capability of registration. Passed registration wrapper pointer is nullptr.";
}
if (!registration->GetRegistration())
{
- mitkThrow() << "Cannot check refine capability of registration. Passed registration wrapper containes no registration.";
+ mitkThrow() << "Cannot check refine capability of registration. Passed registration wrapper contains no registration.";
}
return canRefineGeometry(registration->GetRegistration());
}
diff --git a/Modules/MatchPointRegistration/src/Helper/mitkMAPAlgorithmHelper.cpp b/Modules/MatchPointRegistration/src/Helper/mitkMAPAlgorithmHelper.cpp
index 0b11df2dad..d6a022136a 100644
--- a/Modules/MatchPointRegistration/src/Helper/mitkMAPAlgorithmHelper.cpp
+++ b/Modules/MatchPointRegistration/src/Helper/mitkMAPAlgorithmHelper.cpp
@@ -1,407 +1,407 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkMAPAlgorithmHelper.h"
//itk
#include <itkImageDuplicator.h>
// Mitk
#include <mitkImageAccessByItk.h>
#include <mitkPointSetMappingHelper.h>
// MatchPoint
#include <mapImageRegistrationAlgorithmInterface.h>
#include <mapRegistrationAlgorithmInterface.h>
#include <mapPointSetRegistrationAlgorithmInterface.h>
#include <mapDummyImageRegistrationAlgorithm.h>
#include <mapAlgorithmIdentificationInterface.h>
namespace mitk
{
MAPAlgorithmHelper::MAPAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase *algorithm)
: m_AlgorithmBase(algorithm), m_Error(CheckError::none)
{
m_AllowImageCasting = true;
}
bool MAPAlgorithmHelper::HasImageAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm)
{
using InternalDefault2DImageType = itk::Image<map::core::discrete::InternalPixelType, 2>;
using InternalDefault3DImageType = itk::Image<map::core::discrete::InternalPixelType, 3>;
using Alg2DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<InternalDefault2DImageType, InternalDefault2DImageType>;
if (dynamic_cast<Alg2DType*>(algorithm) != nullptr) return true;
using Alg3DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<InternalDefault3DImageType, InternalDefault3DImageType>;
if (dynamic_cast<Alg3DType*>(algorithm) != nullptr) return true;
using Alg2D3DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<InternalDefault2DImageType, InternalDefault3DImageType>;
if (dynamic_cast<Alg2D3DType*>(algorithm) != nullptr) return true;
using Alg3D2DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<InternalDefault3DImageType, InternalDefault2DImageType>;
if (dynamic_cast<Alg3D2DType*>(algorithm) != nullptr) return true;
return false;
}
bool MAPAlgorithmHelper::HasPointSetAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm)
{
typedef ::map::core::continuous::Elements<3>::InternalPointSetType InternalDefaultPointSetType;
typedef const ::map::algorithm::facet::PointSetRegistrationAlgorithmInterface<InternalDefaultPointSetType, InternalDefaultPointSetType>
PointSetRegInterface;
return dynamic_cast<PointSetRegInterface*>(algorithm) != nullptr;
}
map::core::RegistrationBase::Pointer
MAPAlgorithmHelper::
GetRegistration() const
{
map::core::RegistrationBase::Pointer spResult;
unsigned int movingDim = m_AlgorithmBase->getMovingDimensions();
unsigned int targetDim = m_AlgorithmBase->getTargetDimensions();
if (movingDim != targetDim)
{
mapDefaultExceptionStaticMacro( <<
"Error, algorithm instance has unequal dimensionality and is therefore not supported in the current version of MAPAlgorithmHelper.");
}
if (movingDim > 3)
{
mapDefaultExceptionStaticMacro( <<
"Error, algorithm instance has a dimensionality larger than 3 and is therefore not supported in the current version of MAPAlgorithmHelper.");
}
typedef ::map::algorithm::facet::RegistrationAlgorithmInterface<2, 2> RegistrationAlg2D2DInterface;
typedef ::map::algorithm::facet::RegistrationAlgorithmInterface<3, 3> RegistrationAlg3D3DInterface;
RegistrationAlg2D2DInterface* pRegAlgorithm2D2D = dynamic_cast<RegistrationAlg2D2DInterface*>
(m_AlgorithmBase.GetPointer());
RegistrationAlg3D3DInterface* pRegAlgorithm3D3D = dynamic_cast<RegistrationAlg3D3DInterface*>
(m_AlgorithmBase.GetPointer());
if (pRegAlgorithm2D2D)
{
spResult = pRegAlgorithm2D2D->getRegistration();
}
if (pRegAlgorithm3D3D)
{
spResult = pRegAlgorithm3D3D->getRegistration();
}
return spResult;
}
mitk::MAPRegistrationWrapper::Pointer
MAPAlgorithmHelper::
GetMITKRegistrationWrapper() const
{
map::core::RegistrationBase::Pointer spInternalResult = GetRegistration();
mitk::MAPRegistrationWrapper::Pointer spResult = mitk::MAPRegistrationWrapper::New(spInternalResult);
return spResult;
}
static const mitk::Image* GetDataAsImage(const mitk::BaseData* data)
{
return dynamic_cast<const mitk::Image*>(data);
}
static const mitk::PointSet* GetDataAsPointSet(const mitk::BaseData* data)
{
return dynamic_cast<const mitk::PointSet*>(data);
}
bool
MAPAlgorithmHelper::
CheckData(const mitk::BaseData* moving, const mitk::BaseData* target, CheckError::Type& error) const
{
if (! m_AlgorithmBase)
{
mapDefaultExceptionStaticMacro( << "Error, cannot check data. Helper has no algorithm defined.");
}
if (! moving)
{
mapDefaultExceptionStaticMacro( << "Error, cannot check data. Moving data pointer is nullptr.");
}
if (! target)
{
mapDefaultExceptionStaticMacro( << "Error, cannot check data. Target data pointer is nullptr.");
}
bool result = false;
m_Error = CheckError::unsupportedDataType;
unsigned int movingDim = m_AlgorithmBase->getMovingDimensions();
unsigned int targetDim = m_AlgorithmBase->getTargetDimensions();
if (movingDim != targetDim)
{
m_Error = CheckError::wrongDimension;
}
else
{
//First check if data are point sets or images
if (GetDataAsPointSet(target) && GetDataAsPointSet(moving))
{
typedef ::map::core::continuous::Elements<3>::InternalPointSetType InternalDefaultPointSetType;
typedef ::map::algorithm::facet::PointSetRegistrationAlgorithmInterface<InternalDefaultPointSetType, InternalDefaultPointSetType>
PointSetRegInterface;
PointSetRegInterface* pPointSetInterface = dynamic_cast<PointSetRegInterface*>
(m_AlgorithmBase.GetPointer());
if (!pPointSetInterface)
{
result = false;
m_Error = CheckError::unsupportedDataType;
}
}
else if (GetDataAsImage(moving) && GetDataAsImage(target))
{
if (movingDim == 2)
{
AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoCheckImages,
2);
}
else if (movingDim == 3)
{
AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoCheckImages,
3);
}
else
{
m_Error = CheckError::wrongDimension;
}
if (m_Error == CheckError::none || (m_AllowImageCasting && m_Error == CheckError::onlyByCasting))
{
result = true;
}
}
}
error = m_Error;
return result;
}
void MAPAlgorithmHelper::SetAllowImageCasting(bool allowCasting)
{
this->m_AllowImageCasting = allowCasting;
}
bool MAPAlgorithmHelper::GetAllowImageCasting() const
{
return this->m_AllowImageCasting;
}
void MAPAlgorithmHelper::SetData(const mitk::BaseData* moving, const mitk::BaseData* target)
{
if (! m_AlgorithmBase)
{
mapDefaultExceptionStaticMacro( << "Error, cannot check data. Helper has no algorithm defined.");
}
if (! moving)
{
mapDefaultExceptionStaticMacro( << "Error, cannot check data. Moving data pointer is nullptr.");
}
if (! target)
{
mapDefaultExceptionStaticMacro( << "Error, cannot check data. Target data pointer is nullptr.");
}
unsigned int movingDim = m_AlgorithmBase->getMovingDimensions();
unsigned int targetDim = m_AlgorithmBase->getTargetDimensions();
if (movingDim != targetDim)
{
mapDefaultExceptionStaticMacro( <<
"Error, cannot set data. Current version of MAPAlgorithmHelper only supports images/point sets with same dimensionality.");
}
if (GetDataAsPointSet(target) && GetDataAsPointSet(moving))
{
typedef ::map::core::continuous::Elements<3>::InternalPointSetType InternalDefaultPointSetType;
typedef ::map::algorithm::facet::PointSetRegistrationAlgorithmInterface<InternalDefaultPointSetType, InternalDefaultPointSetType>
PointSetRegInterface;
PointSetRegInterface* pPointSetInterface = dynamic_cast<PointSetRegInterface*>
(m_AlgorithmBase.GetPointer());
pPointSetInterface->setMovingPointSet(mitk::PointSetMappingHelper::ConvertPointSetMITKtoMAP(
GetDataAsPointSet(moving)->GetPointSet()));
pPointSetInterface->setTargetPointSet(mitk::PointSetMappingHelper::ConvertPointSetMITKtoMAP(
GetDataAsPointSet(target)->GetPointSet()));
}
else if (GetDataAsImage(moving) && GetDataAsImage(target))
{
if (movingDim == 2)
{
AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoSetImages, 2);
}
else if (movingDim == 3)
{
AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoSetImages, 3);
}
}
}
template<typename TInImageType, typename TOutImageType>
typename TOutImageType::Pointer MAPAlgorithmHelper::CastImage(const TInImageType* input) const
{
typedef itk::CastImageFilter< TInImageType, TOutImageType > CastFilterType;
typename CastFilterType::Pointer spImageCaster = CastFilterType::New();
spImageCaster->SetInput(input);
typename TOutImageType::Pointer spImage = spImageCaster->GetOutput();
spImageCaster->Update();
return spImage;
}
template<typename TPixelType1, unsigned int VImageDimension1,
typename TPixelType2, unsigned int VImageDimension2>
void MAPAlgorithmHelper::DoSetImages(const itk::Image<TPixelType1, VImageDimension1>* moving,
const itk::Image<TPixelType2, VImageDimension2>* target)
{
typedef itk::Image<TPixelType1, VImageDimension1> MovingImageType;
typedef itk::Image<TPixelType2, VImageDimension2> TargetImageType;
typedef itk::Image<map::core::discrete::InternalPixelType, VImageDimension1>
InternalDefaultMovingImageType;
typedef itk::Image<map::core::discrete::InternalPixelType, VImageDimension2>
InternalDefaultTargetImageType;
typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<MovingImageType, TargetImageType>
ImageRegInterface;
typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<InternalDefaultMovingImageType, InternalDefaultTargetImageType>
DefaultImageRegInterface;
ImageRegInterface* pImageInterface = dynamic_cast<ImageRegInterface*>(m_AlgorithmBase.GetPointer());
DefaultImageRegInterface* pDefaultImageInterface = dynamic_cast<DefaultImageRegInterface*>
(m_AlgorithmBase.GetPointer());
if (pImageInterface)
{
//just set directly and you are done
- /**@todo the duplication work arround is needed due to a insufficuence
+ /**@todo the duplication work around is needed due to a insufficuence
in the AccessTwoImagesFixedDimensionByItk macro. The macro always cast
the passed image into non const (even if tha image was passed as const).
This behavior enforces the unnecessary use of an writeaccessor, which as a consequence
will lead to redundant access exceptions as long as the algorithm exists;
e.g. in the typical scenario with the MatchPoint Plugins*/
typedef itk::ImageDuplicator< MovingImageType > MovingDuplicatorType;
typedef itk::ImageDuplicator< TargetImageType > TargetDuplicatorType;
typename MovingDuplicatorType::Pointer mDuplicator = MovingDuplicatorType::New();
mDuplicator->SetInputImage(moving);
mDuplicator->Update();
typename TargetDuplicatorType::Pointer tDuplicator = TargetDuplicatorType::New();
tDuplicator->SetInputImage(target);
tDuplicator->Update();
typename MovingImageType::Pointer clonedMoving = mDuplicator->GetOutput();
typename TargetImageType::Pointer clonedTarget = tDuplicator->GetOutput();
pImageInterface->setTargetImage(clonedTarget);
pImageInterface->setMovingImage(clonedMoving);
}
else if (pDefaultImageInterface)
{
//you may convert it to the default image type and use it then
if (! m_AllowImageCasting)
{
mapDefaultExceptionStaticMacro( <<
"Error, cannot set images. MAPAlgorithmHelper has to convert them into MatchPoint default images, but is not allowed. Please reconfigure helper.");
}
typename InternalDefaultTargetImageType::Pointer spCastedTarget =
CastImage<TargetImageType, InternalDefaultTargetImageType>(target);
typename InternalDefaultMovingImageType::Pointer spCastedMoving =
CastImage<MovingImageType, InternalDefaultMovingImageType>(moving);
pDefaultImageInterface->setTargetImage(spCastedTarget);
pDefaultImageInterface->setMovingImage(spCastedMoving);
}
else
{
mapDefaultExceptionStaticMacro( << "Error, algorithm is not able to use the based images.");
}
}
template<typename TPixelType1, unsigned int VImageDimension1,
typename TPixelType2, unsigned int VImageDimension2>
void MAPAlgorithmHelper::DoCheckImages(const itk::Image<TPixelType1, VImageDimension1>* /*moving*/,
const itk::Image<TPixelType2, VImageDimension2>* /*target*/) const
{
typedef itk::Image<TPixelType1, VImageDimension1> MovingImageType;
typedef itk::Image<TPixelType2, VImageDimension2> TargetImageType;
typedef itk::Image<map::core::discrete::InternalPixelType, VImageDimension1>
InternalDefaultMovingImageType;
typedef itk::Image<map::core::discrete::InternalPixelType, VImageDimension2>
InternalDefaultTargetImageType;
typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<MovingImageType, TargetImageType>
ImageRegInterface;
typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface<InternalDefaultMovingImageType, InternalDefaultTargetImageType>
DefaultImageRegInterface;
ImageRegInterface* pImageInterface = dynamic_cast<ImageRegInterface*>(m_AlgorithmBase.GetPointer());
DefaultImageRegInterface* pDefaultImageInterface = dynamic_cast<DefaultImageRegInterface*>
(m_AlgorithmBase.GetPointer());
if (pImageInterface)
{
//just set directly and you are done
m_Error = CheckError::none;
}
else if (pDefaultImageInterface)
{
//you may convert it to the default image type and use it then
m_Error = CheckError::onlyByCasting;
}
else
{
m_Error = CheckError::unsupportedDataType;
}
}
mapGenerateAlgorithmUIDPolicyMacro(DummyRegIDPolicy, "de.dkfz.dipp", "Identity", "1.0.0", "");
mitk::MAPRegistrationWrapper::Pointer GenerateIdentityRegistration3D()
{
typedef map::algorithm::DummyImageRegistrationAlgorithm<map::core::discrete::Elements<3>::InternalImageType, map::core::discrete::Elements<3>::InternalImageType, DummyRegIDPolicy>
DummyRegType;
DummyRegType::Pointer regAlg = DummyRegType::New();
mitk::MAPAlgorithmHelper helper(regAlg);
map::core::discrete::Elements<3>::InternalImageType::Pointer dummyImg =
map::core::discrete::Elements<3>::InternalImageType::New();
dummyImg->Allocate();
regAlg->setTargetImage(dummyImg);
regAlg->setMovingImage(dummyImg);
auto dummyReg = mitk::MAPRegistrationWrapper::New(regAlg->getRegistration());
return dummyReg;
}
}
diff --git a/Modules/MatchPointRegistration/src/Helper/mitkPointSetMappingHelper.cpp b/Modules/MatchPointRegistration/src/Helper/mitkPointSetMappingHelper.cpp
index c71d4bc2ff..5f33e4740a 100644
--- a/Modules/MatchPointRegistration/src/Helper/mitkPointSetMappingHelper.cpp
+++ b/Modules/MatchPointRegistration/src/Helper/mitkPointSetMappingHelper.cpp
@@ -1,144 +1,144 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mapRegistration.h"
#include "mitkPointSetMappingHelper.h"
#include "mitkRegistrationHelper.h"
#include "mapPointSetMappingTask.h"
::map::core::continuous::Elements<3>::InternalPointSetType::Pointer mitk::PointSetMappingHelper::ConvertPointSetMITKtoMAP(const mitk::PointSet::DataType* mitkSet)
{
if (! mitkSet) mapDefaultExceptionStaticMacro(<< "Error, cannot convert point set. Passed mitk point set is null.");
::map::core::continuous::Elements<3>::InternalPointSetType::Pointer mapSet = ::map::core::continuous::Elements<3>::InternalPointSetType::New();
::map::core::continuous::Elements<3>::InternalPointSetType::PointsContainer::Pointer mapContainer = ::map::core::continuous::Elements<3>::InternalPointSetType::PointsContainer::New();
::map::core::continuous::Elements<3>::InternalPointSetType::PointDataContainer::Pointer mapDataContainer = ::map::core::continuous::Elements<3>::InternalPointSetType::PointDataContainer::New();
mapSet->SetPoints(mapContainer);
mapSet->SetPointData(mapDataContainer);
unsigned int pointCount = mitkSet->GetNumberOfPoints();
for (unsigned int pointId = 0; pointId < pointCount; ++pointId)
{
mapSet->SetPoint(pointId, mitkSet->GetPoint(pointId));
mitk::PointSet::PointDataType data;
if (mitkSet->GetPointData(pointId,&data))
{
mapSet->SetPointData(pointId,data.id);
}
}
return mapSet;
}
::mitk::PointSet::Pointer
mitk::PointSetMappingHelper::map(const ::mitk::PointSet* input, const mitk::PointSetMappingHelper::RegistrationType* registration, int timeStep,
bool throwOnMappingError, const ::mitk::PointSet::PointDataType& errorPointValue)
{
if (!registration)
{
mitkThrow() << "Cannot map point set. Passed registration wrapper pointer is nullptr.";
}
if (!input)
{
mitkThrow() << "Cannot map point set. Passed point set pointer is nullptr.";
}
if (static_cast<int>(input->GetTimeSteps())<=timeStep && timeStep>=0)
{
mitkThrow() << "Cannot set point set. Selected time step is larger then mitk point set. MITK time step count: "<<input->GetTimeSteps()<<"; selected time step: "<<timeStep;
}
::mitk::PointSet::Pointer result = input->Clone();
typedef ::map::core::continuous::Elements<3>::InternalPointSetType MAPPointSetType;
typedef ::map::core::Registration<3,3> ConcreteRegistrationType;
const ConcreteRegistrationType* castedReg = dynamic_cast<const ConcreteRegistrationType*>(registration);
if (!castedReg)
{
mitkThrow() <<"Moving and/or fixed dimension of the registration is not 3. Cannot map point 3D set.";
}
typedef ::map::core::PointSetMappingTask<ConcreteRegistrationType, MAPPointSetType, MAPPointSetType> MappingTaskType;
MappingTaskType::ErrorPointValueType internalErrorValue = itk::NumericTraits<MappingTaskType::ErrorPointValueType>::NonpositiveMin();
MappingTaskType::Pointer spTask = MappingTaskType::New();
spTask->setRegistration(castedReg);
spTask->setThrowOnMappingError(throwOnMappingError);
spTask->setErrorPointValue(internalErrorValue);
unsigned int timePos = timeStep;
unsigned int timeEndPos = timeStep+1;
if (timeStep < 0)
{
timePos = 0;
timeEndPos = input->GetTimeSteps();
}
while (timePos<timeEndPos)
{
MAPPointSetType::Pointer inputTempSet = ConvertPointSetMITKtoMAP(input->GetPointSet(timePos));
spTask->setInputPointSet(inputTempSet);
spTask->execute();
MAPPointSetType::Pointer mappedSet = spTask->getResultPointSet();
unsigned int pointCount = input->GetSize(timePos);
for (unsigned int pointId = 0; pointId < pointCount; ++pointId)
{
result->SetPoint(pointId, mappedSet->GetPoint(pointId), timePos);
bool invalid = true;
MAPPointSetType::PixelType mappedData;
if (mappedSet->GetPointData(pointId,&mappedData))
{
invalid = mappedData == internalErrorValue;
}
if (invalid)
{
result->GetPointSet(timePos)->GetPointData()->SetElement(pointId,errorPointValue);
}
else
{
result->GetPointSet(timePos)->GetPointData()->SetElement(pointId,input->GetPointSet(timePos)->GetPointData()->GetElement(pointId));
}
}
++timePos;
}
return result;
}
::mitk::PointSet::Pointer
mitk::PointSetMappingHelper::map(const ::mitk::PointSet* input, const MITKRegistrationType* registration, int timeStep,
bool throwOnMappingError, const ::mitk::PointSet::PointDataType& errorPointValue)
{
if (!registration)
{
mitkThrow() << "Cannot map point set. Passed registration wrapper pointer is nullptr.";
}
if (!registration->GetRegistration())
{
- mitkThrow() << "Cannot map point set. Passed registration wrapper containes no registration.";
+ mitkThrow() << "Cannot map point set. Passed registration wrapper contains no registration.";
}
if (!input)
{
mitkThrow() << "Cannot map point set. Passed point set pointer is nullptr.";
}
::mitk::PointSet::Pointer result = map(input, registration->GetRegistration(), timeStep, throwOnMappingError, errorPointValue);
return result;
}
diff --git a/Modules/MatchPointRegistration/src/Helper/mitkTimeFramesRegistrationHelper.cpp b/Modules/MatchPointRegistration/src/Helper/mitkTimeFramesRegistrationHelper.cpp
index 5950e7ba52..4b260cc338 100644
--- a/Modules/MatchPointRegistration/src/Helper/mitkTimeFramesRegistrationHelper.cpp
+++ b/Modules/MatchPointRegistration/src/Helper/mitkTimeFramesRegistrationHelper.cpp
@@ -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.
============================================================================*/
#include "itkCommand.h"
#include "mitkTimeFramesRegistrationHelper.h"
#include <mitkImageTimeSelector.h>
#include <mitkImageReadAccessor.h>
#include <mitkMaskedAlgorithmHelper.h>
#include <mitkMAPAlgorithmHelper.h>
mitk::Image::Pointer
mitk::TimeFramesRegistrationHelper::GetFrameImage(const mitk::Image* image,
mitk::TimePointType timePoint) const
{
mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput(image);
imageTimeSelector->SetTimeNr(timePoint);
imageTimeSelector->UpdateLargestPossibleRegion();
mitk::Image::Pointer frameImage = imageTimeSelector->GetOutput();
return frameImage;
};
void
mitk::TimeFramesRegistrationHelper::Generate()
{
CheckValidInputs();
//prepare processing
mitk::Image::Pointer targetFrame = GetFrameImage(this->m_4DImage, 0);
this->m_Registered4DImage = this->m_4DImage->Clone();
Image::ConstPointer mask;
if (m_TargetMask.IsNotNull())
{
if (m_TargetMask->GetTimeSteps() > 1)
{
mask = GetFrameImage(m_TargetMask, 0);
}
else
{
mask = m_TargetMask;
}
}
double progressDelta = 1.0 / ((this->m_4DImage->GetTimeSteps() - 1) * 3.0);
m_Progress = 0.0;
//process the frames
for (unsigned int i = 1; i < this->m_4DImage->GetTimeSteps(); ++i)
{
Image::Pointer movingFrame = GetFrameImage(this->m_4DImage, i);
Image::Pointer mappedFrame;
IgnoreListType::iterator finding = std::find(m_IgnoreList.begin(), m_IgnoreList.end(), i);
if (finding == m_IgnoreList.end())
{
//frame should be processed
RegistrationPointer reg = DoFrameRegistration(movingFrame, targetFrame, mask);
m_Progress += progressDelta;
this->InvokeEvent(::mitk::FrameRegistrationEvent(nullptr,
- "Registred frame #" +::map::core::convert::toStr(i)));
+ "Registered frame #" +::map::core::convert::toStr(i)));
mappedFrame = DoFrameMapping(movingFrame, reg, targetFrame);
m_Progress += progressDelta;
this->InvokeEvent(::mitk::FrameMappingEvent(nullptr,
"Mapped frame #" + ::map::core::convert::toStr(i)));
mitk::ImageReadAccessor accessor(mappedFrame, mappedFrame->GetVolumeData(0, 0, nullptr,
mitk::Image::ReferenceMemory));
this->m_Registered4DImage->SetVolume(accessor.GetData(), i);
this->m_Registered4DImage->GetTimeGeometry()->SetTimeStepGeometry(mappedFrame->GetGeometry(), i);
m_Progress += progressDelta;
}
else
{
m_Progress += 3 * progressDelta;
}
this->InvokeEvent(::itk::ProgressEvent());
}
};
mitk::Image::Pointer
mitk::TimeFramesRegistrationHelper::GetRegisteredImage()
{
if (this->HasOutdatedResult())
{
Generate();
}
return m_Registered4DImage;
};
void
mitk::TimeFramesRegistrationHelper::
SetIgnoreList(const IgnoreListType& il)
{
m_IgnoreList = il;
this->Modified();
}
void
mitk::TimeFramesRegistrationHelper::ClearIgnoreList()
{
m_IgnoreList.clear();
this->Modified();
};
mitk::TimeFramesRegistrationHelper::RegistrationPointer
mitk::TimeFramesRegistrationHelper::DoFrameRegistration(const mitk::Image* movingFrame,
const mitk::Image* targetFrame, const mitk::Image* targetMask) const
{
mitk::MAPAlgorithmHelper algHelper(m_Algorithm);
algHelper.SetAllowImageCasting(true);
algHelper.SetData(movingFrame, targetFrame);
if (targetMask)
{
mitk::MaskedAlgorithmHelper maskHelper(m_Algorithm);
maskHelper.SetMasks(nullptr, targetMask);
}
return algHelper.GetRegistration();
};
mitk::Image::Pointer mitk::TimeFramesRegistrationHelper::DoFrameMapping(
const mitk::Image* movingFrame, const RegistrationType* reg, const mitk::Image* targetFrame) const
{
return mitk::ImageMappingHelper::map(movingFrame, reg, !m_AllowUndefPixels, m_PaddingValue,
targetFrame->GetGeometry(), !m_AllowUnregPixels, m_ErrorValue, m_InterpolatorType);
};
bool
mitk::TimeFramesRegistrationHelper::HasOutdatedResult() const
{
if (m_Registered4DImage.IsNull())
{
return true;
}
bool result = false;
if (m_Registered4DImage->GetMTime() > this->GetMTime())
{
result = true;
}
if (m_Algorithm.IsNotNull())
{
if (m_Algorithm->GetMTime() > this->GetMTime())
{
result = true;
}
}
if (m_4DImage.IsNotNull())
{
if (m_4DImage->GetMTime() > this->GetMTime())
{
result = true;
}
}
if (m_TargetMask.IsNotNull())
{
if (m_TargetMask->GetMTime() > this->GetMTime())
{
result = true;
}
}
return result;
};
void
mitk::TimeFramesRegistrationHelper::CheckValidInputs() const
{
if (m_4DImage.IsNull())
{
mitkThrow() << "Cannot register image. Input 4D image is not set.";
}
if (m_Algorithm.IsNull())
{
mitkThrow() << "Cannot register image. Algorithm is not set.";
}
if (m_4DImage->GetTimeSteps() <= 1)
{
mitkThrow() << "Cannot register image. Input 4D image must have 2 or more time steps.";
}
for (IgnoreListType::const_iterator pos = this->m_IgnoreList.begin();
pos != this->m_IgnoreList.end(); ++pos)
{
if (*pos >= m_4DImage->GetTimeSteps())
{
mitkThrow() <<
- "Cannot register image. Ignore list containes at least one inexistant frame. Invalid frame index: "
+ "Cannot register image. Ignore list contains at least one inexistant frame. Invalid frame index: "
<< *pos;
}
}
};
double
mitk::TimeFramesRegistrationHelper::GetProgress() const
{
return m_Progress;
};
diff --git a/Modules/MatchPointRegistration/src/Rendering/mitkRegEvaluationMapper2D.cpp b/Modules/MatchPointRegistration/src/Rendering/mitkRegEvaluationMapper2D.cpp
index fafaf04fb8..fbad35068c 100644
--- a/Modules/MatchPointRegistration/src/Rendering/mitkRegEvaluationMapper2D.cpp
+++ b/Modules/MatchPointRegistration/src/Rendering/mitkRegEvaluationMapper2D.cpp
@@ -1,836 +1,836 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkPlaneGeometry.h>
#include <mitkProperties.h>
#include <mitkResliceMethodProperty.h>
#include <mitkVtkResliceInterpolationProperty.h>
#include <mitkPixelType.h>
#include <mitkTransferFunctionProperty.h>
#include "mitkImageStatisticsHolder.h"
#include "mitkPlaneClipping.h"
#include "mitkRegVisPropertyTags.h"
#include "mitkRegVisHelper.h"
#include "mitkRegEvalStyleProperty.h"
#include "mitkRegEvalWipeStyleProperty.h"
//MITK Rendering
#include "mitkRegEvaluationMapper2D.h"
#include "vtkMitkThickSlicesFilter.h"
#include "vtkMitkLevelWindowFilter.h"
#include "vtkNeverTranslucentTexture.h"
//VTK
#include <vtkProperty.h>
#include <vtkTransform.h>
#include <vtkMatrix4x4.h>
#include <vtkLookupTable.h>
#include <vtkImageData.h>
#include <vtkPoints.h>
#include <vtkGeneralTransform.h>
#include <vtkImageExtractComponents.h>
#include <vtkImageReslice.h>
#include <vtkImageChangeInformation.h>
#include <vtkPlaneSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkCellArray.h>
#include <vtkCamera.h>
#include <vtkColorTransferFunction.h>
#include <vtkImageCheckerboard.h>
#include <vtkImageWeightedSum.h>
#include <vtkImageMathematics.h>
#include <vtkImageRectilinearWipe.h>
#include <vtkImageGradientMagnitude.h>
#include <vtkImageAppendComponents.h>
#include <vtkImageExtractComponents.h>
//ITK
#include <itkRGBAPixel.h>
#include <mitkRenderingModeProperty.h>
//MatchPoint
#include <mitkRegEvaluationObject.h>
#include <mitkImageMappingHelper.h>
mitk::RegEvaluationMapper2D::RegEvaluationMapper2D()
{
}
mitk::RegEvaluationMapper2D::~RegEvaluationMapper2D()
{
}
//set the two points defining the textured plane according to the dimension and spacing
void mitk::RegEvaluationMapper2D::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 sagittal) 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::RegEvaluationMapper2D::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::RegEvaluationMapper2D::GetTargetImage( void )
{
const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >( GetDataNode()->GetData() );
if (evalObj)
{
return evalObj->GetTargetImage();
}
return nullptr;
}
const mitk::Image* mitk::RegEvaluationMapper2D::GetMovingImage( void )
{
const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >( GetDataNode()->GetData() );
if (evalObj)
{
return evalObj->GetMovingImage();
}
return nullptr;
}
const mitk::DataNode* mitk::RegEvaluationMapper2D::GetTargetNode(void)
{
const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >(GetDataNode()->GetData());
if (evalObj)
{
return evalObj->GetTargetNode();
}
return nullptr;
}
const mitk::DataNode* mitk::RegEvaluationMapper2D::GetMovingNode(void)
{
const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >(GetDataNode()->GetData());
if (evalObj)
{
return evalObj->GetMovingNode();
}
return nullptr;
}
const mitk::MAPRegistrationWrapper* mitk::RegEvaluationMapper2D::GetRegistration( void )
{
const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >( GetDataNode()->GetData() );
if (evalObj)
{
return evalObj->GetRegistration();
}
return nullptr;
}
vtkProp* mitk::RegEvaluationMapper2D::GetVtkProp(mitk::BaseRenderer* renderer)
{
//return the actor corresponding to the renderer
return m_LSH.GetLocalStorage(renderer)->m_Actors;
}
void mitk::RegEvaluationMapper2D::GenerateDataForRenderer( mitk::BaseRenderer *renderer )
{
bool updated = false;
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
mitk::Image::Pointer targetInput = const_cast< mitk::Image * >( this->GetTargetImage() );
mitk::DataNode* datanode = this->GetDataNode();
if ( targetInput.IsNull() || targetInput->IsInitialized() == false )
{
return;
}
mitk::Image::ConstPointer movingInput = this->GetMovingImage();
if ( movingInput.IsNull() || movingInput->IsInitialized() == false )
{
return;
}
mitk::MAPRegistrationWrapper::ConstPointer reg = this->GetRegistration();
//check if there is a valid worldGeometry
const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
if( ( worldGeometry == nullptr ) || ( !worldGeometry->IsValid() ) || ( !worldGeometry->HasReferenceGeometry() ))
{
return;
}
if(targetInput->GetMTime()>localStorage->m_LastUpdateTime
|| (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) //was the geometry modified?
|| (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()))
{ //target input has been modified -> reslice target input
targetInput->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, targetInput->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_EvaluationImage = nullptr;
localStorage->m_Mapper->SetInputData( localStorage->m_EmptyPolyData );
return;
}
//set main input for ExtractSliceFilter
localStorage->m_Reslicer->SetInput(targetInput);
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( targetInput->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 ( (targetInput->GetDimension() >= 3) && (targetInput->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);
}
- //this is needed when thick mode was enable bevore. These variable have to be reset to default values
+ //this is needed when thick mode was enable before. 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_slicedTargetImage = localStorage->m_Reslicer->GetOutput();
updated = true;
}
if(updated ||
movingInput->GetMTime() > localStorage->m_LastUpdateTime ||
reg->GetMTime() > localStorage->m_LastUpdateTime)
{
//Map moving image
localStorage->m_slicedMappedImage = mitk::ImageMappingHelper::map(movingInput,reg,false,0,localStorage->m_slicedTargetImage->GetGeometry(),false,0);
updated = true;
}
// Bounds information for reslicing (only required if reference geometry
// is present)
//this used for generating a vtkPLaneSource with the right size
double sliceBounds[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
if (updated
|| (localStorage->m_LastUpdateTime < datanode->GetPropertyList()->GetMTime()) //was a property modified?
|| (localStorage->m_LastUpdateTime < datanode->GetPropertyList(renderer)->GetMTime())
|| (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetMTime())
|| (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetMTime()))
{
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] = { 0.0, 0.0, 0.0, 0.0, 0.0, 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.
const PlaneGeometry *planeGeometry = dynamic_cast<const PlaneGeometry *>(worldGeometry);
mitk::PlaneClipping::CalculateClippedPlaneBounds(targetInput->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_TargetLevelWindowFilter->SetClippingBounds(textureClippingBounds);
localStorage->m_MappedLevelWindowFilter->SetClippingBounds(textureClippingBounds);
}
this->ApplyLookuptable(renderer, this->GetTargetNode(), localStorage->m_TargetLevelWindowFilter);
this->ApplyLookuptable(renderer, this->GetMovingNode(), localStorage->m_MappedLevelWindowFilter);
this->ApplyLevelWindow(renderer, this->GetTargetNode(), localStorage->m_TargetLevelWindowFilter);
this->ApplyLevelWindow(renderer, this->GetMovingNode(), localStorage->m_MappedLevelWindowFilter);
//connect the input with the levelwindow filter
localStorage->m_TargetLevelWindowFilter->SetInputData(localStorage->m_slicedTargetImage->GetVtkImageData());
localStorage->m_MappedLevelWindowFilter->SetInputData(localStorage->m_slicedMappedImage->GetVtkImageData());
localStorage->m_TargetExtractFilter->SetInputConnection(localStorage->m_TargetLevelWindowFilter->GetOutputPort());
localStorage->m_MappedExtractFilter->SetInputConnection(localStorage->m_MappedLevelWindowFilter->GetOutputPort());
localStorage->m_TargetExtractFilter->SetComponents(0);
localStorage->m_MappedExtractFilter->SetComponents(0);
updated = true;
}
//Generate evaluation image
bool isStyleOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalStyle,localStorage->m_LastUpdateTime);
bool isBlendOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalBlendFactor,localStorage->m_LastUpdateTime);
bool isCheckerOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalCheckerCount,localStorage->m_LastUpdateTime);
bool isWipeStyleOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalWipeStyle,localStorage->m_LastUpdateTime);
bool isContourOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalTargetContour,localStorage->m_LastUpdateTime);
bool isPositionOutdated = mitk::PropertyIsOutdated(datanode, mitk::nodeProp_RegEvalCurrentPosition, localStorage->m_LastUpdateTime);
if (updated ||
isStyleOutdated ||
isBlendOutdated ||
isCheckerOutdated ||
isWipeStyleOutdated ||
isContourOutdated ||
isPositionOutdated)
{
mitk::RegEvalStyleProperty::Pointer evalStyleProp = mitk::RegEvalStyleProperty::New();
datanode->GetProperty(evalStyleProp, mitk::nodeProp_RegEvalStyle);
switch (evalStyleProp->GetValueAsId())
{
case 0 :
{
PrepareBlend(datanode, localStorage);
break;
}
case 1 :
{
PrepareColorBlend(localStorage);
break;
}
case 2 :
{
PrepareCheckerBoard(datanode, localStorage);
break;
}
case 3 :
{
const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
Point3D currentPos3D;
datanode->GetPropertyValue<Point3D>(mitk::nodeProp_RegEvalCurrentPosition, currentPos3D);
Point2D currentPos2D;
worldGeometry->Map(currentPos3D, currentPos2D);
Point2D currentIndex2D;
worldGeometry->WorldToIndex(currentPos2D, currentIndex2D);
PrepareWipe(datanode, localStorage, currentIndex2D);
break;
}
case 4 :
{
PrepareDifference(localStorage);
break;
}
case 5 :
{
PrepareContour(datanode, localStorage);
break;
}
}
updated = true;
}
if(updated
|| (localStorage->m_LastUpdateTime < datanode->GetPropertyList()->GetMTime()) //was a property modified?
|| (localStorage->m_LastUpdateTime < datanode->GetPropertyList(renderer)->GetMTime()) )
{
this->ApplyOpacity( renderer );
// do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter)
localStorage->m_Texture->SetColorModeToDirectScalars();
// 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->SetInputData(localStorage->m_EvaluationImage);
this->TransformActor( renderer );
vtkActor* contourShadowActor = dynamic_cast<vtkActor*> (localStorage->m_Actors->GetParts()->GetItemAsObject(0));
//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::RegEvaluationMapper2D::PrepareContour( mitk::DataNode* datanode, LocalStorage * localStorage )
{
bool targetContour = true;
datanode->GetBoolProperty(mitk::nodeProp_RegEvalTargetContour,targetContour);
vtkSmartPointer<vtkImageGradientMagnitude> magFilter =
vtkSmartPointer<vtkImageGradientMagnitude>::New();
if(targetContour)
{
magFilter->SetInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort());
}
else
{
magFilter->SetInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort());
}
vtkSmartPointer<vtkImageAppendComponents> appendFilter =
vtkSmartPointer<vtkImageAppendComponents>::New();
appendFilter->AddInputConnection(magFilter->GetOutputPort());
appendFilter->AddInputConnection(magFilter->GetOutputPort());
if(targetContour)
{
appendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort());
}
else
{
appendFilter->AddInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort());
}
appendFilter->Update();
localStorage->m_EvaluationImage = appendFilter->GetOutput();
}
void mitk::RegEvaluationMapper2D::PrepareDifference( LocalStorage * localStorage )
{
vtkSmartPointer<vtkImageMathematics> diffFilter =
vtkSmartPointer<vtkImageMathematics>::New();
vtkSmartPointer<vtkImageMathematics> minFilter =
vtkSmartPointer<vtkImageMathematics>::New();
vtkSmartPointer<vtkImageMathematics> maxFilter =
vtkSmartPointer<vtkImageMathematics>::New();
minFilter->SetInputConnection(0, localStorage->m_TargetExtractFilter->GetOutputPort());
minFilter->SetInputConnection(1, localStorage->m_MappedExtractFilter->GetOutputPort());
minFilter->SetOperationToMin();
maxFilter->SetInputConnection(0, localStorage->m_TargetExtractFilter->GetOutputPort());
maxFilter->SetInputConnection(1, localStorage->m_MappedExtractFilter->GetOutputPort());
maxFilter->SetOperationToMax();
diffFilter->SetInputConnection(0, maxFilter->GetOutputPort());
diffFilter->SetInputConnection(1, minFilter->GetOutputPort());
diffFilter->SetOperationToSubtract();
diffFilter->Update();
localStorage->m_EvaluationImage = diffFilter->GetOutput();
}
void mitk::RegEvaluationMapper2D::PrepareWipe(mitk::DataNode* datanode, LocalStorage * localStorage, const Point2D& currentIndex2D)
{
mitk::RegEvalWipeStyleProperty::Pointer evalWipeStyleProp = mitk::RegEvalWipeStyleProperty::New();
datanode->GetProperty(evalWipeStyleProp, mitk::nodeProp_RegEvalWipeStyle);
vtkSmartPointer<vtkImageRectilinearWipe> wipedFilter =
vtkSmartPointer<vtkImageRectilinearWipe>::New();
wipedFilter->SetInputConnection(0, localStorage->m_TargetLevelWindowFilter->GetOutputPort());
wipedFilter->SetInputConnection(1, localStorage->m_MappedLevelWindowFilter->GetOutputPort());
wipedFilter->SetPosition(currentIndex2D[0], currentIndex2D[1]);
if (evalWipeStyleProp->GetValueAsId() == 0)
{
wipedFilter->SetWipeToQuad();
}
else if (evalWipeStyleProp->GetValueAsId() == 1)
{
wipedFilter->SetWipeToHorizontal();
}
else if (evalWipeStyleProp->GetValueAsId() == 2)
{
wipedFilter->SetWipeToVertical();
}
wipedFilter->Update();
localStorage->m_EvaluationImage = wipedFilter->GetOutput();
}
void mitk::RegEvaluationMapper2D::PrepareCheckerBoard( mitk::DataNode* datanode, LocalStorage * localStorage )
{
int checkerCount = 5;
datanode->GetIntProperty(mitk::nodeProp_RegEvalCheckerCount,checkerCount);
vtkSmartPointer<vtkImageCheckerboard> checkerboardFilter =
vtkSmartPointer<vtkImageCheckerboard>::New();
checkerboardFilter->SetInputConnection(0, localStorage->m_TargetLevelWindowFilter->GetOutputPort());
checkerboardFilter->SetInputConnection(1, localStorage->m_MappedLevelWindowFilter->GetOutputPort());
checkerboardFilter->SetNumberOfDivisions(checkerCount, checkerCount, 1);
checkerboardFilter->Update();
localStorage->m_EvaluationImage = checkerboardFilter->GetOutput();
}
void mitk::RegEvaluationMapper2D::PrepareColorBlend( LocalStorage * localStorage )
{
vtkSmartPointer<vtkImageAppendComponents> appendFilter =
vtkSmartPointer<vtkImageAppendComponents>::New();
//red channel
appendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort());
//green channel
appendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort());
//blue channel
appendFilter->AddInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort());
appendFilter->Update();
localStorage->m_EvaluationImage = appendFilter->GetOutput();
}
void mitk::RegEvaluationMapper2D::PrepareBlend( mitk::DataNode* datanode, LocalStorage * localStorage )
{
int blendfactor = 50;
datanode->GetIntProperty(mitk::nodeProp_RegEvalBlendFactor,blendfactor);
vtkSmartPointer<vtkImageWeightedSum> blendFilter =
vtkSmartPointer<vtkImageWeightedSum>::New();
blendFilter->AddInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort());
blendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort());
blendFilter->SetWeight(0, (100 - blendfactor) / 100.);
blendFilter->SetWeight(1,blendfactor/100.);
blendFilter->Update();
localStorage->m_EvaluationImage = blendFilter->GetOutput();
}
void mitk::RegEvaluationMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer, const mitk::DataNode* dataNode, vtkMitkLevelWindowFilter* levelFilter)
{
LevelWindow levelWindow;
dataNode->GetLevelWindow(levelWindow, renderer, "levelwindow");
levelFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound());
mitk::LevelWindow opacLevelWindow;
if (dataNode->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow"))
{
//pass the opaque level window to the filter
levelFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound());
levelFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound());
}
else
{
//no opaque level window
levelFilter->SetMinOpacity(0.0);
levelFilter->SetMaxOpacity(255.0);
}
}
void mitk::RegEvaluationMapper2D::ApplyLookuptable(mitk::BaseRenderer* renderer, const mitk::DataNode* dataNode, vtkMitkLevelWindowFilter* levelFilter)
{
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*>(dataNode->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.
+ //in every iteration of the rendering.
}
levelFilter->SetLookupTable(usedLookupTable);
}
void mitk::RegEvaluationMapper2D::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::RegEvaluationMapper2D::Update(mitk::BaseRenderer* renderer)
{
bool visible = true;
GetDataNode()->GetVisibility(visible, renderer, "visible");
if ( !visible )
{
return;
}
mitk::Image* data = const_cast<mitk::Image *>( this->GetTargetImage() );
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())
|| (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetMTime()) //was the target node modified?
|| (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetMTime()) //was the moving node modified?
|| (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetPropertyList()->GetMTime()) //was a target node property modified?
|| (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetPropertyList(renderer)->GetMTime())
|| (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetPropertyList()->GetMTime()) //was a moving node property modified?
|| (localStorage->m_LastUpdateTime < this->GetMovingNode()->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::RegEvaluationMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite)
{
mitk::RegEvaluationObject* regEval = dynamic_cast<mitk::RegEvaluationObject*>(node->GetData());
if(!regEval)
{
return;
}
// Properties common for both images and segmentations
node->AddProperty( "depthOffset", mitk::FloatProperty::New( 0.0 ), renderer, overwrite );
if(regEval->GetTargetImage() && regEval->GetTargetImage()->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 ) ); // set to user configurable default value (see global options)
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();
mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New();
mitkLutProp->SetLookupTable(mitkLut);
node->SetProperty("LookupTable", mitkLutProp);
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);
node->AddProperty(mitk::nodeProp_RegEvalStyle, mitk::RegEvalStyleProperty::New(0), renderer, overwrite);
node->AddProperty(mitk::nodeProp_RegEvalBlendFactor, mitk::IntProperty::New(50), renderer, overwrite);
node->AddProperty(mitk::nodeProp_RegEvalCheckerCount, mitk::IntProperty::New(3), renderer, overwrite);
node->AddProperty(mitk::nodeProp_RegEvalTargetContour, mitk::BoolProperty::New(true), renderer, overwrite);
node->AddProperty(mitk::nodeProp_RegEvalWipeStyle, mitk::RegEvalWipeStyleProperty::New(0), renderer, overwrite);
node->AddProperty(mitk::nodeProp_RegEvalCurrentPosition, mitk::Point3dProperty::New(mitk::Point3D()), renderer, overwrite);
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
mitk::RegEvaluationMapper2D::LocalStorage* mitk::RegEvaluationMapper2D::GetLocalStorage(mitk::BaseRenderer* renderer)
{
return m_LSH.GetLocalStorage(renderer);
}
void mitk::RegEvaluationMapper2D::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 sagittal
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 sagittal)
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::RegEvaluationMapper2D::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::RegEvaluationMapper2D::LocalStorage::~LocalStorage()
{
}
mitk::RegEvaluationMapper2D::LocalStorage::LocalStorage()
{
m_TargetLevelWindowFilter = vtkSmartPointer<vtkMitkLevelWindowFilter>::New();
m_MappedLevelWindowFilter = vtkSmartPointer<vtkMitkLevelWindowFilter>::New();
m_TargetExtractFilter = vtkSmartPointer<vtkImageExtractComponents>::New();
m_MappedExtractFilter = vtkSmartPointer<vtkImageExtractComponents>::New();
m_mmPerPixel = nullptr;
//Do as much actions as possible in here to avoid double executions.
m_Plane = vtkSmartPointer<vtkPlaneSource>::New();
//m_Texture = vtkSmartPointer<vtkNeverTranslucentTexture>::New().GetPointer();
m_Texture = vtkSmartPointer<vtkOpenGLTexture>::New().GetPointer();
m_DefaultLookupTable = 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_EvaluationImage = vtkSmartPointer<vtkImageData>::New();
m_EmptyPolyData = vtkSmartPointer<vtkPolyData>::New();
mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New();
//built a default lookuptable
mitkLUT->SetType(mitk::LookupTable::GRAYSCALE);
m_DefaultLookupTable = mitkLUT->GetVtkLookupTable();
mitkLUT->SetType(mitk::LookupTable::JET);
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/MatchPointRegistration/src/Rendering/mitkRegistrationWrapperMapperBase.cpp b/Modules/MatchPointRegistration/src/Rendering/mitkRegistrationWrapperMapperBase.cpp
index a41e15fc8f..5fd5fda2f2 100644
--- a/Modules/MatchPointRegistration/src/Rendering/mitkRegistrationWrapperMapperBase.cpp
+++ b/Modules/MatchPointRegistration/src/Rendering/mitkRegistrationWrapperMapperBase.cpp
@@ -1,278 +1,278 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <vtkPropAssembly.h>
#include <vtkPointData.h>
#include <vtkProperty.h>
#include <vtkCellArray.h>
#include <vtkColorTransferFunction.h>
#include <vtkPolyDataMapper.h>
#include <vtkPolyData.h>
#include <vtkActor.h>
#include <mitkProperties.h>
#include <mitkExceptionMacro.h>
#include <mitkException.h>
#include "mitkMAPRegistrationWrapper.h"
#include "mitkRegistrationWrapperMapperBase.h"
#include "mitkRegVisColorStyleProperty.h"
#include "mitkRegVisHelper.h"
#include "mitkRegVisPropertyTags.h"
mitk::MITKRegistrationWrapperMapperBase::MITKRegistrationWrapperMapperBase()
{
}
mitk::MITKRegistrationWrapperMapperBase::~MITKRegistrationWrapperMapperBase()
{
}
void mitk::MITKRegistrationWrapperMapperBase::GenerateDataForRenderer( mitk::BaseRenderer *renderer )
{
mitk::DataNode::Pointer node = this->GetDataNode();
if (node.IsNull())
return;
bool isVisible = true;
node->GetVisibility(isVisible, renderer);
if (!isVisible)
return;
RegWrapperLocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
- //check if updates occured in the node or on the display
+ //check if updates occurred in the node or on the display
bool outdatedRendererGeometry = RendererGeometryIsOutdated(renderer,localStorage->m_LastUpdateTime);
if ( (localStorage->m_LastUpdateTime < node->GetMTime())
|| (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) //was a property modified?
|| (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime())
|| outdatedRendererGeometry)
{
MITK_DEBUG << "UPDATE NEEDED FOR _ " << renderer->GetName();
bool isGridActive = false;
node->GetBoolProperty(mitk::nodeProp_RegVisGrid,isGridActive);
bool isGlyphActive = false;
node->GetBoolProperty(mitk::nodeProp_RegVisGlyph,isGlyphActive);
bool isPointsActive = false;
node->GetBoolProperty(mitk::nodeProp_RegVisPoints,isPointsActive);
bool showStartGrid = false;
node->GetBoolProperty(mitk::nodeProp_RegVisGridShowStart,showStartGrid);
bool isGridActiveOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisGrid,localStorage->m_LastUpdateTime);
bool isGlyphActiveOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisGlyph,localStorage->m_LastUpdateTime);
bool isPointsActiveOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisPoints,localStorage->m_LastUpdateTime);
bool showStartGridOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisGridShowStart,localStorage->m_LastUpdateTime);
mitk::BaseData::Pointer baseData = node->GetData();
if (baseData.IsNull())
return;
const mitk::MAPRegistrationWrapper* regWrapper = dynamic_cast<const mitk::MAPRegistrationWrapper*>(baseData.GetPointer());
if (regWrapper == nullptr)
return;
//////////////////////////////////////////////////////////////////////////
//1. Check the FOV and presentation styles
bool outdatedFOV = mitk::GridIsOutdated(node,localStorage->m_LastUpdateTime);
if (outdatedFOV ||isGridActiveOutdated || isGlyphActiveOutdated || isPointsActiveOutdated || outdatedRendererGeometry)
{ // we need to generate the grids/presentation again
const map::core::RegistrationKernelBase<3,3>* regKernel= mitk::GetRelevantRegKernelOfNode(node);
if(!regKernel)
{
mitkThrow() << "No reg kernel for visualization";
}
mitk::BaseGeometry::ConstPointer gridDesc;
unsigned int gridFrequ =5;
if (!GetGeometryDescription(renderer,gridDesc, gridFrequ))
{
return;
};
if(isGridActive)
{
localStorage->m_DeformedGridData = mitk::Generate3DDeformationGrid(gridDesc, gridFrequ, regKernel);
localStorage->m_StartGridData = mitk::Generate3DDeformationGrid(gridDesc,gridFrequ);
localStorage->m_DeformedGridMapper->SetInputData(localStorage->m_DeformedGridData);
localStorage->m_StartGridMapper->SetInputData(localStorage->m_StartGridData);
}
else if (isGlyphActive)
{
localStorage->m_DeformedGridData = mitk::Generate3DDeformationGlyph(gridDesc, regKernel);
localStorage->m_StartGridData = nullptr;
localStorage->m_DeformedGridMapper->SetInputData(localStorage->m_DeformedGridData);
}
else
{
mitkThrow() << "No reg kernel visualization style activated.";
}
}
//////////////////////////////////////////////////////////////////////////
//2.Check if the mappers or actors must be modified
bool isColorStyleOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColorStyle,localStorage->m_LastUpdateTime);
bool isColorUniOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColorUni,localStorage->m_LastUpdateTime);
bool isColor1Outdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor1Value,localStorage->m_LastUpdateTime);
bool isColor2Outdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor2Value,localStorage->m_LastUpdateTime);
bool isColor3Outdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor3Value,localStorage->m_LastUpdateTime);
bool isColor4Outdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor4Value,localStorage->m_LastUpdateTime);
bool isColor2MagOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor2Magnitude,localStorage->m_LastUpdateTime);
bool isColor3MagOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor3Magnitude,localStorage->m_LastUpdateTime);
bool isColor4MagOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColor4Magnitude,localStorage->m_LastUpdateTime);
bool isColorInterpolateOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisColorInterpolate,localStorage->m_LastUpdateTime);
if(isColorStyleOutdated || isColorUniOutdated || isColor1Outdated ||
isColor2Outdated || isColor2MagOutdated || isColor3Outdated || isColor3MagOutdated ||
isColor4Outdated || isColor4MagOutdated || isColorInterpolateOutdated)
{
localStorage->m_DeformedGridMapper->ScalarVisibilityOn();
localStorage->m_DeformedGridMapper->SetScalarModeToUsePointData();
localStorage->m_DeformedGridMapper->SelectColorArray( "VectorMagnitude" );
mitk::RegVisColorStyleProperty* colorStyleProp = nullptr;
node->GetProperty(colorStyleProp, mitk::nodeProp_RegVisColorStyle);
float color1[3] = {0.0,0.0,0.0};
node->GetColor( color1, nullptr, mitk::nodeProp_RegVisColor1Value );
float color2[3] = {0.25,0.25,0.25};
node->GetColor( color2, nullptr, mitk::nodeProp_RegVisColor2Value );
float color3[3] = {0.5,0.5,0.5};
node->GetColor( color3, nullptr, mitk::nodeProp_RegVisColor3Value );
float color4[3] = {1.0,1.0,1.0};
node->GetColor( color4, nullptr, mitk::nodeProp_RegVisColor4Value );
double mag2 = 0;
node->GetPropertyValue(mitk::nodeProp_RegVisColor2Magnitude, mag2);
double mag3 = 0;
node->GetPropertyValue(mitk::nodeProp_RegVisColor3Magnitude, mag3);
double mag4 = 0;
node->GetPropertyValue(mitk::nodeProp_RegVisColor4Magnitude, mag4);
bool interpolate = true;
node->GetBoolProperty(mitk::nodeProp_RegVisColorInterpolate,interpolate);
//default :color by vector magnitude
localStorage->m_DeformedGridMapper->SelectColorArray( "VectorMagnitude" );
localStorage->m_DeformedGridMapper->SetUseLookupTableScalarRange(true);
localStorage->m_LUT = vtkSmartPointer<vtkColorTransferFunction>::New();
if (!colorStyleProp || colorStyleProp->GetValueAsId()==0)
{ //uni color mode
float temprgb[3] = {1.0,1.0,1.0};
node->GetColor( temprgb, nullptr, mitk::nodeProp_RegVisColorUni );
localStorage->m_LUT->AddRGBSegment(0.0,temprgb[0],temprgb[1],temprgb[2],1.0,temprgb[0],temprgb[1],temprgb[2]);
localStorage->m_LUT->Build();
localStorage->m_DeformedGridMapper->SetLookupTable(localStorage->m_LUT);
}
else
{
localStorage->m_LUT->AddRGBPoint(0.0,color1[0],color1[1],color1[2]);
localStorage->m_LUT->AddRGBPoint(mag2,color2[0],color2[1],color2[2]);
localStorage->m_LUT->AddRGBPoint(mag3,color3[0],color3[1],color3[2]);
localStorage->m_LUT->AddRGBPoint(mag4,color4[0],color4[1],color4[2]);
if (!interpolate)
{
localStorage->m_LUT->AddRGBPoint(0.99*mag2,color1[0],color1[1],color1[2]);
localStorage->m_LUT->AddRGBPoint(0.99*mag3,color2[0],color2[1],color2[2]);
localStorage->m_LUT->AddRGBPoint(0.99*mag4,color3[0],color3[1],color3[2]);
};
}
localStorage->m_LUT->Build();
localStorage->m_DeformedGridMapper->SetLookupTable(localStorage->m_LUT);
localStorage->m_DeformedGridMapper->Update();
}
bool isGridStartColorOutdated = mitk::PropertyIsOutdated(node,mitk::nodeProp_RegVisGridStartColor,localStorage->m_LastUpdateTime);
if(isGridStartColorOutdated)
{
localStorage->m_StartGridMapper->ScalarVisibilityOn();
localStorage->m_StartGridMapper->SetScalarModeToUsePointFieldData();
float temprgb[3];
if (node->GetColor( temprgb, nullptr, mitk::nodeProp_RegVisGridStartColor ))
{
double trgb[3] = { (double) temprgb[0], (double) temprgb[1], (double) temprgb[2] };
localStorage->m_StartGridActor->GetProperty()->SetColor(trgb);
}
}
//////////////////////////////////////////////////////////////////////////
//3. Check if Assembly must be updated
if(isGridActiveOutdated||isGlyphActiveOutdated||isPointsActiveOutdated||showStartGridOutdated)
{
localStorage->m_RegAssembly = vtkSmartPointer<vtkPropAssembly>::New();
if (isGridActive)
{
localStorage->m_RegAssembly->AddPart(localStorage->m_DeformedGridActor);
if (showStartGrid)
{
localStorage->m_RegAssembly->AddPart(localStorage->m_StartGridActor);
}
}
else if (isGlyphActive)
{
localStorage->m_RegAssembly->AddPart(localStorage->m_DeformedGridActor);
}
}
localStorage->m_LastUpdateTime.Modified();
}
}
void mitk::MITKRegistrationWrapperMapperBase::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite)
{
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
vtkProp* mitk::MITKRegistrationWrapperMapperBase::GetVtkProp(mitk::BaseRenderer *renderer)
{
return m_LSH.GetLocalStorage(renderer)->m_RegAssembly;
}
mitk::MITKRegistrationWrapperMapperBase::RegWrapperLocalStorage::RegWrapperLocalStorage()
{
m_DeformedGridActor = vtkSmartPointer<vtkActor>::New();
m_DeformedGridMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_DeformedGridActor->SetMapper(m_DeformedGridMapper);
m_StartGridActor = vtkSmartPointer<vtkActor>::New();
m_StartGridMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_StartGridActor->SetMapper(m_StartGridMapper);
m_RegAssembly = vtkSmartPointer<vtkPropAssembly>::New();
m_LUT = vtkSmartPointer<vtkColorTransferFunction>::New();
m_DeformedGridData = nullptr;
m_StartGridData = nullptr;
}
diff --git a/Modules/MatchPointRegistration/src/mitkMAPRegistrationWrapper.cpp b/Modules/MatchPointRegistration/src/mitkMAPRegistrationWrapper.cpp
index b029d62d78..58cccbbdd7 100644
--- a/Modules/MatchPointRegistration/src/mitkMAPRegistrationWrapper.cpp
+++ b/Modules/MatchPointRegistration/src/mitkMAPRegistrationWrapper.cpp
@@ -1,169 +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.
============================================================================*/
#include "mitkMAPRegistrationWrapper.h"
#include <mapExceptionObjectMacros.h>
#include <mapRegistrationManipulator.h>
mitk::MAPRegistrationWrapper::MAPRegistrationWrapper(map::core::RegistrationBase* registration) : m_spRegistration(registration)
{
if (registration == nullptr)
{
mitkThrow() << "Error. Cannot create MAPRegistrationWrapper with invalid registration instance (nullptr).";
}
Identifiable::SetUID(registration->getRegistrationUID());
}
mitk::MAPRegistrationWrapper::~MAPRegistrationWrapper()
{
}
void mitk::MAPRegistrationWrapper::SetRequestedRegionToLargestPossibleRegion()
{
//nothing to do
}
bool mitk::MAPRegistrationWrapper::RequestedRegionIsOutsideOfTheBufferedRegion()
{
return false;
}
bool mitk::MAPRegistrationWrapper::VerifyRequestedRegion()
{
return true;
}
bool mitk::MAPRegistrationWrapper::IsEmptyTimeStep(unsigned int /*t*/) const
{
return m_spRegistration.IsNull();
}
bool mitk::MAPRegistrationWrapper::IsEmpty() const
{
return m_spRegistration.IsNull();
}
void mitk::MAPRegistrationWrapper::SetRequestedRegion(const itk::DataObject*)
{
//nothing to do
}
unsigned int mitk::MAPRegistrationWrapper::GetMovingDimensions() const
{
if (m_spRegistration.IsNull())
{
mitkThrow()<< "Error. Cannot return moving dimension. Wrapper points to invalid registration (nullptr).";
}
return m_spRegistration->getMovingDimensions();
}
unsigned int mitk::MAPRegistrationWrapper::GetTargetDimensions() const
{
if (m_spRegistration.IsNull())
{
mitkThrow()<< "Error. Cannot return target dimension. Wrapper points to invalid registration (nullptr).";
}
return m_spRegistration->getTargetDimensions();
}
const mitk::MAPRegistrationWrapper::TagMapType& mitk::MAPRegistrationWrapper::GetTags() const
{
if (m_spRegistration.IsNull())
{
mitkThrow()<< "Error. Cannot return registration tags. Wrapper points to invalid registration (nullptr).";
}
return m_spRegistration->getTags();
}
bool mitk::MAPRegistrationWrapper::GetTagValue(const TagType & tag, ValueType & value) const
{
if (m_spRegistration.IsNull())
{
mitkThrow()<< "Error. Cannot return registration tag value. Wrapper points to invalid registration (nullptr). Tag: " << tag;
}
return m_spRegistration->getTagValue(tag,value);
}
bool mitk::MAPRegistrationWrapper::HasLimitedTargetRepresentation() const
{
if (m_spRegistration.IsNull())
{
- mitkThrow()<< "Error. Cannot determin HasLimitedTargetRepresentation(). Wrapper points to invalid registration (nullptr).";
+ mitkThrow()<< "Error. Cannot determine HasLimitedTargetRepresentation(). Wrapper points to invalid registration (nullptr).";
}
return m_spRegistration->hasLimitedTargetRepresentation();
}
bool mitk::MAPRegistrationWrapper::HasLimitedMovingRepresentation() const
{
if (m_spRegistration.IsNull())
{
- mitkThrow()<< "Error. Cannot determin HasLimitedMovingRepresentation(). Wrapper points to invalid registration (nullptr).";
+ mitkThrow()<< "Error. Cannot determine HasLimitedMovingRepresentation(). Wrapper points to invalid registration (nullptr).";
}
return m_spRegistration->hasLimitedMovingRepresentation();
}
map::core::RegistrationBase* mitk::MAPRegistrationWrapper::GetRegistration()
{
return m_spRegistration;
}
const map::core::RegistrationBase* mitk::MAPRegistrationWrapper::GetRegistration() const
{
return m_spRegistration;
}
void mitk::MAPRegistrationWrapper::PrintSelf (std::ostream &os, itk::Indent indent) const
{
Superclass::PrintSelf(os,indent);
if (m_spRegistration.IsNull())
{
os<< "Error. Wrapper points to invalid registration (nullptr).";
}
else
{
os<<std::endl<<indent<<"MatchPoint registration instance:";
m_spRegistration->Print(os,indent.GetNextIndent());
typedef map::core::Registration<3,3> CastedRegType;
const CastedRegType* pCastedReg = dynamic_cast<const CastedRegType*>(m_spRegistration.GetPointer());
os<<std::endl<<indent<<"MatchPoint registration direct kernel instance:";
pCastedReg->getDirectMapping().Print(os,indent.GetNextIndent());
os<<std::endl<<indent<<"MatchPoint registration inverse kernel instance:";
pCastedReg->getInverseMapping().Print(os,indent.GetNextIndent());
}
}
void mitk::MAPRegistrationWrapper::SetUID(const UIDType& uid)
{
if (m_spRegistration.IsNull())
{
mitkThrow() << "Error. Cannot set UID. Wrapper points to invalid registration (nullptr).";
}
Identifiable::SetUID(uid);
::map::core::RegistrationBaseManipulator manip(m_spRegistration);
manip.getTagValues()[::map::tags::RegistrationUID] = uid;
};
mitk::Identifiable::UIDType mitk::MAPRegistrationWrapper::GetUID() const
{
if (m_spRegistration.IsNull())
{
mitkThrow() << "Error. Cannot return UID. Wrapper points to invalid registration (nullptr).";
}
return m_spRegistration->getRegistrationUID();
};
diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.cpp b/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.cpp
index 1606973326..07b79afc3c 100644
--- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.cpp
+++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.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 "QmitkFramesRegistrationJob.h"
// Mitk
#include <mitkImageAccessByItk.h>
// Qt
#include <QThreadPool>
// Map4CTK
#include <mitkImageMappingHelper.h>
#include <mitkMAPRegistrationWrapper.h>
#include <mitkMatchPointPropertyTags.h>
#include <mitkUIDHelper.h>
// MatchPoint
#include <mapAlgorithmEvents.h>
#include <mapAlgorithmWrapperEvent.h>
#include <mapExceptionObjectMacros.h>
#include <mapImageRegistrationAlgorithmInterface.h>
#include <mapRegistrationAlgorithmInterface.h>
const mitk::Image *QmitkFramesRegistrationJob::GetTargetDataAsImage() const
{
return dynamic_cast<const mitk::Image *>(m_spTargetData.GetPointer());
};
const map::algorithm::RegistrationAlgorithmBase *QmitkFramesRegistrationJob::GetLoadedAlgorithm() const
{
return m_spLoadedAlgorithm;
};
void QmitkFramesRegistrationJob::OnMapAlgorithmEvent(::itk::Object *, const itk::EventObject &event)
{
const map::events::AlgorithmEvent *pAlgEvent = dynamic_cast<const map::events::AlgorithmEvent *>(&event);
const map::events::AlgorithmIterationEvent *pIterationEvent =
dynamic_cast<const map::events::AlgorithmIterationEvent *>(&event);
const map::events::AlgorithmWrapperEvent *pWrapEvent =
dynamic_cast<const map::events::AlgorithmWrapperEvent *>(&event);
const map::events::AlgorithmResolutionLevelEvent *pLevelEvent =
dynamic_cast<const map::events::AlgorithmResolutionLevelEvent *>(&event);
const map::events::InitializingAlgorithmEvent *pInitEvent =
dynamic_cast<const map::events::InitializingAlgorithmEvent *>(&event);
const map::events::StartingAlgorithmEvent *pStartEvent =
dynamic_cast<const map::events::StartingAlgorithmEvent *>(&event);
const map::events::StoppingAlgorithmEvent *pStoppingEvent =
dynamic_cast<const map::events::StoppingAlgorithmEvent *>(&event);
const map::events::StoppedAlgorithmEvent *pStoppedEvent =
dynamic_cast<const map::events::StoppedAlgorithmEvent *>(&event);
const map::events::FinalizingAlgorithmEvent *pFinalizingEvent =
dynamic_cast<const map::events::FinalizingAlgorithmEvent *>(&event);
const map::events::FinalizedAlgorithmEvent *pFinalizedEvent =
dynamic_cast<const map::events::FinalizedAlgorithmEvent *>(&event);
const itk::ProgressEvent *pProgressEvent = dynamic_cast<const itk::ProgressEvent *>(&event);
const mitk::FrameRegistrationEvent *pFrameRegEvent = dynamic_cast<const mitk::FrameRegistrationEvent *>(&event);
const mitk::FrameMappingEvent *pFrameMapEvent = dynamic_cast<const mitk::FrameMappingEvent *>(&event);
if (pProgressEvent)
{
emit FrameProcessed(m_helper->GetProgress());
}
else if (pFrameRegEvent)
{
emit FrameRegistered(m_helper->GetProgress());
}
else if (pFrameMapEvent)
{
emit FrameMapped(m_helper->GetProgress());
}
else if (pInitEvent)
{
emit AlgorithmStatusChanged(QString("Initializing algorithm ..."));
}
else if (pStartEvent)
{
emit AlgorithmStatusChanged(QString("Starting algorithm ..."));
}
else if (pStoppingEvent)
{
emit AlgorithmStatusChanged(QString("Stopping algorithm ..."));
}
else if (pStoppedEvent)
{
emit AlgorithmStatusChanged(QString("Stopped algorithm ..."));
if (!pStoppedEvent->getComment().empty())
{
emit AlgorithmInfo(QString("Stopping condition: ") + QString::fromStdString(pStoppedEvent->getComment()));
}
}
else if (pFinalizingEvent)
{
emit AlgorithmStatusChanged(QString("Finalizing algorithm and results ..."));
}
else if (pFinalizedEvent)
{
emit AlgorithmStatusChanged(QString("Finalized algorithm ..."));
}
else if (pIterationEvent)
{
const IIterativeAlgorithm *pIterative =
dynamic_cast<const IIterativeAlgorithm *>(this->m_spLoadedAlgorithm.GetPointer());
map::algorithm::facet::IterativeAlgorithmInterface::IterationCountType count = 0;
bool hasCount = false;
if (pIterative && pIterative->hasIterationCount())
{
hasCount = true;
count = pIterative->getCurrentIteration();
}
emit AlgorithmIterated(QString::fromStdString(pIterationEvent->getComment()), hasCount, count);
}
else if (pLevelEvent)
{
const IMultiResAlgorithm *pResAlg =
dynamic_cast<const IMultiResAlgorithm *>(this->m_spLoadedAlgorithm.GetPointer());
map::algorithm::facet::MultiResRegistrationAlgorithmInterface::ResolutionLevelCountType count = 0;
bool hasCount = false;
QString info = QString::fromStdString(pLevelEvent->getComment());
if (pResAlg && pResAlg->hasLevelCount())
{
count = pResAlg->getCurrentLevel() + 1;
hasCount = true;
info = QString("Level #") + QString::number(pResAlg->getCurrentLevel() + 1) + QString(" ") + info;
}
emit LevelChanged(info, hasCount, count);
}
else if (pAlgEvent && !pWrapEvent)
{
emit AlgorithmInfo(QString::fromStdString(pAlgEvent->getComment()));
}
}
QmitkFramesRegistrationJob::QmitkFramesRegistrationJob(map::algorithm::RegistrationAlgorithmBase *pAlgorithm)
: m_TargetDataUID("Missing target UID"), m_spLoadedAlgorithm(pAlgorithm)
{
m_MappedName = "Unnamed RegJob";
m_spTargetMask = nullptr;
m_spCommand = ::itk::MemberCommand<QmitkFramesRegistrationJob>::New();
m_spCommand->SetCallbackFunction(this, &QmitkFramesRegistrationJob::OnMapAlgorithmEvent);
m_ObserverID = m_spLoadedAlgorithm->AddObserver(::map::events::AlgorithmEvent(), m_spCommand);
};
QmitkFramesRegistrationJob::~QmitkFramesRegistrationJob()
{
m_spLoadedAlgorithm->RemoveObserver(m_ObserverID);
};
void QmitkFramesRegistrationJob::run()
{
try
{
m_helper = mitk::TimeFramesRegistrationHelper::New();
m_helper->Set4DImage(this->GetTargetDataAsImage());
m_helper->SetTargetMask(this->m_spTargetMask);
m_helper->SetAlgorithm(this->m_spLoadedAlgorithm);
m_helper->SetIgnoreList(this->m_IgnoreList);
m_helper->SetAllowUndefPixels(this->m_allowUndefPixels);
m_helper->SetAllowUnregPixels(this->m_allowUnregPixels);
m_helper->SetErrorValue(this->m_errorValue);
m_helper->SetPaddingValue(this->m_paddingValue);
m_helper->SetInterpolatorType(this->m_InterpolatorType);
m_helper->AddObserver(::map::events::AnyMatchPointEvent(), m_spCommand);
m_helper->AddObserver(::itk::ProgressEvent(), m_spCommand);
// perform registration
m_spMappedImageNode = m_helper->GetRegisteredImage();
// wrap the registration in a data node
if (m_spMappedImageNode.IsNull())
{
emit Error(QString("Error. No registration was determined. No results to store."));
}
else
{
emit ResultIsAvailable(m_spMappedImageNode, this);
}
}
catch (::std::exception &e)
{
emit Error(QString("Error while registering data. Details: ") + QString::fromLatin1(e.what()));
}
catch (...)
{
- emit Error(QString("Unkown error when registering data."));
+ emit Error(QString("Unknown error when registering data."));
}
emit Finished();
};
diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.h b/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.h
index 14f486b92f..4ddb75cdd3 100644
--- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.h
+++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkFramesRegistrationJob.h
@@ -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.
============================================================================*/
#ifndef QmitkFramesRegistrationJob_h
#define QmitkFramesRegistrationJob_h
// QT
#include <QObject>
#include <QRunnable>
// ITK
#include <itkCommand.h>
// MITK
#include <QmitkMappingJob.h>
#include <mitkDataNode.h>
#include <mitkImage.h>
// MatchPoint
#include <mapDeploymentDLLInfo.h>
#include <mapIterativeAlgorithmInterface.h>
#include <mapMultiResRegistrationAlgorithmInterface.h>
#include <mapRegistrationAlgorithmBase.h>
#include <mapRegistrationBase.h>
// Map4CTK
#include "mitkUIDHelper.h"
#include <mitkTimeFramesRegistrationHelper.h>
#include <MitkMatchPointRegistrationUIExports.h>
-/** Simple helper job class that could be used to process a frame registration in a paralell thread.
+/** Simple helper job class that could be used to process a frame registration in a parallel thread.
* This is e.g. used be plugins to keep the GUI responsive while doing a frame registration*/
class MITKMATCHPOINTREGISTRATIONUI_EXPORT QmitkFramesRegistrationJob : public QObject,
public QRunnable,
public QmitkMappingJobSettings
{
// this is needed for all Qt objects that should have a Qt meta-object
// (everything that derives from QObject and wants to have signal/slots)
Q_OBJECT
public:
QmitkFramesRegistrationJob(map::algorithm::RegistrationAlgorithmBase *pAlgorithm);
~QmitkFramesRegistrationJob() override;
void run() override;
signals:
void Finished();
void Error(QString err);
void ResultIsAvailable(mitk::Image::Pointer spResult, const QmitkFramesRegistrationJob *pJob);
void AlgorithmIterated(QString info, bool hasIterationCount, unsigned long currentIteration);
void LevelChanged(QString info, bool hasLevelCount, unsigned long currentLevel);
void AlgorithmStatusChanged(QString info);
void AlgorithmInfo(QString info);
void FrameProcessed(double progress);
void FrameRegistered(double progress);
void FrameMapped(double progress);
public:
// Inputs
mitk::BaseData::ConstPointer m_spTargetData;
mitk::Image::ConstPointer m_spTargetMask;
// job settings
mitk::TimeFramesRegistrationHelper::IgnoreListType m_IgnoreList;
mitk::NodeUIDType m_TargetDataUID;
mitk::NodeUIDType m_TargetMaskDataUID;
const map::algorithm::RegistrationAlgorithmBase *GetLoadedAlgorithm() const;
private:
typedef map::algorithm::facet::IterativeAlgorithmInterface IIterativeAlgorithm;
typedef map::algorithm::facet::MultiResRegistrationAlgorithmInterface IMultiResAlgorithm;
mitk::Image::Pointer m_spMappedImageNode;
::itk::MemberCommand<QmitkFramesRegistrationJob>::Pointer m_spCommand;
unsigned long m_ObserverID;
map::algorithm::RegistrationAlgorithmBase::Pointer m_spLoadedAlgorithm;
mitk::TimeFramesRegistrationHelper::Pointer m_helper;
// Helper functions
const mitk::Image *GetTargetDataAsImage() const;
void OnMapAlgorithmEvent(::itk::Object *, const itk::EventObject &event);
};
#endif
diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkMAPAlgorithmModel.h b/Modules/MatchPointRegistrationUI/Qmitk/QmitkMAPAlgorithmModel.h
index 216bdba330..e5f81596bf 100644
--- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkMAPAlgorithmModel.h
+++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkMAPAlgorithmModel.h
@@ -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.
============================================================================*/
#ifndef QmitkMAPAlgorithmModel_h
#define QmitkMAPAlgorithmModel_h
#include <QAbstractTableModel>
#include <QStringList>
// MITK
#include "MitkMatchPointRegistrationUIExports.h"
// MatchPoint
#include <mapMetaPropertyAlgorithmInterface.h>
#include <mapRegistrationAlgorithmBase.h>
/*!
\class QmitkMAPAlgorithmModel
Helper class that implements a model to handle the MetaProperty interface of a MatchPoint algorithm
- in contect of the QT view-model-concept. A algorithm can be set as data source for the model.
+ in context of the QT view-model-concept. A algorithm can be set as data source for the model.
The model retrieves all information through the MetaPropertyInterface. Changes in the view will
be propagated by the model into the algorithm.
\remarks The model only keep a simple pointer to the MetaPropertyInterface of the algorithm.
You have to ensure to reset the algorithm if the pointer goes invalid.
\warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation.
*/
class MITKMATCHPOINTREGISTRATIONUI_EXPORT QmitkMAPAlgorithmModel : public QAbstractTableModel
{
Q_OBJECT
public:
QmitkMAPAlgorithmModel(QObject *parent = nullptr);
~QmitkMAPAlgorithmModel() override{};
void SetAlgorithm(map::algorithm::RegistrationAlgorithmBase *pAlgorithm);
void SetAlgorithm(map::algorithm::facet::MetaPropertyAlgorithmInterface *pMetaInterface);
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;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
private:
void UpdateMetaProperties() const;
/** Method uses m_pMetaInterface to retrieve the MetaProperty and unwraps it into an
* suitable QVariant depending on the passed QT role. If the MetaProperty type is not supported, the QVariant is
* invalid.
*/
QVariant GetPropertyValue(const map::algorithm::MetaPropertyInfo *pInfo, int role) const;
template <typename TValueType>
bool CheckCastAndSetProp(const map::algorithm::MetaPropertyInfo *pInfo, const QVariant &value);
bool SetPropertyValue(const map::algorithm::MetaPropertyInfo *pInfo, const QVariant &value);
map::algorithm::facet::MetaPropertyAlgorithmInterface *m_pMetaInterface;
mutable map::algorithm::facet::MetaPropertyAlgorithmInterface::MetaPropertyVectorType m_MetaProperties;
};
#endif
diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkMapperSettingsWidget.ui b/Modules/MatchPointRegistrationUI/Qmitk/QmitkMapperSettingsWidget.ui
index aa2465cde2..27efbd150a 100644
--- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkMapperSettingsWidget.ui
+++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkMapperSettingsWidget.ui
@@ -1,304 +1,304 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmitkMapperSettingsWidget</class>
<widget class="QWidget" name="QmitkMapperSettingsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="m_groupAllowUndefPixels">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Allows that pixels may not be defined in the mapped image because they are outside of the field of view of the used input image.&lt;/p&gt;&lt;p&gt;The pixels will be marked with the given padding value.&lt;/p&gt;&lt;p&gt;If unchecked the mapping will be aborted in a case of undefined pixels.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="title">
<string>Allow undefined pixels</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Padding value:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="m_sbPaddingValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Pixel value that indicates pixels that are outside of the input image</string>
</property>
<property name="minimum">
<number>-5000</number>
</property>
<property name="maximum">
<number>5000</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="m_groupAllowUnregPixels">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Allows that pixels may not be registred because they are outside of the field of view of the used registration. The location in the correlated input image pixel(s) are therefore unkown. The pixels will be marked witrh the given error value.&lt;/p&gt;&lt;p&gt;If unchecked the mapping will be aborted in a case of unregistered pixels.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Allows that pixels may not be registered because they are outside of the field of view of the used registration. The location in the correlated input image pixel(s) are therefore unknown. The pixels will be marked with the given error value.&lt;/p&gt;&lt;p&gt;If unchecked the mapping will be aborted in a case of unregistered pixels.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="title">
- <string>Allow unregistred pixels</string>
+ <string>Allow unregistered pixels</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="spacing">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Error value:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="m_sbErrorValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Value of pixels that cannot be registered because of an unsufficient field of view of the selected registration instance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>-5000</number>
</property>
<property name="maximum">
<number>5000</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Interpolator:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="m_comboInterpolator">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Interpolation function that should be used to map the pixel values from the input image into the result image.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>Nearest Neighbor</string>
</property>
</item>
<item>
<property name="text">
<string>Linear</string>
</property>
</item>
<item>
<property name="text">
<string>BSpline (3rd order)</string>
</property>
</item>
<item>
<property name="text">
<string>Windowed Sinc (Hamming)</string>
</property>
</item>
<item>
<property name="text">
<string>Windowed Sinc (Welch)</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QGroupBox" name="m_groupActivateSampling">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Activate super/sub sampling</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>5</number>
</property>
<item>
<widget class="QCheckBox" name="m_cbLinkFactors">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Check to ensure that x, y and z dimension use the same sampling factor.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>linked factors</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="label_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>x:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbXFactor">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indicate the sampling factor to change the resolution.&lt;/p&gt;&lt;p&gt;2.0: doubled resolution; e.g. 100 pixels -&amp;gt; 200 pixels and spacing 1 -&amp;gt; spacing 0.5&lt;/p&gt;&lt;p&gt;0.5: half resolution; e.g. 100 pixels -&amp;gt; 50 pixels and spacing 1 -&amp;gt; spacing 2&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>y:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbYFactor"/>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>z:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbZFactor"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkMappingJob.cpp b/Modules/MatchPointRegistrationUI/Qmitk/QmitkMappingJob.cpp
index a79e39b1fc..a5d2c1c523 100644
--- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkMappingJob.cpp
+++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkMappingJob.cpp
@@ -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.
============================================================================*/
#include "QmitkMappingJob.h"
// Mitk
#include <mitkImageAccessByItk.h>
#include <mitkMAPRegistrationWrapper.h>
#include <mitkMatchPointPropertyTags.h>
#include <mitkPointSetMappingHelper.h>
#include <mitkProperties.h>
// Qt
#include <QThreadPool>
#include <mapEvents.h>
QmitkMappingJobSettings::QmitkMappingJobSettings()
{
m_doGeometryRefinement = false;
m_MappedName = "";
m_allowUndefPixels = true;
m_paddingValue = 0;
m_allowUnregPixels = true;
m_errorValue = 0;
m_InterpolatorType = mitk::ImageMappingInterpolator::Linear;
};
const mitk::Image *QmitkMappingJob::GetInputDataAsImage() const
{
return dynamic_cast<const mitk::Image *>(m_spInputData.GetPointer());
};
const mitk::PointSet *QmitkMappingJob::GetInputDataAsPointSet() const
{
return dynamic_cast<const mitk::PointSet *>(m_spInputData.GetPointer());
};
const map::core::RegistrationBase *QmitkMappingJob::GetRegistration() const
{
const mitk::MAPRegistrationWrapper *wrapper =
dynamic_cast<const mitk::MAPRegistrationWrapper *>(m_spRegNode->GetData());
return dynamic_cast<const map::core::RegistrationBase *>(wrapper->GetRegistration());
};
void QmitkMappingJob::OnMapAlgorithmEvent(::itk::Object *, const itk::EventObject &event)
{
const map::events::AnyMatchPointEvent *pMAPEvent = dynamic_cast<const map::events::AnyMatchPointEvent *>(&event);
if (pMAPEvent)
{
emit AlgorithmInfo(QString::fromStdString(pMAPEvent->getComment()));
}
}
QmitkMappingJob::QmitkMappingJob()
{
m_spRefGeometry = nullptr;
m_spCommand = ::itk::MemberCommand<QmitkMappingJob>::New();
m_spCommand->SetCallbackFunction(this, &QmitkMappingJob::OnMapAlgorithmEvent);
};
QmitkMappingJob::~QmitkMappingJob(){};
void QmitkMappingJob::run()
{
const mitk::Image *inputImage = this->GetInputDataAsImage();
const mitk::PointSet *inputSet = this->GetInputDataAsPointSet();
m_spMappedData = nullptr;
if (m_doGeometryRefinement)
{
try
{
mitk::Image::Pointer spResultImage = nullptr;
if (inputImage)
{
spResultImage = mitk::ImageMappingHelper::refineGeometry(inputImage, this->GetRegistration(), true);
}
m_spMappedData = spResultImage;
if (spResultImage.IsNotNull())
{
emit MapResultIsAvailable(spResultImage.GetPointer(), this);
}
else
{
emit Error(QString("Error when when refining image geometry."));
}
}
catch (std::exception &e)
{
emit Error(QString("Error when refining image geometry. Error description: ") + QString::fromLatin1(e.what()));
}
catch (...)
{
emit Error(QString("Unknown error when refining image geometry."));
}
}
else
{
try
{
mitk::BaseData::Pointer spResultData = nullptr;
if (inputImage)
{
spResultData = mitk::ImageMappingHelper::map(this->GetInputDataAsImage(),
this->GetRegistration(),
!(this->m_allowUndefPixels),
this->m_paddingValue,
this->m_spRefGeometry,
!(this->m_allowUnregPixels),
this->m_errorValue,
this->m_InterpolatorType)
.GetPointer();
}
else if (inputSet)
{
mitk::PointSet::PointDataType errorValue;
errorValue.id = -1;
errorValue.pointSpec = mitk::PTUNDEFINED;
errorValue.selected = false;
spResultData = mitk::PointSetMappingHelper::map(inputSet, this->GetRegistration(), -1, false, errorValue);
}
if (spResultData.IsNotNull())
{
emit MapResultIsAvailable(spResultData, this);
}
else
{
emit Error(QString("Error when mapping input data to result."));
}
m_spMappedData = spResultData;
}
catch (std::exception &e)
{
emit Error(QString("Error when mapping data. Error description: ") + QString::fromLatin1(e.what()));
}
catch (...)
{
- emit Error(QString("Unkown error when mapping data."));
+ emit Error(QString("Unknown error when mapping data."));
}
}
};
diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp b/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp
index 76197de9b9..6327b1912f 100644
--- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp
+++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp
@@ -1,200 +1,200 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkRegistrationJob.h"
// Mitk
#include <mitkMAPAlgorithmHelper.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageMappingHelper.h>
#include <mitkMAPRegistrationWrapper.h>
#include <mitkMaskedAlgorithmHelper.h>
#include <mitkMatchPointPropertyTags.h>
#include <mitkUIDHelper.h>
// Qt
#include <QThreadPool>
// MatchPoint
#include <mapAlgorithmEvents.h>
#include <mapAlgorithmWrapperEvent.h>
#include <mapExceptionObjectMacros.h>
#include <mapImageRegistrationAlgorithmInterface.h>
#include <mapRegistrationAlgorithmInterface.h>
const mitk::Image *QmitkRegistrationJob::GetTargetDataAsImage() const
{
return dynamic_cast<const mitk::Image *>(m_spTargetData.GetPointer());
}
const mitk::Image *QmitkRegistrationJob::GetMovingDataAsImage() const
{
return dynamic_cast<const mitk::Image *>(m_spMovingData.GetPointer());
}
const map::algorithm::RegistrationAlgorithmBase *QmitkRegistrationJob::GetLoadedAlgorithm() const
{
return m_spLoadedAlgorithm;
}
void QmitkRegistrationJob::OnMapAlgorithmEvent(::itk::Object *, const itk::EventObject &event)
{
const map::events::AlgorithmEvent *pAlgEvent = dynamic_cast<const map::events::AlgorithmEvent *>(&event);
const map::events::AlgorithmIterationEvent *pIterationEvent =
dynamic_cast<const map::events::AlgorithmIterationEvent *>(&event);
const map::events::AlgorithmWrapperEvent *pWrapEvent =
dynamic_cast<const map::events::AlgorithmWrapperEvent *>(&event);
const map::events::AlgorithmResolutionLevelEvent *pLevelEvent =
dynamic_cast<const map::events::AlgorithmResolutionLevelEvent *>(&event);
const map::events::InitializingAlgorithmEvent *pInitEvent =
dynamic_cast<const map::events::InitializingAlgorithmEvent *>(&event);
const map::events::StartingAlgorithmEvent *pStartEvent =
dynamic_cast<const map::events::StartingAlgorithmEvent *>(&event);
const map::events::StoppingAlgorithmEvent *pStoppingEvent =
dynamic_cast<const map::events::StoppingAlgorithmEvent *>(&event);
const map::events::StoppedAlgorithmEvent *pStoppedEvent =
dynamic_cast<const map::events::StoppedAlgorithmEvent *>(&event);
const map::events::FinalizingAlgorithmEvent *pFinalizingEvent =
dynamic_cast<const map::events::FinalizingAlgorithmEvent *>(&event);
const map::events::FinalizedAlgorithmEvent *pFinalizedEvent =
dynamic_cast<const map::events::FinalizedAlgorithmEvent *>(&event);
if (pInitEvent)
{
emit AlgorithmStatusChanged(QString("Initializing algorithm ..."));
}
else if (pStartEvent)
{
emit AlgorithmStatusChanged(QString("Starting algorithm ..."));
}
else if (pStoppingEvent)
{
emit AlgorithmStatusChanged(QString("Stopping algorithm ..."));
}
else if (pStoppedEvent)
{
emit AlgorithmStatusChanged(QString("Stopped algorithm ..."));
if (!pStoppedEvent->getComment().empty())
{
emit AlgorithmInfo(QString("Stopping condition: ") + QString::fromStdString(pStoppedEvent->getComment()));
}
}
else if (pFinalizingEvent)
{
emit AlgorithmStatusChanged(QString("Finalizing algorithm and results ..."));
}
else if (pFinalizedEvent)
{
emit AlgorithmStatusChanged(QString("Finalized algorithm ..."));
}
else if (pIterationEvent)
{
const IIterativeAlgorithm *pIterative =
dynamic_cast<const IIterativeAlgorithm *>(this->m_spLoadedAlgorithm.GetPointer());
map::algorithm::facet::IterativeAlgorithmInterface::IterationCountType count = 0;
bool hasCount = false;
if (pIterative && pIterative->hasIterationCount())
{
hasCount = true;
count = pIterative->getCurrentIteration();
}
emit AlgorithmIterated(QString::fromStdString(pIterationEvent->getComment()), hasCount, count);
}
else if (pLevelEvent)
{
const IMultiResAlgorithm *pResAlg =
dynamic_cast<const IMultiResAlgorithm *>(this->m_spLoadedAlgorithm.GetPointer());
map::algorithm::facet::MultiResRegistrationAlgorithmInterface::ResolutionLevelCountType count = 0;
bool hasCount = false;
QString info = QString::fromStdString(pLevelEvent->getComment());
if (pResAlg && pResAlg->hasLevelCount())
{
count = pResAlg->getCurrentLevel() + 1;
hasCount = true;
info = QString("Level #") + QString::number(pResAlg->getCurrentLevel() + 1) + QString(" ") + info;
}
emit LevelChanged(info, hasCount, count);
}
else if (pAlgEvent && !pWrapEvent)
{
emit AlgorithmInfo(QString::fromStdString(pAlgEvent->getComment()));
}
}
QmitkRegistrationJob::QmitkRegistrationJob(map::algorithm::RegistrationAlgorithmBase *pAlgorithm)
{
m_MapEntity = false;
m_StoreReg = false;
m_ErrorOccured = false;
m_spLoadedAlgorithm = pAlgorithm;
m_JobName = "Unnamed RegJob";
m_MovingDataUID = "Missing moving UID";
m_TargetDataUID = "Missing target UID";
m_spTargetMask = nullptr;
m_spMovingMask = nullptr;
m_spCommand = ::itk::MemberCommand<QmitkRegistrationJob>::New();
m_spCommand->SetCallbackFunction(this, &QmitkRegistrationJob::OnMapAlgorithmEvent);
m_ObserverID = m_spLoadedAlgorithm->AddObserver(::map::events::AlgorithmEvent(), m_spCommand);
}
QmitkRegistrationJob::~QmitkRegistrationJob()
{
m_spLoadedAlgorithm->RemoveObserver(m_ObserverID);
}
void QmitkRegistrationJob::run()
{
try
{
mitk::MAPAlgorithmHelper helper(m_spLoadedAlgorithm);
mitk::MaskedAlgorithmHelper maskedHelper(m_spLoadedAlgorithm);
//*@TODO Data Check and failure handle
helper.SetData(this->m_spMovingData, this->m_spTargetData);
maskedHelper.SetMasks(this->m_spMovingMask, this->m_spTargetMask);
// perform registration
m_spResultRegistration = helper.GetRegistration();
// wrap the registration in a data node
if (m_spResultRegistration.IsNull())
{
emit Error(QString("Error. No registration was determined. No results to store."));
}
else
{
auto spRegWrapper = mitk::MAPRegistrationWrapper::New(m_spResultRegistration);
emit RegResultIsAvailable(spRegWrapper, this);
}
}
catch (::std::exception &e)
{
emit Error(QString("Error while registering data. Details: ") + QString::fromLatin1(e.what()));
}
catch (...)
{
- emit Error(QString("Unkown error when registering data."));
+ emit Error(QString("Unknown error when registering data."));
}
emit Finished();
}
diff --git a/Modules/ModelFit/autoload/IO/mitkModelFitIOActivator.cpp b/Modules/ModelFit/autoload/IO/mitkModelFitIOActivator.cpp
index 66eafb2386..baf7f10834 100644
--- a/Modules/ModelFit/autoload/IO/mitkModelFitIOActivator.cpp
+++ b/Modules/ModelFit/autoload/IO/mitkModelFitIOActivator.cpp
@@ -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.
============================================================================*/
#include <usModuleActivator.h>
#include <usModuleContext.h>
#include <mitkCoreServices.h>
#include <mitkIPropertyDescriptions.h>
#include <mitkIPropertyPersistence.h>
#include <mitkModelFitConstants.h>
#include <mitkScalarListLookupTablePropertySerializer.h>
namespace mitk
{
/*
* This is the module activator for the IO aspects of the "ModelFit" module.
*/
class ModelFitIOActivator : public us::ModuleActivator
{
public:
void registerProperty(const std::string& name, const std::string& key, const std::string& description)
{
mitk::CoreServicePointer<mitk::IPropertyDescriptions> propDescService(mitk::CoreServices::GetPropertyDescriptions());
propDescService->AddDescription(name, description);
mitk::PropertyPersistenceInfo::Pointer ppi = mitk::PropertyPersistenceInfo::New();
ppi->SetNameAndKey(name, key);
mitk::CoreServicePointer<mitk::IPropertyPersistence> propPersistenceService(mitk::CoreServices::GetPropertyPersistence());
propPersistenceService->AddInfo(ppi, true);
}
void registerProperty(const std::string& name, const std::string& key, const std::string& description, const PropertyPersistenceInfo::DeserializationFunctionType &deFnc, const PropertyPersistenceInfo::SerializationFunctionType &serFnc)
{
mitk::CoreServicePointer<mitk::IPropertyDescriptions> propDescService(mitk::CoreServices::GetPropertyDescriptions());
propDescService->AddDescription(name, description);
mitk::PropertyPersistenceInfo::Pointer ppi = mitk::PropertyPersistenceInfo::New();
ppi->SetNameAndKey(name, key);
ppi->SetDeserializationFunction(deFnc);
ppi->SetSerializationFunction(serFnc);
mitk::CoreServicePointer<mitk::IPropertyPersistence> propPersistenceService(mitk::CoreServices::GetPropertyPersistence());
propPersistenceService->AddInfo(ppi, true);
}
void Load(us::ModuleContext* /*context*/) override
{
//register relevant properties
registerProperty(mitk::ModelFitConstants::INPUT_VARIABLES_PROPERTY_NAME(), "modelfit_input_variables", "Array of input variables used in/for a model fit.", PropertyPersistenceDeserialization::deserializeXMLToScalarListLookupTableProperty, PropertyPersistenceSerialization::serializeScalarListLookupTablePropertyToXML);
registerProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME(), "modelfit_parameter_name", "Name of the parameter, that is represented by the data and how it is used in the function string (modelfit.model.function).");
registerProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME(), "modelfit_parameter_unit", "Unit string of the Parameter. Is only used for display. Default value: \"\" (no unit)");
registerProperty(mitk::ModelFitConstants::PARAMETER_SCALE_PROPERTY_NAME(), "modelfit_parameter_scale", "Scaling of the parameter. Default value: 1.");
registerProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME(), "modelfit_parameter_type", "Type of the parameters. Default value: parameter. Other options: derived, criterion, evaluation.");
- registerProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME(), "modelfit_model_type", "Value specifies the type of model (helpfull for classification; e.g. MR perfusion)");
+ registerProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME(), "modelfit_model_type", "Value specifies the type of model (helpful for classification; e.g. MR perfusion)");
registerProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME(), "modelfit_model_name", "Name of the specific fit. Only used for display.");
registerProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME(), "modelfit_model_function", "Function string, that specifies the model and will be parsed, to plot the curves based on the parameter. Optional parameter that must not be set.");
registerProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME(), "modelfit_model_functionClass", "ID of the model class implementation.");
registerProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME(), "modelfit_model_x", "Name of the forumar parameter 'x', that is specified on the x axis. Only needed if model function string is set and formular must be parsed.");
registerProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME(), "modelfit_xaxis_name", "Display name of the x axis.");
registerProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME(), "modelfit_xaxis_unit", "Unit of the x axis.");
registerProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME(), "modelfit_yaxis_name", "Display name of the y axis.");
registerProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME(), "modelfit_yaxis_unit", "Unit of the y axis.");
registerProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME(), "modelfit_fit_uid", "UID of the fit.");
registerProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME(), "modelfit_fit_name", "Human readable name for the fit.");
registerProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME(), "modelfit_fit_type", "Type of the fit (e.g. ROI based or pixel based).");
registerProperty(mitk::ModelFitConstants::FIT_INPUT_ROIUID_PROPERTY_NAME(), "modelfit_fit_input_roiUID", "UID of the ROI used for the fit.");
registerProperty(mitk::ModelFitConstants::FIT_INPUT_DATA_PROPERTY_NAME(), "modelfit_fit_input_data", "Property containing input data directly stored in the fit information.");
registerProperty(mitk::ModelFitConstants::FIT_STATIC_PARAMETERS_PROPERTY_NAME(), "modelfit_fit_staticParameters", "Property containing static parameters used for the fit.", PropertyPersistenceDeserialization::deserializeXMLToScalarListLookupTableProperty, PropertyPersistenceSerialization::serializeScalarListLookupTablePropertyToXML);
//legacy properties for backwards compatibility
registerProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME(), "data_uid", "UID used to identify data in an MITK session.");
registerProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME(), "modelfit_fit_input_imageUID", "UID of the input image that used to fit the model.");
}
void Unload(us::ModuleContext* ) override
{
}
};
}
US_EXPORT_MODULE_ACTIVATOR(mitk::ModelFitIOActivator)
diff --git a/Modules/ModelFit/cmdapps/Fuse3Dto4DImageMiniApp.cpp b/Modules/ModelFit/cmdapps/Fuse3Dto4DImageMiniApp.cpp
index de03cf4055..03fe3bc2d1 100644
--- a/Modules/ModelFit/cmdapps/Fuse3Dto4DImageMiniApp.cpp
+++ b/Modules/ModelFit/cmdapps/Fuse3Dto4DImageMiniApp.cpp
@@ -1,167 +1,167 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// std includes
#include <string>
#include <numeric>
// itk includes
#include "itksys/SystemTools.hxx"
// CTK includes
#include "mitkCommandLineParser.h"
// MITK includes
#include <mitkIOUtil.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
#include <mitkTemporalJoinImagesFilter.h>
mitkCommandLineParser::StringContainerType inFilenames;
std::string outFileName;
std::vector<mitk::Image::Pointer> images;
std::vector<mitk::TimePointType> timebounds;
void setupParser(mitkCommandLineParser& parser)
{
// set general information about your MiniApp
parser.setCategory("Dynamic Data Analysis Tools");
parser.setTitle("Fuse 3D to 4D Image");
parser.setDescription("MiniApp that allows to fuse several 3D images (with same geometry) into a 3D+t (4D) image that can be processed as dynamic data.");
parser.setContributor("DKFZ MIC");
//! [create parser]
//! [add arguments]
// how should arguments be prefixed
parser.setArgumentPrefix("--", "-");
// add each argument, unless specified otherwise each argument is optional
// see mitkCommandLineParser::addArgument for more information
parser.beginGroup("Required I/O parameters");
parser.addArgument(
- "inputs", "i", mitkCommandLineParser::StringList, "Input files", "Pathes to the input images that should be fused", us::Any(), false, false, false, mitkCommandLineParser::Input);
+ "inputs", "i", mitkCommandLineParser::StringList, "Input files", "Paths to the input images that should be fused", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file path",
"Path to the fused 3D+t image.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument(
- "time", "t", mitkCommandLineParser::StringList, "Time bounds", "Defines the time geometry of the resulting dynamic image in [ms]. The first number is the start time point of the first time step. All other numbers are the max bound of a time step. So the structure is [minBound0 maxBound1 [maxBound2 [... maxBoundN]]]; e.g. \"2 3.5 10\" encodes a time geometry with two time steps and that starts at 2 ms and the second time step starts at 3.5 ms and ends at 10 ms. If not set e propertional time geometry with 1 ms duration will be generated!", us::Any(), true, false, false, mitkCommandLineParser::Input);
+ "time", "t", mitkCommandLineParser::StringList, "Time bounds", "Defines the time geometry of the resulting dynamic image in [ms]. The first number is the start time point of the first time step. All other numbers are the max bound of a time step. So the structure is [minBound0 maxBound1 [maxBound2 [... maxBoundN]]]; e.g. \"2 3.5 10\" encodes a time geometry with two time steps and that starts at 2 ms and the second time step starts at 3.5 ms and ends at 10 ms. If not set e proportional time geometry with 1 ms duration will be generated!", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
//! [add arguments]
}
bool configureApplicationSettings(std::map<std::string, us::Any> parsedArgs)
{
if (parsedArgs.size() == 0)
return false;
inFilenames = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["inputs"]);
outFileName = us::any_cast<std::string>(parsedArgs["output"]);
if (parsedArgs.count("time"))
{
auto timeBoundsStr = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["time"]);
for (const auto& timeBoundStr : timeBoundsStr)
{
std::istringstream stream;
stream.imbue(std::locale("C"));
stream.str(timeBoundStr);
mitk::TimePointType time = 0 ;
stream >> time;
timebounds.emplace_back(time);
}
}
return true;
}
int main(int argc, char* argv[])
{
mitkCommandLineParser parser;
setupParser(parser);
mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" });
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
if (!configureApplicationSettings(parsedArgs))
{
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
if (timebounds.empty())
{
timebounds.resize(inFilenames.size()+1);
std::iota(timebounds.begin(), timebounds.end(), 0.0);
}
else if (inFilenames.size() + 1 != timebounds.size())
{
std::cerr << "Cannot fuse images. Explicitly specified time bounds do not match. Use --help for more information on how to specify time bounds correctly.";
return EXIT_FAILURE;
};
//! [do processing]
try
{
std::cout << "Load images:" << std::endl;
auto filter = mitk::TemporalJoinImagesFilter::New();
unsigned int step = 0;
for (auto path : inFilenames)
{
std::cout << "Time step #"<<step<<" @ "<<timebounds[step]<< " ms: " << path << std::endl;
auto image = mitk::IOUtil::Load<mitk::Image>(path, &readerFilterFunctor);
images.push_back(image);
filter->SetInput(step, image);
++step;
}
filter->SetFirstMinTimeBound(timebounds[0]);
filter->SetMaxTimeBounds({ timebounds.begin() + 1, timebounds.end() });
std::cout << "Fuse the images ..." << std::endl;
filter->Update();
auto output = filter->GetOutput();
std::cout << "Save output image: " << outFileName << std::endl;
mitk::IOUtil::Save(output, outFileName);
std::cout << "Processing finished." << std::endl;
return EXIT_SUCCESS;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
}
diff --git a/Modules/ModelFit/cmdapps/GenericFittingMiniApp.cpp b/Modules/ModelFit/cmdapps/GenericFittingMiniApp.cpp
index 0e45f362ed..49709c4a9a 100644
--- a/Modules/ModelFit/cmdapps/GenericFittingMiniApp.cpp
+++ b/Modules/ModelFit/cmdapps/GenericFittingMiniApp.cpp
@@ -1,360 +1,360 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// std includes
#include <string>
// itk includes
#include "itksys/SystemTools.hxx"
// CTK includes
#include "mitkCommandLineParser.h"
// MITK includes
#include <mitkIOUtil.h>
#include <mitkPixelBasedParameterFitImageGenerator.h>
#include <mitkROIBasedParameterFitImageGenerator.h>
#include <mitkLinearModelParameterizer.h>
#include <mitkGenericParamModelParameterizer.h>
#include <mitkModelFitInfo.h>
#include <mitkMaskedDynamicImageStatisticsGenerator.h>
#include <mitkLevenbergMarquardtModelFitFunctor.h>
#include <mitkNormalizedSumOfSquaredDifferencesFitCostFunction.h>
#include <mitkExtractTimeGrid.h>
#include <mitkModelFitCmdAppsHelper.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
std::string inFilename;
std::string outFileName;
std::string maskFileName;
bool verbose(false);
bool roibased(false);
std::string functionName;
std::string formular;
mitk::Image::Pointer image;
mitk::Image::Pointer mask;
void onFitEvent(::itk::Object* caller, const itk::EventObject & event, void* /*data*/)
{
itk::ProgressEvent progressEvent;
if (progressEvent.CheckEvent(&event))
{
mitk::ParameterFitImageGeneratorBase* castedReporter = dynamic_cast<mitk::ParameterFitImageGeneratorBase*>(caller);
std::cout <<castedReporter->GetProgress()*100 << "% ";
}
}
void setupParser(mitkCommandLineParser& parser)
{
// set general information about your MiniApp
parser.setCategory("Dynamic Data Analysis Tools");
parser.setTitle("Generic Fitting");
parser.setDescription("MiniApp that allows to make a pixel based fitting on the intensity signal over time for a given model function.");
parser.setContributor("DKFZ MIC");
//! [create parser]
//! [add arguments]
// how should arguments be prefixed
parser.setArgumentPrefix("--", "-");
// add each argument, unless specified otherwise each argument is optional
// see mitkCommandLineParser::addArgument for more information
parser.beginGroup("Model parameters");
parser.addArgument(
"function", "f", mitkCommandLineParser::String, "Model function", "Function that should be used to fit the intensity signals. Options are: \"Linear\" or \"<Parameter Number>\" (for generic formulas).", us::Any(std::string("Linear")));
parser.addArgument(
"formular", "y", mitkCommandLineParser::String, "Generic model function formular", "Formular of a generic model (if selected) that will be parsed and fitted.", us::Any());
parser.endGroup();
parser.beginGroup("Required I/O parameters");
parser.addArgument(
"input", "i", mitkCommandLineParser::File, "Input file", "input 3D+t image file", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file template",
"where to save the output parameter images. The specified path will be used as template to determine the format (via extension) and the name \"root\". For each parameter a suffix will be added to the name.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument(
"mask", "m", mitkCommandLineParser::File, "Mask file", "Mask that defines the spatial image region that should be fitted. Must have the same geometry as the input image!", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument(
"verbose", "v", mitkCommandLineParser::Bool, "Verbose Output", "Whether to produce verbose output");
parser.addArgument(
- "roibased", "r", mitkCommandLineParser::Bool, "Roi based fitting", "Will compute a mean intesity signal over the ROI before fitting it. If this mode is used a mask must be specified.");
+ "roibased", "r", mitkCommandLineParser::Bool, "Roi based fitting", "Will compute a mean intensity signal over the ROI before fitting it. If this mode is used a mask must be specified.");
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
//! [add arguments]
}
bool configureApplicationSettings(std::map<std::string, us::Any> parsedArgs)
{
if (parsedArgs.size() == 0)
return false;
// parse, cast and set required arguments
functionName = "Linear";
if (parsedArgs.count("function"))
{
functionName = us::any_cast<std::string>(parsedArgs["function"]);
}
if (parsedArgs.count("formular"))
{
formular = us::any_cast<std::string>(parsedArgs["formular"]);
}
inFilename = us::any_cast<std::string>(parsedArgs["input"]);
outFileName = us::any_cast<std::string>(parsedArgs["output"]);
verbose = false;
if (parsedArgs.count("verbose"))
{
verbose = us::any_cast<bool>(parsedArgs["verbose"]);
}
roibased = false;
if (parsedArgs.count("roibased"))
{
roibased = us::any_cast<bool>(parsedArgs["roibased"]);
}
if (parsedArgs.count("mask"))
{
maskFileName = us::any_cast<std::string>(parsedArgs["mask"]);
}
return true;
}
void configureInitialParametersOfParameterizer(mitk::ModelParameterizerBase*
parameterizer)
{
mitk::GenericParamModelParameterizer* genericParameterizer =
dynamic_cast<mitk::GenericParamModelParameterizer*>(parameterizer);
if (genericParameterizer)
{
genericParameterizer->SetFunctionString(formular);
}
}
mitk::ModelFitFunctorBase::Pointer createDefaultFitFunctor(
const mitk::ModelParameterizerBase* parameterizer)
{
mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor =
mitk::LevenbergMarquardtModelFitFunctor::New();
mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::Pointer chi2 =
mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::New();
fitFunctor->RegisterEvaluationParameter("Chi^2", chi2);
mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel();
::itk::LevenbergMarquardtOptimizer::ScalesType scales;
scales.SetSize(refModel->GetNumberOfParameters());
scales.Fill(1.0);
fitFunctor->SetScales(scales);
fitFunctor->SetDebugParameterMaps(true);
return fitFunctor.GetPointer();
}
template <typename TParameterizer>
void generateModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer&
/*modelFitInfo*/, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::PixelBasedParameterFitImageGenerator::New();
typename TParameterizer::Pointer modelParameterizer =
TParameterizer::New();
configureInitialParametersOfParameterizer(modelParameterizer);
//Specify fitting strategy and criterion parameters
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
fitGenerator->SetMask(mask);
fitGenerator->SetDynamicImage(image);
fitGenerator->SetFitFunctor(fitFunctor);
generator = fitGenerator.GetPointer();
}
template <typename TParameterizer>
void generateModelFit_ROIBased(
mitk::modelFit::ModelFitInfo::Pointer& /*modelFitInfo*/,
mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::ROIBasedParameterFitImageGenerator::New();
typename TParameterizer::Pointer modelParameterizer =
TParameterizer::New();
configureInitialParametersOfParameterizer(modelParameterizer);
//Compute ROI signal
mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator =
mitk::MaskedDynamicImageStatisticsGenerator::New();
signalGenerator->SetMask(mask);
signalGenerator->SetDynamicImage(image);
signalGenerator->Generate();
mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean();
//Specify fitting strategy and criterion parameters
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
fitGenerator->SetMask(mask);
fitGenerator->SetFitFunctor(fitFunctor);
fitGenerator->SetSignal(roiSignal);
fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image));
generator = fitGenerator.GetPointer();
}
void doFitting()
{
mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr;
mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr;
::itk::CStyleCommand::Pointer command = ::itk::CStyleCommand::New();
command->SetCallback(onFitEvent);
bool isLinearFactory = functionName == "Linear";
if (isLinearFactory)
{
std::cout << "Model: linear" << std::endl;
if (!roibased)
{
generateModelFit_PixelBased<mitk::LinearModelParameterizer>(fitSession, generator);
}
else
{
generateModelFit_ROIBased<mitk::LinearModelParameterizer>(fitSession, generator);
}
}
else
{
std::cout << "Model: generic (2 parameter)" << std::endl;
if (!roibased)
{
generateModelFit_PixelBased<mitk::GenericParamModelParameterizer>(fitSession, generator);
}
else
{
generateModelFit_ROIBased<mitk::GenericParamModelParameterizer>(fitSession, generator);
}
}
if (generator.IsNotNull() )
{
std::cout << "Started fitting process..." << std::endl;
generator->AddObserver(::itk::AnyEvent(), command);
generator->Generate();
std::cout << std::endl << "Finished fitting process" << std::endl;
mitk::storeModelFitGeneratorResults(outFileName, generator, fitSession);
}
else
{
- mitkThrow() << "Fitting error! Could not initalize fitting job.";
+ mitkThrow() << "Fitting error! Could not initialize fitting job.";
}
}
int main(int argc, char* argv[])
{
mitkCommandLineParser parser;
setupParser(parser);
mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" });
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
if (!configureApplicationSettings(parsedArgs))
{
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
//! [do processing]
try
{
image = mitk::IOUtil::Load<mitk::Image>(inFilename, &readerFilterFunctor);
std::cout << "Input: " << inFilename << std::endl;
if (!maskFileName.empty())
{
mask = mitk::IOUtil::Load<mitk::Image>(maskFileName, &readerFilterFunctor);
std::cout << "Mask: " << maskFileName << std::endl;
}
else
{
std::cout << "Mask: none" << std::endl;
}
if (roibased && mask.IsNull())
{
mitkThrow() << "Error. Cannot fit. Please specify mask if you select roi based fitting.";
}
std::cout << "Style: ";
if (roibased)
{
std::cout << "ROI based";
}
else
{
std::cout << "pixel based";
}
std::cout << std::endl;
doFitting();
std::cout << "Processing finished." << std::endl;
return EXIT_SUCCESS;
}
catch (const itk::ExceptionObject& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
}
diff --git a/Modules/ModelFit/cmdapps/PixelDumpMiniApp.cpp b/Modules/ModelFit/cmdapps/PixelDumpMiniApp.cpp
index d3323ed319..4535644ecc 100644
--- a/Modules/ModelFit/cmdapps/PixelDumpMiniApp.cpp
+++ b/Modules/ModelFit/cmdapps/PixelDumpMiniApp.cpp
@@ -1,447 +1,447 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// std includes
#include <string>
// itk includes
#include "itksys/SystemTools.hxx"
#include "itkImageRegionConstIteratorWithIndex.h"
#include "itkCastImageFilter.h"
#include "itkExtractImageFilter.h"
// CTK includes
#include "mitkCommandLineParser.h"
// MITK includes
#include <mitkIOUtil.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
#include <mitkImageTimeSelector.h>
#include "mitkImageAccessByItk.h"
#include "mitkImageCast.h"
mitkCommandLineParser::StringContainerType inFilenames;
std::string outFileName;
std::string maskFileName;
mitkCommandLineParser::StringContainerType captions;
using ImageVectorType = std::vector<mitk::Image::Pointer>;
ImageVectorType images;
mitk::Image::Pointer mask;
bool verbose(false);
typedef itk::Image<mitk::ScalarType, 3> InternalImageType;
typedef std::map<std::string, InternalImageType::Pointer> InternalImageMapType;
InternalImageMapType internalImages;
itk::ImageRegion<3> relevantRegion;
InternalImageType::PointType relevantOrigin;
InternalImageType::SpacingType relevantSpacing;
InternalImageType::DirectionType relevantDirection;
typedef itk::Index<3> DumpIndexType;
typedef std::vector<mitk::ScalarType> DumpedValuesType;
struct DumpIndexCompare
{
bool operator() (const DumpIndexType& lhs, const DumpIndexType& rhs) const
{
if (lhs[0] < rhs[0])
{
return true;
}
else if (lhs[0] > rhs[0])
{
return false;
}
if (lhs[1] < rhs[1])
{
return true;
}
else if (lhs[1] > rhs[1])
{
return false;
}
return lhs[2] < rhs[2];
}
};
typedef std::map<DumpIndexType, DumpedValuesType, DumpIndexCompare> DumpPixelMapType;
DumpPixelMapType dumpedPixels;
void setupParser(mitkCommandLineParser& parser)
{
// set general information about your MiniApp
parser.setCategory("Generic Analysis Tools");
parser.setTitle("Pixel Dumper");
parser.setDescription("MiniApp that allows to dump the pixel values of all passed files into a csv. The region of dumping can defined by a mask. All images (and mask) must have the same geometrie.");
parser.setContributor("DKFZ MIC");
//! [create parser]
//! [add arguments]
// how should arguments be prefixed
parser.setArgumentPrefix("--", "-");
// add each argument, unless specified otherwise each argument is optional
// see mitkCommandLineParser::addArgument for more information
parser.beginGroup("Required I/O parameters");
parser.addArgument(
"inputs", "i", mitkCommandLineParser::StringList, "Input files", "list of the images that should be dumped.", us::Any(), false);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file",
"where to save the csv.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument(
"mask", "m", mitkCommandLineParser::File, "Mask file", "Mask that defines the spatial image region that should be dumped. Must have the same geometry as the input images!", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument(
- "captions", "c", mitkCommandLineParser::StringList, "Captions of image columns", "If provided the pixel columns of the csv will be named according to the passed values instead of using the image pathes. Number of images and names must be equal.", us::Any(), false);
+ "captions", "c", mitkCommandLineParser::StringList, "Captions of image columns", "If provided the pixel columns of the csv will be named according to the passed values instead of using the image paths. Number of images and names must be equal.", us::Any(), false);
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
//! [add arguments]
}
bool configureApplicationSettings(std::map<std::string, us::Any> parsedArgs)
{
if (parsedArgs.size() == 0)
return false;
// parse, cast and set required arguments
inFilenames = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["inputs"]);
outFileName = us::any_cast<std::string>(parsedArgs["output"]);
if (parsedArgs.count("mask"))
{
maskFileName = us::any_cast<std::string>(parsedArgs["mask"]);
}
captions = inFilenames;
if (parsedArgs.count("captions"))
{
captions = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["captions"]);
}
return true;
}
template < typename TPixel, unsigned int VImageDimension >
void ExtractRelevantInformation(
const itk::Image< TPixel, VImageDimension > *image)
{
relevantRegion = image->GetLargestPossibleRegion();
relevantOrigin = image->GetOrigin();
relevantSpacing = image->GetSpacing();
relevantDirection = image->GetDirection();
}
template < typename TPixel, unsigned int VImageDimension >
void DoInternalImageConversion(
const itk::Image< TPixel, VImageDimension > *image,
InternalImageType::Pointer& internalImage)
{
typedef itk::Image< TPixel, VImageDimension > ImageType;
//check if image fit to geometry
// Make sure that spacing are the same
typename ImageType::SpacingType imageSpacing = image->GetSpacing();
typename ImageType::PointType zeroPoint; zeroPoint.Fill(0.0);
if ((zeroPoint + imageSpacing).SquaredEuclideanDistanceTo((zeroPoint + relevantSpacing)) >
1e-6) // for the dumper we are not as strict as mitk normally would be (mitk::eps)
{
mitkThrow() << "Images need to have same spacing! (Image spacing: " << imageSpacing
<< "; relevant spacing: " << relevantSpacing << ")";
}
// Make sure that orientation of mask and image are the same
typename ImageType::DirectionType imageDirection = image->GetDirection();
for (unsigned int i = 0; i < imageDirection.RowDimensions; ++i)
{
for (unsigned int j = 0; j < imageDirection.ColumnDimensions; ++j)
{
double differenceDirection = imageDirection[i][j] - relevantDirection[i][j];
if (fabs(differenceDirection) > 1e-6) // SD: 1e6 wird hier zum zweiten mal als Magic Number benutzt -> Konstante
{
// for the dumper we are not as strict as mitk normally would be (mitk::eps)
mitkThrow() << "Images need to have same direction! (Image direction: "
<< imageDirection << "; relevant direction: " << relevantDirection << ")";
}
}
}
// Make sure that origin of mask and image are the same
typename ImageType::PointType imageOrigin = image->GetOrigin();
if (imageOrigin.SquaredEuclideanDistanceTo(relevantOrigin) > 1e-6)
{
// for the dumper we are not as strict as mitk normally would be (mitk::eps)
mitkThrow() << "Image need to have same spacing! (Image spacing: "
<< imageSpacing << "; relevant spacing: " << relevantOrigin << ")";
}
typename ImageType::RegionType imageRegion = image->GetLargestPossibleRegion();
if (!imageRegion.IsInside(relevantRegion) && imageRegion != relevantRegion)
{
mitkThrow() << "Images need to have same region! (Image region: "
<< imageRegion << "; relevant region: " << relevantRegion << ")";
}
//convert to internal image
typedef itk::ExtractImageFilter<ImageType, ImageType> ExtractFilterType;
typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New();
typedef itk::CastImageFilter<ImageType, InternalImageType> CastFilterType;
typename CastFilterType::Pointer castFilter = CastFilterType::New();
extractFilter->SetInput(image);
extractFilter->SetExtractionRegion(relevantRegion);
castFilter->SetInput(extractFilter->GetOutput());
castFilter->Update();
internalImage = castFilter->GetOutput();
}
template < typename TPixel, unsigned int VImageDimension >
void DoMaskedCollecting(
const itk::Image< TPixel, VImageDimension > *image)
{
typedef itk::Image< TPixel, VImageDimension > ImageType;
itk::ImageRegionConstIteratorWithIndex<ImageType> it(image, relevantRegion);
it.GoToBegin();
while (!it.IsAtEnd())
{
if (mask.IsNull() || it.Get() > 0)
{
DumpedValuesType values;
const auto index = it.GetIndex();
for (auto& imagePos : internalImages)
{
double value = imagePos.second->GetPixel(index);
values.push_back(value);
}
dumpedPixels.insert(std::make_pair(index, values));
}
++it;
}
}
InternalImageMapType ConvertImageTimeSteps(mitk::Image* image)
{
InternalImageMapType map;
InternalImageType::Pointer internalImage;
for (unsigned int i = 0; i < image->GetTimeSteps(); ++i)
{
mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput(image);
imageTimeSelector->SetTimeNr(i);
imageTimeSelector->UpdateLargestPossibleRegion();
mitk::Image::Pointer imageTimePoint = imageTimeSelector->GetOutput();
AccessFixedDimensionByItk_1(imageTimePoint,
DoInternalImageConversion,
3,
internalImage);
std::stringstream stream;
stream << "[" << i << "]";
map.insert(std::make_pair(stream.str(), internalImage));
}
return map;
}
void doDumping()
{
if (mask.IsNotNull() && mask->GetTimeSteps() > 1)
{
std::cout <<
"Pixel Dumper: Selected mask has multiple timesteps. Only use first timestep to mask the pixel dumping." << std::endl;
mitk::ImageTimeSelector::Pointer maskTimeSelector = mitk::ImageTimeSelector::New();
maskTimeSelector->SetInput(mask);
maskTimeSelector->SetTimeNr(0);
maskTimeSelector->UpdateLargestPossibleRegion();
mask = maskTimeSelector->GetOutput();
}
try
{
if (mask.IsNotNull())
{ // if mask exist, we use the mask because it could be a sub region.
AccessFixedDimensionByItk(mask, ExtractRelevantInformation, 3);
}
else
{
AccessFixedDimensionByItk(images.front(), ExtractRelevantInformation, 3);
}
}
catch (const std::exception& e)
{
std::cerr << "Error extracting image geometry. Error text: " << e.what();
throw;
}
for (unsigned int index = 0; index < images.size(); ++index)
{
try
{
InternalImageMapType conversionMap = ConvertImageTimeSteps(images[index]);
if (conversionMap.size() == 1)
{
internalImages.insert(std::make_pair(captions[index], conversionMap.begin()->second));
}
else if (conversionMap.size() > 1)
{
for (auto& pos : conversionMap)
{
std::stringstream namestream;
namestream << captions[index] << " " << pos.first;
internalImages.insert(std::make_pair(namestream.str(), pos.second));
}
}
}
catch (const std::exception& e)
{
std::stringstream errorStr;
errorStr << "Inconsistent image \"" << captions[index] << "\" will be excluded from the collection. Error: " << e.what();
std::cerr << errorStr.str() << std::endl;
}
}
if (mask.IsNotNull())
{ // if mask exist, we use the mask because it could be a sub region.
AccessFixedDimensionByItk(mask, DoMaskedCollecting, 3);
}
else
{
AccessFixedDimensionByItk(images.front(), DoMaskedCollecting, 3);
}
}
void storeCSV()
{
std::ofstream resultfile;
resultfile.open(outFileName.c_str());
resultfile << "x,y,z";
for (auto aImage : internalImages)
{
resultfile << "," << aImage.first;
}
resultfile << std::endl;
for (auto dumpPos : dumpedPixels)
{
resultfile << dumpPos.first[0] << "," << dumpPos.first[1] << "," << dumpPos.first[2];
for(auto d : dumpPos.second)
{
resultfile << "," << d;
}
resultfile << std::endl;
}
}
int main(int argc, char* argv[])
{
mitkCommandLineParser parser;
setupParser(parser);
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" });
if (!configureApplicationSettings(parsedArgs))
{
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
if (!captions.empty() && inFilenames.size() != captions.size())
{
std::cerr << "Cannot dump images. Number of given captions does not equal number of given images.";
return EXIT_FAILURE;
};
//! [do processing]
try
{
std::cout << "Load images:" << std::endl;
for (auto path : inFilenames)
{
std::cout << "Input: " << path << std::endl;
auto image = mitk::IOUtil::Load<mitk::Image>(path, &readerFilterFunctor);
images.push_back(image);
}
if (!maskFileName.empty())
{
mask = mitk::IOUtil::Load<mitk::Image>(maskFileName, &readerFilterFunctor);
std::cout << "Mask: " << maskFileName << std::endl;
}
else
{
std::cout << "Mask: none" << std::endl;
}
doDumping();
storeCSV();
std::cout << "Processing finished." << std::endl;
return EXIT_SUCCESS;
}
catch (const itk::ExceptionObject& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
}
diff --git a/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp b/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp
index 6f2f9adb75..9177da0232 100644
--- a/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp
+++ b/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp
@@ -1,233 +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
+ // Free memory regularly
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/mitkExponentialSaturationModel.h b/Modules/ModelFit/include/mitkExponentialSaturationModel.h
index daba982745..76cb11ecec 100644
--- a/Modules/ModelFit/include/mitkExponentialSaturationModel.h
+++ b/Modules/ModelFit/include/mitkExponentialSaturationModel.h
@@ -1,132 +1,132 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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_EXPONENTIAL_SATURATION_MODEL_H_
#define __MITK_EXPONENTIAL_SATURATION_MODEL_H_
#include "mitkModelBase.h"
#include "MitkModelFitExports.h"
namespace mitk
{
/** @class ExponentialSaturationModel
- * @brief This genric model has the form: if x<onset: y(x) = baseline , else: y(x) = baseline + (y_final-baseline) * (1 - exp(-rate*(x-onset)))
+ * @brief This generic model has the form: if x<onset: y(x) = baseline , else: y(x) = baseline + (y_final-baseline) * (1 - exp(-rate*(x-onset)))
*/
class MITKMODELFIT_EXPORT ExponentialSaturationModel : public mitk::ModelBase
{
public:
typedef ExponentialSaturationModel Self;
typedef mitk::ModelBase Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
typedef Superclass::ParameterNameType ParameterNameType;
typedef Superclass::ParametersSizeType ParametersSizeType;
/** Method for creation through the object factory. */
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Run-time type information (and related methods). */
itkTypeMacro(ExponentialSaturationModel, ModelBase);
static const std::string NAME_PARAMETER_BAT;
static const std::string NAME_PARAMETER_y_bl;
static const std::string NAME_PARAMETER_y_fin;
static const std::string NAME_PARAMETER_k;
static const unsigned int NUMBER_OF_PARAMETERS;
static const std::string UNIT_PARAMETER_BAT;
static const std::string UNIT_PARAMETER_y_bl;
static const std::string UNIT_PARAMETER_y_fin;
static const std::string UNIT_PARAMETER_k;
static const unsigned int POSITION_PARAMETER_BAT;
static const unsigned int POSITION_PARAMETER_y_bl;
static const unsigned int POSITION_PARAMETER_y_fin;
static const unsigned int POSITION_PARAMETER_k;
static const unsigned int NUMBER_OF_STATIC_PARAMETERS;
static const std::string MODEL_DISPLAY_NAME;
static const std::string MODEL_TYPE;
static const std::string FUNCTION_STRING;
static const std::string X_NAME;
static const std::string X_AXIS_NAME;
static const std::string X_AXIS_UNIT;
static const std::string Y_AXIS_NAME;
static const std::string Y_AXIS_UNIT;
ParameterNamesType GetParameterNames() const override;
ParametersSizeType GetNumberOfParameters() const override;
ParamterUnitMapType GetParameterUnits() const override;
ParameterNamesType GetStaticParameterNames() const override;
ParametersSizeType GetNumberOfStaticParameters() const override;
std::string GetModelDisplayName() const override;
std::string GetModelType() const override;
FunctionStringType GetFunctionString() const override;
std::string GetXName() const override;
std::string GetXAxisName() const override;
std::string GetXAxisUnit() const override;
std::string GetYAxisName() const override;
std::string GetYAxisUnit() const override;
protected:
ExponentialSaturationModel() {};
~ExponentialSaturationModel() override {};
/**
* Actual implementation of the clone method. This method should be reimplemeted
* in subclasses to clone the extra required parameters.
*/
itk::LightObject::Pointer InternalClone() const override;
ModelResultType ComputeModelfunction(const ParametersType& parameters) const override;
void SetStaticParameter(const ParameterNameType& name,
const StaticParameterValuesType& values) override;
StaticParameterValuesType GetStaticParameterValue(const ParameterNameType& name) const override;
private:
//No copy constructor allowed
ExponentialSaturationModel(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkGaussianNoiseFunctor.h b/Modules/ModelFit/include/mitkGaussianNoiseFunctor.h
index 632d7529de..00a393e7ea 100644
--- a/Modules/ModelFit/include/mitkGaussianNoiseFunctor.h
+++ b/Modules/ModelFit/include/mitkGaussianNoiseFunctor.h
@@ -1,98 +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 mitkGaussianNoiseFunctor_h
#define mitkGaussianNoiseFunctor_h
#include <iostream>
#include <cstdlib>
namespace mitk
{
template <class TInputPixel, class TOutputPixel>
class GaussianNoiseFunctor
{
public:
GaussianNoiseFunctor(): m_Mu(0), m_Sigma(0) {};
~GaussianNoiseFunctor() {};
void SetMean(double mu)
{
this->m_Mu = mu;
}
void SetSigma(double sigma)
{
this->m_Sigma = sigma;
}
bool operator!=(const GaussianNoiseFunctor& other) const
{
return !(*this == other);
}
bool operator==(const GaussianNoiseFunctor& other) const
{
return (this->m_Mu == other.m_Mu) && (this->m_Sigma == other.m_Sigma) ;
}
inline TOutputPixel operator()(const TInputPixel& value) const
{
double n = noise(this->m_Mu, this->m_Sigma);
TOutputPixel result = value + n;
return result;
}
private:
double m_Mu, m_Sigma;
/** @todo #2 Better function?
* This function is meant to generate a random number from a normal distribution with the passed mean and standard deviation.
- * I found this code online, under c++11 there is supposed to be a std - libary for that: std::normal_distribution
+ * I found this code online, under c++11 there is supposed to be a std - library for that: std::normal_distribution
**/
inline double noise(double mu, double sigma) const
{
double u1, u2, W, mult;
static double x1, x2;
static int i = 0;
if (i == 1)
{
i = !i;
return (mu + sigma * (double)x2);
}
do
{
u1 = -1 + (static_cast <double>(rand()) / static_cast <double>(RAND_MAX)) * 2;
u2 = -1 + (static_cast <double>(rand()) / static_cast <double>(RAND_MAX)) * 2;
W = u1 * u1 + u2 * u2;
}
while (W >= 1 || W == 0);
mult = sqrt((-2 * log(W)) / W);
x1 = u1 * mult;
x2 = u2 * mult;
i = !i;
return (mu + sigma * (double)x1);
}
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkGenericParamModel.h b/Modules/ModelFit/include/mitkGenericParamModel.h
index 6803f8801b..140449727a 100644
--- a/Modules/ModelFit/include/mitkGenericParamModel.h
+++ b/Modules/ModelFit/include/mitkGenericParamModel.h
@@ -1,108 +1,108 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkGenericParamModel_h
#define mitkGenericParamModel_h
#include "mitkModelBase.h"
#include "MitkModelFitExports.h"
namespace mitk
{
/** Model that can parse a user specified function string and uses it as model function
that is represented by the model instance.
The parser used to interpret the string can handle simple mathematical formulas (e.g. "3.5 + a * x * sin(x) - 1 / 2").
The parser is able to recognize:
- sums, differences, products and divisions (a + b, 4 - 3, 2 * x, 9 / 3)
- algebraic signs ( +5, -5)
- exponentiation ( 2 ^ 4 )
- parentheses (3 * (4 + 2))
- following unary functions: abs, exp, sin, cos, tan, sind (sine in degrees), cosd (cosine in degrees), tand (tangent in degrees)
- variables (x, a, b, ... j)
Remark: The variable "x" is reserved. It is the signal position / timepoint.
Remark: The current version supports up to 10 model parameter.
Don't use it for a model parameter that should be deduced by fitting (these are a..j).*/
class MITKMODELFIT_EXPORT GenericParamModel : public mitk::ModelBase
{
public:
typedef GenericParamModel Self;
typedef mitk::ModelBase Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
typedef Superclass::ParameterNameType ParameterNameType;
typedef Superclass::ParametersSizeType ParametersSizeType;
/** Method for creation through the object factory. */
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** Run-time type information (and related methods). */
itkTypeMacro(GenericParamModel, ModelBase);
static const std::string NAME_STATIC_PARAMETER_number;
std::string GetModelDisplayName() const override;
std::string GetModelType() const override;
FunctionStringType GetFunctionString() const override;
itkSetStringMacro(FunctionString);
- /**@pre The Number of paremeters must be between 1 and 10.*/
+ /**@pre The Number of parameters must be between 1 and 10.*/
itkSetClampMacro(NumberOfParameters, ParametersSizeType, 1, 10);
std::string GetXName() const override;
ParameterNamesType GetParameterNames() const override;
ParametersSizeType GetNumberOfParameters() const override;
ParameterNamesType GetStaticParameterNames() const override;
ParametersSizeType GetNumberOfStaticParameters() const override;
protected:
GenericParamModel();
~GenericParamModel() override {};
/**
* Actual implementation of the clone method. This method should be reimplemeted
* in subclasses to clone the extra required parameters.
*/
itk::LightObject::Pointer InternalClone() const override;
ModelResultType ComputeModelfunction(const ParametersType& parameters) const override;
void SetStaticParameter(const ParameterNameType& name,
const StaticParameterValuesType& values) override;
StaticParameterValuesType GetStaticParameterValue(const ParameterNameType& name) const override;
private:
/**Function string that should be parsed when computing the model function.*/
FunctionStringType m_FunctionString;
/**Number of parameters the model should offer / the function string contains.*/
ParametersSizeType m_NumberOfParameters;
//No copy constructor allowed
GenericParamModel(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkGenericParamModelParameterizer.h b/Modules/ModelFit/include/mitkGenericParamModelParameterizer.h
index 6808374681..f27af4fb9f 100644
--- a/Modules/ModelFit/include/mitkGenericParamModelParameterizer.h
+++ b/Modules/ModelFit/include/mitkGenericParamModelParameterizer.h
@@ -1,79 +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 mitkGenericParamModelParameterizer_h
#define mitkGenericParamModelParameterizer_h
#include "mitkConcreteModelParameterizerBase.h"
#include "mitkGenericParamModel.h"
namespace mitk
{
/** Parameterizer for the GenricParamModel.
*/
class MITKMODELFIT_EXPORT GenericParamModelParameterizer : public ConcreteModelParameterizerBase
<GenericParamModel>
{
public:
typedef GenericParamModelParameterizer Self;
typedef ConcreteModelParameterizerBase<GenericParamModel> Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkTypeMacro(GenericParamModelParameterizer, ConcreteModelParameterizerBase);
itkFactorylessNewMacro(Self);
typedef typename Superclass::ModelBaseType ModelBaseType;
typedef typename Superclass::ModelBasePointer ModelBasePointer;
typedef typename Superclass::ModelType ModelType;
typedef typename Superclass::ModelPointer ModelPointer;
typedef typename Superclass::StaticParameterValueType StaticParameterValueType;
typedef typename Superclass::StaticParameterValuesType StaticParameterValuesType;
typedef typename Superclass::StaticParameterMapType StaticParameterMapType;
typedef typename Superclass::IndexType IndexType;
itkSetMacro(FunctionString, mitk::ModelBase::FunctionStringType);
- /**@pre The Number of paremeters must be between 1 and 10.*/
+ /**@pre The Number of parameters must be between 1 and 10.*/
itkSetClampMacro(NumberOfParameters, ParametersSizeType, 1, 10);
mitk::ModelBase::FunctionStringType GetFunctionString() const override;
using Superclass::GenerateParameterizedModel;
ModelBasePointer GenerateParameterizedModel(const IndexType& currentPosition) const override;
StaticParameterMapType GetGlobalStaticParameters() const override;
ParametersType GetDefaultInitialParameterization() const override;
protected:
GenericParamModelParameterizer();
~GenericParamModelParameterizer() override;
mitk::ModelBase::FunctionStringType m_FunctionString;
/**Number of parameters the model should offer / the function string contains.*/
ParametersSizeType m_NumberOfParameters;
private:
//No copy constructor allowed
GenericParamModelParameterizer(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkIModelProvider.h b/Modules/ModelFit/include/mitkIModelProvider.h
index 3df525229c..9d248ff791 100644
--- a/Modules/ModelFit/include/mitkIModelProvider.h
+++ b/Modules/ModelFit/include/mitkIModelProvider.h
@@ -1,114 +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.
============================================================================*/
#ifndef mitkIModelProvider_h
#define mitkIModelProvider_h
#include <mitkDataStorage.h>
#include <mitkIFileIO.h>
#include <mitkServiceInterface.h>
namespace mitk
{
class BaseData;
}
namespace itk
{
template <class T>
class SmartPointer;
}
namespace mitk
{
/**
* \ingroup MicroServices_Interfaces
*
* \brief The common interface for all providers of models for fitting in mitk.
*
* Implementations of this interface must be registered as a service
- * to make themselve available via the service registry. If the
+ * to make themselves available via the service registry. If the
* implementation is state-full, the service should be registered using
* a PrototypeServiceFactory.
*
* It is recommended to derive new implementations from ModelFitProviderBase,
* which provide correct service registration semantics.
*
* \sa ModelFitProviderBase
*/
struct MITKCORE_EXPORT IFileReader /** \cond */ : public IFileIO /** \endcond */
{
virtual ~IFileReader();
/**
* \brief Set the input location.
* \param location The file name to read from.
*/
virtual void SetInput(const std::string &location) = 0;
/**
* @brief Set an input stream to read from.
* @param location A custom label for the input stream.
* @param is The input stream.
*
* If \c is is \c NULL, this clears the current input stream and \c location
* is interpreted as a file-system path. Otherwise, \c location is a custom
* label describing the input stream \c is.
*/
virtual void SetInput(const std::string &location, std::istream *is) = 0;
/**
* @brief Get the current input location.
* @return The input location.
*/
virtual std::string GetInputLocation() const = 0;
/**
* @brief Get the input stream.
* @return The currently set input stream.
*/
virtual std::istream *GetInputStream() const = 0;
/**
* \brief Reads the specified file or input stream and returns its contents.
*
* \return A list of created BaseData objects.
*
* If GetInputStream() returns a non-null value, this method must use
* the returned stream object to read the data from. If no input stream
* was set, the data must be read from the path returned by GetInputLocation().
*
* \throws mitk::Exception
*/
virtual std::vector<itk::SmartPointer<BaseData>> Read() = 0;
/**
* \brief Reads the specified file or input stream, loading its
* contents into the provided DataStorage.
*
* \param ds The DataStorage to which the data is added.
* \return The set of added DataNodes to \c ds.
*
* This method may be overridden by implementations to create or
* reconstructed a hierarchy of mitk::DataNode instances in the
* provided mitk::DataStorage.
*
* \throws mitk::Exception
*/
virtual DataStorage::SetOfObjects::Pointer Read(mitk::DataStorage &ds) = 0;
};
} // namespace mitk
MITK_DECLARE_SERVICE_INTERFACE(mitk::IFileReader, "org.mitk.IFileReader")
#endif
diff --git a/Modules/ModelFit/include/mitkLevenbergMarquardtModelFitFunctor.h b/Modules/ModelFit/include/mitkLevenbergMarquardtModelFitFunctor.h
index 6db669329f..8fb930fee7 100644
--- a/Modules/ModelFit/include/mitkLevenbergMarquardtModelFitFunctor.h
+++ b/Modules/ModelFit/include/mitkLevenbergMarquardtModelFitFunctor.h
@@ -1,104 +1,104 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkLevenbergMarquardtModelFitFunctor_h
#define mitkLevenbergMarquardtModelFitFunctor_h
#include <itkObject.h>
#include <itkLevenbergMarquardtOptimizer.h>
#include "mitkModelBase.h"
#include "mitkModelFitFunctorBase.h"
#include "mitkMVConstrainedCostFunctionDecorator.h"
#include "MitkModelFitExports.h"
namespace mitk
{
class MITKMODELFIT_EXPORT LevenbergMarquardtModelFitFunctor : public ModelFitFunctorBase
{
public:
typedef LevenbergMarquardtModelFitFunctor Self;
typedef ModelFitFunctorBase Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkNewMacro(Self);
itkTypeMacro(LevenbergMarquardtModelFitFunctor, ModelFitFunctorBase);
typedef Superclass::InputPixelArrayType InputPixelArrayType;
typedef Superclass::OutputPixelArrayType OutputPixelArrayType;
itkSetMacro(Epsilon, double);
itkSetMacro(GradientTolerance, double);
itkSetMacro(ValueTolerance, double);
itkSetMacro(DerivativeStepLength, double);
itkSetMacro(Iterations, unsigned int);
itkSetMacro(Scales, ::itk::LevenbergMarquardtOptimizer::ScalesType);
itkGetMacro(Epsilon, double);
itkGetMacro(GradientTolerance, double);
itkGetMacro(ValueTolerance, double);
itkGetMacro(DerivativeStepLength, double);
itkGetMacro(Iterations, unsigned int);
itkGetMacro(Scales, ::itk::LevenbergMarquardtOptimizer::ScalesType);
itkSetConstObjectMacro(ConstraintChecker, ConstraintCheckerBase);
itkGetConstObjectMacro(ConstraintChecker, ConstraintCheckerBase);
itkSetMacro(ActivateFailureThreshold, bool);
itkGetConstMacro(ActivateFailureThreshold, bool);
ParameterNamesType GetCriterionNames() const override;
protected:
typedef Superclass::ParametersType ParametersType;
typedef Superclass::SignalType SignalType;
LevenbergMarquardtModelFitFunctor();
~LevenbergMarquardtModelFitFunctor() override;
ParametersType DoModelFit(const SignalType& value, const ModelBase* model,
const ModelBase::ParametersType& initialParameters,
DebugParameterMapType& debugParameters) const override;
OutputPixelArrayType GetCriteria(const ModelBase* model, const ParametersType& parameters,
const SignalType& sample) const override;
/** Generator function that instantiates and parameterizes the cost function that should be used by the fit functor*/
virtual MVModelFitCostFunction::Pointer GenerateCostFunction(const SignalType& value,
const ModelBase* model) const;
ParameterNamesType DefineDebugParameterNames() const override;
private:
double m_Epsilon;
double m_GradientTolerance;
double m_ValueTolerance;
unsigned int m_Iterations;
double m_DerivativeStepLength;
::itk::LevenbergMarquardtOptimizer::ScalesType m_Scales;
/**Constraint checker. If set it will be used by the optimization strategies to add additional constraints to the
given cost function. */
ConstraintCheckerBase::ConstPointer m_ConstraintChecker;
- /**If set to true and an constraint checker is set. The cost function will allways fail if the penalty of the
+ /**If set to true and an constraint checker is set. The cost function will always fail if the penalty of the
checker reaches the threshold. In this case no function evaluation will be done-*/
bool m_ActivateFailureThreshold;
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkMVModelFitCostFunction.h b/Modules/ModelFit/include/mitkMVModelFitCostFunction.h
index d6d5027c86..a2e4dab1f1 100644
--- a/Modules/ModelFit/include/mitkMVModelFitCostFunction.h
+++ b/Modules/ModelFit/include/mitkMVModelFitCostFunction.h
@@ -1,78 +1,78 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkMVModelFitCostFunction_h
#define mitkMVModelFitCostFunction_h
#include <itkMultipleValuedCostFunction.h>
#include <itkMacro.h>
#include "mitkModelFitCostFunctionInterface.h"
#include "MitkModelFitExports.h"
namespace mitk
{
/** Base class for all model fit cost function that return a multiple cost value
* It offers also a default implementation for the numerical computation of the
- * derivatives. Normaly you just have to (re)implement CalcMeasure().
+ * derivatives. Normally you just have to (re)implement CalcMeasure().
*/
class MITKMODELFIT_EXPORT MVModelFitCostFunction : public itk::MultipleValuedCostFunction, public ModelFitCostFunctionInterface
{
public:
typedef MVModelFitCostFunction Self;
typedef itk::MultipleValuedCostFunction Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
typedef ModelFitCostFunctionInterface::SignalType SignalType;
typedef Superclass::MeasureType MeasureType;
typedef Superclass::DerivativeType DerivativeType;
void SetSample(const SignalType &sampleSet) override;
MeasureType GetValue(const ParametersType& parameter) const override;
void GetDerivative (const ParametersType &parameters, DerivativeType &derivative) const override;
unsigned int GetNumberOfValues (void) const override;
unsigned int GetNumberOfParameters (void) const override;
itkSetConstObjectMacro(Model, ModelBase);
itkGetConstObjectMacro(Model, ModelBase);
itkSetMacro(DerivativeStepLength, double);
itkGetConstMacro(DerivativeStepLength, double);
protected:
virtual MeasureType CalcMeasure(const ParametersType &parameters, const SignalType& signal) const = 0;
MVModelFitCostFunction() : m_DerivativeStepLength(1e-5)
{
}
~MVModelFitCostFunction() override{}
SignalType m_Sample;
private:
ModelBase::ConstPointer m_Model;
/**value (delta of parameters) used to compute the derivatives numerically*/
double m_DerivativeStepLength;
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkModelBase.h b/Modules/ModelFit/include/mitkModelBase.h
index c68c14e1b3..339ed810d6 100644
--- a/Modules/ModelFit/include/mitkModelBase.h
+++ b/Modules/ModelFit/include/mitkModelBase.h
@@ -1,213 +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 mitkModelBase_h
#define mitkModelBase_h
#include <iostream>
#include <itkArray.h>
#include <itkArray2D.h>
#include <itkObject.h>
#include "MitkModelFitExports.h"
#include "mitkModelTraitsInterface.h"
namespace mitk
{
/**@class ModelBase
* @brief Base class for (dynamic) models.
* A model can be used to calculate its signal given the discrete time grid of the signal
* and the parameters of the model.\n
* A model has 3 types of parameters:\n
* - parameters
* - static parameters
* - derived parameters
* .
* "Parameters" and "static parameters" are used to compute the signal of the model.
* "Parameters" are the ones that will be changed for/by model fitting.
* "Static parameters" are used to configure the model for fitting but are itself not
* part of the fitting scope (compare itk::Transform parameters and static parameters).
* "Derived parameters" are model specific parameters computed from "Parameters" e.g. (DerivedParam1 = Param1/Param2).
* It may be implemented if e.g. for practical usage not the fitted parameters are needed but
* derivation of them.
* @remark: If you implement your own model calls regard const correctness and do not change
* or undermine the constness of this base class. It is important because in case of fitting
* models are used in a multi threaded environment and must be thread safe. Thus the getter and
* computation functions are implemented as const and thread safe methods.*/
class MITKMODELFIT_EXPORT ModelBase : public itk::Object, public ModelTraitsInterface
{
public:
typedef ModelBase Self;
typedef itk::Object Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkTypeMacro(ModelBase, itk::Object);
typedef ModelTraitsInterface::ModelResultType ModelResultType;
typedef ModelTraitsInterface::ParameterValueType ParameterValueType;
typedef ModelTraitsInterface::ParametersType ParametersType;
/** Type defining the time grid used be models.
* @remark the model time grid has a resolution in sec and not like the time geometry which uses ms.*/
typedef itk::Array<double> TimeGridType;
typedef ModelTraitsInterface::ParameterNameType ParameterNameType;
typedef ModelTraitsInterface::ParameterNamesType ParameterNamesType;
typedef ModelTraitsInterface::ParametersSizeType ParametersSizeType;
typedef ModelTraitsInterface::DerivedParameterNamesType DerivedParameterNamesType;
typedef ModelTraitsInterface::DerivedParametersSizeType DerivedParametersSizeType;
typedef double StaticParameterValueType;
typedef std::vector<StaticParameterValueType> StaticParameterValuesType;
typedef std::map<ParameterNameType, StaticParameterValuesType> StaticParameterMapType;
typedef double DerivedParameterValueType;
typedef std::map<ParameterNameType, DerivedParameterValueType> DerivedParameterMapType;
/**Default implementation returns a scale of 1.0 for every defined parameter.*/
ParamterScaleMapType GetParameterScales() const override;
/**Default implementation returns no unit string ("") for every defined parameter.*/
ParamterUnitMapType GetParameterUnits() const override;
/**Default implementation returns a scale of 1.0 for every defined derived parameter.*/
DerivedParamterScaleMapType GetDerivedParameterScales() const override;
/**Default implementation returns no unit string ("") for every defined derived parameter.*/
DerivedParamterUnitMapType GetDerivedParameterUnits() const override;
/**Default implementation returns GetClassID as display name.*/
std::string GetModelDisplayName() const override;
/**Default implementation returns "Unkown" as model type.*/
std::string GetModelType() const override;
/**Default implementation returns an empty functions string.*/
FunctionStringType GetFunctionString() const override;
/**Default implementation the class name of the concrete instance as ID.*/
ModellClassIDType GetClassID() const override;
/**Default implementation returns an empty string.*/
std::string GetXName() const override;
/**Default implementation returns an empty string.*/
std::string GetXAxisName() const override;
/**Default implementation returns an empty string.*/
std::string GetXAxisUnit() const override;
/**Default implementation returns an empty string.*/
std::string GetYAxisName() const override;
/**Default implementation returns an empty string.*/
std::string GetYAxisUnit() const override;
/** Returns the names of static parameters that will be used when using
* the model to compute the signal (but are not defined via GetSignal()).*/
virtual ParameterNamesType GetStaticParameterNames() const = 0;
/** Returns the number of static parameters that will be used when using
* the model to compute the signal (but are not defined via GetSignal()).*/
virtual ParametersSizeType GetNumberOfStaticParameters() const = 0;
/**Default implementation returns no unit string ("") for every defined parameter.*/
virtual ParamterUnitMapType GetStaticParameterUnits() const;
/** Returns the names of derived parameters that can/will be computed by the model
* given specific model parameters.
* @remark Default implementation has no derived parameters*/
DerivedParameterNamesType GetDerivedParameterNames() const override;
/** Returns the number of derived parameters that can/will be computed by the model
* given specific model parameters.
* @remark Default implementation has no derived parameters*/
DerivedParametersSizeType GetNumberOfDerivedParameters() const override;
/** Generic interface method that can be used to set the static parameters of the model
* before it is used.
* It checks the validity of the passed map and uses SetStaticParameter to set the values.
* @param parameters The map with the static parameters and their values.
* @param allParameters If true an exception will be thrown if the keys of passed parameters do
* not equal the return of GetStaticParameterNames. Thus if true, one must set all static
* parameters of the model.
* @pre Parameters must only contain keys that exist in GetStaticParameterNames()
* @pre If allParameters == true, parameters must define all keys of GetStaticParameterNames()*/
void SetStaticParameters(const StaticParameterMapType& parameters, bool allParameters = true);
/** Generic interface method that can be used to retrieve the static parameters of the model;
* e.g. in order to serialize the model settings.
* It calls GetStaticParameter for every name defined in GetStaticParameterNames().*/
StaticParameterMapType GetStaticParameters() const;
/** Generic interface method that computes all derived parameters implemented for the given models.
* To changed the derived parameter computation. ComputeDerivedParameters must be (re)implemented.
* @pre parameters must have the right size.
* @param parameters The parameters of the model for which the derived parameters should be computed.
* It calls GetStaticParameter for every name defined in GetStaticParameterNames().
* @remark Default implementation has no derived parameters*/
DerivedParameterMapType GetDerivedParameters(const ParametersType& parameters) const;
- /** Sets the time grid of the model. It indicates the time points correlated with the signal the modell should
+ /** Sets the time grid of the model. It indicates the time points correlated with the signal the model should
produce.
@remark The resolution of the time grid is in seconds. (Not in ms like the mitk::TimeGeometry)*/
virtual void SetTimeGrid(const TimeGridType& grid);
- /** Gets the time grid of the model. It indicates the time points correlated with the signal the modell should
+ /** Gets the time grid of the model. It indicates the time points correlated with the signal the model should
produce.
@remark The resolution of the time grid is in seconds. (Not in ms like the mitk::TimeGeometry)*/
itkGetConstReferenceMacro(TimeGrid, TimeGridType);
ModelResultType GetSignal(const ParametersType& parameters) const;
protected:
virtual ModelResultType ComputeModelfunction(const ParametersType& parameters) const = 0;
/** Member is called by GetSignal() before ComputeModelfunction(). It indicates if model is in a valid state and
* ready to compute the signal. The default implementation checks nothing and always returns true.
* Reimplement to realize special behavior for derived classes.
* @param [out] error Set internally to indicate the error reason if method returns false. Is used by GetSignal() for the
* exception comment.
* @return Returns true if the model is valid and can compute a signal. Otherwise it returns false.*/
virtual bool ValidateModel(std::string& error) const;
/** Helper function called by GetDerivedParameters(). Implement in derived classes to realize
* the concrete computation of derived parameters.
* @remark Default implementation has no derived parameters*/
virtual DerivedParameterMapType ComputeDerivedParameters(const ParametersType& parameters) const;
/** Helper function called by SetStaticParameters(). Implement in derived classes to realize
* the concrete setting of static parameters.*/
virtual void SetStaticParameter(const ParameterNameType& name,
const StaticParameterValuesType& values) = 0;
/** Helper function called by GetStaticParameters(). Implement in derived classes to realize
* the concrete retrieval of static parameters.*/
virtual StaticParameterValuesType GetStaticParameterValue(const ParameterNameType& name) const = 0;
ModelBase();
~ModelBase() override;
void PrintSelf(std::ostream& os, ::itk::Indent indent) const override;
//timeGrid in seconds
TimeGridType m_TimeGrid;
private:
//No copy constructor allowed
ModelBase(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkModelFitInfoSignalGenerationFunctor.h b/Modules/ModelFit/include/mitkModelFitInfoSignalGenerationFunctor.h
index 4a9ddec0ae..030a64c434 100644
--- a/Modules/ModelFit/include/mitkModelFitInfoSignalGenerationFunctor.h
+++ b/Modules/ModelFit/include/mitkModelFitInfoSignalGenerationFunctor.h
@@ -1,81 +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 mitkModelFitInfoSignalGenerationFunctor_h
#define mitkModelFitInfoSignalGenerationFunctor_h
#include "mitkModelBasedValueFunctorBase.h"
#include "mitkModelParameterizerBase.h"
#include "mitkModelFitInfo.h"
#include "mitkModelFitParameterValueExtraction.h"
#include <MitkModelFitExports.h>
namespace mitk
{
/** Functor class that can be used to generate a model signal for each index.
This class is similar to ModelDataGenerationFunctor. But instead of using
the passed input value as model parameters in the compute function, the model
fit info is used to deduce the parameters based on the index.
The time grid and the parameterized model are provided by the model
parameterizer. This this functor will generate a signal for each index position
and return the signal as output.*/
class MITKMODELFIT_EXPORT ModelFitInfoSignalGenerationFunctor : public ModelBasedValueFunctorBase
{
public:
typedef ModelFitInfoSignalGenerationFunctor Self;
typedef ModelBasedValueFunctorBase Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkFactorylessNewMacro(Self);
itkTypeMacro(ModelFitInfoSignalGenerationFunctor, ModelBasedValueFunctorBase);
typedef std::vector<std::string> ParameterNamesType;
typedef ModelBase::ModelResultType SignalType;
typedef itk::Array<ModelBase::ParameterValueType> ModelParametersType;
itkSetConstObjectMacro(ModelParameterizer, ModelParameterizerBase);
itkGetConstObjectMacro(ModelParameterizer, ModelParameterizerBase);
itkSetConstObjectMacro(FitInfo, mitk::modelFit::ModelFitInfo);
itkGetConstObjectMacro(FitInfo, mitk::modelFit::ModelFitInfo);
IndexedValueFunctorBase::OutputPixelVectorType Compute(const InputPixelVectorType & value, const IndexType& currentIndex) const override;
- /** Convinient overload because this functor does not need the value */
+ /** Convenient overload because this functor does not need the value */
virtual IndexedValueFunctorBase::OutputPixelVectorType Compute(const IndexType& currentIndex) const;
unsigned int GetNumberOfOutputs() const override;
GridArrayType GetGrid() const override;
protected:
/**Method is called by Compute() to specify the parameters used to generate the model signal for the current index.
The default implementation just extracts the parameters out of the model fit info and maps it into the result vector.
Reimplement the method to change this behavior.*/
virtual ModelBase::ParametersType CompileModelParameters(const IndexType& currentIndex, const ModelBase * model) const;
ModelFitInfoSignalGenerationFunctor();
~ModelFitInfoSignalGenerationFunctor() override;
private:
ModelParameterizerBase::ConstPointer m_ModelParameterizer;
modelFit::ModelFitInfo::ConstPointer m_FitInfo;
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h b/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h
index 4ee68c1612..b99fb94918 100644
--- a/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h
+++ b/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h
@@ -1,167 +1,167 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkModelFitPlotDataHelper_h
#define mitkModelFitPlotDataHelper_h
#include <vector>
#include "mitkPoint.h"
#include "mitkPointSet.h"
#include "mitkModelBase.h"
#include "mitkWeakPointer.h"
#include "mitkCommon.h"
#include "mitkModelFitInfo.h"
#include "itkMapContainer.h"
#include "MitkModelFitExports.h"
namespace mitk
{
class ModelParameterizerBase;
class Image;
using PlotDataValues = std::vector<std::pair<double, double>>;
/** Simple helper structure that represents a curve for plots and its generation time*/
class MITKMODELFIT_EXPORT PlotDataCurve : public ::itk::Object
{
public:
mitkClassMacroItkParent(PlotDataCurve, itk::Object);
itkFactorylessNewMacro(Self);
using ValuesType = PlotDataValues;
virtual void SetValues(const ValuesType& _arg);
virtual void SetValues(ValuesType&& _arg);
itkGetConstReferenceMacro(Values, ValuesType);
itkGetMacro(Values, ValuesType);
PlotDataCurve& operator=(const PlotDataCurve& rhs);
PlotDataCurve& operator=(PlotDataCurve&& rhs) noexcept;
void Reset();
protected:
PlotDataCurve();
~PlotDataCurve() override = default;
private:
/** values of the curve */
ValuesType m_Values;
PlotDataCurve(const PlotDataCurve& other) = delete;
};
/** Collection of plot curves, e.g. every plot curve for a certain world coordinate position*/
using PlotDataCurveCollection = itk::MapContainer<std::string, PlotDataCurve::Pointer>;
/** Structure containing all information for one model fit */
struct MITKMODELFIT_EXPORT ModelFitPlotData
{
/** Plots that are related to the world coordinate labeled as current position.*/
PlotDataCurveCollection::Pointer currentPositionPlots;
using PositionalCurveCollection = std::pair<mitk::Point3D, PlotDataCurveCollection::Pointer>;
using PositionalCollectionMap = std::map<mitk::PointSet::PointIdentifier, PositionalCurveCollection>;
/** Plot collections that are related to specific world coordinates (inspection position bookmarks).*/
PositionalCollectionMap positionalPlots;
/** Plot collection for static plots of the fit that do not depend on some coordinates. */
PlotDataCurveCollection::Pointer staticPlots;
/** Pointer to the model fit that correspondens with this plot data.*/
mitk::modelFit::ModelFitInfo::Pointer fitInfo;
/** Helper function to get the collection of the current position.
Returns nullptr if no current position exists.*/
static const PlotDataCurve* GetSamplePlot(const PlotDataCurveCollection* coll);
static const PlotDataCurve* GetSignalPlot(const PlotDataCurveCollection* coll);
static const PlotDataCurve* GetInterpolatedSignalPlot(const PlotDataCurveCollection* coll);
/** Helper function that generates a humand readable name for the passed value of a positional collection map.*/
static std::string GetPositionalCollectionName(const PositionalCollectionMap::value_type& mapValue);
const PlotDataCurveCollection* GetPositionalPlot(const mitk::Point3D& point) const;
const PlotDataCurveCollection* GetPositionalPlot(mitk::PointSet::PointIdentifier id) const;
/**returns the minimum (first element) and maximum (second element) of x of all plot data*/
PlotDataValues::value_type GetXMinMax() const;
/**returns the minimum (first element) and maximum (second element) of y of all plot data*/
PlotDataValues::value_type GetYMinMax() const;
ModelFitPlotData();
};
/** Helper function that actualizes min and max by the y values given in data.*/
void CheckYMinMaxFromPlotDataValues(const PlotDataValues& data, double& min, double& max);
/** Helper function that actualizes min and max by the x values given in data.*/
void CheckXMinMaxFromPlotDataValues(const PlotDataValues& data, double& min, double& max);
- /** Function generates curve data for the signal defined by the passed informations.
+ /** Function generates curve data for the signal defined by the passed information.
@param position The position in world coordinates the curve should be generated for.
@param fitInfo Pointer to the fit info that defines the model/fit that produces the signal.
@param timeGrid Defines the time grid of the generated signal.
@param parameterizer Pointer to a parameterizer instance that is used to configure the model to generate the signal.
If pointer is not set. The default parameterizer based on the fitInfo instance will be used.
@pre position must be within the model fit input image
@pre fitInfo must be a valid pointer.
*/
MITKMODELFIT_EXPORT PlotDataCurve::Pointer
GenerateModelSignalPlotData(const mitk::Point3D& position, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid, mitk::ModelParameterizerBase* parameterizer = nullptr);
- /** Function generates curve data for all additinal inputs (e.g. ROI signal, AIF)
+ /** Function generates curve data for all additional inputs (e.g. ROI signal, AIF)
stored in the fit information. The keys in the map are the same keys like in the fitInfo.
@param position The position in world coordinates the curve should be generated for.
@param fitInfo Pointer to the fit info that defines the model/fit that produces the signal.
@param timeGrid Defines the time grid of the generated signal.
@pre position must be within the model fit input image
@pre fitInfo must be a valid pointer.
*/
MITKMODELFIT_EXPORT PlotDataCurveCollection::Pointer
GenerateAdditionalModelFitPlotData(const mitk::Point3D& position, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid);
/** Function generates curve data for a given image instance.
@param position The position in world coordinates the curve should be generated for.
@param image Pointer to the image the signal should be extracted from.
@param timeGrid Defines the time grid of the generated signal.
@pre position must be within the model fit input image
@pre image must be a valid pointer.
@pre image time steps must equal the timeGrid size.
*/
MITKMODELFIT_EXPORT PlotDataCurve::Pointer
GenerateImageSamplePlotData(const mitk::Point3D& position, const mitk::Image* image, const mitk::ModelBase::TimeGridType& timeGrid);
/**
* Keyword used in curve collections as key for the sample (extracted from an image) plot.
*/
MITKMODELFIT_EXPORT const std::string MODEL_FIT_PLOT_SAMPLE_NAME();
/**
* Keyword used in curve collections as key for the signal (generated by a model) plot.
*/
MITKMODELFIT_EXPORT const std::string MODEL_FIT_PLOT_SIGNAL_NAME();
/**
* Keyword used in curve collections as key for the interpolated (hires) signal (generated by a model) plot.
*/
MITKMODELFIT_EXPORT const std::string MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME();
}
#endif
diff --git a/Modules/ModelFit/include/mitkModelFitResultHelper.h b/Modules/ModelFit/include/mitkModelFitResultHelper.h
index 58bb0bc186..1190754d2c 100644
--- a/Modules/ModelFit/include/mitkModelFitResultHelper.h
+++ b/Modules/ModelFit/include/mitkModelFitResultHelper.h
@@ -1,61 +1,61 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkModelFitResultHelper_h
#define mitkModelFitResultHelper_h
#include <mitkImage.h>
#include <mitkDataNode.h>
#include "mitkScalarListLookupTableProperty.h"
#include "mitkModelBase.h"
#include "mitkModelFitParameter.h"
#include "mitkModelFitStaticParameterMap.h"
#include "MitkModelFitExports.h"
namespace mitk
{
class DataStorage;
class ModelTraitsInterface;
class ModelBase;
namespace modelFit
{
class ModelFitInfo;
typedef std::map<ModelBase::ParameterNameType,Image::Pointer> ModelFitResultImageMapType;
typedef std::vector<DataNode::Pointer> ModelFitResultNodeVectorType;
/**Helper function that sets the properties of the passed base data according to the given model fit info instance and parameter specification.
@param data Instance that properties should be configured.
@param name Name of the parameter this data instance represents.
@param dataType Type of the parameter this data instance represents.
- @param fitInfo Instance to the fit info that containes the information of the fit that derived the parameter.
+ @param fitInfo Instance to the fit info that contains the information of the fit that derived the parameter.
@pre Data must point to a valid instance.
@pre fitInfo must point to an valid instance.
*/
MITKMODELFIT_EXPORT void SetModelFitDataProperties(mitk::BaseData* data, const ModelBase::ParameterNameType& name, modelFit::Parameter::Type dataType, const modelFit::ModelFitInfo* fitInfo);
MITKMODELFIT_EXPORT mitk::ScalarListLookupTableProperty::Pointer ConvertStaticParametersToProperty(const mitk::modelFit::StaticParameterMap& params);
MITKMODELFIT_EXPORT DataNode::Pointer CreateResultNode(const ModelBase::ParameterNameType& name, modelFit::Parameter::Type nodeType, Image* parameterImage, const ModelFitInfo* modelFitInfo);
MITKMODELFIT_EXPORT ModelFitResultNodeVectorType CreateResultNodeMap(const ModelFitResultImageMapType& results, const ModelFitResultImageMapType& derivedResults, const ModelFitResultImageMapType& criterionResults, const ModelFitResultImageMapType& evaluationResults, const ModelFitInfo* fitInfo);
MITKMODELFIT_EXPORT void StoreResultsInDataStorage(DataStorage* storage, const ModelFitResultNodeVectorType& resultNodes, DataNode* parentNode = nullptr);
}
}
#endif
diff --git a/Modules/ModelFit/include/mitkSVModelFitCostFunction.h b/Modules/ModelFit/include/mitkSVModelFitCostFunction.h
index 0e520593f4..acff805330 100644
--- a/Modules/ModelFit/include/mitkSVModelFitCostFunction.h
+++ b/Modules/ModelFit/include/mitkSVModelFitCostFunction.h
@@ -1,74 +1,74 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkSVModelFitCostFunction_h
#define mitkSVModelFitCostFunction_h
#include <itkSingleValuedCostFunction.h>
#include <itkMacro.h>
#include "mitkModelFitCostFunctionInterface.h"
#include "MitkModelFitExports.h"
namespace mitk
{
-/** Base class for all model fit cost function that return a singel cost value*/
+/** Base class for all model fit cost function that return a single cost value*/
class MITKMODELFIT_EXPORT SVModelFitCostFunction : public itk::SingleValuedCostFunction, public ModelFitCostFunctionInterface
{
public:
typedef SVModelFitCostFunction Self;
typedef itk::SingleValuedCostFunction Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
typedef ModelFitCostFunctionInterface::SignalType SignalType;
typedef Superclass::MeasureType MeasureType;
typedef Superclass::DerivativeType DerivativeType;
void SetSample(const SignalType &sampleSet) override;
MeasureType GetValue(const ParametersType& parameter) const override;
void GetDerivative (const ParametersType &parameters, DerivativeType &derivative) const override;
unsigned int GetNumberOfParameters (void) const override;
itkSetConstObjectMacro(Model, ModelBase);
itkGetConstObjectMacro(Model, ModelBase);
itkSetMacro(DerivativeStepLength, double);
itkGetConstMacro(DerivativeStepLength, double);
protected:
virtual MeasureType CalcMeasure(const ParametersType &parameters, const SignalType& signal) const = 0;
SVModelFitCostFunction(): m_DerivativeStepLength(1e-5)
{
}
~SVModelFitCostFunction() override{}
SignalType m_Sample;
private:
ModelBase::ConstPointer m_Model;
/**value (delta of parameters) used to compute the derivatives numerically*/
double m_DerivativeStepLength;
};
}
#endif
diff --git a/Modules/ModelFit/include/mitkTimeGridHelper.h b/Modules/ModelFit/include/mitkTimeGridHelper.h
index 3993bb3a20..4579dfc5bd 100644
--- a/Modules/ModelFit/include/mitkTimeGridHelper.h
+++ b/Modules/ModelFit/include/mitkTimeGridHelper.h
@@ -1,37 +1,37 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkTimeGridHelper_h
#define mitkTimeGridHelper_h
#include "mitkModelBase.h"
#include "MitkModelFitExports.h"
namespace mitk
{
/* Checks if the time grid is monotone increasing (timeGrid[n] <= timeGrid[n+1]).
* It is a precondition for the helper interpolate time grid.*/
MITKMODELFIT_EXPORT bool TimeGridIsMonotonIncreasing(const ModelBase::TimeGridType timeGrid);
/* Helper function that interpolates a passed signal to a new time grid.
* @pre The time grids must be monotone increasing. Use TimeGridIsMonotonIncreasing() to verify that.*/
MITKMODELFIT_EXPORT ModelBase::ModelResultType InterpolateSignalToNewTimeGrid(const ModelBase::ModelResultType& inputSignal, const ModelBase::TimeGridType& inputGrid, const ModelBase::TimeGridType& outputGrid);
/** Super sample passed time grid by a given supersampling rate and interpolates linear in between original time steps.
* @param grid
- * @param samplingRate Defines how many samples should be generated between to original time steps (including the preceeding time step). E.g. a sampling rate of 1 will just returns the original grid untouched;
- a sampling rate of 3 will generate to aditional steps between to original steps.*/
+ * @param samplingRate Defines how many samples should be generated between to original time steps (including the preceding time step). E.g. a sampling rate of 1 will just returns the original grid untouched;
+ a sampling rate of 3 will generate to additional steps between to original steps.*/
MITKMODELFIT_EXPORT ModelBase::TimeGridType GenerateSupersampledTimeGrid(const mitk::ModelBase::TimeGridType& grid, const unsigned int samplingRate);
}
#endif
diff --git a/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.cpp b/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.cpp
index 1467eabd61..0654f2220e 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.");
+ itkExceptionMacro("ModelFitInfo implementation seems to be inconsistent. 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]);
+ itkExceptionMacro("ModelFitInfo implementation seems to be inconsistent. 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_CostFunctionMap.clear();
m_Mutex.unlock();
};
void
mitk::ModelFitFunctorBase::RegisterEvaluationParameter(const std::string& parameterName,
SVModelFitCostFunction* evaluationCostFunction)
{
m_Mutex.lock();
SVModelFitCostFunction::Pointer costFunctPtr = evaluationCostFunction;
m_CostFunctionMap.insert(std::make_pair(parameterName, costFunctPtr));
m_Mutex.unlock();
};
mitk::ModelFitFunctorBase::ParameterNamesType
mitk::ModelFitFunctorBase::GetEvaluationParameterNames() const
{
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();
return result;
};
const mitk::SVModelFitCostFunction*
mitk::ModelFitFunctorBase::GetEvaluationParameterCostFunction(const std::string& parameterName)
const
{
const SVModelFitCostFunction* result = nullptr;
m_Mutex.lock();
CostFunctionMapType::const_iterator pos = m_CostFunctionMap.find(parameterName);
if (pos != m_CostFunctionMap.end())
{
result = (pos->second).GetPointer();
}
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();
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();
return result;
};
diff --git a/Modules/ModelFit/src/Models/mitkModelBase.cpp b/Modules/ModelFit/src/Models/mitkModelBase.cpp
index df8d2ebadb..91689db33c 100644
--- a/Modules/ModelFit/src/Models/mitkModelBase.cpp
+++ b/Modules/ModelFit/src/Models/mitkModelBase.cpp
@@ -1,266 +1,266 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkModelBase.h"
#include "itkMacro.h"
#include <algorithm>
mitk::ModelBase::ParamterScaleMapType
mitk::ModelBase::GetParameterScales() const
{
ParamterScaleMapType result;
ParameterNamesType names = GetParameterNames();
for (ParameterNamesType::iterator pos = names.begin(); pos != names.end(); ++pos)
{
result.insert(std::make_pair(*pos, 1.0));
}
return result;
};
mitk::ModelBase::ParamterUnitMapType
mitk::ModelBase::GetParameterUnits() const
{
ParamterUnitMapType result;
ParameterNamesType names = GetParameterNames();
for (ParameterNamesType::iterator pos = names.begin(); pos != names.end(); ++pos)
{
result.insert(std::make_pair(*pos, ""));
}
return result;
};
mitk::ModelBase::DerivedParamterScaleMapType
mitk::ModelBase::GetDerivedParameterScales() const
{
DerivedParamterScaleMapType result;
DerivedParameterNamesType names = this->GetDerivedParameterNames();
for (DerivedParameterNamesType::iterator pos = names.begin(); pos != names.end(); ++pos)
{
result.insert(std::make_pair(*pos, 1.0));
}
return result;
};
mitk::ModelBase::DerivedParamterUnitMapType
mitk::ModelBase::GetDerivedParameterUnits() const
{
DerivedParamterUnitMapType result;
DerivedParameterNamesType names = this->GetDerivedParameterNames();
for (DerivedParameterNamesType::iterator pos = names.begin(); pos != names.end(); ++pos)
{
result.insert(std::make_pair(*pos, ""));
}
return result;
};
std::string
mitk::ModelBase::GetModelDisplayName() const
{
return this->GetClassID();
};
std::string mitk::ModelBase::GetModelType() const
{
return "Unkown";
};
mitk::ModelBase::FunctionStringType mitk::ModelBase::GetFunctionString() const
{
return "";
};
mitk::ModelBase::ModellClassIDType mitk::ModelBase::GetClassID() const
{
return this->GetNameOfClass();
};
std::string mitk::ModelBase::GetXName() const
{
return "";
};
std::string mitk::ModelBase::GetXAxisName() const
{
return "";
};
std::string mitk::ModelBase::GetXAxisUnit() const
{
return "";
};
std::string mitk::ModelBase::GetYAxisName() const
{
return "";
};
std::string mitk::ModelBase::GetYAxisUnit() const
{
return "";
}
mitk::ModelBase::ModelBase()
{
}
mitk::ModelBase::~ModelBase()
{
}
mitk::ModelBase::ModelResultType mitk::ModelBase::GetSignal(const ParametersType& parameters) const
{
if (parameters.size() != this->GetNumberOfParameters())
{
itkExceptionMacro("Passed parameter set has wrong size for model. Cannot evaluate model. Required size: "
<< this->GetNumberOfParameters() << "; passed parameters: " << parameters);
}
std::string error;
if (!ValidateModel(error))
{
itkExceptionMacro("Cannot evaluate model and return signal. Model is in an invalid state. Validation error: "
<< error);
}
ModelResultType signal = ComputeModelfunction(parameters);
return signal;
}
bool mitk::ModelBase::ValidateModel(std::string& /*error*/) const
{
return true;
};
void mitk::ModelBase::SetTimeGrid(const TimeGridType& grid)
{
itkDebugMacro("setting TimeGrid to " << grid);
if (grid.GetSize() == 0)
{
itkExceptionMacro("Time Grid Vector is empty! Set valid time grid");
}
if (this->m_TimeGrid != grid)
{
this->m_TimeGrid = grid;
this->Modified();
}
}
void mitk::ModelBase::PrintSelf(std::ostream& os, ::itk::Indent indent) const
{
Superclass::PrintSelf(os, indent);
os << indent << "Time grid: " << m_TimeGrid;
};
void mitk::ModelBase::SetStaticParameters(const StaticParameterMapType& parameters,
bool allParameters)
{
ParameterNamesType names = this->GetStaticParameterNames();
if ((parameters.size() != names.size()) && allParameters)
{
itkExceptionMacro("Cannot set static parameter of model. Passed parameters does not define all parameters correctly. Required size:"
<< names.size() << "; passed size: " << parameters.size());
}
for (StaticParameterMapType::const_iterator pos = parameters.begin(); pos != parameters.end();
++pos)
{
ParameterNamesType::iterator finding = std::find(names.begin(), names.end(), pos->first);
if (finding == names.end())
{
itkExceptionMacro("Cannot set static parameter of model. Passed parameter name is not in the list of valid names. Passed name: "
<< pos->first);
}
}
//Setting is done in an other loop to ensure that the state of the model only changes if all values are valid.
for (StaticParameterMapType::const_iterator pos = parameters.begin(); pos != parameters.end();
++pos)
{
this->SetStaticParameter(pos->first, pos->second);
}
};
mitk::ModelBase::StaticParameterMapType mitk::ModelBase::GetStaticParameters() const
{
StaticParameterMapType result;
ParameterNamesType names = this->GetStaticParameterNames();
for (ParameterNamesType::const_iterator pos = names.begin(); pos != names.end(); ++pos)
{
StaticParameterValuesType values = this->GetStaticParameterValue(*pos);
result.insert(std::make_pair(*pos, values));
}
return result;
};
mitk::ModelBase::DerivedParameterNamesType mitk::ModelBase::GetDerivedParameterNames() const
{
ParameterNamesType emptyResult;
return emptyResult;
};
mitk::ModelBase::ParamterUnitMapType
mitk::ModelBase::GetStaticParameterUnits() const
{
ParamterUnitMapType result;
ParameterNamesType names = GetStaticParameterNames();
for (ParameterNamesType::iterator pos = names.begin(); pos != names.end(); ++pos)
{
result.insert(std::make_pair(*pos, ""));
}
return result;
};
mitk::ModelBase::DerivedParametersSizeType mitk::ModelBase::GetNumberOfDerivedParameters() const
{
return 0;
};
mitk::ModelBase::DerivedParameterMapType mitk::ModelBase::GetDerivedParameters(
const mitk::ModelBase::ParametersType& parameters) const
{
if (parameters.size() != this->GetNumberOfParameters())
{
- itkExceptionMacro("Cannot compute derived parametes. Passed parameters does not define all parameters correctly. Required size:"
+ itkExceptionMacro("Cannot compute derived parameters. Passed parameters does not define all parameters correctly. Required size:"
<< this->GetNumberOfParameters() << "; passed size: " << parameters.size());
}
return ComputeDerivedParameters(parameters);
};
mitk::ModelBase::DerivedParameterMapType mitk::ModelBase::ComputeDerivedParameters(
const mitk::ModelBase::ParametersType& /*parameters*/) const
{
DerivedParameterMapType emptyResult;
return emptyResult;
};
diff --git a/Modules/ModelFit/src/Models/mitkThreeStepLinearModel.cpp b/Modules/ModelFit/src/Models/mitkThreeStepLinearModel.cpp
index 6ec5cd98dc..3741f05822 100644
--- a/Modules/ModelFit/src/Models/mitkThreeStepLinearModel.cpp
+++ b/Modules/ModelFit/src/Models/mitkThreeStepLinearModel.cpp
@@ -1,321 +1,321 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkThreeStepLinearModel.h"
#include <mitkIOUtil.h>
const std::string mitk::ThreeStepLinearModel::NAME_PARAMETER_y_bl = "baseline";
const std::string mitk::ThreeStepLinearModel::NAME_PARAMETER_x0 = "x_changepoint1";
const std::string mitk::ThreeStepLinearModel::NAME_PARAMETER_x1 = "x_changepoint2";
const std::string mitk::ThreeStepLinearModel::NAME_PARAMETER_b0 = "slope1";
const std::string mitk::ThreeStepLinearModel::NAME_PARAMETER_b1 = "slope2";
const unsigned int mitk::ThreeStepLinearModel::NUMBER_OF_PARAMETERS = 5;
const std::string mitk::ThreeStepLinearModel::UNIT_PARAMETER_y_bl = "[unit of y]";
const std::string mitk::ThreeStepLinearModel::UNIT_PARAMETER_x0 = "[unit of x]";
const std::string mitk::ThreeStepLinearModel::UNIT_PARAMETER_x1 = "[unit of x]";
const std::string mitk::ThreeStepLinearModel::UNIT_PARAMETER_b0 = "[unit of y]/[unit of x]";
const std::string mitk::ThreeStepLinearModel::UNIT_PARAMETER_b1 = "[unit of y]/[unit of x]";
const unsigned int mitk::ThreeStepLinearModel::POSITION_PARAMETER_y_bl = 0;
const unsigned int mitk::ThreeStepLinearModel::POSITION_PARAMETER_x0 = 1;
const unsigned int mitk::ThreeStepLinearModel::POSITION_PARAMETER_x1 = 2;
const unsigned int mitk::ThreeStepLinearModel::POSITION_PARAMETER_b0 = 3;
const unsigned int mitk::ThreeStepLinearModel::POSITION_PARAMETER_b1 = 4;
const std::string mitk::ThreeStepLinearModel::NAME_DERIVED_PARAMETER_auc = "auc";
const std::string mitk::ThreeStepLinearModel::NAME_DERIVED_PARAMETER_x_fin = "x_final";
const std::string mitk::ThreeStepLinearModel::NAME_DERIVED_PARAMETER_y_fin = "y_final";
const std::string mitk::ThreeStepLinearModel::NAME_DERIVED_PARAMETER_y_max = "y_max";
const std::string mitk::ThreeStepLinearModel::NAME_DERIVED_PARAMETER_y1 = "y-intercept1";
const std::string mitk::ThreeStepLinearModel::NAME_DERIVED_PARAMETER_y2 = "y-intercept2";
const unsigned int mitk::ThreeStepLinearModel::NUMBER_OF_DERIVED_PARAMETERS = 6;
const std::string mitk::ThreeStepLinearModel::UNIT_DERIVED_PARAMETER_auc = "[unit of x]*[unit of y]";
const std::string mitk::ThreeStepLinearModel::UNIT_DERIVED_PARAMETER_x_fin = "[unit of x]";
const std::string mitk::ThreeStepLinearModel::UNIT_DERIVED_PARAMETER_y_fin = "[unit of y]";
const std::string mitk::ThreeStepLinearModel::UNIT_DERIVED_PARAMETER_y_max = "[unit of y]";
const std::string mitk::ThreeStepLinearModel::UNIT_DERIVED_PARAMETER_y1 = "[unit of y]";
const std::string mitk::ThreeStepLinearModel::UNIT_DERIVED_PARAMETER_y2 = "[unit of y]";
const unsigned int mitk::ThreeStepLinearModel::NUMBER_OF_STATIC_PARAMETERS = 0;
const std::string mitk::ThreeStepLinearModel::MODEL_DISPLAY_NAME = "Three Step Linear Model";
const std::string mitk::ThreeStepLinearModel::MODEL_TYPE = "Generic";
const std::string mitk::ThreeStepLinearModel::FUNCTION_STRING = "if x < x_changepoint1: y(x) = baseline, else if x_changepoint1 <= x <= x_changepoint2: y(x) = y-intercept1 + slope1*x, else if x>x_changepoint2: y(x) = y-intercept2 + slope2*x";
const std::string mitk::ThreeStepLinearModel::X_NAME = "x";
const std::string mitk::ThreeStepLinearModel::X_AXIS_NAME = "X";
const std::string mitk::ThreeStepLinearModel::X_AXIS_UNIT = "unit of x";
const std::string mitk::ThreeStepLinearModel::Y_AXIS_NAME = "Y";
const std::string mitk::ThreeStepLinearModel::Y_AXIS_UNIT = "unit of y";
std::string mitk::ThreeStepLinearModel::GetModelDisplayName() const
{
return MODEL_DISPLAY_NAME;
};
std::string mitk::ThreeStepLinearModel::GetModelType() const
{
return MODEL_TYPE;
};
mitk::ThreeStepLinearModel::FunctionStringType mitk::ThreeStepLinearModel::GetFunctionString() const
{
return FUNCTION_STRING;
};
std::string mitk::ThreeStepLinearModel::GetXName() const
{
return X_NAME;
};
std::string mitk::ThreeStepLinearModel::GetXAxisName() const
{
return X_AXIS_NAME;
};
std::string mitk::ThreeStepLinearModel::GetXAxisUnit() const
{
return X_AXIS_UNIT;
}
std::string mitk::ThreeStepLinearModel::GetYAxisName() const
{
return Y_AXIS_NAME;
};
std::string mitk::ThreeStepLinearModel::GetYAxisUnit() const
{
return Y_AXIS_UNIT;
}
mitk::ThreeStepLinearModel::ParameterNamesType
mitk::ThreeStepLinearModel::GetParameterNames() const
{
ParameterNamesType result;
result.push_back(NAME_PARAMETER_y_bl);
result.push_back(NAME_PARAMETER_x0);
result.push_back(NAME_PARAMETER_x1);
result.push_back(NAME_PARAMETER_b0);
result.push_back(NAME_PARAMETER_b1);
return result;
};
mitk::ThreeStepLinearModel::ParamterUnitMapType mitk::ThreeStepLinearModel::GetParameterUnits() const
{
ParamterUnitMapType result;
result.insert(std::make_pair(NAME_PARAMETER_y_bl, UNIT_PARAMETER_y_bl));
result.insert(std::make_pair(NAME_PARAMETER_x0, UNIT_PARAMETER_x0));
result.insert(std::make_pair(NAME_PARAMETER_x1, UNIT_PARAMETER_x1));
result.insert(std::make_pair(NAME_PARAMETER_b0, UNIT_PARAMETER_b0));
result.insert(std::make_pair(NAME_PARAMETER_b1, UNIT_PARAMETER_b1));
return result;
}
mitk::ThreeStepLinearModel::ParametersSizeType
mitk::ThreeStepLinearModel::GetNumberOfParameters() const
{
return NUMBER_OF_PARAMETERS;
};
mitk::ThreeStepLinearModel::ParameterNamesType
mitk::ThreeStepLinearModel::GetDerivedParameterNames() const
{
ParameterNamesType result;
result.push_back(NAME_DERIVED_PARAMETER_auc);
result.push_back(NAME_DERIVED_PARAMETER_x_fin);
result.push_back(NAME_DERIVED_PARAMETER_y_fin);
result.push_back(NAME_DERIVED_PARAMETER_y_max);
result.push_back(NAME_DERIVED_PARAMETER_y1);
result.push_back(NAME_DERIVED_PARAMETER_y2);
return result;
};
mitk::ThreeStepLinearModel::ParametersSizeType
mitk::ThreeStepLinearModel::GetNumberOfDerivedParameters() const
{
return NUMBER_OF_DERIVED_PARAMETERS;
};
mitk::ThreeStepLinearModel::ParamterUnitMapType mitk::ThreeStepLinearModel::GetDerivedParameterUnits() const
{
ParamterUnitMapType result;
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_auc, UNIT_DERIVED_PARAMETER_auc));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_x_fin, UNIT_DERIVED_PARAMETER_x_fin));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_fin, UNIT_DERIVED_PARAMETER_y_fin));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_max, UNIT_DERIVED_PARAMETER_y_max));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y1, UNIT_DERIVED_PARAMETER_y1));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y2, UNIT_DERIVED_PARAMETER_y2));
return result;
};
double mitk::ThreeStepLinearModel::ComputeSignalFromParameters(double x, double y_bl, double x0, double x1, double b0, double b1, double y1, double y2)
{
double signal = 0.0;
if (x < x0)
{
signal = y_bl;
}
else if (x >= x0 && x <= x1)
{
signal = b0 * x + y1;
}
else
{
signal = b1 * x + y2;
}
return signal;
};
mitk::ThreeStepLinearModel::ModelResultType
mitk::ThreeStepLinearModel::ComputeModelfunction(const ParametersType& parameters) const
{
//Model Parameters
const double y_bl = (double) parameters[POSITION_PARAMETER_y_bl];
const double x0 = (double) parameters[POSITION_PARAMETER_x0] ;
const double x1 = (double) parameters[POSITION_PARAMETER_x1] ;
const double b0 = (double) parameters[POSITION_PARAMETER_b0] ;
const double b1 = (double) parameters[POSITION_PARAMETER_b1] ;
double y1 = y_bl - b0 * x0;
double y2 = (b0 * x1 + y1) - (b1 * x1);
ModelResultType signal(m_TimeGrid.GetSize());
TimeGridType::const_iterator timeGridEnd = m_TimeGrid.end();
ModelResultType::iterator signalPos = signal.begin();
for (TimeGridType::const_iterator gridPos = m_TimeGrid.begin(); gridPos != timeGridEnd; ++gridPos, ++signalPos)
{
*signalPos = ComputeSignalFromParameters(*gridPos, y_bl, x0, x1, b0, b1, y1, y2);
}
return signal;
};
mitk::ThreeStepLinearModel::ParameterNamesType mitk::ThreeStepLinearModel::GetStaticParameterNames() const
{
ParameterNamesType result;
return result;
}
mitk::ThreeStepLinearModel::ParametersSizeType mitk::ThreeStepLinearModel::GetNumberOfStaticParameters() const
{
return 0;
}
void mitk::ThreeStepLinearModel::SetStaticParameter(const ParameterNameType&,
const StaticParameterValuesType&)
{
//do nothing
};
mitk::ThreeStepLinearModel::StaticParameterValuesType mitk::ThreeStepLinearModel::GetStaticParameterValue(
const ParameterNameType&) const
{
StaticParameterValuesType result;
//do nothing
return result;
};
mitk::ModelBase::DerivedParameterMapType mitk::ThreeStepLinearModel::ComputeDerivedParameters(
const mitk::ModelBase::ParametersType& parameters) const
{
const double y_bl = (double) parameters[POSITION_PARAMETER_y_bl];
const double x0 = (double) parameters[POSITION_PARAMETER_x0] ;
const double x1 = (double) parameters[POSITION_PARAMETER_x1] ;
const double b0 = (double) parameters[POSITION_PARAMETER_b0] ;
const double b1 = (double) parameters[POSITION_PARAMETER_b1] ;
const double y1 = y_bl - b0 * x0;
const double y2 = (b0 * x1 + y1) - (b1 * x1);
unsigned int timeSteps = m_TimeGrid.GetSize();
- const double x_fin = (m_TimeGrid.empty() == false) ? (m_TimeGrid.GetElement(timeSteps - 1)) : ( mitkThrow() << "An exception occured because time grid is empty, method can't continue.");
+ const double x_fin = (m_TimeGrid.empty() == false) ? (m_TimeGrid.GetElement(timeSteps - 1)) : ( mitkThrow() << "An exception occurred because time grid is empty, method can't continue.");
const double y_fin = b1 * x_fin + y2;
double y_max = y_fin;
if ((b0 >= 0) && (b1 >= 0))
y_max = y_fin;
else if ((b0 < 0) && (b1 < 0))
y_max = y_bl;
else if ((b0 > 0) && (b1 < 0))
y_max = (b0 * x1 + y1);
else
{
if (abs(b0 * (x1 - x0)) >= abs(b1 * (x_fin - x1)))
y_max = y_bl;
else y_max = y_fin;
}
double auc = 0.0;
TimeGridType::const_iterator timeGridEnd = m_TimeGrid.end();
for (TimeGridType::const_iterator gridPos = m_TimeGrid.begin(); gridPos != timeGridEnd - 1; ++gridPos)
{
double currentGridPos = *gridPos;
double nextGridPos = *(++gridPos);
double deltaX = nextGridPos - currentGridPos;
double deltaY = ComputeSignalFromParameters(nextGridPos, y_bl, x0, x1, b0, b1, y1, y2) - ComputeSignalFromParameters(currentGridPos, y_bl, x0, x1, b0, b1, y1, y2);
double Yi = ComputeSignalFromParameters(currentGridPos, y_bl, x0, x1, b0, b1, y1, y2 );
double intI = 0.5 * deltaX * deltaY + Yi * deltaX;
auc += std::abs(intI);
--gridPos;
}
DerivedParameterMapType result;
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_auc, auc));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_x_fin, x_fin));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_fin, y_fin));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_max, y_max));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y1, y1));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y2, y2));
return result;
};
itk::LightObject::Pointer mitk::ThreeStepLinearModel::InternalClone() const
{
ThreeStepLinearModel::Pointer newClone = ThreeStepLinearModel::New();
newClone->SetTimeGrid(this->m_TimeGrid);
return newClone.GetPointer();
};
diff --git a/Modules/ModelFit/src/Models/mitkTwoStepLinearModel.cpp b/Modules/ModelFit/src/Models/mitkTwoStepLinearModel.cpp
index ab17e6770f..bc72b1d958 100644
--- a/Modules/ModelFit/src/Models/mitkTwoStepLinearModel.cpp
+++ b/Modules/ModelFit/src/Models/mitkTwoStepLinearModel.cpp
@@ -1,285 +1,285 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkTwoStepLinearModel.h"
#include <mitkIOUtil.h>
const std::string mitk::TwoStepLinearModel::NAME_PARAMETER_y0 = "y-intercept";
const std::string mitk::TwoStepLinearModel::NAME_PARAMETER_x0 = "x_changepoint";
const std::string mitk::TwoStepLinearModel::NAME_PARAMETER_b0 = "slope1";
const std::string mitk::TwoStepLinearModel::NAME_PARAMETER_b1 = "slope2";
const unsigned int mitk::TwoStepLinearModel::NUMBER_OF_PARAMETERS = 4;
const std::string mitk::TwoStepLinearModel::UNIT_PARAMETER_y0 = "[unit of y]";
const std::string mitk::TwoStepLinearModel::UNIT_PARAMETER_x0 = "[unit of x]";
const std::string mitk::TwoStepLinearModel::UNIT_PARAMETER_b0 = "[unit of y]/[unit of x]";
const std::string mitk::TwoStepLinearModel::UNIT_PARAMETER_b1 = "[unit of y]/[unit of x]";
const unsigned int mitk::TwoStepLinearModel::POSITION_PARAMETER_y0 = 0;
const unsigned int mitk::TwoStepLinearModel::POSITION_PARAMETER_x0 = 1;
const unsigned int mitk::TwoStepLinearModel::POSITION_PARAMETER_b0 = 2;
const unsigned int mitk::TwoStepLinearModel::POSITION_PARAMETER_b1 = 3;
const std::string mitk::TwoStepLinearModel::NAME_DERIVED_PARAMETER_auc = "auc";
const std::string mitk::TwoStepLinearModel::NAME_DERIVED_PARAMETER_y_fin = "y_final";
const std::string mitk::TwoStepLinearModel::NAME_DERIVED_PARAMETER_y_max = "y_max";
const std::string mitk::TwoStepLinearModel::NAME_DERIVED_PARAMETER_y1 = "y-intercept1";
const unsigned int mitk::TwoStepLinearModel::NUMBER_OF_DERIVED_PARAMETERS = 4;
const std::string mitk::TwoStepLinearModel::UNIT_DERIVED_PARAMETER_auc = "[unit of x]*[unit of y]";
const std::string mitk::TwoStepLinearModel::UNIT_DERIVED_PARAMETER_y_fin = "[unit of y]";
const std::string mitk::TwoStepLinearModel::UNIT_DERIVED_PARAMETER_y_max = "[unit of y]";
const std::string mitk::TwoStepLinearModel::UNIT_DERIVED_PARAMETER_y1 = "[unit of y]";
const unsigned int mitk::TwoStepLinearModel::NUMBER_OF_STATIC_PARAMETERS = 0;
const std::string mitk::TwoStepLinearModel::MODEL_DISPLAY_NAME = "Two Step Linear Model";
const std::string mitk::TwoStepLinearModel::MODEL_TYPE = "Generic";
const std::string mitk::TwoStepLinearModel::FUNCTION_STRING = "if x < x_changepoint: y(x) = y-intercept + slope1*x, else: y(x) = y-intercept1 + slope2*x";
const std::string mitk::TwoStepLinearModel::X_NAME = "x";
const std::string mitk::TwoStepLinearModel::X_AXIS_NAME = "X";
const std::string mitk::TwoStepLinearModel::X_AXIS_UNIT = "unit of x";
const std::string mitk::TwoStepLinearModel::Y_AXIS_NAME = "Y";
const std::string mitk::TwoStepLinearModel::Y_AXIS_UNIT = "unit of y";
std::string mitk::TwoStepLinearModel::GetModelDisplayName() const
{
return MODEL_DISPLAY_NAME;
};
std::string mitk::TwoStepLinearModel::GetModelType() const
{
return MODEL_TYPE;
};
mitk::TwoStepLinearModel::FunctionStringType mitk::TwoStepLinearModel::GetFunctionString() const
{
return FUNCTION_STRING;
};
std::string mitk::TwoStepLinearModel::GetXName() const
{
return X_NAME;
};
std::string mitk::TwoStepLinearModel::GetXAxisName() const
{
return X_AXIS_NAME;
};
std::string mitk::TwoStepLinearModel::GetXAxisUnit() const
{
return X_AXIS_UNIT;
}
std::string mitk::TwoStepLinearModel::GetYAxisName() const
{
return Y_AXIS_NAME;
};
std::string mitk::TwoStepLinearModel::GetYAxisUnit() const
{
return Y_AXIS_UNIT;
}
mitk::TwoStepLinearModel::ParameterNamesType
mitk::TwoStepLinearModel::GetParameterNames() const
{
ParameterNamesType result;
result.push_back(NAME_PARAMETER_y0);
result.push_back(NAME_PARAMETER_x0);
result.push_back(NAME_PARAMETER_b0);
result.push_back(NAME_PARAMETER_b1);
return result;
};
mitk::TwoStepLinearModel::ParamterUnitMapType mitk::TwoStepLinearModel::GetParameterUnits() const
{
ParamterUnitMapType result;
result.insert(std::make_pair(NAME_PARAMETER_y0, UNIT_PARAMETER_y0));
result.insert(std::make_pair(NAME_PARAMETER_x0, UNIT_PARAMETER_x0));
result.insert(std::make_pair(NAME_PARAMETER_b0, UNIT_PARAMETER_b0));
result.insert(std::make_pair(NAME_PARAMETER_b1, UNIT_PARAMETER_b1));
return result;
}
mitk::TwoStepLinearModel::ParametersSizeType
mitk::TwoStepLinearModel::GetNumberOfParameters() const
{
return NUMBER_OF_PARAMETERS;
};
mitk::TwoStepLinearModel::ParameterNamesType
mitk::TwoStepLinearModel::GetDerivedParameterNames() const
{
ParameterNamesType result;
result.push_back(NAME_DERIVED_PARAMETER_auc);
result.push_back(NAME_DERIVED_PARAMETER_y_fin);
result.push_back(NAME_DERIVED_PARAMETER_y_max);
result.push_back(NAME_DERIVED_PARAMETER_y1);
return result;
};
mitk::TwoStepLinearModel::ParametersSizeType
mitk::TwoStepLinearModel::GetNumberOfDerivedParameters() const
{
return NUMBER_OF_DERIVED_PARAMETERS;
};
mitk::TwoStepLinearModel::ParamterUnitMapType mitk::TwoStepLinearModel::GetDerivedParameterUnits() const
{
ParamterUnitMapType result;
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_auc, UNIT_DERIVED_PARAMETER_auc));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_fin, UNIT_DERIVED_PARAMETER_y_fin));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_max, UNIT_DERIVED_PARAMETER_y_max));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y1, UNIT_DERIVED_PARAMETER_y1));
return result;
};
double mitk::TwoStepLinearModel::ComputeSignalFromParameters(double x, double x0, double b0, double b1, double y0, double y1)
{
return (x < x0) ? (b0 * x + y0) : (b1 * x + y1);
};
mitk::TwoStepLinearModel::ModelResultType
mitk::TwoStepLinearModel::ComputeModelfunction(const ParametersType& parameters) const
{
//Model Parameters
const auto y0 = parameters[POSITION_PARAMETER_y0];
const auto x0 = parameters[POSITION_PARAMETER_x0] ;
const auto b0 = parameters[POSITION_PARAMETER_b0] ;
const auto b1 = parameters[POSITION_PARAMETER_b1] ;
double y1 = (b0 - b1) * x0 + y0;
ModelResultType signal(m_TimeGrid.GetSize());
TimeGridType::const_iterator timeGridEnd = m_TimeGrid.end();
ModelResultType::iterator signalPos = signal.begin();
for (TimeGridType::const_iterator gridPos = m_TimeGrid.begin(); gridPos != timeGridEnd; ++gridPos, ++signalPos)
{
*signalPos = ComputeSignalFromParameters(*gridPos, x0, b0, b1, y0, y1);
}
return signal;
};
mitk::TwoStepLinearModel::ParameterNamesType mitk::TwoStepLinearModel::GetStaticParameterNames() const
{
ParameterNamesType result;
return result;
}
mitk::TwoStepLinearModel::ParametersSizeType mitk::TwoStepLinearModel::GetNumberOfStaticParameters() const
{
return 0;
}
void mitk::TwoStepLinearModel::SetStaticParameter(const ParameterNameType& /*name*/,
const StaticParameterValuesType& /*values*/)
{
//do nothing
};
mitk::TwoStepLinearModel::StaticParameterValuesType mitk::TwoStepLinearModel::GetStaticParameterValue(
const ParameterNameType& /*name*/) const
{
StaticParameterValuesType result;
//do nothing
return result;
};
mitk::ModelBase::DerivedParameterMapType mitk::TwoStepLinearModel::ComputeDerivedParameters(
const mitk::ModelBase::ParametersType& parameters) const
{
const auto y0 = parameters[POSITION_PARAMETER_y0];
const auto x0 = parameters[POSITION_PARAMETER_x0] ;
const auto b0 = parameters[POSITION_PARAMETER_b0] ;
const auto b1 = parameters[POSITION_PARAMETER_b1] ;
const auto y1 = (b0 - b1) * x0 + y0;
unsigned int timeSteps = m_TimeGrid.GetSize();
- const double taq = (m_TimeGrid.empty() == false) ? (m_TimeGrid.GetElement(timeSteps - 1)) : (mitkThrow() << "An exception occured because time grid is empty, method can't continue.");
+ const double taq = (m_TimeGrid.empty() == false) ? (m_TimeGrid.GetElement(timeSteps - 1)) : (mitkThrow() << "An exception occurred because time grid is empty, method can't continue.");
const double y_fin = b1 * taq + y1;
double y_max = y_fin;
if ((b0 >= 0) && (b1 >= 0))
y_max = y_fin;
else if ((b0 < 0) && (b1 < 0))
y_max = y0;
else if ((b0 > 0) && (b1 < 0))
y_max = (b0 * x0 + y0);
else
{
if (abs(b0 * x0) >= abs(b1 * (taq - x0)))
y_max = y0;
else y_max = y_fin;
}
double auc = 0.0;
TimeGridType::const_iterator timeGridEnd = m_TimeGrid.end();
for (TimeGridType::const_iterator gridPos = m_TimeGrid.begin(); gridPos != timeGridEnd -1; ++gridPos)
{
double currentGridPos = *gridPos;
double nextGridPos = *(++gridPos);
double deltaX = nextGridPos - currentGridPos;
double deltaY = ComputeSignalFromParameters(nextGridPos, x0, b0, b1, y0, y1) - ComputeSignalFromParameters(currentGridPos, x0, b0, b1, y0, y1);
double Yi = ComputeSignalFromParameters(currentGridPos, x0, b0, b1, y0, y1);
double intI = 0.5 * deltaX * deltaY + Yi * deltaX;
auc += std::abs(intI);
--gridPos;
}
DerivedParameterMapType result;
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_auc, auc));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_fin, y_fin));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y_max, y_max));
result.insert(std::make_pair(NAME_DERIVED_PARAMETER_y1, y1));
return result;
};
itk::LightObject::Pointer mitk::TwoStepLinearModel::InternalClone() const
{
TwoStepLinearModel::Pointer newClone = TwoStepLinearModel::New();
newClone->SetTimeGrid(this->m_TimeGrid);
return newClone.GetPointer();
};
diff --git a/Modules/ModelFit/test/mitkModelFitInfoTest.cpp b/Modules/ModelFit/test/mitkModelFitInfoTest.cpp
index 692892d094..c082dd9fc6 100644
--- a/Modules/ModelFit/test/mitkModelFitInfoTest.cpp
+++ b/Modules/ModelFit/test/mitkModelFitInfoTest.cpp
@@ -1,425 +1,425 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <iostream>
#include "mitkProperties.h"
#include "mitkStandaloneDataStorage.h"
#include "mitkModelFitInfo.h"
#include "mitkModelFitConstants.h"
#include "mitkModelFitException.h"
#include "mitkModelFitResultRelationRule.h"
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkUIDGenerator.h>
mitk::modelFit::ModelFitInfo::UIDType ensureModelFitUID(mitk::BaseData * data)
{
mitk::BaseProperty::Pointer uidProp = data->GetProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME().c_str());
std::string propUID = "";
if (uidProp.IsNotNull())
{
propUID = uidProp->GetValueAsString();
}
else
{
mitk::UIDGenerator generator;
propUID = generator.GetUID();
data->SetProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(propUID));
}
return propUID;
};
mitk::DataNode::Pointer generateModelFitTestNode()
{
mitk::DataNode::Pointer node = mitk::DataNode::New();
node->SetName("Param1");
auto testImage = mitk::Image::New();
node->SetData(testImage);
testImage->SetProperty("modelfit.testEmpty", mitk::StringProperty::New(""));
testImage->SetProperty("modelfit.testValid", mitk::StringProperty::New("test"));
ensureModelFitUID(testImage);
testImage->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy"));
testImage->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName"));
testImage->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str()));
testImage->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("input UID"));
testImage->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels"));
testImage->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1"));
testImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New(""));
testImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass"));
testImage->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX"));
testImage->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str()));
testImage->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h"));
testImage->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str()));
testImage->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg"));
return node;
}
class mitkModelFitInfoTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkModelFitInfoTestSuite);
MITK_TEST(CheckModelFitInfo);
MITK_TEST(CheckGetMandatoryProperty);
MITK_TEST(CheckCreateFitInfoFromNode_Legacy);
MITK_TEST(CheckCreateFitInfoFromNode);
MITK_TEST(CheckGetNodesOfFit);
MITK_TEST(CheckGetFitUIDsOfNode);
CPPUNIT_TEST_SUITE_END();
mitk::StandaloneDataStorage::Pointer m_Storage;
mitk::Image::Pointer m_ParamImage;
mitk::Image::Pointer m_ParamImage2;
mitk::Image::Pointer m_ParamImage_legacy;
mitk::Image::Pointer m_ParamImage2_legacy;
mitk::DataNode::Pointer m_ParamImageNode;
mitk::DataNode::Pointer m_ParamImageNode2;
mitk::DataNode::Pointer m_ParamImageNode_legacy;
mitk::DataNode::Pointer m_ParamImageNode2_legacy;
public:
void setUp() override
{
m_Storage = mitk::StandaloneDataStorage::New();
//create input node
mitk::DataNode::Pointer inputNode = mitk::DataNode::New();
inputNode->SetName("Input");
mitk::Image::Pointer image = mitk::Image::New();
inputNode->SetData(image);
mitk::modelFit::ModelFitInfo::UIDType inputUID = ensureModelFitUID(image);
m_Storage->Add(inputNode);
mitk::DataStorage::SetOfObjects::Pointer parents = mitk::DataStorage::SetOfObjects::New();
parents->push_back(inputNode);
/////////////////////////////////////////////////////
//Create nodes for a fit (new style using rules)
/////////////////////////////////////////////////////
auto rule = mitk::ModelFitResultRelationRule::New();
//create first result for FitLegacy
m_ParamImageNode = mitk::DataNode::New();
m_ParamImage = mitk::Image::New();
m_ParamImageNode->SetData(m_ParamImage);
m_ParamImageNode->SetName("Param1");
m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str()));
m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New(""));
m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param1"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("b"));
m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER().c_str()));
rule->Connect(m_ParamImage, image);
m_Storage->Add(m_ParamImageNode, parents);
//create second result for Fit
m_ParamImageNode2 = mitk::DataNode::New();
m_ParamImageNode2->SetName("Param2");
m_ParamImage2 = mitk::Image::New();
m_ParamImageNode2->SetData(m_ParamImage2);
m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str()));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New(""));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param2"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a"));
m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER().c_str()));
rule->Connect(m_ParamImage2, image);
m_Storage->Add(m_ParamImageNode2, parents);
/////////////////////////////////////////////////////
//Create nodes for a fit in legacy mode (old fit uid)
/////////////////////////////////////////////////////
//create first result for FitLegacy
m_ParamImageNode_legacy = mitk::DataNode::New();
m_ParamImage_legacy = mitk::Image::New();
m_ParamImageNode_legacy->SetData(m_ParamImage_legacy);
m_ParamImageNode_legacy->SetName("Param1_legacy");
ensureModelFitUID(m_ParamImage_legacy);
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str()));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(inputUID.c_str()));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New(""));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param1_legacy"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("b"));
m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER().c_str()));
m_Storage->Add(m_ParamImageNode_legacy, parents);
//create second result for FitLegacy
m_ParamImageNode2_legacy = mitk::DataNode::New();
m_ParamImageNode2_legacy->SetName("Param2_legacy");
m_ParamImage2_legacy = mitk::Image::New();
m_ParamImageNode2_legacy->SetData(m_ParamImage2_legacy);
ensureModelFitUID(m_ParamImage2_legacy);
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str()));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(inputUID.c_str()));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New(""));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str()));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param2_legacy"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a"));
m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER().c_str()));
m_Storage->Add(m_ParamImageNode2_legacy, parents);
/////////////////////////////////////////////////////
//Create nodes for a fit on other input
auto anotherInputNode = mitk::DataNode::New();
anotherInputNode->SetName("AnotherInput");
auto anotherImage = mitk::Image::New();
anotherInputNode->SetData(anotherImage);
m_Storage->Add(anotherInputNode);
parents = mitk::DataStorage::SetOfObjects::New();
parents->push_back(anotherInputNode);
mitk::DataNode::Pointer node3 = mitk::DataNode::New();
node3->SetName("Param_Other");
mitk::Image::Pointer paramImage3 = mitk::Image::New();
node3->SetData(paramImage3);
paramImage3->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit2"));
paramImage3->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName2"));
paramImage3->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str()));
paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels"));
paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_2"));
paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New(""));
paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass_B"));
paramImage3->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param_Other"));
paramImage3->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a"));
rule->Connect(paramImage3, anotherImage);
m_Storage->Add(node3, parents);
}
void tearDown() override
{
}
void CheckModelFitInfo()
{
mitk::modelFit::Parameter::Pointer p = mitk::modelFit::Parameter::New();
p->name = "p";
mitk::modelFit::ModelFitInfo::Pointer fit = mitk::modelFit::ModelFitInfo::New();
fit->AddParameter(p);
CPPUNIT_ASSERT_MESSAGE("AddParameter unsuccessfully adds a parameter.", fit->GetParameters().size() == 1);
mitk::modelFit::Parameter::ConstPointer resultParam = fit->GetParameter("test",
mitk::modelFit::Parameter::ParameterType);
CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns NULL for wrong parameter.", resultParam.IsNull());
resultParam = fit->GetParameter("p", mitk::modelFit::Parameter::ParameterType);
CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns the correct parameter.", resultParam == p);
p->type = mitk::modelFit::Parameter::CriterionType;
resultParam = fit->GetParameter("p", mitk::modelFit::Parameter::CriterionType);
CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns the correct parameter with a non-default type.", resultParam == p);
fit->DeleteParameter("test", mitk::modelFit::Parameter::CriterionType);
CPPUNIT_ASSERT_MESSAGE("Testing if DeleteParameter fails for wrong parameter.", fit->GetParameters().size() == 1);
fit->DeleteParameter("p", mitk::modelFit::Parameter::CriterionType);
CPPUNIT_ASSERT_MESSAGE("Testing if DeleteParameter successfully removes a parameter.", fit->GetParameters().size() == 0);
}
void CheckGetMandatoryProperty()
{
mitk::DataNode::Pointer node = generateModelFitTestNode();
mitk::DataNode::Pointer invalideNode = mitk::DataNode::New();
CPPUNIT_ASSERT_THROW(mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testInvalid"), mitk::modelFit::ModelFitException);
CPPUNIT_ASSERT_THROW(mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testEmpty"), mitk::modelFit::ModelFitException);
CPPUNIT_ASSERT_MESSAGE("Testing if GetMandatoryProperty returns the correct value.", mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testValid")
== "test");
}
void CheckCreateFitInfoFromNode_Legacy()
{
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for invalid node.", mitk::modelFit::CreateFitInfoFromNode("FitLegacy", nullptr).IsNull());
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for node with missing properties.", mitk::modelFit::CreateFitInfoFromNode("invalide_UID", m_Storage).IsNull());
mitk::modelFit::ModelFitInfo::Pointer resultFit = mitk::modelFit::CreateFitInfoFromNode("FitLegacy", m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns a valid model fit info.", resultFit.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode creates a fit with correct attributes.",
resultFit->fitType == mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED() &&
resultFit->uid == "FitLegacy" &&
resultFit->fitName == "MyName1" &&
resultFit->modelType == "TestModels" &&
resultFit->modelName == "TestModel_1" &&
resultFit->function == "" &&
resultFit->functionClassID == "ModelClass" &&
resultFit->x == "myX" &&
resultFit->xAxisName == mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT() &&
resultFit->xAxisUnit == "h" &&
resultFit->yAxisName == mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT() &&
resultFit->yAxisUnit == "kg" &&
resultFit->GetParameters().size() == 2);
mitk::modelFit::Parameter::ConstPointer param = resultFit->GetParameter("Param1_legacy", mitk::modelFit::Parameter::ParameterType);
CPPUNIT_ASSERT_MESSAGE("Testing if param 1 exists.", param.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing if param 1 is configured correctly.", param->name == "Param1_legacy" && param->unit == "b" && param->image == m_ParamImage_legacy);
mitk::modelFit::Parameter::ConstPointer param2 = resultFit->GetParameter("Param2_legacy", mitk::modelFit::Parameter::DerivedType);
CPPUNIT_ASSERT_MESSAGE("Testing if param 2 exists.", param2.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing if param 2 is configured correctly.", param2->name == "Param2_legacy" && param2->unit == "a" && param2->image == m_ParamImage2_legacy);
}
void CheckCreateFitInfoFromNode()
{
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for invalid node.", mitk::modelFit::CreateFitInfoFromNode("Fit", nullptr).IsNull());
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for node with missing properties.", mitk::modelFit::CreateFitInfoFromNode("invalide_UID", m_Storage).IsNull());
mitk::modelFit::ModelFitInfo::Pointer resultFit = mitk::modelFit::CreateFitInfoFromNode("Fit", m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns a valid model fit info.", resultFit.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode creates a fit with correct attributes.",
resultFit->fitType == mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED() &&
resultFit->uid == "Fit" &&
resultFit->fitName == "MyName1" &&
resultFit->modelType == "TestModels" &&
resultFit->modelName == "TestModel_1" &&
resultFit->function == "" &&
resultFit->functionClassID == "ModelClass" &&
resultFit->x == "myX" &&
resultFit->xAxisName == mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT() &&
resultFit->xAxisUnit == "h" &&
resultFit->yAxisName == mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT() &&
resultFit->yAxisUnit == "kg" &&
resultFit->GetParameters().size() == 2);
mitk::modelFit::Parameter::ConstPointer param = resultFit->GetParameter("Param1", mitk::modelFit::Parameter::ParameterType);
CPPUNIT_ASSERT_MESSAGE("Testing if param 1 exists.", param.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing if param 1 is configured correctly.", param->name == "Param1" && param->unit == "b" && param->image == m_ParamImage);
mitk::modelFit::Parameter::ConstPointer param2 = resultFit->GetParameter("Param2", mitk::modelFit::Parameter::DerivedType);
CPPUNIT_ASSERT_MESSAGE("Testing if param 2 exists.", param2.IsNotNull());
CPPUNIT_ASSERT_MESSAGE("Testing if param 2 is configured correctly.", param2->name == "Param2" && param2->unit == "a" && param2->image == m_ParamImage2);
}
void CheckGetNodesOfFit()
{
auto nodes = mitk::modelFit::GetNodesOfFit("Fit", m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for Fit",
nodes->Size() == 2);
CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode.GetPointer()) != nodes->end());
CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode2.GetPointer()) != nodes->end());
nodes = mitk::modelFit::GetNodesOfFit("FitLegacy", m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for FitLegacy",
nodes->Size() == 2);
CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode_legacy.GetPointer()) != nodes->end());
CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode2_legacy.GetPointer()) != nodes->end());
CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for Fit2",
mitk::modelFit::GetNodesOfFit("Fit2", m_Storage)->Size() == 1);
- CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for unkown fits.",
+ CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for unknown fits.",
mitk::modelFit::GetNodesOfFit("unkown_fit", m_Storage)->Size() == 0);
CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for illegal calls.",
mitk::modelFit::GetNodesOfFit("unkown_fit", nullptr).IsNull());
}
void CheckGetFitUIDsOfNode()
{
auto testNode = m_Storage->GetNamedNode("Input");
mitk::modelFit::NodeUIDSetType uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly.",
uidSet.size() == 2 &&
uidSet.find("Fit") != uidSet.end() &&
uidSet.find("FitLegacy") != uidSet.end());
testNode = m_Storage->GetNamedNode("AnotherInput");
uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly.",
uidSet.size() == 1 &&
uidSet.find("Fit2") != uidSet.end());
uidSet = mitk::modelFit::GetFitUIDsOfNode(nullptr, m_Storage);
CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly with invalid node.",
uidSet.size() == 0);
uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, nullptr);
CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly with invalid storage.",
uidSet.size() == 0);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkModelFitInfo)
\ No newline at end of file
diff --git a/Modules/ModelFit/test/mitkModelFitResultRelationRuleTest.cpp b/Modules/ModelFit/test/mitkModelFitResultRelationRuleTest.cpp
index 7fb29e0d42..5e70556220 100644
--- a/Modules/ModelFit/test/mitkModelFitResultRelationRuleTest.cpp
+++ b/Modules/ModelFit/test/mitkModelFitResultRelationRuleTest.cpp
@@ -1,732 +1,732 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkModelFitResultRelationRule.h"
#include "mitkDataNode.h"
#include "mitkPointSet.h"
#include "mitkStringProperty.h"
#include "mitkTestFixture.h"
#include "mitkTestingMacros.h"
#include "mitkPropertyNameHelper.h"
#include "mitkTemporoSpatialStringProperty.h"
#include "mitkPropertyNameHelper.h"
#include "mitkModelFitConstants.h"
#include "mitkUIDGenerator.h"
#include <regex>
class mitkModelFitResultRelationRuleTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkModelFitResultRelationRuleTestSuite);
MITK_TEST(ConstructionAndGetter);
MITK_TEST(IsSourceCandidate);
MITK_TEST(IsDestinationCandidate);
MITK_TEST(IsSource);
MITK_TEST(HasRelation);
MITK_TEST(GetExistingRelations);
MITK_TEST(GetRelationUIDs);
MITK_TEST(GetSourceCandidateIndicator);
MITK_TEST(GetDestinationCandidateIndicator);
MITK_TEST(GetConnectedSourcesDetector);
MITK_TEST(GetSourcesDetector);
MITK_TEST(Connect);
MITK_TEST(Disconnect);
CPPUNIT_TEST_SUITE_END();
private:
mitk::ModelFitResultRelationRule::Pointer rule;
mitk::Image::Pointer unRelated;
mitk::DataNode::Pointer unRelated_Node;
mitk::Image::Pointer source_implicit_1;
mitk::DataNode::Pointer source_implicit_1_Node;
mitk::Image::Pointer source_Data_1;
mitk::DataNode::Pointer source_Data_1_Node;
mitk::Image::Pointer source_idOnly_1;
mitk::DataNode::Pointer source_idOnly_1_Node;
mitk::Image::Pointer source_1;
mitk::DataNode::Pointer source_1_Node;
mitk::Image::Pointer source_legacy_1;
mitk::DataNode::Pointer source_legacy_1_Node;
mitk::Image::Pointer source_otherRule;
mitk::DataNode::Pointer source_otherRule_Node;
mitk::Image::Pointer source_otherPurpose;
mitk::DataNode::Pointer source_otherPurpose_Node; //relevant for abstract rule checks. Abstract rule should see it concrete rule not.
mitk::DataNode::Pointer dest_1_Node;
mitk::Image::Pointer dest_1;
mitk::DataNode::Pointer dest_2_Node;
mitk::Image::Pointer dest_2;
bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const
{
auto keyPath = mitk::PropertyRelationRuleBase::GetRootKeyPath();
if (!instance.empty())
{
keyPath.AddElement(instance);
}
auto prefix = mitk::PropertyKeyPathToPropertyName(keyPath);
auto keys = provider->GetPropertyKeys();
for (const auto &key : keys)
{
if (key.find(prefix) == 0)
{
return true;
}
}
return false;
}
std::vector<std::string> GetReferenceSequenceIndices(const mitk::IPropertyProvider * source,
const mitk::IPropertyProvider * destination) const
{
std::vector<std::string> result;
auto destInstanceUIDProp = destination->GetConstProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018));
if (destInstanceUIDProp.IsNull())
{
return result;
}
mitk::PropertyKeyPath referencedInstanceUIDs;
referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155");
auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);;
auto regEx = std::regex(sourceRegExStr);
std::vector<std::string> keys;
//workaround until T24729 is done. Please remove if T24728 is done
keys = source->GetPropertyKeys();
//end workaround for T24729
for (const auto &key : keys)
{
if (std::regex_match(key, regEx))
{
auto refUIDProp = source->GetConstProperty(key);
if (*refUIDProp == *destInstanceUIDProp)
{
mitk::PropertyKeyPath finding = mitk::PropertyNameToPropertyKeyPath(key);
result.push_back(std::to_string(finding.GetNode(2).selection));
}
}
}
return result;
};
void SetDICOMReferenceInfo(mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement)
{
mitk::PropertyKeyPath refInstanceUIDPath;
refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155");
owner->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), mitk::TemporoSpatialStringProperty::New(instanceUID));
mitk::PropertyKeyPath refClassUIDPath;
refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150");
owner->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), mitk::TemporoSpatialStringProperty::New(classUID));
mitk::PropertyKeyPath purposePath;
purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104");
owner->SetProperty(PropertyKeyPathToPropertyName(purposePath), mitk::TemporoSpatialStringProperty::New(purpose));
}
bool IsCorrectDICOMReference(const mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) const
{
mitk::PropertyKeyPath refInstanceUIDPath;
refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155");
auto prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath));
if (prop->GetValueAsString() != instanceUID)
{
return false;
}
mitk::PropertyKeyPath refClassUIDPath;
refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150");
prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refClassUIDPath));
if (prop->GetValueAsString() != classUID)
{
return false;
}
mitk::PropertyKeyPath purposePath;
purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104");
prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(purposePath));
if (prop->GetValueAsString() != purpose)
{
return false;
}
return true;
}
public:
void setUp() override
{
auto instanceUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018);
auto classUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0016);
rule = mitk::ModelFitResultRelationRule::New();
unRelated = mitk::Image::New();
unRelated->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("unRelated"));
unRelated->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image"));
unRelated_Node = mitk::DataNode::New();
unRelated_Node->SetData(unRelated);
dest_1_Node = mitk::DataNode::New();
dest_1_Node->SetName("dest_1");
dest_1 = mitk::Image::New();
dest_1->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_1"));
dest_1->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image"));
dest_1->GetTimeGeometry()->Expand(2);
dest_1_Node->SetData(dest_1);
//support of legacy mode
mitk::UIDGenerator generator;
auto legacyUID = generator.GetUID();
dest_1->SetProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(legacyUID));
dest_2_Node = mitk::DataNode::New();
dest_2_Node->SetName("dest_2");
dest_2 = mitk::Image::New();
dest_2->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_2"));
dest_2->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image"));
dest_2->GetTimeGeometry()->Expand(3);
dest_2_Node->SetData(dest_2);
source_implicit_1 = mitk::Image::New();
SetDICOMReferenceInfo(source_implicit_1, "dest_1", "image", "Model fit input", 0);
source_implicit_1_Node = mitk::DataNode::New();
source_implicit_1_Node->SetData(source_implicit_1);
source_idOnly_1 = mitk::Image::New();
std::string name = "MITK.Relations.1.relationUID";
source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid1"));
name = "MITK.Relations.1.destinationUID";
source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID()));
name = "MITK.Relations.1.ruleID";
source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID()));
source_idOnly_1_Node = mitk::DataNode::New();
source_idOnly_1_Node->SetData(source_idOnly_1);
source_Data_1 = mitk::Image::New();
SetDICOMReferenceInfo(source_Data_1, "dest_1", "image", "Model fit input", 0);
SetDICOMReferenceInfo(source_Data_1, "dest_2", "image", "otherpurpose", 1);
name = "MITK.Relations.1.relationUID";
source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid2"));
name = "MITK.Relations.1.ruleID";
source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID()));
name = "MITK.Relations.1.SourceImageSequenceItem";
source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("0"));
name = "MITK.Relations.2.relationUID";
source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid10"));
name = "MITK.Relations.2.SourceImageSequenceItem";
source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("1"));
name = "MITK.Relations.2.ruleID";
source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose"));
source_Data_1_Node = mitk::DataNode::New();
source_Data_1_Node->SetData(source_Data_1);
source_1 = mitk::Image::New();
SetDICOMReferenceInfo(source_1, "dest_1", "image", "Model fit input", 0);
SetDICOMReferenceInfo(source_1, "dest_2", "image", "otherpurpose", 1);
name = "MITK.Relations.1.relationUID";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid3"));
name = "MITK.Relations.1.destinationUID";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID()));
name = "MITK.Relations.1.ruleID";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID()));
name = "MITK.Relations.1.SourceImageSequenceItem";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New("0"));
name = "MITK.Relations.2.relationUID";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid8"));
name = "MITK.Relations.2.destinationUID";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_2->GetUID()));
name = "MITK.Relations.2.ruleID";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose"));
name = "MITK.Relations.2.SourceImageSequenceItem";
source_1->SetProperty(name.c_str(), mitk::StringProperty::New("1"));
source_1_Node = mitk::DataNode::New();
source_1_Node->SetData(source_1);
source_legacy_1 = mitk::Image::New();
source_legacy_1->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(legacyUID.c_str()));
source_legacy_1_Node = mitk::DataNode::New();
source_legacy_1_Node->SetData(source_legacy_1);
source_otherRule = mitk::Image::New();
name = "MITK.Relations.1.relationUID";
source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("uid7"));
name = "MITK.Relations.1.destinationUID";
source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID()));
name = "MITK.Relations.1.ruleID";
source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("otherRuleID"));
source_otherRule_Node = mitk::DataNode::New();
source_otherRule_Node->SetData(source_otherRule);
source_otherPurpose = mitk::Image::New();
name = "MITK.Relations.1.relationUID";
source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("uid9"));
name = "MITK.Relations.1.destinationUID";
source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID()));
name = "MITK.Relations.1.ruleID";
source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose"));
source_otherPurpose_Node = mitk::DataNode::New();
source_otherPurpose_Node->SetData(source_otherPurpose);
}
void tearDown() override {}
void ConstructionAndGetter()
{
CPPUNIT_ASSERT(!rule->IsAbstract());
CPPUNIT_ASSERT_EQUAL(rule->GetRuleID(), std::string("SourceImageRelation Model fit input"));
CPPUNIT_ASSERT_EQUAL(rule->GetDisplayName(), std::string("Model fit result relation"));
CPPUNIT_ASSERT_EQUAL(rule->GetSourceRoleName(), std::string("fit result"));
CPPUNIT_ASSERT_EQUAL(rule->GetDestinationRoleName(), std::string("source image"));
}
void IsSourceCandidate()
{
CPPUNIT_ASSERT(rule->IsSourceCandidate(mitk::DataNode::New()));
CPPUNIT_ASSERT(!rule->IsSourceCandidate(nullptr));
}
void IsDestinationCandidate()
{
CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1_Node));
CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1));
CPPUNIT_ASSERT(!rule->IsDestinationCandidate(mitk::DataNode::New()));
CPPUNIT_ASSERT(!rule->IsDestinationCandidate(nullptr));
CPPUNIT_ASSERT(!rule->IsDestinationCandidate(mitk::Image::New()));
}
void IsSource()
{
CPPUNIT_ASSERT_THROW_MESSAGE(
"Violated precondition (nullptr) does not throw.", rule->IsSource(nullptr), itk::ExceptionObject);
CPPUNIT_ASSERT(!rule->IsSource(unRelated));
CPPUNIT_ASSERT(rule->IsSource(source_implicit_1));
CPPUNIT_ASSERT(rule->IsSource(source_Data_1));
CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1));
CPPUNIT_ASSERT(rule->IsSource(source_1));
CPPUNIT_ASSERT(rule->IsSource(source_legacy_1));
CPPUNIT_ASSERT(!rule->IsSource(source_otherRule));
CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose));
CPPUNIT_ASSERT(rule->IsSource(source_implicit_1_Node));
CPPUNIT_ASSERT(rule->IsSource(source_Data_1_Node));
CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1_Node));
CPPUNIT_ASSERT(rule->IsSource(source_1_Node));
CPPUNIT_ASSERT(!rule->IsSource(source_otherRule_Node));
CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose_Node));
}
void HasRelation()
{
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.",
rule->HasRelation(nullptr, dest_1),
itk::ExceptionObject);
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.",
rule->HasRelation(source_1, nullptr),
itk::ExceptionObject);
CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(!rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(!rule->HasRelation(source_otherRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(!rule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1));
CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID));
CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1));
CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID));
CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1));
CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID));
CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1));
CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID));
CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
CPPUNIT_ASSERT(rule->HasRelation(source_legacy_1, dest_1));
CPPUNIT_ASSERT(rule->HasRelation(source_legacy_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(rule->HasRelation(source_legacy_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
CPPUNIT_ASSERT(!rule->HasRelation(source_legacy_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID));
CPPUNIT_ASSERT(!rule->HasRelation(source_legacy_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None));
}
void GetExistingRelations()
{
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.",
rule->GetExistingRelations(nullptr),
itk::ExceptionObject);
CPPUNIT_ASSERT(rule->GetExistingRelations(unRelated).empty());
CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherRule).empty());
CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherPurpose).empty());
auto uids = rule->GetExistingRelations(source_implicit_1);
CPPUNIT_ASSERT(uids.size() == 1);
CPPUNIT_ASSERT(uids.front() == "DICOM.0008.2112.[0].0008.1155");
uids = rule->GetExistingRelations(source_idOnly_1);
CPPUNIT_ASSERT(uids.size() == 1);
CPPUNIT_ASSERT(uids.front() == "uid1");
uids = rule->GetExistingRelations(source_Data_1);
CPPUNIT_ASSERT(uids.size() == 1);
CPPUNIT_ASSERT(uids.front() == "uid2");
uids = rule->GetExistingRelations(source_1);
CPPUNIT_ASSERT(uids.size() == 1);
CPPUNIT_ASSERT(uids.front() == "uid3");
uids = rule->GetExistingRelations(source_legacy_1);
CPPUNIT_ASSERT(uids.size() == 1);
CPPUNIT_ASSERT(uids.front() == "model.fit.input.image.legacy.relation");
}
void GetRelationUIDs()
{
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.",
rule->GetRelationUIDs(nullptr, dest_1),
itk::ExceptionObject);
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.",
rule->GetRelationUIDs(source_1, nullptr),
itk::ExceptionObject);
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, unRelated).empty());
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, dest_2).empty());
CPPUNIT_ASSERT(rule->GetRelationUIDs(unRelated, dest_1).empty());
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherRule, dest_1).empty());
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherPurpose, dest_1).empty());
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1");
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_Data_1, dest_1).front() == "uid2");
auto uids = rule->GetRelationUIDs(source_1, dest_1);
CPPUNIT_ASSERT(uids.size() == 1);
CPPUNIT_ASSERT(uids.front() == "uid3");
CPPUNIT_ASSERT(rule->GetRelationUIDs(source_legacy_1, dest_1).front() == "model.fit.input.image.legacy.relation");
}
void GetSourceCandidateIndicator()
{
auto predicate = rule->GetSourceCandidateIndicator();
CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New()));
CPPUNIT_ASSERT(!predicate->CheckNode(nullptr));
}
void GetDestinationCandidateIndicator()
{
auto predicate = rule->GetDestinationCandidateIndicator();
CPPUNIT_ASSERT(predicate->CheckNode(this->dest_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(mitk::DataNode::New()));
CPPUNIT_ASSERT(!predicate->CheckNode(nullptr));
CPPUNIT_ASSERT(!predicate->CheckNode(mitk::DataNode::New()));
}
void GetConnectedSourcesDetector()
{
auto predicate = rule->GetConnectedSourcesDetector();
CPPUNIT_ASSERT(!predicate->CheckNode(nullptr));
CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node));
}
void GetSourcesDetector()
{
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.",
rule->GetSourcesDetector(nullptr),
itk::ExceptionObject);
auto predicate = rule->GetSourcesDetector(dest_1);
CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_legacy_1_Node));
predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data);
CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_legacy_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_legacy_1_Node));
predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID);
CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_legacy_1_Node));
predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete);
CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node));
CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_legacy_1_Node));
predicate = rule->GetSourcesDetector(dest_2, mitk::PropertyRelationRuleBase::RelationType::Data);
CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_1_Node));
CPPUNIT_ASSERT(!predicate->CheckNode(source_legacy_1_Node));
}
void Connect()
{
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.",
rule->Connect(nullptr, dest_1),
itk::ExceptionObject);
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.",
rule->Connect(source_1, nullptr),
itk::ExceptionObject);
// check upgrade of an implicit connection
CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
rule->Connect(source_implicit_1, dest_1);
CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
auto dcmRefs = GetReferenceSequenceIndices(source_implicit_1, dest_1);
CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1);
CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_implicit_1, "dest_1", "image", "Model fit input", 0));
// check upgrade and reuse of an Data connection (no new relation should be generated).
CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data));
rule->Connect(source_Data_1, dest_1);
CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
auto relUID = rule->GetRelationUIDs(source_Data_1, dest_1);
CPPUNIT_ASSERT(relUID.size() == 1);
std::string name = "MITK.Relations.1.destinationUID";
auto prop = source_Data_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE(
"Destination uid was not stored with the correct key. Already existing session should be used.", prop);
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID());
name = "MITK.Relations.1.ruleID";
prop = source_Data_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID());
name = "MITK.Relations.1.SourceImageSequenceItem";
prop = source_Data_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0");
dcmRefs = GetReferenceSequenceIndices(source_Data_1, dest_1);
CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1);
CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_Data_1, "dest_1", "image", "Model fit input", 0));
// check actualization of an id only connection
rule->Connect(source_idOnly_1, dest_1);
CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
- CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.",
+ CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating existing one.",
rule->GetExistingRelations(source_1).size() == 1);
// check actualization of an existing connection
rule->Connect(source_1, dest_1);
CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
- CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.",
+ CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating existing one.",
rule->GetExistingRelations(source_1).size() == 1);
name = "MITK.Relations.1.destinationUID";
prop = source_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE(
"Destination uid was not stored with the correct key. Already existing session should be used.", prop);
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID());
name = "MITK.Relations.1.ruleID";
prop = source_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID());
name = "MITK.Relations.1.SourceImageSequenceItem";
prop = source_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0");
dcmRefs = GetReferenceSequenceIndices(source_1, dest_1);
CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1);
CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Model fit input", 0));
// check creation of an new connection
rule->Connect(unRelated, dest_1);
CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete));
- CPPUNIT_ASSERT_MESSAGE("Relation was not defined instead of updating exting one.",
+ CPPUNIT_ASSERT_MESSAGE("Relation was not defined instead of updating existing one.",
rule->GetExistingRelations(unRelated).size() == 1);
name = "MITK.Relations.1.destinationUID";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID());
name = "MITK.Relations.1.ruleID";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID());
name = "MITK.Relations.1.SourceImageSequenceItem";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0");
dcmRefs = GetReferenceSequenceIndices(unRelated, dest_1);
CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1);
- CPPUNIT_ASSERT_MESSAGE("Dicom reference squence is corrupted. Should be just an index 0.", dcmRefs[0] == "0");
+ CPPUNIT_ASSERT_MESSAGE("Dicom reference sequence is corrupted. Should be just an index 0.", dcmRefs[0] == "0");
CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Model fit input", 0));
// check creation of a 2nd connection of the same purpose
rule->Connect(unRelated, dest_2);
CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete));
CPPUNIT_ASSERT_MESSAGE("Additional relation was not defined.",
rule->GetExistingRelations(unRelated).size() == 2);
name = "MITK.Relations.1.destinationUID";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID());
name = "MITK.Relations.1.ruleID";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID());
name = "MITK.Relations.1.SourceImageSequenceItem";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0");
name = "MITK.Relations.2.destinationUID";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_2->GetUID());
name = "MITK.Relations.2.ruleID";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID());
name = "MITK.Relations.2.SourceImageSequenceItem";
prop = unRelated->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "1");
dcmRefs = GetReferenceSequenceIndices(unRelated, dest_2);
CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was not defined.", dcmRefs.size() == 1);
CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Model fit input", 0));
CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_2", "image", "Model fit input", 1));
}
void Disconnect()
{
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.",
rule->Disconnect(nullptr, dest_1),
itk::ExceptionObject);
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.",
rule->Disconnect(source_1, nullptr),
itk::ExceptionObject);
CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.",
rule->Disconnect(nullptr, "uid"),
itk::ExceptionObject);
CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None));
rule->Disconnect(source_1, unRelated);
CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT_MESSAGE("Other relationData property was removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Model fit input", 0));
//check if index correction is correct, when disconnecting
rule->Connect(source_1, dest_2);
rule->Connect(source_1, unRelated);
rule->Disconnect(source_1, dest_2);
CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None));
CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "1"));
CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "2"));
CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "3"));
CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "4"));
CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_1 has been removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Model fit input", 0));
CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_2 (other purpose) has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "dest_2", "image", "otherpurpose", 1));
CPPUNIT_ASSERT_MESSAGE("Dicom reference to unRelated has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "unRelated", "image", "Model fit input", 2));
std::string name = "MITK.Relations.4.destinationUID";
auto prop = source_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE(
"Destination uid was not stored with the correct key. Already existing session should be used.", prop);
CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == unRelated->GetUID());
name = "MITK.Relations.4.SourceImageSequenceItem";
prop = source_1->GetProperty(name.c_str());
CPPUNIT_ASSERT_MESSAGE("SourceImageSequenceItem was not actualized correctly.", prop->GetValueAsString() == "2");
rule->Disconnect(source_otherPurpose, dest_1);
CPPUNIT_ASSERT_MESSAGE("Data of other rule purpose was removed.", this->hasRelationProperties(source_otherPurpose, "1"));
}
};
MITK_TEST_SUITE_REGISTRATION(mitkModelFitResultRelationRule)
diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.cpp b/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.cpp
index 8b968fb5d6..5d8e1c4e92 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.cpp
+++ b/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.cpp
@@ -1,338 +1,350 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkModelFitParameterValueExtraction.h"
#include "QmitkFitParameterModel.h"
QmitkFitParameterModel::
QmitkFitParameterModel(QObject* parent) :
QAbstractTableModel(parent)
{
m_CurrentPos.Fill(0.0);
}
const QmitkFitParameterModel::FitVectorType&
QmitkFitParameterModel::
getFits() const
{
return m_Fits;
};
mitk::Point3D
QmitkFitParameterModel::
getCurrentPosition() const
{
return m_CurrentPos;
};
const mitk::PointSet*
QmitkFitParameterModel::
getPositionBookmarks() const
{
return m_Bookmarks;
};
void
QmitkFitParameterModel::
setFits(const FitVectorType& fits)
{
emit beginResetModel();
m_Fits = fits;
emit endResetModel();
};
void
QmitkFitParameterModel::
setCurrentPosition(const mitk::Point3D& currentPos)
{
emit beginResetModel();
m_CurrentPos = currentPos;
emit endResetModel();
};
void
QmitkFitParameterModel::
setPositionBookmarks(const mitk::PointSet* bookmarks)
{
emit beginResetModel();
m_Bookmarks = bookmarks;
emit endResetModel();
};
bool
QmitkFitParameterModel::
hasSingleFit() const
{
return this->m_Fits.size() == 1;
};
int
QmitkFitParameterModel::
rowCount(const QModelIndex& parent) const
{
if (this->hasSingleFit())
{
if (parent.isValid())
{
return 0;
}
else
{
return this->m_Fits.front()->GetParameters().size() + this->m_Fits.front()->staticParamMap.Size();
}
}
else
{
if (parent.isValid())
{
auto row = static_cast<std::size_t>(parent.row());
assert(row < this->m_Fits.size());
return this->m_Fits[row]->GetParameters().size() + this->m_Fits[row]->staticParamMap.Size();
}
else
{
return this->m_Fits.size();
}
}
}
std::size_t
QmitkFitParameterModel::
getBookmarksCount() const
{
if (m_Bookmarks.IsNotNull())
{
return m_Bookmarks->GetSize();
}
return 0;
}
int
QmitkFitParameterModel::
columnCount(const QModelIndex&) const
{
return 3 + this->getBookmarksCount();
}
/** Helper function returns the name of the static parameter indicates by the index.
If the index does not indicate a static parameter an empty string will be returned.*/
std::string GetStaticParameterName(const mitk::modelFit::ModelFitInfo* currentFit, const QModelIndex& index)
{
const auto paramSize = static_cast<int>(currentFit->GetParameters().size());
std::string staticParamName;
if (index.row() >= paramSize)
{
int pos = paramSize;
for (const auto& iter : currentFit->staticParamMap)
{
if (pos == index.row())
{
staticParamName = iter.first;
break;
}
++pos;
}
}
return staticParamName;
}
QVariant
QmitkFitParameterModel::
data(const QModelIndex& index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
QVariant result;
if (!index.parent().isValid() && !this->hasSingleFit())
{ //we need the fit names
if (index.row() < static_cast<int>(m_Fits.size()) && index.column() == 0)
{
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
result = QVariant(QString::fromStdString(m_Fits[index.row()]->fitName)+QString("(") + QString::fromStdString(m_Fits[index.row()]->uid) + QString(")"));
}
else if (role == Qt::ToolTipRole)
{
result = QVariant("Name (UID) of the fit.");
}
}
}
else
- { // realy want to get the values of the current fit
+ { // really want to get the values of the current fit
const mitk::modelFit::ModelFitInfo* currentFit = nullptr;
if (this->hasSingleFit() && !index.parent().isValid())
{
currentFit = m_Fits.front();
}
else if (index.parent().isValid() && index.parent().row() < static_cast<int>(m_Fits.size()))
{
currentFit = m_Fits[index.parent().row()];
}
if (currentFit)
{
const auto paramSize = static_cast<int>(currentFit->GetParameters().size());
const auto staticParamSize = static_cast<int>(currentFit->staticParamMap.Size());
if (index.row() < paramSize + staticParamSize)
{
std::string staticParamName = GetStaticParameterName(currentFit, index);
switch (index.column())
{
case 0:
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
if (index.row() < paramSize)
{
const auto& param = currentFit->GetParameters()[index.row()];
result = QVariant(QString::fromStdString(param->name));
}
else
{
result = QVariant(QString::fromStdString(staticParamName));
}
}
else if (role == Qt::ToolTipRole)
{
result = QVariant("Name of the parameter.");
}
break;
case 1:
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
if (index.row() < paramSize)
{
const auto& param = currentFit->GetParameters()[index.row()];
std::string paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER();
if (param->type == mitk::modelFit::Parameter::DerivedType)
{
paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER();
}
else if (param->type == mitk::modelFit::Parameter::CriterionType)
{
paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_CRITERION();
}
else if (param->type == mitk::modelFit::Parameter::EvaluationType)
{
paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_EVALUATION_PARAMETER();
}
result = QVariant(QString::fromStdString(paramType));
}
else
{
result = QVariant("static");
}
}
else if (role == Qt::ToolTipRole)
{
result = QVariant("Type of the parameter.");
}
break;
default:
if (index.column() - 2 < static_cast<int>(this->getBookmarksCount()+1))
{
mitk::Point3D pos = m_CurrentPos;
if (index.column() > 2)
{
pos = m_Bookmarks->GetPoint(index.column() - 3);
}
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
if (index.row() < paramSize)
{
auto value = mitk::ReadVoxel(currentFit->GetParameters()[index.row()]->image, pos);
result = QVariant(QString::number(value));
}
else
{
- auto value = currentFit->staticParamMap.Get(staticParamName).front();
- result = QVariant(QString::number(value));
+ QString concatenatedValues;
+ for (const auto &value : currentFit->staticParamMap.Get(staticParamName))
+ {
+ concatenatedValues += QString::number(value) + ", ";
+ }
+ if (!concatenatedValues.isEmpty())
+ {
+ concatenatedValues.chop(2);
+ }
+ result = QVariant(concatenatedValues);
+
+
+
+
}
}
else if (role == Qt::ToolTipRole)
{
result = QVariant("Value of a (static) fit parameter");
}
}
break;
}
}
}
}
return result;
}
Qt::ItemFlags
QmitkFitParameterModel::
flags(const QModelIndex& index) const
{
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
return flags;
}
QVariant
QmitkFitParameterModel::
headerData(int section, Qt::Orientation orientation, int role) const
{
if ((Qt::DisplayRole == role) &&
(Qt::Horizontal == orientation))
{
if (section == 0)
{
return QVariant("Name");
}
else if (section == 1)
{
return QVariant("Type");
}
else if (section == 2)
{
return QVariant("Value");
}
else if (section - 3 < static_cast<int>(this->getBookmarksCount()))
{
const auto & pos = m_Bookmarks->GetPoint(section - 3);
std::ostringstream strm;
strm.imbue(std::locale("C"));
strm << std::setprecision(3) << "Value @ Pos " << section -3 << " (" << pos[0] << "|" << pos[1] << "|" << pos[2] << ")";
return QVariant(QString::fromStdString(strm.str()));
}
}
return QVariant();
}
bool
QmitkFitParameterModel::
setData(const QModelIndex&, const QVariant&, int)
{
return false;
};
diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.h b/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.h
index e33b05eb6e..978fc33f46 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.h
+++ b/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.h
@@ -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.
============================================================================*/
#ifndef QmitkFitParameterModel_h
#define QmitkFitParameterModel_h
#include <QAbstractTableModel>
#include "mitkModelFitInfo.h"
#include "mitkPointSet.h"
#include "MitkModelFitUIExports.h"
/*!
\class QmitkFitParameterModel
Model that can be used to display the parameter values of ModelFitInfo instances for different world coordinate positions.
-If more then one ModelFitInfo instance is given the model will use a tree hirarchy. The first level are the fits,
+If more then one ModelFitInfo instance is given the model will use a tree hierarchy. The first level are the fits,
the seconds level are the parameter of the fit.
*/
class MITKMODELFITUI_EXPORT QmitkFitParameterModel : public QAbstractTableModel
{
Q_OBJECT
public:
using FitVectorType = std::vector<mitk::modelFit::ModelFitInfo::ConstPointer>;
QmitkFitParameterModel(QObject* parent = nullptr);
~QmitkFitParameterModel() override {};
const FitVectorType& getFits() const;
mitk::Point3D getCurrentPosition() const;
const mitk::PointSet* getPositionBookmarks() 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;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
public Q_SLOTS:
void setFits(const FitVectorType& fits);
void setCurrentPosition(const mitk::Point3D& currentPos);
void setPositionBookmarks(const mitk::PointSet* bookmarks);
protected:
std::size_t getBookmarksCount() const;
private:
bool hasSingleFit() const;
FitVectorType m_Fits;
mitk::PointSet::ConstPointer m_Bookmarks;
mitk::Point3D m_CurrentPos;
};
#endif
diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.h b/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.h
index e5e9025f9c..fd40e0297d 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.h
+++ b/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.h
@@ -1,77 +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.
============================================================================*/
#ifndef QmitkFitParameterWidget_h
#define QmitkFitParameterWidget_h
#include "mitkModelFitInfo.h"
#include "mitkPointSet.h"
#include "MitkModelFitUIExports.h"
#include "ui_QmitkFitParameterWidget.h"
#include <QWidget>
class QmitkFitParameterModel;
/**
* \class QmitkFitParameterWidget
* Widget that displays the parameters of all set ModelFitInfo instances for all given
* world coordinate points.
* In addition it allows to transfer this information as CSV into the clipboard or a file.
*/
class MITKMODELFITUI_EXPORT QmitkFitParameterWidget : public QWidget
{
Q_OBJECT
public:
using FitVectorType = std::vector<mitk::modelFit::ModelFitInfo::ConstPointer>;
QmitkFitParameterWidget(QWidget* parent = nullptr);
~QmitkFitParameterWidget() override;
const FitVectorType& getFits() const;
mitk::Point3D getCurrentPosition() const;
const mitk::PointSet* getPositionBookmarks() const;
public Q_SLOTS:
void setFits(const FitVectorType& fits);
void setCurrentPosition(const mitk::Point3D& currentPos);
void setPositionBookmarks(const mitk::PointSet* bookmarks);
protected Q_SLOTS:
void OnExportClicked() const;
/** @brief Saves the results table to clipboard */
void OnClipboardResultsButtonClicked() const;
protected:
std::string streamModelToString() const;
QmitkFitParameterModel * m_InternalModel;
Ui::QmitkFitParameterWidget m_Controls;
};
-/** Helper function to sanatize strings before used in a csv export
+/** Helper function to sanitize strings before used in a csv export
Moved to header in order to be reusabel for other ModelFitUI widgets.*/
std::string SanatizeString(std::string str);
#endif
diff --git a/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.cpp b/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.cpp
index 4ce6139b67..17704d4149 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.cpp
+++ b/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.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 <QColor>
#include <QSize>
#include "mitkExceptionMacro.h"
#include "mitkImage.h"
#include "mitkImageBasedParameterizationDelegate.h"
#include "QmitkInitialValuesModel.h"
QmitkInitialValuesModel::
QmitkInitialValuesModel(QObject* parent) :
QAbstractTableModel(parent), m_modified(false)
{
}
void
QmitkInitialValuesModel::
setInitialValues(const mitk::ModelTraitsInterface::ParameterNamesType& names,
const mitk::ModelTraitsInterface::ParametersType values)
{
if (names.size() != values.size())
{
mitkThrow() <<
"Error. Cannot set initial value model. Passed parameter names vector and default values vector have different size.";
}
emit beginResetModel();
this->m_ParameterNames = names;
this->m_Values = values;
this->m_modified = false;
emit endResetModel();
};
void
QmitkInitialValuesModel::
setInitialValues(const mitk::ModelTraitsInterface::ParameterNamesType& names)
{
mitk::ModelTraitsInterface::ParametersType values;
values.set_size(names.size());
values.fill(0.0);
setInitialValues(names, values);
};
void
QmitkInitialValuesModel::
addInitialParameterImage(const mitk::DataNode* node, mitk::ModelTraitsInterface::ParametersType::size_type paramIndex)
{
if (!node) mitkThrow() << "Try to set a null ptr as initial value source image.";
if (!dynamic_cast<mitk::Image*>(node->GetData())) mitkThrow() << "Error. Passed node does not contain an image.";
emit beginResetModel();
this->m_ParameterImageMap[paramIndex] = node;
emit endResetModel();
};
void QmitkInitialValuesModel::resetInitialParameterImage()
{
emit beginResetModel();
this->m_ParameterImageMap.clear();
emit endResetModel();
};
mitk::ModelTraitsInterface::ParametersType
QmitkInitialValuesModel::
getInitialValues() const
{
return this->m_Values;
};
mitk::InitialParameterizationDelegateBase::Pointer
QmitkInitialValuesModel::
getInitialParametrizationDelegate() const
{
mitk::ImageBasedParameterizationDelegate::Pointer initDelegate = mitk::ImageBasedParameterizationDelegate::New();
initDelegate->SetInitialParameterization(m_Values);
for (const auto& pos : this->m_ParameterImageMap)
{
initDelegate->AddInitialParameterImage(dynamic_cast<mitk::Image*>(pos.second->GetData()), pos.first);
}
return initDelegate.GetPointer();
};
bool
QmitkInitialValuesModel::
hasValidInitialValues() const
{
for (const auto& pos : this->m_ParameterImageMap)
{
if (pos.second.IsNull()) return false;
}
return true;
};
int
QmitkInitialValuesModel::
rowCount(const QModelIndex& parent) const
{
if (parent.isValid())
{
return 0;
}
return m_Values.size();
}
int
QmitkInitialValuesModel::
columnCount(const QModelIndex& parent) const
{
if (parent.isValid())
{
return 0;
}
return 3;
}
int
QmitkInitialValuesModel::
valueType(const QModelIndex& index) const
{
if (m_ParameterImageMap.find(index.row()) != m_ParameterImageMap.end())
{ //image type
return 1;
}
else
{ //simple scalar
return 0;
}
}
QVariant
QmitkInitialValuesModel::
data(const QModelIndex& index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
QVariant result;
if (index.row() < static_cast<int>(m_Values.size()))
{
switch (index.column())
{
case 0:
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
result = QVariant(QString::fromStdString(m_ParameterNames[index.row()]));
}
else if (role == Qt::ToolTipRole)
{
result = QVariant("Name of the parameter.");
}
break;
case 1:
if (role == Qt::DisplayRole)
{
if (valueType(index) == 1)
{ //image type
result = QVariant("image");
}
else
{ //simple scalar
result = QVariant("scalar");
}
}
else if (role == Qt::EditRole)
{
result = QVariant(valueType(index));
}
else if (role == Qt::ToolTipRole)
{
- result = QVariant("type of the inital value.");
+ result = QVariant("type of the initial value.");
}
break;
case 2:
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
const auto& finding = m_ParameterImageMap.find(index.row());
if (finding != m_ParameterImageMap.end())
{ //image type -> return the name
if (finding->second.IsNotNull())
{
result = QVariant(finding->second->GetName().c_str());
}
else
{
result = QVariant("Invalid. Select image.");
}
}
else
{ //simple scalar
result = QVariant(m_Values(index.row()));
}
}
else if (role == Qt::UserRole)
{
result = QVariant(valueType(index));
}
else if (role == Qt::ToolTipRole)
{
result = QVariant("Initial values for the parameter.");
}
break;
}
}
return result;
}
Qt::ItemFlags
QmitkInitialValuesModel::
flags(const QModelIndex& index) const
{
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
if (index.row() < static_cast<int>(m_Values.size()))
{
if (index.column() > 0)
{
flags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}
else
{
flags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
}
return flags;
}
QVariant
QmitkInitialValuesModel::
headerData(int section, Qt::Orientation orientation, int role) const
{
if ((Qt::DisplayRole == role) &&
(Qt::Horizontal == orientation))
{
if (section == 0)
{
return QVariant("Parameters");
}
else if (section == 1)
{
return QVariant("Type");
}
else if (section == 2)
{
return QVariant("Value");
}
}
return QVariant();
}
bool
QmitkInitialValuesModel::
setData(const QModelIndex& index, const QVariant& value, int role)
{
if (!index.isValid() || index.row() >= static_cast<int>(m_Values.size()) || (index.column() == 0))
{
return false;
}
if (Qt::EditRole == role)
{
emit dataChanged(index, index);
emit beginResetModel();
bool result = false;
if (index.column() == 1)
{
if (value.toInt() == 0)
{
this->m_ParameterImageMap.erase(index.row());
m_modified = true;
result = true;
}
else
{
this->m_ParameterImageMap[index.row()] = nullptr;
m_modified = true;
result = true;
}
}
else
{
if (valueType(index) == 0)
{
m_Values[index.row()] = value.toDouble();
m_modified = true;
result = true;
}
else
{
mitk::DataNode *node = static_cast<mitk::DataNode*>(value.value<void*>());
if (node && dynamic_cast<mitk::Image*>(node->GetData()))
{
this->m_ParameterImageMap[index.row()] = node;
m_modified = true;
result = true;
}
}
}
emit endResetModel();
return result;
}
return false;
};
bool QmitkInitialValuesModel::isModified()
{
return m_modified;
}
diff --git a/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.h b/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.h
index c929641cfa..7e08ff6671 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.h
+++ b/Modules/ModelFitUI/Qmitk/QmitkInitialValuesModel.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 QmitkInitialValuesModel_h
#define QmitkInitialValuesModel_h
#include <QAbstractTableModel>
#include "mitkSimpleBarrierConstraintChecker.h"
#include "mitkModelTraitsInterface.h"
#include "mitkDataNode.h"
#include "mitkInitialParameterizationDelegateBase.h"
#include "MitkModelFitUIExports.h"
/*!
\class QmitkInitialValuesModel
-Model that handles the definition of inital model values.
+Model that handles the definition of initial model values.
*/
class MITKMODELFITUI_EXPORT QmitkInitialValuesModel : public QAbstractTableModel
{
Q_OBJECT
public:
QmitkInitialValuesModel(QObject* parent = nullptr);
~QmitkInitialValuesModel() override {};
/** Sets the names and the values of the initial parameter set for the model.
@param names List of all possible parameter names. It is assumed that the
index of the list equals the parameter index in the respective fitting model and its parameter values.
@param values Default values to start with.*/
void setInitialValues(const mitk::ModelTraitsInterface::ParameterNamesType& names,
const mitk::ModelTraitsInterface::ParametersType values);
/**@overload
- Convinience method that sets the default initial values always to zero.*/
+ Convenience method that sets the default initial values always to zero.*/
void setInitialValues(const mitk::ModelTraitsInterface::ParameterNamesType& names);
/** Adds an image as a source for the initial value of a parameter.
* @param node Pointer to the image that is the value source.
* @param paramIndex Indicates which parameter is defined by the source image.
* It equals the position of the vector defined by setInitialValues().
* @remark setting an image for an index overwrites the value for this index set by
* SetInitialParameterization.
* @pre paramIndex must be in bound of the initial parametrization vector.
* @pre node must be a valid image instance*/
void addInitialParameterImage(const mitk::DataNode* node, mitk::ModelTraitsInterface::ParametersType::size_type paramIndex);
bool hasValidInitialValues() const;
void resetInitialParameterImage();
/** Returns a pointer to a delegate instance that represents the parameterization of the model.*/
mitk::InitialParameterizationDelegateBase::Pointer getInitialParametrizationDelegate() const;
/** Returns the current set initial values of the model.
* @remark this are only the simpel scalar initial values. If an source image was set, this is missed here.
* Use getInitialParametrizationDelegate() to get everything at once.*/
mitk::ModelTraitsInterface::ParametersType getInitialValues() 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;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
/**Indicates if the content of the model was modified since the data was set via setInitialValues()*/
bool isModified();
private:
int valueType(const QModelIndex& index) const;
mitk::ModelTraitsInterface::ParametersType m_Values;
mitk::ModelTraitsInterface::ParameterNamesType m_ParameterNames;
typedef std::map<mitk::ModelTraitsInterface::ParametersType::size_type, mitk::DataNode::ConstPointer> ImageMapType;
ImageMapType m_ParameterImageMap;
/** Indicates if the data of the model was modified, since the model was set. */
bool m_modified;
};
#endif
diff --git a/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.cpp b/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.cpp
index 2664f3f934..1cfcd493de 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.cpp
+++ b/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.cpp
@@ -1,115 +1,115 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkParameterFitBackgroundJob.h"
#include "mitkModelFitInfo.h"
void ParameterFitBackgroundJob::OnFitEvent(::itk::Object* caller, const itk::EventObject & event)
{
itk::ProgressEvent progressEvent;
itk::InitializeEvent initEvent;
itk::StartEvent startEvent;
itk::EndEvent endEvent;
if (progressEvent.CheckEvent(&event))
{
mitk::ParameterFitImageGeneratorBase* castedReporter = dynamic_cast<mitk::ParameterFitImageGeneratorBase*>(caller);
emit JobProgress(castedReporter->GetProgress());
}
else if (initEvent.CheckEvent(&event))
{
emit JobStatusChanged(QString("Initializing parameter fit generator"));
}
else if (startEvent.CheckEvent(&event))
{
emit JobStatusChanged(QString("Started fitting process."));
}
else if (endEvent.CheckEvent(&event))
{
emit JobStatusChanged(QString("Finished fitting process."));
}
}
ParameterFitBackgroundJob::
ParameterFitBackgroundJob(mitk::ParameterFitImageGeneratorBase* generator, const mitk::modelFit::ModelFitInfo* fitInfo, mitk::DataNode* parentNode) : ParameterFitBackgroundJob(generator, fitInfo, parentNode, {})
{
};
ParameterFitBackgroundJob::
ParameterFitBackgroundJob(mitk::ParameterFitImageGeneratorBase* generator, const mitk::modelFit::ModelFitInfo* fitInfo, mitk::DataNode* parentNode, mitk::modelFit::ModelFitResultNodeVectorType additionalRelevantNodes)
{
if (!generator)
{
mitkThrow() << "Cannot create parameter fit background job. Passed fit generator is NULL.";
}
if (!fitInfo)
{
mitkThrow() << "Cannot create parameter fit background job. Passed model traits interface is NULL.";
}
m_Generator = generator;
m_ModelFitInfo = fitInfo;
m_ParentNode = parentNode;
m_AdditionalRelevantNodes = additionalRelevantNodes;
m_spCommand = ::itk::MemberCommand<ParameterFitBackgroundJob>::New();
m_spCommand->SetCallbackFunction(this, &ParameterFitBackgroundJob::OnFitEvent);
m_ObserverID = m_Generator->AddObserver(::itk::AnyEvent(), m_spCommand);
};
mitk::DataNode*
ParameterFitBackgroundJob::
GetParentNode() const
{
return m_ParentNode;
};
mitk::modelFit::ModelFitResultNodeVectorType ParameterFitBackgroundJob::GetAdditionalRelevantNodes() const
{
return m_AdditionalRelevantNodes;
};
ParameterFitBackgroundJob::
~ParameterFitBackgroundJob()
{
m_Generator->RemoveObserver(m_ObserverID);
};
void
ParameterFitBackgroundJob::
run()
{
try
{
emit JobStatusChanged(QString("Started fit session.Generate UID: ")+QString::fromStdString(m_ModelFitInfo->uid));
m_Generator->Generate();
emit JobStatusChanged(QString("Generate result nodes."));
m_Results = mitk::modelFit::CreateResultNodeMap(m_Generator->GetParameterImages(), m_Generator->GetDerivedParameterImages(), m_Generator->GetCriterionImages(), m_Generator->GetEvaluationParameterImages(), m_ModelFitInfo);
emit ResultsAreAvailable(m_Results, this);
}
catch (::std::exception& e)
{
emit Error(QString("Error while fitting data. Details: ")+QString::fromLatin1(e.what()));
}
catch (...)
{
- emit Error(QString("Unkown error when fitting the data."));
+ emit Error(QString("Unknown error when fitting the data."));
}
emit Finished();
};
diff --git a/Modules/ModelFitUI/Qmitk/QmitkSimpleBarrierParametersDelegate.h b/Modules/ModelFitUI/Qmitk/QmitkSimpleBarrierParametersDelegate.h
index dd3b29dde3..5f100d497e 100644
--- a/Modules/ModelFitUI/Qmitk/QmitkSimpleBarrierParametersDelegate.h
+++ b/Modules/ModelFitUI/Qmitk/QmitkSimpleBarrierParametersDelegate.h
@@ -1,49 +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.
============================================================================*/
#ifndef QmitkSimpleBarrierParametersDelegate_h
#define QmitkSimpleBarrierParametersDelegate_h
/// Toolkit includes.
#include <QStyledItemDelegate>
#include "MitkModelFitUIExports.h"
/** \class QmitkSimpleBarrierParametersDelegate
\brief An item delegate for rendering and editing the parameters relevant for
a barrier constraint. The delegate assumes the following: 1) if the data is requested with the
edit role, it gets a string list of all possible options. 2) if the data is requested with the
display role it gets only a list of all currently selected options.
-If the data is transfered back to the model it contains all selected parameter names in a string list.*/
+If the data is transferred back to the model it contains all selected parameter names in a string list.*/
class MITKMODELFITUI_EXPORT QmitkSimpleBarrierParametersDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
QmitkSimpleBarrierParametersDelegate(QObject* parent = nullptr);
void paint(QPainter* painter, const QStyleOptionViewItem& option
, const QModelIndex& index) const override;
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option
, const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
};
#endif
diff --git a/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp b/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp
index e71358bd70..2e453af4a9 100644
--- a/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp
+++ b/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp
@@ -1,664 +1,664 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkImageStatisticsHolder.h>
#include <mitkLabelSetImage.h>
#include <mitkLabelSetImageConverter.h>
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkAutoCropImageFilter.h>
namespace CppUnit
{
namespace StringHelper
{
template<> inline std::string toString(const mitk::LabelSetImage::LabelValueVectorType& lvs)
{
std::ostringstream stream;
stream << "[";
for (mitk::LabelSetImage::LabelValueVectorType::const_iterator iter = lvs.begin(); iter!=lvs.end(); ++iter)
{
stream << *iter;
if (iter + 1 != lvs.end()) stream << ", ";
}
stream << "]";
return stream.str();
}
template<> inline std::string toString(const std::vector<std::string>& strings)
{
std::ostringstream stream;
stream << "[";
for (std::vector<std::string>::const_iterator iter = strings.begin(); iter != strings.end(); ++iter)
{
stream << *iter;
if (iter + 1 != strings.end()) stream << ", ";
}
stream << "]";
return stream.str();
}
}
}
class mitkLabelSetImageTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkLabelSetImageTestSuite);
MITK_TEST(TestInitialize);
MITK_TEST(TestClone);
MITK_TEST(TestAddLayer);
MITK_TEST(TestGetActiveLabelSet);
MITK_TEST(TestGetActiveLabel);
MITK_TEST(TestInitializeByLabeledImage);
MITK_TEST(TestGetLabel);
MITK_TEST(TestGetLabelValues);
MITK_TEST(TestGetLabelClassNames);
MITK_TEST(TestSetUnlabeledLabelLock);
MITK_TEST(TestGetTotalNumberOfLabels);
MITK_TEST(TestGetNumberOfLabels);
MITK_TEST(TestExistsLabel);
MITK_TEST(TestExistsGroup);
MITK_TEST(TestSetActiveLayer);
MITK_TEST(TestRemoveLayer);
MITK_TEST(TestRemoveLabels);
MITK_TEST(TestEraseLabels);
MITK_TEST(TestMergeLabels);
MITK_TEST(TestCreateLabelMask);
CPPUNIT_TEST_SUITE_END();
private:
mitk::LabelSetImage::Pointer m_LabelSetImage;
int m_LabelAddedEventCount;
int m_LabelModifiedEventCount;
int m_LabelRemovedEventCount;
int m_LabelsChangedEventCount;
int m_GroupAddedEventCount;
int m_GroupModifiedEventCount;
int m_GroupRemovedEventCount;
public:
void setUp() override
{
// Create a new labelset image
m_LabelSetImage = mitk::LabelSetImage::New();
mitk::Image::Pointer regularImage = mitk::Image::New();
unsigned int dimensions[3] = { 96, 128, 52 };
regularImage->Initialize(mitk::MakeScalarPixelType<char>(), 3, dimensions);
m_LabelSetImage->Initialize(regularImage);
this->ResetEvents();
m_LabelSetImage->AddObserver(mitk::LabelAddedEvent(), [this](const itk::EventObject&) { ++(this->m_LabelAddedEventCount); });
m_LabelSetImage->AddObserver(mitk::LabelModifiedEvent(), [this](const itk::EventObject&) { ++(this->m_LabelModifiedEventCount); });
m_LabelSetImage->AddObserver(mitk::LabelRemovedEvent(), [this](const itk::EventObject&) { ++(this->m_LabelRemovedEventCount); });
m_LabelSetImage->AddObserver(mitk::LabelsChangedEvent(), [this](const itk::EventObject&) { ++(this->m_LabelsChangedEventCount); });
m_LabelSetImage->AddObserver(mitk::GroupAddedEvent(), [this](const itk::EventObject&) { ++(this->m_GroupAddedEventCount); });
m_LabelSetImage->AddObserver(mitk::GroupModifiedEvent(), [this](const itk::EventObject&) { ++(this->m_GroupModifiedEventCount); });
m_LabelSetImage->AddObserver(mitk::GroupRemovedEvent(), [this](const itk::EventObject&) { ++(this->m_GroupRemovedEventCount); });
}
void tearDown() override
{
// Delete LabelSetImage
m_LabelSetImage = nullptr;
}
void ResetEvents()
{
m_LabelAddedEventCount = 0;
m_LabelModifiedEventCount = 0;
m_LabelRemovedEventCount = 0;
m_LabelsChangedEventCount = 0;
m_GroupAddedEventCount = 0;
m_GroupModifiedEventCount = 0;
m_GroupRemovedEventCount = 0;
}
bool CheckEvents(int lAdd, int lMod, int lRem, int lsC, int gAdd, int gMod, int gRem)
{
return m_GroupAddedEventCount == gAdd && m_GroupModifiedEventCount == gMod && m_GroupRemovedEventCount == gRem
&& m_LabelAddedEventCount == lAdd && m_LabelModifiedEventCount == lMod && m_LabelRemovedEventCount == lRem
&& m_LabelsChangedEventCount == lsC;
}
void InitializeTestSegmentation()
{
mitk::Label::Pointer label1 = mitk::Label::New(1, "Label1");
mitk::Label::Pointer label2 = mitk::Label::New(20, "Label2");
mitk::Label::Pointer label22 = mitk::Label::New(22, "Label2");
mitk::Label::Pointer label3 = mitk::Label::New(30, "Label3");
m_LabelSetImage->AddLabel(label1, 0);
m_LabelSetImage->AddLayer({ label2, label22, label3 });
m_LabelSetImage->AddLayer();
this->ResetEvents();
}
void TestInitialize()
{
// LabelSet image should always has the pixel type mitk::Label::PixelType
CPPUNIT_ASSERT_MESSAGE("LabelSetImage has wrong pixel type",
m_LabelSetImage->GetPixelType() == mitk::MakeScalarPixelType<mitk::Label::PixelType>());
mitk::Image::Pointer regularImage = mitk::Image::New();
unsigned int dimensions[3] = { 96, 128, 52 };
regularImage->Initialize(mitk::MakeScalarPixelType<char>(), 3, dimensions);
mitk::BaseGeometry::Pointer regularImageGeo = regularImage->GetGeometry();
mitk::BaseGeometry::Pointer labelImageGeo = m_LabelSetImage->GetGeometry();
MITK_ASSERT_EQUAL(labelImageGeo, regularImageGeo, "LabelSetImage has wrong geometry");
// By default one layer should be added
CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - number of layers is not one",
m_LabelSetImage->GetNumberOfLayers() == 1);
CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - active layer has wrong ID",
m_LabelSetImage->GetActiveLayer() == 0);
CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - no active label should be selected",
m_LabelSetImage->GetActiveLabel() == nullptr);
}
void TestClone()
{
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
label1->SetValue(1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
label2->SetValue(200);
mitk::Label::Pointer label3 = mitk::Label::New();
label2->SetName("Label3");
label2->SetValue(300);
m_LabelSetImage->AddLabel(label1, 0);
m_LabelSetImage->AddLayer({ label2, label3 });
auto clone = m_LabelSetImage->Clone();
MITK_ASSERT_EQUAL(m_LabelSetImage, clone, "LabelSetImage clone is not equal.");
}
void TestAddLayer()
{
CPPUNIT_ASSERT_MESSAGE("Number of layers is not zero", m_LabelSetImage->GetNumberOfLayers() == 1);
m_LabelSetImage->AddLayer();
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - number of layers is not one",
m_LabelSetImage->GetNumberOfLayers() == 2);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active layer has wrong ID",
m_LabelSetImage->GetActiveLayer() == 0);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - no active label should be selected",
m_LabelSetImage->GetActiveLabel() == nullptr);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0,0,0,0,1,0,0));
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
label1->SetValue(1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
label2->SetValue(200);
const auto layerID = m_LabelSetImage->AddLayer({ label1, label2 });
m_LabelSetImage->SetActiveLabel(200);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - number of layers is not two",
m_LabelSetImage->GetNumberOfLayers() == 3);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active layer has wrong ID",
m_LabelSetImage->GetActiveLayer() == layerID);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong",
m_LabelSetImage->GetActiveLabel()->GetValue() == 200);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0, 0, 0, 0, 2, 0, 0));
}
void TestGetActiveLabelSet()
{
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
label1->SetValue(1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
label2->SetValue(200);
mitk::LabelSetImage::ConstLabelVectorType refLayer = { label1, label2 };
unsigned int layerID = m_LabelSetImage->AddLayer(refLayer);
m_LabelSetImage->SetActiveLabel(200);
auto activeLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
CPPUNIT_ASSERT_MESSAGE("Wrong layer ID was returned", layerID == 1);
CPPUNIT_ASSERT_MESSAGE("Wrong layer ID was returned", layerID == m_LabelSetImage->GetActiveLayer());
CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(refLayer, activeLayer, 0.00001, true));
}
void TestGetActiveLabel()
{
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
mitk::Label::PixelType value1 = 1;
label1->SetValue(value1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
mitk::Label::PixelType value2 = 200;
label2->SetValue(value2);
m_LabelSetImage->AddLabel(label1,0);
m_LabelSetImage->AddLabel(label2,0);
m_LabelSetImage->SetActiveLabel(1);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong",
m_LabelSetImage->GetActiveLabel()->GetValue() == value1);
m_LabelSetImage->SetActiveLabel(value2);
CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong",
m_LabelSetImage->GetActiveLabel()->GetValue() == value2);
- CPPUNIT_ASSERT_MESSAGE("Active Label was not correctly retreived with const getter",
+ CPPUNIT_ASSERT_MESSAGE("Active Label was not correctly retrieved with const getter",
const_cast<const mitk::LabelSetImage*>(m_LabelSetImage.GetPointer())->GetActiveLabel()->GetValue() == value2);
}
void TestInitializeByLabeledImage()
{
mitk::Image::Pointer image =
mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
m_LabelSetImage->InitializeByLabeledImage(image);
CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 5", m_LabelSetImage->GetNumberOfLabels(0) == 5);
}
void TestGetLabel()
{
mitk::Label::Pointer label1 = mitk::Label::New(1, "Label1");
mitk::Label::Pointer label2 = mitk::Label::New(20,"Label2");
m_LabelSetImage->AddLabel(label1,0);
m_LabelSetImage->AddLayer();
m_LabelSetImage->AddLabel(label2,1);
this->ResetEvents();
CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for active layer",
mitk::Equal(*m_LabelSetImage->GetLabel(1), *label1, 0.0001, true));
CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for layer 1",
mitk::Equal(*m_LabelSetImage->GetLabel(20), *label2, 0.0001, true));
// Try to get a non existing label
mitk::Label *unkownLabel = m_LabelSetImage->GetLabel(1000);
CPPUNIT_ASSERT_MESSAGE("Non existing label should be nullptr", unkownLabel == nullptr);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0, 0, 0, 0, 0, 0, 0));
}
void TestGetLabelValues()
{
InitializeTestSegmentation();
auto labels = m_LabelSetImage->GetLabelValuesByGroup(0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for group 0",
mitk::LabelSetImage::LabelValueVectorType({ 1 }), labels);
labels = m_LabelSetImage->GetLabelValuesByGroup(1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for group 1",
mitk::LabelSetImage::LabelValueVectorType({ 20, 22, 30 }), labels);
labels = m_LabelSetImage->GetLabelValuesByGroup(2);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for group 2",
mitk::LabelSetImage::LabelValueVectorType(), labels);
CPPUNIT_ASSERT_THROW(m_LabelSetImage->GetLabelValuesByGroup(3), mitk::Exception);
labels = m_LabelSetImage->GetLabelValuesByName(0, "Label1");
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for \"Label2\" in group 0",
mitk::LabelSetImage::LabelValueVectorType({ 1 }), labels);
labels = m_LabelSetImage->GetLabelValuesByName(1, "Label2");
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for \"Label2\" in group 1",
mitk::LabelSetImage::LabelValueVectorType({ 20, 22 }), labels);
labels = m_LabelSetImage->GetLabelValuesByName(1, "Label3");
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for \"Label3\" in group 1",
mitk::LabelSetImage::LabelValueVectorType({ 30 }), labels);
labels = m_LabelSetImage->GetLabelValuesByName(2, "Label1");
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for group 2",
mitk::LabelSetImage::LabelValueVectorType(), labels);
labels = m_LabelSetImage->GetLabelValuesByName(0, "unkown");
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for unkown name",
mitk::LabelSetImage::LabelValueVectorType(), labels);
CPPUNIT_ASSERT_THROW(m_LabelSetImage->GetLabelValuesByName(3,"invalid"), mitk::Exception);
labels = m_LabelSetImage->GetAllLabelValues();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong label values retrieved for unkown name",
mitk::LabelSetImage::LabelValueVectorType({1,20,22,30}), labels);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0, 0, 0, 0, 0, 0, 0));
}
void TestGetLabelClassNames()
{
InitializeTestSegmentation();
auto names = m_LabelSetImage->GetLabelClassNames();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong names retrieved",
std::vector<std::string>({ "Label1", "Label2", "Label3"}), names);
names = m_LabelSetImage->GetLabelClassNamesByGroup(0);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong names retrieved for group 0",
std::vector<std::string>({ "Label1"}), names);
names = m_LabelSetImage->GetLabelClassNamesByGroup(1);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong names retrieved for group 1",
std::vector<std::string>({"Label2", "Label3" }), names);
names = m_LabelSetImage->GetLabelClassNamesByGroup(2);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong names retrieved for group 2",
std::vector<std::string>(), names);
CPPUNIT_ASSERT_THROW(m_LabelSetImage->GetLabelValuesByGroup(3), mitk::Exception);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0, 0, 0, 0, 0, 0, 0));
}
void TestSetUnlabeledLabelLock()
{
auto locked = m_LabelSetImage->GetUnlabeledLabelLock();
CPPUNIT_ASSERT_MESSAGE("Wrong UnlabeledLabelLock default state",
locked == false);
m_LabelSetImage->SetUnlabeledLabelLock(true);
locked = m_LabelSetImage->GetUnlabeledLabelLock();
CPPUNIT_ASSERT_MESSAGE("Wrong UnlabeledLabelLock state",
locked == true);
}
void TestGetTotalNumberOfLabels()
{
this->InitializeTestSegmentation();
CPPUNIT_ASSERT_MESSAGE(
"Wrong total number of labels",
m_LabelSetImage->GetTotalNumberOfLabels() == 4);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0, 0, 0, 0, 0, 0, 0));
}
void TestGetNumberOfLabels()
{
this->InitializeTestSegmentation();
CPPUNIT_ASSERT_MESSAGE(
"Wrong number of labels in group 0",
m_LabelSetImage->GetNumberOfLabels(0) == 1);
CPPUNIT_ASSERT_MESSAGE(
"Wrong number of labels in group 1",
m_LabelSetImage->GetNumberOfLabels(1) == 3);
CPPUNIT_ASSERT_MESSAGE(
"Wrong number of labels in group 2",
m_LabelSetImage->GetNumberOfLabels(2) == 0);
CPPUNIT_ASSERT_THROW(m_LabelSetImage->GetNumberOfLabels(3), mitk::Exception);
CPPUNIT_ASSERT_MESSAGE("Event count incorrect", CheckEvents(0, 0, 0, 0, 0, 0, 0));
}
void TestExistsLabel()
{
mitk::Label::Pointer label = mitk::Label::New();
label->SetName("Label2");
mitk::Label::PixelType value = 200;
label->SetValue(value);
m_LabelSetImage->AddLayer();
m_LabelSetImage->AddLabel(label,1);
m_LabelSetImage->SetActiveLayer(0);
CPPUNIT_ASSERT_MESSAGE("Existing label was not found", m_LabelSetImage->ExistLabel(value) == true);
CPPUNIT_ASSERT_MESSAGE("Non existing label was found", m_LabelSetImage->ExistLabel(10000) == false);
}
void TestExistsGroup()
{
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
label1->SetValue(1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
label2->SetValue(200);
m_LabelSetImage->AddLayer({label1, label2});
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(0) == true);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(1) == true);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(20) == false);
}
void TestSetActiveLayer()
{
// Cache active layer
auto refActiveLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
// Add new layer
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
label1->SetValue(1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
label2->SetValue(200);
mitk::LabelSetImage::ConstLabelVectorType newlayer = { label1, label2 };
unsigned int layerID = m_LabelSetImage->AddLayer(newlayer);
// Set initial layer as active layer
m_LabelSetImage->SetActiveLayer(0);
auto activeLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned",
mitk::Equal(refActiveLayer, activeLayer, 0.00001, true));
// Set previously added layer as active layer
m_LabelSetImage->SetActiveLayer(layerID);
activeLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned",
mitk::Equal(newlayer, activeLayer, 0.00001, true));
// Set a non existing layer as active layer - nothing should change
m_LabelSetImage->SetActiveLayer(10000);
CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned",
mitk::Equal(newlayer, activeLayer, 0.00001, true));
}
void TestRemoveLayer()
{
// Cache active layer
auto refActiveLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
// Add new layers
m_LabelSetImage->AddLayer();
mitk::Label::Pointer label1 = mitk::Label::New();
label1->SetName("Label1");
label1->SetValue(1);
mitk::Label::Pointer label2 = mitk::Label::New();
label2->SetName("Label2");
label2->SetValue(200);
mitk::LabelSetImage::ConstLabelVectorType newlayer = { label1, label2 };
unsigned int layerID = m_LabelSetImage->AddLayer(newlayer);
m_LabelSetImage->SetActiveLayer(layerID);
auto activeLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned",
mitk::Equal(newlayer, activeLayer, 0.00001, true));
m_LabelSetImage->RemoveGroup(1);
CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed",
m_LabelSetImage->GetNumberOfLayers() == 2);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(2) == false);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(1) == true);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(0) == true);
m_LabelSetImage->RemoveGroup(1);
activeLayer = m_LabelSetImage->GetConstLabelsByValue(m_LabelSetImage->GetLabelValuesByGroup(m_LabelSetImage->GetActiveLayer()));
CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed",
m_LabelSetImage->GetNumberOfLayers() == 1);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(1) == false);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(0) == true);
CPPUNIT_ASSERT_MESSAGE("Wrong active layer",
mitk::Equal(refActiveLayer, activeLayer, 0.00001, true));
m_LabelSetImage->RemoveGroup(0);
CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed",
m_LabelSetImage->GetNumberOfLayers() == 0);
CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistGroup(0) == false);
CPPUNIT_ASSERT_THROW_MESSAGE("GetActiveLayers does not fail although all layer have been removed",
m_LabelSetImage->GetActiveLayer(), mitk::Exception);
}
void TestRemoveLabels()
{
mitk::Image::Pointer image =
mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
m_LabelSetImage = nullptr;
m_LabelSetImage = mitk::LabelSetImage::New();
m_LabelSetImage->InitializeByLabeledImage(image);
CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels(0) == 5);
// 2ndMin because of unlabeled pixels = 0
CPPUNIT_ASSERT_MESSAGE("Wrong MIN value", m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 1);
CPPUNIT_ASSERT_MESSAGE("Wrong MAX value", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 7);
CPPUNIT_ASSERT_MESSAGE("Label with ID 3 does not exist after initialization",
m_LabelSetImage->ExistLabel(3) == true);
m_LabelSetImage->RemoveLabel(1);
std::vector<mitk::Label::PixelType> labelsToBeRemoved;
labelsToBeRemoved.push_back(3);
labelsToBeRemoved.push_back(7);
m_LabelSetImage->RemoveLabels(labelsToBeRemoved);
CPPUNIT_ASSERT_MESSAGE("Wrong number of labels after some have been removed",
m_LabelSetImage->GetNumberOfLabels(0) == 2);
// Values within the image are 0, 1, 3, 5, 6, 7 - New Min / Max value should be 5 / 6
// 2ndMin because of unlabeled pixels = 0
CPPUNIT_ASSERT_MESSAGE("Labels with value 1 and 3 were not removed from the image",
m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 5);
CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not removed from the image",
m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 6);
}
void TestEraseLabels()
{
mitk::Image::Pointer image =
mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
m_LabelSetImage = nullptr;
m_LabelSetImage = mitk::LabelSetImage::New();
m_LabelSetImage->InitializeByLabeledImage(image);
CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels(0) == 5);
// 2ndMin because of unlabeled pixels = 0
CPPUNIT_ASSERT_MESSAGE("Wrong MIN value", m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 1);
CPPUNIT_ASSERT_MESSAGE("Wrong MAX value", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 7);
CPPUNIT_ASSERT_MESSAGE("Label with ID 3 does not exist after initialization",
m_LabelSetImage->ExistLabel(3) == true);
m_LabelSetImage->EraseLabel(1);
std::vector<mitk::Label::PixelType> labelsToBeErased;
labelsToBeErased.push_back(3);
labelsToBeErased.push_back(7);
m_LabelSetImage->EraseLabels(labelsToBeErased);
CPPUNIT_ASSERT_MESSAGE("Wrong number of labels since none have been removed",
m_LabelSetImage->GetNumberOfLabels(0) == 5);
// Values within the image are 0, 1, 3, 5, 6, 7 - New Min / Max value should be 5 / 6
// 2ndMin because of unlabeled pixels = 0
CPPUNIT_ASSERT_MESSAGE("Labels with value 1 and 3 were not erased from the image",
m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 5);
CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not erased from the image",
m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 6);
}
void TestMergeLabels()
{
mitk::Image::Pointer image =
mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
m_LabelSetImage = nullptr;
m_LabelSetImage = mitk::LabelSetImage::New();
m_LabelSetImage->InitializeByLabeledImage(image);
CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels(0) == 5);
// 2ndMin because of unlabeled pixels = 0
CPPUNIT_ASSERT_MESSAGE("Wrong MIN value", m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 1);
CPPUNIT_ASSERT_MESSAGE("Wrong MAX value", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 7);
CPPUNIT_ASSERT_MESSAGE("Label with ID 6 does not exist after initialization",
m_LabelSetImage->ExistLabel(6) == true);
// Merge label 7 with label 6. Result should be that label 7 is not present anymore.
m_LabelSetImage->MergeLabel(6, 7);
CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not removed from the image", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 6);
// Count all pixels with value 6 = 507
// Count all pixels with value 7 = 823
// Check if merged label has 507 + 823 = 1330 pixels
CPPUNIT_ASSERT_MESSAGE("Labels were not correctly merged", m_LabelSetImage->GetStatistics()->GetCountOfMaxValuedVoxels() == 1330);
CPPUNIT_ASSERT_MESSAGE("Label with ID 3 does not exist after initialization",
m_LabelSetImage->ExistLabel(3) == true);
CPPUNIT_ASSERT_MESSAGE("Label with ID 5 does not exist after initialization",
m_LabelSetImage->ExistLabel(5) == true);
// Merge labels 5 and 6 with 3. Result should be that labels 5 and 6 are not present anymore.
std::vector<mitk::Label::PixelType> vectorOfSourcePixelValues{ 5, 6 };
m_LabelSetImage->MergeLabels(3, vectorOfSourcePixelValues);
// Values within the image are 0, 1, 3, 5, 6, 7 - New Max value should be 3
CPPUNIT_ASSERT_MESSAGE("Labels with value 5 and 6 were not removed from the image", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 3);
// Count all pixels with value 3 = 1893
// Count all pixels with value 5 = 2143
// Count all pixels with value 6 = 1330
// Check if merged label has 1893 + 2143 + 1330 = 5366 pixels
CPPUNIT_ASSERT_MESSAGE("Labels were not correctly merged", m_LabelSetImage->GetStatistics()->GetCountOfMaxValuedVoxels() == 5366);
}
void TestCreateLabelMask()
{
mitk::Image::Pointer image =
mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"));
m_LabelSetImage = nullptr;
m_LabelSetImage = mitk::LabelSetImage::New();
m_LabelSetImage->InitializeByLabeledImage(image);
auto labelMask = mitk::CreateLabelMask(m_LabelSetImage,6);
mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New();
cropFilter->SetInput(labelMask);
cropFilter->SetBackgroundValue(0);
cropFilter->SetMarginFactor(1.15);
cropFilter->Update();
auto maskImage = cropFilter->GetOutput();
// Count all pixels with value 6 = 507
CPPUNIT_ASSERT_MESSAGE("Label mask not correctly created", maskImage->GetStatistics()->GetCountOfMaxValuedVoxels() == 507);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkLabelSetImage)
diff --git a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp
index ad1fcb89d7..af3faacdea 100644
--- a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp
+++ b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp
@@ -1,694 +1,739 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 __mitkDICOMSegmentationIO__cpp
#define __mitkDICOMSegmentationIO__cpp
#include "mitkDICOMSegmentationIO.h"
#include "mitkDICOMSegIOMimeTypes.h"
#include "mitkDICOMSegmentationConstants.h"
#include <mitkDICOMDCMTKTagScanner.h>
#include <mitkDICOMIOHelper.h>
#include <mitkDICOMProperty.h>
#include <mitkIDICOMTagsOfInterest.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <mitkLocaleSwitch.h>
#include <mitkPropertyNameHelper.h>
// itk
#include <itkThresholdImageFilter.h>
// dcmqi
-#include <dcmqi/ImageSEGConverter.h>
+#include <dcmqi/Itk2DicomConverter.h>
+#include <dcmqi/Dicom2ItkConverter.h>
+#include <dcmtk/dcmdata/dcdeftag.h>
// us
#include <usGetModuleContext.h>
#include <usModuleContext.h>
namespace mitk
{
DICOMSegmentationIO::DICOMSegmentationIO()
: AbstractFileIO(LabelSetImage::GetStaticNameOfClass(),
mitk::MitkDICOMSEGIOMimeTypes::DICOMSEG_MIMETYPE_NAME(),
"DICOM Segmentation")
{
AbstractFileWriter::SetRanking(10);
AbstractFileReader::SetRanking(10);
this->RegisterService();
}
std::vector<mitk::DICOMTagPath> DICOMSegmentationIO::GetDICOMTagsOfInterest()
{
std::vector<mitk::DICOMTagPath> result;
result.emplace_back(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_LABEL_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH());
result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH());
result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH());
result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH());
result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH());
result.emplace_back(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH());
return result;
}
IFileIO::ConfidenceLevel DICOMSegmentationIO::GetWriterConfidenceLevel() const
{
if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported)
return Unsupported;
// Check if the input file is a segmentation
const LabelSetImage *input = dynamic_cast<const LabelSetImage *>(this->GetInput());
if (input)
{
if ((input->GetDimension() != 3))
{
MITK_INFO << "DICOM segmentation writer is tested only with 3D images, sorry.";
return Unsupported;
}
// Check if input file has dicom information for the referenced image (original DICOM image, e.g. CT) Still necessary, see write()
mitk::StringLookupTableProperty::Pointer dicomFilesProp =
dynamic_cast<mitk::StringLookupTableProperty *>(input->GetProperty("referenceFiles").GetPointer());
if (dicomFilesProp.IsNotNull())
return Supported;
}
return Unsupported;
}
void DICOMSegmentationIO::Write()
{
ValidateOutputLocation();
mitk::LocaleSwitch localeSwitch("C");
LocalFile localFile(this);
const std::string path = localFile.GetFileName();
auto input = dynamic_cast<const LabelSetImage *>(this->GetInput());
if (input == nullptr)
mitkThrow() << "Cannot write non-image data";
// Get DICOM information from referenced image
vector<std::unique_ptr<DcmDataset>> dcmDatasetsSourceImage;
- std::unique_ptr<DcmFileFormat> readFileFormat(new DcmFileFormat());
+ std::unique_ptr<DcmFileFormat> readFileFormat = std::make_unique<DcmFileFormat>();
try
{
// TODO: Generate dcmdataset witk DICOM tags from property list; ATM the source are the filepaths from the
// property list
mitk::StringLookupTableProperty::Pointer filesProp =
dynamic_cast<mitk::StringLookupTableProperty *>(input->GetProperty("referenceFiles").GetPointer());
if (filesProp.IsNull())
{
mitkThrow() << "No property with dicom file path.";
return;
}
StringLookupTable filesLut = filesProp->GetValue();
const StringLookupTable::LookupTableType &lookUpTableMap = filesLut.GetLookupTable();
for (const auto &it : lookUpTableMap)
{
const char *fileName = (it.second).c_str();
if (readFileFormat->loadFile(fileName, EXS_Unknown).good())
{
std::unique_ptr<DcmDataset> readDCMDataset(readFileFormat->getAndRemoveDataset());
dcmDatasetsSourceImage.push_back(std::move(readDCMDataset));
}
}
}
catch (const std::exception &e)
{
MITK_ERROR << "An error occurred while getting the dicom informations: " << e.what() << endl;
return;
}
// Iterate over all layers. For each a dcm file will be generated
for (unsigned int layer = 0; layer < input->GetNumberOfLayers(); ++layer)
{
vector<itkInternalImageType::Pointer> segmentations;
try
{
auto mitkLayerImage = input->GetGroupImage(layer);
// Cast mitk layer image to itk
ImageToItk<itkInputImageType>::Pointer imageToItkFilter = ImageToItk<itkInputImageType>::New();
imageToItkFilter->SetInput(mitkLayerImage);
// Cast from original itk type to dcmqi input itk image type
typedef itk::CastImageFilter<itkInputImageType, itkInternalImageType> castItkImageFilterType;
castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New();
castFilter->SetInput(imageToItkFilter->GetOutput());
castFilter->Update();
itkInternalImageType::Pointer itkLabelImage = castFilter->GetOutput();
itkLabelImage->DisconnectPipeline();
// Iterate over all labels. For each label a segmentation image will be created
auto labelSet = input->GetConstLabelsByValue(input->GetLabelValuesByGroup(layer));
for (const auto& label : labelSet)
{
- // Thresold over the image with the given label value
+ // Threshold over the image with the given label value
itk::ThresholdImageFilter<itkInternalImageType>::Pointer thresholdFilter =
itk::ThresholdImageFilter<itkInternalImageType>::New();
thresholdFilter->SetInput(itkLabelImage);
thresholdFilter->ThresholdOutside(label->GetValue(), label->GetValue());
thresholdFilter->SetOutsideValue(0);
thresholdFilter->Update();
itkInternalImageType::Pointer segmentImage = thresholdFilter->GetOutput();
segmentImage->DisconnectPipeline();
segmentations.push_back(segmentImage);
}
}
catch (const itk::ExceptionObject &e)
{
MITK_ERROR << e.GetDescription() << endl;
return;
}
// Create segmentation meta information
const std::string tmpMetaInfoFile = this->CreateMetaDataJsonFile(layer);
MITK_INFO << "Writing image: " << path << std::endl;
try
{
//TODO is there a better way? Interface expects a vector of raw pointer.
vector<DcmDataset*> rawVecDataset;
for (const auto& dcmDataSet : dcmDatasetsSourceImage)
rawVecDataset.push_back(dcmDataSet.get());
// Convert itk segmentation images to dicom image
- std::unique_ptr<dcmqi::ImageSEGConverter> converter = std::make_unique<dcmqi::ImageSEGConverter>();
+ auto converter = std::make_unique<dcmqi::Itk2DicomConverter>();
std::unique_ptr<DcmDataset> result(converter->itkimage2dcmSegmentation(rawVecDataset, segmentations, tmpMetaInfoFile, false));
+ //We store only one group, thus we can specify the SegmentsOverlap Tag (0062,0013)
+ // as NO
+ auto condition = result->putAndInsertString(DCM_SegmentsOverlap, "NO");
+ if (condition.bad())
+ {
+ MITK_DEBUG << "unable to set SegmentOverlap tag.";
+ }
+
// Write dicom file
DcmFileFormat dcmFileFormat(result.get());
std::string filePath = path.substr(0, path.find_last_of("."));
// If there is more than one layer, we have to write more than 1 dicom file
if (input->GetNumberOfLayers() != 1)
filePath = filePath + std::to_string(layer) + ".dcm";
else
filePath = filePath + ".dcm";
dcmFileFormat.saveFile(filePath.c_str(), EXS_LittleEndianExplicit);
}
catch (const std::exception &e)
{
MITK_ERROR << "An error occurred during writing the DICOM Seg: " << e.what() << endl;
return;
}
} // Write a dcm file for the next layer
}
IFileIO::ConfidenceLevel DICOMSegmentationIO::GetReaderConfidenceLevel() const
{
if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported)
return Unsupported;
const std::string fileName = this->GetLocalFileName();
DcmFileFormat dcmFileFormat;
OFCondition status = dcmFileFormat.loadFile(fileName.c_str());
if (status.bad())
return Unsupported;
OFString modality;
if (dcmFileFormat.getDataset()->findAndGetOFString(DCM_Modality, modality).good())
{
if (modality.compare("SEG") == 0)
return Supported;
else
return Unsupported;
}
return Unsupported;
}
std::vector<BaseData::Pointer> DICOMSegmentationIO::DoRead()
{
mitk::LocaleSwitch localeSwitch("C");
LabelSetImage::Pointer labelSetImage;
std::vector<BaseData::Pointer> result;
const std::string path = this->GetLocalFileName();
MITK_INFO << "loading " << path << std::endl;
if (path.empty())
mitkThrow() << "Empty filename in mitk::ItkImageIO ";
try
{
// Get the dcm data set from file path
DcmFileFormat dcmFileFormat;
OFCondition status = dcmFileFormat.loadFile(path.c_str());
if (status.bad())
mitkThrow() << "Can't read the input file!";
DcmDataset *dataSet = dcmFileFormat.getDataset();
if (dataSet == nullptr)
mitkThrow() << "Can't read data from input file!";
+ //Get the value of SegmentsOverlap Tag (0062,0013) for this dataset
+ OFString overlapValue;
+ bool assumeOverlappingSegments = true;
+ status = dataSet->findAndGetOFString(DCM_SegmentsOverlap, overlapValue);
+ if (status.good())
+ {
+ assumeOverlappingSegments = "NO" != overlapValue //DCM allows only NO, YES and UNDEFINED
+ && "no" != overlapValue //never the less we add lower and mixed case
+ && "No" != overlapValue; //version to be more robust with non-compliant DCM files
+ }
+
//=============================== dcmqi part ====================================
// Read the DICOM SEG images (segItkImages) and DICOM tags (metaInfo)
- std::unique_ptr<dcmqi::ImageSEGConverter> converter = std::make_unique<dcmqi::ImageSEGConverter>();
- pair<map<unsigned, itkInternalImageType::Pointer>, string> dcmqiOutput =
- converter->dcmSegmentation2itkimage(dataSet);
+ auto converter = std::make_unique<dcmqi::Dicom2ItkConverter>();
+ std::string metaInfoString;
+ auto convert_condition = converter->dcmSegmentation2itkimage(dataSet, metaInfoString, false);
- map<unsigned, itkInternalImageType::Pointer> segItkImages = dcmqiOutput.first;
+ std::vector<itkInternalImageType::Pointer> segItkImages;
- dcmqi::JSONSegmentationMetaInformationHandler metaInfo(dcmqiOutput.second.c_str());
+ if (convert_condition.good())
+ {
+ auto image = converter->begin();
+ while (image.IsNotNull())
+ {
+ segItkImages.emplace_back(image);
+ image = converter->next();
+ }
+ }
+
+ dcmqi::JSONSegmentationMetaInformationHandler metaInfo(metaInfoString.c_str());
metaInfo.read();
MITK_INFO << "Input " << metaInfo.getJSONOutputAsString();
//===============================================================================
// Get the label information from segment attributes for each itk image
vector<map<unsigned, dcmqi::SegmentAttributes *>>::const_iterator segmentIter =
metaInfo.segmentsAttributesMappingList.begin();
// For each itk image add a layer to the LabelSetImage output
- for (auto &element : segItkImages)
+ for (auto &segItkImage : segItkImages)
{
// Get the labeled image and cast it to mitkImage
typedef itk::CastImageFilter<itkInternalImageType, itkInputImageType> castItkImageFilterType;
castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New();
- castFilter->SetInput(element.second);
+ castFilter->SetInput(segItkImage);
castFilter->Update();
- Image::Pointer layerImage;
- CastToMitkImage(castFilter->GetOutput(), layerImage);
+ Image::Pointer segmentImage;
+ CastToMitkImage(castFilter->GetOutput(), segmentImage);
// Get pixel value of the label
itkInternalImageType::ValueType segValue = 1;
typedef itk::ImageRegionIterator<const itkInternalImageType> IteratorType;
// Iterate over the image to find the pixel value of the label
- IteratorType iter(element.second, element.second->GetLargestPossibleRegion());
+ IteratorType iter(segItkImage, segItkImage->GetLargestPossibleRegion());
iter.GoToBegin();
while (!iter.IsAtEnd())
{
itkInputImageType::PixelType value = iter.Get();
if (value != LabelSetImage::UNLABELED_VALUE)
{
segValue = value;
break;
}
++iter;
}
// Get Segment information map
map<unsigned, dcmqi::SegmentAttributes *> segmentMap = (*segmentIter);
map<unsigned, dcmqi::SegmentAttributes *>::const_iterator segmentMapIter = (*segmentIter).begin();
dcmqi::SegmentAttributes *segmentAttribute = (*segmentMapIter).second;
- OFString labelName;
+ OFString labelName = segmentAttribute->getSegmentLabel();
- if (segmentAttribute->getSegmentedPropertyTypeCodeSequence() != nullptr)
+ if (labelName.empty())
{
- segmentAttribute->getSegmentedPropertyTypeCodeSequence()->getCodeMeaning(labelName);
- if (segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence() != nullptr)
+ if (segmentAttribute->getSegmentedPropertyTypeCodeSequence() != nullptr)
{
- OFString modifier;
- segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence()->getCodeMeaning(modifier);
- labelName.append(" (").append(modifier).append(")");
+ segmentAttribute->getSegmentedPropertyTypeCodeSequence()->getCodeMeaning(labelName);
+ if (segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence() != nullptr)
+ {
+ OFString modifier;
+ segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence()->getCodeMeaning(modifier);
+ labelName.append(" (").append(modifier).append(")");
+ }
+ }
+ else
+ {
+ labelName = std::to_string(segmentAttribute->getLabelID()).c_str();
+ if (labelName.empty())
+ labelName = "Unnamed";
}
- }
- else
- {
- labelName = std::to_string(segmentAttribute->getLabelID()).c_str();
- if (labelName.empty())
- labelName = "Unnamed";
}
float tmp[3] = { 0.0, 0.0, 0.0 };
if (segmentAttribute->getRecommendedDisplayRGBValue() != nullptr)
{
tmp[0] = segmentAttribute->getRecommendedDisplayRGBValue()[0] / 255.0;
tmp[1] = segmentAttribute->getRecommendedDisplayRGBValue()[1] / 255.0;
tmp[2] = segmentAttribute->getRecommendedDisplayRGBValue()[2] / 255.0;
}
- Label *newLabel = nullptr;
+ Label::Pointer newLabel = nullptr;
// If labelSetImage do not exists (first image)
if (labelSetImage.IsNull())
{
// Initialize the labelSetImage with the read image
labelSetImage = LabelSetImage::New();
- labelSetImage->InitializeByLabeledImage(layerImage);
+ labelSetImage->InitializeByLabeledImage(segmentImage);
// Already a label was generated, so set the information to this
newLabel = labelSetImage->GetActiveLabel();
newLabel->SetName(labelName.c_str());
newLabel->SetColor(Color(tmp));
newLabel->SetValue(segValue);
}
else
{
- // Add a new layer to the labelSetImage. Background label is set automatically
- labelSetImage->AddLayer(layerImage);
+ LabelSetImage::GroupIndexType groupID = 0;
+ if (assumeOverlappingSegments)
+ {
+ // Add a new group because we have to expect every label to be overlapping
+ // the label content is directly transfered here.
+ groupID = labelSetImage->AddLayer(segmentImage);
+ }
- // Add new label
- newLabel = new Label;
+ // Add the new label
+ newLabel = Label::New();
newLabel->SetName(labelName.c_str());
newLabel->SetColor(Color(tmp));
newLabel->SetValue(segValue);
- labelSetImage->AddLabel(newLabel, labelSetImage->GetActiveLayer());
+ labelSetImage->AddLabel(newLabel, groupID, true, true);
+
+ if (!assumeOverlappingSegments)
+ {
+ //if we know the labels are non overlapping we can put everything in one image
+ //the label content has to be transfered, as no new group was added.
+ mitk::TransferLabelContent(segmentImage, labelSetImage->GetGroupImage(groupID),
+ labelSetImage->GetConstLabelsByValue(labelSetImage->GetLabelValuesByGroup(groupID)),
+ mitk::LabelSetImage::UNLABELED_VALUE, mitk::LabelSetImage::UNLABELED_VALUE, false, {{segValue,newLabel->GetValue()}});
+ }
+
}
// Add some more label properties
this->SetLabelProperties(newLabel, segmentAttribute);
++segmentIter;
}
labelSetImage->SetAllLabelsVisible(true);
// Add some general DICOM Segmentation properties
mitk::IDICOMTagsOfInterest *toiSrv = DICOMIOHelper::GetTagsOfInterestService();
auto tagsOfInterest = toiSrv->GetTagsOfInterest();
DICOMTagPathList tagsOfInterestList;
for (const auto &tag : tagsOfInterest)
{
tagsOfInterestList.push_back(tag.first);
}
mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New();
scanner->SetInputFiles({ GetInputLocation() });
scanner->AddTagPaths(tagsOfInterestList);
scanner->Scan();
mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList();
if (frames.empty())
{
MITK_ERROR << "Error reading the DICOM Seg file" << std::endl;
return result;
}
auto findings = DICOMIOHelper::ExtractPathsOfInterest(tagsOfInterestList, frames);
DICOMIOHelper::SetProperties(labelSetImage, findings);
// Set active layer to the first layer of the labelset image
if (labelSetImage->GetNumberOfLayers() > 1 && labelSetImage->GetActiveLayer() != 0)
labelSetImage->SetActiveLayer(0);
}
catch (const std::exception &e)
{
MITK_ERROR << "An error occurred while reading the DICOM Seg file: " << e.what();
return result;
}
catch (...)
{
MITK_ERROR << "An error occurred in dcmqi while reading the DICOM Seg file";
return result;
}
result.push_back(labelSetImage.GetPointer());
return result;
}
const std::string mitk::DICOMSegmentationIO::CreateMetaDataJsonFile(int layer)
{
const mitk::LabelSetImage *image = dynamic_cast<const mitk::LabelSetImage *>(this->GetInput());
const std::string output;
dcmqi::JSONSegmentationMetaInformationHandler handler;
// 1. Metadata attributes that will be listed in the resulting DICOM SEG object
std::string contentCreatorName;
if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0070, 0x0084).c_str(),
contentCreatorName))
contentCreatorName = "MITK";
handler.setContentCreatorName(contentCreatorName);
std::string clinicalTrailSeriesId;
if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0071).c_str(),
clinicalTrailSeriesId))
clinicalTrailSeriesId = "Session 1";
handler.setClinicalTrialSeriesID(clinicalTrailSeriesId);
std::string clinicalTrialTimePointID;
if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0050).c_str(),
clinicalTrialTimePointID))
clinicalTrialTimePointID = "0";
handler.setClinicalTrialTimePointID(clinicalTrialTimePointID);
std::string clinicalTrialCoordinatingCenterName = "";
if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0060).c_str(),
clinicalTrialCoordinatingCenterName))
clinicalTrialCoordinatingCenterName = "Unknown";
handler.setClinicalTrialCoordinatingCenterName(clinicalTrialCoordinatingCenterName);
std::string seriesDescription;
if (!image->GetPropertyList()->GetStringProperty("name", seriesDescription))
seriesDescription = "MITK Segmentation";
handler.setSeriesDescription(seriesDescription);
handler.setSeriesNumber("0" + std::to_string(layer));
handler.setInstanceNumber("1");
handler.setBodyPartExamined("");
auto labelSet = image->GetConstLabelsByValue(image->GetLabelValuesByGroup(layer));
+ unsigned int segmentNumber = 0;
+
for (const auto& label : labelSet)
{
+ ++segmentNumber;
if (label != nullptr)
{
- TemporoSpatialStringProperty *segmentNumberProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
- mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str()));
+ //Deactivated. Currently contains LabelID, but that is not valid. See T30157. Must be reworked/removed in conjunction with
+ // T30157
+ //TemporoSpatialStringProperty *segmentNumberProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
+ // mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str()));
TemporoSpatialStringProperty *segmentLabelProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str()));
TemporoSpatialStringProperty *algorithmTypeProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str()));
TemporoSpatialStringProperty *segmentCategoryCodeValueProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str()));
TemporoSpatialStringProperty *segmentCategoryCodeSchemeProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str()));
TemporoSpatialStringProperty *segmentCategoryCodeMeaningProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str()));
TemporoSpatialStringProperty *segmentTypeCodeValueProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str()));
TemporoSpatialStringProperty *segmentTypeCodeSchemeProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str()));
TemporoSpatialStringProperty *segmentTypeCodeMeaningProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str()));
TemporoSpatialStringProperty *segmentModifierCodeValueProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str()));
TemporoSpatialStringProperty *segmentModifierCodeSchemeProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str()));
TemporoSpatialStringProperty *segmentModifierCodeMeaningProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(label->GetProperty(
mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str()));
- dcmqi::SegmentAttributes *segmentAttribute = nullptr;
+ auto segmentAttribute = handler.createOrGetSegment(segmentNumber, label->GetValue());
- if (segmentNumberProp->GetValue() == "")
- {
- MITK_ERROR << "Something went wrong with the label ID.";
- }
- else
- {
- int labelId = std::stoi(segmentNumberProp->GetValue());
- segmentAttribute = handler.createAndGetNewSegment(labelId);
- }
if (segmentAttribute != nullptr)
{
segmentAttribute->setSegmentLabel(segmentLabelProp->GetValueAsString());
segmentAttribute->setSegmentDescription(segmentLabelProp->GetValueAsString());
segmentAttribute->setSegmentAlgorithmType(algorithmTypeProp->GetValueAsString());
segmentAttribute->setSegmentAlgorithmName("MITK Segmentation");
if (segmentCategoryCodeValueProp != nullptr && segmentCategoryCodeSchemeProp != nullptr &&
segmentCategoryCodeMeaningProp != nullptr)
segmentAttribute->setSegmentedPropertyCategoryCodeSequence(
segmentCategoryCodeValueProp->GetValueAsString(),
segmentCategoryCodeSchemeProp->GetValueAsString(),
segmentCategoryCodeMeaningProp->GetValueAsString());
else
// some default values
segmentAttribute->setSegmentedPropertyCategoryCodeSequence(
"M-01000", "SRT", "Morphologically Altered Structure");
if (segmentTypeCodeValueProp != nullptr && segmentTypeCodeSchemeProp != nullptr &&
segmentTypeCodeMeaningProp != nullptr)
{
segmentAttribute->setSegmentedPropertyTypeCodeSequence(segmentTypeCodeValueProp->GetValueAsString(),
segmentTypeCodeSchemeProp->GetValueAsString(),
segmentTypeCodeMeaningProp->GetValueAsString());
handler.setBodyPartExamined(segmentTypeCodeMeaningProp->GetValueAsString());
}
else
{
// some default values
segmentAttribute->setSegmentedPropertyTypeCodeSequence("M-03000", "SRT", "Mass");
handler.setBodyPartExamined("Mass");
}
if (segmentModifierCodeValueProp != nullptr && segmentModifierCodeSchemeProp != nullptr &&
segmentModifierCodeMeaningProp != nullptr)
segmentAttribute->setSegmentedPropertyTypeModifierCodeSequence(
segmentModifierCodeValueProp->GetValueAsString(),
segmentModifierCodeSchemeProp->GetValueAsString(),
segmentModifierCodeMeaningProp->GetValueAsString());
Color color = label->GetColor();
segmentAttribute->setRecommendedDisplayRGBValue(color[0] * 255, color[1] * 255, color[2] * 255);
}
}
}
return handler.getJSONOutputAsString();
}
void mitk::DICOMSegmentationIO::SetLabelProperties(mitk::Label *label, dcmqi::SegmentAttributes *segmentAttribute)
{
// Segment Number:Identification number of the segment.The value of Segment Number(0062, 0004) shall be unique
// within the Segmentation instance in which it is created
label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str(),
TemporoSpatialStringProperty::New(std::to_string(label->GetValue())));
// Segment Label: User-defined label identifying this segment.
label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str(),
TemporoSpatialStringProperty::New(label->GetName()));
// Segment Algorithm Type: Type of algorithm used to generate the segment.
if (!segmentAttribute->getSegmentAlgorithmType().empty())
label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str(),
TemporoSpatialStringProperty::New(segmentAttribute->getSegmentAlgorithmType()));
// Add Segmented Property Category Code Sequence tags
auto categoryCodeSequence = segmentAttribute->getSegmentedPropertyCategoryCodeSequence();
if (categoryCodeSequence != nullptr)
{
OFString codeValue; // (0008,0100) Code Value
categoryCodeSequence->getCodeValue(codeValue);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeValue.c_str()));
OFString codeScheme; // (0008,0102) Coding Scheme Designator
categoryCodeSequence->getCodingSchemeDesignator(codeScheme);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeScheme.c_str()));
OFString codeMeaning; // (0008,0104) Code Meaning
categoryCodeSequence->getCodeMeaning(codeMeaning);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeMeaning.c_str()));
}
// Add Segmented Property Type Code Sequence tags
auto typeCodeSequence = segmentAttribute->getSegmentedPropertyTypeCodeSequence();
if (typeCodeSequence != nullptr)
{
OFString codeValue; // (0008,0100) Code Value
typeCodeSequence->getCodeValue(codeValue);
label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeValue.c_str()));
OFString codeScheme; // (0008,0102) Coding Scheme Designator
typeCodeSequence->getCodingSchemeDesignator(codeScheme);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeScheme.c_str()));
OFString codeMeaning; // (0008,0104) Code Meaning
typeCodeSequence->getCodeMeaning(codeMeaning);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeMeaning.c_str()));
}
// Add Segmented Property Type Modifier Code Sequence tags
auto modifierCodeSequence = segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence();
if (modifierCodeSequence != nullptr)
{
OFString codeValue; // (0008,0100) Code Value
modifierCodeSequence->getCodeValue(codeValue);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeValue.c_str()));
OFString codeScheme; // (0008,0102) Coding Scheme Designator
modifierCodeSequence->getCodingSchemeDesignator(codeScheme);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeScheme.c_str()));
OFString codeMeaning; // (0008,0104) Code Meaning
modifierCodeSequence->getCodeMeaning(codeMeaning);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeMeaning.c_str()));
}
// Add Atomic RegionSequence tags
auto atomicRegionSequence = segmentAttribute->getAnatomicRegionSequence();
if (atomicRegionSequence != nullptr)
{
OFString codeValue; // (0008,0100) Code Value
atomicRegionSequence->getCodeValue(codeValue);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeValue.c_str()));
OFString codeScheme; // (0008,0102) Coding Scheme Designator
atomicRegionSequence->getCodingSchemeDesignator(codeScheme);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeScheme.c_str()));
OFString codeMeaning; // (0008,0104) Code Meaning
atomicRegionSequence->getCodeMeaning(codeMeaning);
label->SetProperty(
DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH()).c_str(),
TemporoSpatialStringProperty::New(codeMeaning.c_str()));
}
}
DICOMSegmentationIO *DICOMSegmentationIO::IOClone() const { return new DICOMSegmentationIO(*this); }
} // namespace
#endif //__mitkDICOMSegmentationIO__cpp
diff --git a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h
index 6720ccc7c7..83823ad089 100644
--- a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h
+++ b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h
@@ -1,70 +1,70 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkDICOMSegmentationIO_h
#define mitkDICOMSegmentationIO_h
#include <mitkAbstractFileIO.h>
#include <mitkLabelSetImage.h>
#include <mitkDICOMTagsOfInterestAddHelper.h>
#include <dcmqi/JSONSegmentationMetaInformationHandler.h>
#include <memory>
namespace mitk
{
/**
* Read and Writes a LabelSetImage to a dcm file
* @ingroup Process
*/
class DICOMSegmentationIO : public mitk::AbstractFileIO
{
public:
typedef mitk::LabelSetImage InputType;
typedef itk::Image<unsigned short, 3> itkInputImageType;
typedef itk::Image<short, 3> itkInternalImageType;
DICOMSegmentationIO();
// -------------- AbstractFileReader -------------
using AbstractFileReader::Read;
ConfidenceLevel GetReaderConfidenceLevel() const override;
// -------------- AbstractFileWriter -------------
void Write() override;
ConfidenceLevel GetWriterConfidenceLevel() const override;
static std::vector<mitk::DICOMTagPath> GetDICOMTagsOfInterest();
protected:
/**
* @brief Reads a number of DICOM segmentation from the file system
* @return a vector of mitk::LabelSetImages
- * @throws throws an mitk::Exception if an error ocurrs
+ * @throws throws an mitk::Exception if an error occurs
*/
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
private:
DICOMSegmentationIO *IOClone() const override;
// -------------- DICOMSegmentationIO specific functions -------------
const std::string CreateMetaDataJsonFile(int layer);
void SetLabelProperties(Label *label, dcmqi::SegmentAttributes *segmentAttribute);
};
} // end of namespace mitk
#endif
diff --git a/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h b/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h
index 0a99aebd99..3ae393eccf 100644
--- a/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h
+++ b/Modules/Multilabel/autoload/IO/mitkLegacyLabelSetImageIO.h
@@ -1,59 +1,59 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkLegacyLabelSetImageIO_h
#define mitkLegacyLabelSetImageIO_h
#include <mitkAbstractFileReader.h>
#include <mitkLabelSetImage.h>
namespace mitk
{
/**
* Writes a LabelSetImage to a file.
* mitk::Identifiable UID is supported and will be serialized.
* @ingroup Process
*/
// The export macro should be removed. Currently, the unit
// tests directly instantiate this class.
class LegacyLabelSetImageIO : public mitk::AbstractFileReader
{
public:
typedef mitk::LabelSetImage InputType;
LegacyLabelSetImageIO();
// -------------- AbstractFileReader -------------
using AbstractFileReader::Read;
ConfidenceLevel GetConfidenceLevel() const override;
protected:
/**
* @brief Reads a number of mitk::LabelSetImages from the file system
* @return a vector of mitk::LabelSetImages
- * @throws throws an mitk::Exception if an error ocurrs during parsing the nrrd header
+ * @throws throws an mitk::Exception if an error occurs during parsing the nrrd header
*/
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
// Fills the m_DefaultMetaDataKeys vector with default values
virtual void InitializeDefaultMetaDataKeys();
private:
LegacyLabelSetImageIO *Clone() const override;
std::vector<std::string> m_DefaultMetaDataKeys;
};
} // end of namespace mitk
#endif
diff --git a/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.h b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.h
index b2a5757143..397c26b8c7 100644
--- a/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.h
+++ b/Modules/Multilabel/autoload/IO/mitkMultiLabelSegmentationIO.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 mitkMultiLabelSegmentationIO_h
#define mitkMultiLabelSegmentationIO_h
#include <mitkAbstractFileIO.h>
#include <mitkLabelSetImage.h>
namespace mitk
{
/**
* Writes a LabelSetImage to a file.
* mitk::Identifiable UID is supported and will be serialized.
* @ingroup Process
*/
// The export macro should be removed. Currently, the unit
// tests directly instantiate this class.
class MultiLabelSegmentationIO : public mitk::AbstractFileIO
{
public:
typedef mitk::LabelSetImage InputType;
MultiLabelSegmentationIO();
// -------------- AbstractFileReader -------------
using AbstractFileReader::Read;
ConfidenceLevel GetReaderConfidenceLevel() const override;
// -------------- AbstractFileWriter -------------
void Write() override;
ConfidenceLevel GetWriterConfidenceLevel() const override;
protected:
/**
* @brief Reads a number of mitk::LabelSetImages from the file system
* @return a vector of mitk::LabelSetImages
- * @throws throws an mitk::Exception if an error ocurrs during parsing the nrrd header
+ * @throws throws an mitk::Exception if an error occurs during parsing the nrrd header
*/
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
// Fills the m_DefaultMetaDataKeys vector with default values
virtual void InitializeDefaultMetaDataKeys();
private:
MultiLabelSegmentationIO *IOClone() const override;
std::vector<std::string> m_DefaultMetaDataKeys;
};
} // end of namespace mitk
#endif
diff --git a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp
index 3f54523643..635d664ccc 100644
--- a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp
+++ b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp
@@ -1,146 +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 "mitkMultilabelIOMimeTypes.h"
#include <mitkIOMimeTypes.h>
#include <mitkLog.h>
-#include <filesystem>
+#include <mitkFileSystem.h>
#include <fstream>
#include <nlohmann/json.hpp>
#include <itkNrrdImageIO.h>
#include "itkMetaDataObject.h"
mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::MitkSegmentationTaskListMimeType()
: CustomMimeType(SEGMENTATIONTASKLIST_MIMETYPE_NAME())
{
this->AddExtension("json");
this->SetCategory("MITK Segmentation Task List");
this->SetComment("MITK Segmentation Task List");
}
bool mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::AppliesTo(const std::string& path) const
{
bool result = CustomMimeType::AppliesTo(path);
- if (!std::filesystem::exists(path)) // T18572
+ if (!fs::exists(path)) // T18572
return result;
std::ifstream file(path);
if (!file.is_open())
return false;
auto json = nlohmann::json::parse(file, nullptr, false);
if (json.is_discarded() || !json.is_object())
return false;
if ("MITK Segmentation Task List" != json.value("FileFormat", ""))
return false;
if (1 != json.value<int>("Version", 0))
return false;
return true;
}
mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType* mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::Clone() const
{
return new MitkSegmentationTaskListMimeType(*this);
}
mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType mitk::MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE()
{
return MitkSegmentationTaskListMimeType();
}
std::string mitk::MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE_NAME()
{
return IOMimeTypes::DEFAULT_BASE_NAME() + ".segmentationtasklist";
}
mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType::LegacyLabelSetMimeType()
: CustomMimeType(LEGACYLABELSET_MIMETYPE_NAME())
{
this->AddExtension("nrrd");
this->SetCategory("MITK LabelSetImage");
this->SetComment("MITK LabelSetImage (legacy format)");
}
bool mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType::AppliesTo(const std::string& path) const
{
bool canRead = CustomMimeType::AppliesTo(path);
- if (!std::filesystem::exists(path)) // T18572
+ if (!fs::exists(path)) // T18572
return canRead;
if (!canRead)
{
return false;
}
std::string value("");
try
{
std::ifstream file(path);
if (!file.is_open())
return false;
itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New();
io->SetFileName(path);
io->ReadImageInformation();
itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary();
itk::ExposeMetaData<std::string>(imgMetaDataDictionary, "modality", value);
}
catch(const std::exception& e)
{
MITK_DEBUG << "Error while try to anylize NRRD file for LegacyLabelSetMimeType. File: " << path <<"; Error: " << e.what();
}
catch(...)
{
- MITK_DEBUG << "Unkown error while try to anylize NRRD file for LegacyLabelSetMimeType. File: " << path;
+ MITK_DEBUG << "Unknown error while try to analyze NRRD file for LegacyLabelSetMimeType. File: " << path;
}
if (value.compare("org.mitk.image.multilabel") != 0)
{
return false;
}
return true;
}
mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType* mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType::Clone() const
{
return new LegacyLabelSetMimeType(*this);
}
mitk::MitkMultilabelIOMimeTypes::LegacyLabelSetMimeType mitk::MitkMultilabelIOMimeTypes::LEGACYLABELSET_MIMETYPE()
{
return LegacyLabelSetMimeType();
}
std::string mitk::MitkMultilabelIOMimeTypes::LEGACYLABELSET_MIMETYPE_NAME()
{
return IOMimeTypes::DEFAULT_BASE_NAME() + ".legacylabelsetimage";
}
std::vector<mitk::CustomMimeType*> mitk::MitkMultilabelIOMimeTypes::Get()
{
std::vector<CustomMimeType*> mimeTypes;
mimeTypes.push_back(SEGMENTATIONTASKLIST_MIMETYPE().Clone());
mimeTypes.push_back(LEGACYLABELSET_MIMETYPE().Clone());
return mimeTypes;
}
diff --git a/Modules/Multilabel/autoload/IO/mitkSegmentationTaskListIO.cpp b/Modules/Multilabel/autoload/IO/mitkSegmentationTaskListIO.cpp
index 6afa7c5a81..e883c20426 100644
--- a/Modules/Multilabel/autoload/IO/mitkSegmentationTaskListIO.cpp
+++ b/Modules/Multilabel/autoload/IO/mitkSegmentationTaskListIO.cpp
@@ -1,267 +1,267 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkSegmentationTaskListIO.h"
#include "mitkMultilabelIOMimeTypes.h"
#include <mitkSegmentationTaskList.h>
#include <nlohmann/json.hpp>
-#include <filesystem>
+#include <mitkFileSystem.h>
#include <fstream>
namespace mitk
{
void to_json(nlohmann::json& json, const SegmentationTaskList::Task& task)
{
if (task.HasName())
json["Name"] = task.GetName();
if (task.HasDescription())
json["Description"] = task.GetDescription();
if (task.HasImage())
json["Image"] = task.GetImage().string();
if (task.HasSegmentation())
json["Segmentation"] = task.GetSegmentation().string();
if (task.HasLabelName())
json["LabelName"] = task.GetLabelName();
if (task.HasLabelNameSuggestions())
json["LabelNameSuggestions"] = task.GetLabelNameSuggestions().string();
if (task.HasPreset())
json["Preset"] = task.GetPreset().string();
if (task.HasResult())
json["Result"] = task.GetResult().string();
if (task.HasDynamic())
json["Dynamic"] = task.GetDynamic();
}
void from_json(const nlohmann::json& json, SegmentationTaskList::Task& task)
{
auto iter = json.find("Name");
if (iter != json.end())
task.SetName(json["Name"].get<std::string>());
iter = json.find("Description");
if (iter != json.end())
task.SetDescription(json["Description"].get<std::string>());
iter = json.find("Image");
if (iter != json.end())
task.SetImage(json["Image"].get<std::string>());
iter = json.find("Segmentation");
if (iter != json.end())
task.SetSegmentation(json["Segmentation"].get<std::string>());
iter = json.find("LabelName");
if (iter != json.end())
task.SetLabelName(json["LabelName"].get<std::string>());
iter = json.find("LabelNameSuggestions");
if (iter != json.end())
task.SetLabelNameSuggestions(json["LabelNameSuggestions"].get<std::string>());
iter = json.find("Preset");
if (iter != json.end())
task.SetPreset(json["Preset"].get<std::string>());
iter = json.find("Result");
if (iter != json.end())
task.SetResult(json["Result"].get<std::string>());
iter = json.find("Dynamic");
if (iter != json.end())
task.SetDynamic(json["Dynamic"].get<bool>());
}
}
mitk::SegmentationTaskListIO::SegmentationTaskListIO()
: AbstractFileIO(SegmentationTaskList::GetStaticNameOfClass(), MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE(), "MITK Segmentation Task List")
{
this->RegisterService();
}
std::vector<mitk::BaseData::Pointer> mitk::SegmentationTaskListIO::DoRead()
{
auto* stream = this->GetInputStream();
std::ifstream fileStream;
if (nullptr == stream)
{
auto filename = this->GetInputLocation();
- if (filename.empty() || !std::filesystem::exists(filename))
+ if (filename.empty() || !fs::exists(filename))
mitkThrow() << "Invalid or nonexistent filename: \"" << filename << "\"!";
fileStream.open(filename);
if (!fileStream.is_open())
mitkThrow() << "Could not open file \"" << filename << "\" for reading!";
stream = &fileStream;
}
nlohmann::json json;
try
{
json = nlohmann::json::parse(*stream);
}
catch (const nlohmann::json::exception& e)
{
mitkThrow() << e.what();
}
if (!json.is_object())
mitkThrow() << "Unknown file format (expected JSON object as root)!";
if ("MITK Segmentation Task List" != json.value("FileFormat", ""))
mitkThrow() << "Unknown file format (expected \"MITK Segmentation Task List\")!";
if (1 != json.value<int>("Version", 0))
mitkThrow() << "Unknown file format version (expected \"1\")!";
if (!json.contains("Tasks") || !json["Tasks"].is_array())
mitkThrow() << "Tasks array not found!";
auto segmentationTaskList = SegmentationTaskList::New();
if (json.contains("Name"))
segmentationTaskList->SetProperty("name", StringProperty::New(json["Name"].get<std::string>()));
try
{
if (json.contains("Defaults"))
{
segmentationTaskList->SetDefaults(json["Defaults"].get<SegmentationTaskList::Task>());
if (segmentationTaskList->GetDefaults().HasResult())
mitkThrow() << "Defaults must not contain \"Result\"!";
}
for (const auto& task : json["Tasks"])
{
auto i = segmentationTaskList->AddTask(task.get<SegmentationTaskList::Task>());
if (!segmentationTaskList->HasImage(i))
mitkThrow() << "Task " << i << " must contain \"Image\"!";
- std::filesystem::path imagePath(segmentationTaskList->GetImage(i));
+ fs::path imagePath(segmentationTaskList->GetImage(i));
if (imagePath.is_relative())
{
auto inputLocation = this->GetInputLocation();
/* If we have access to properties, we are reading from an MITK scene
* file. In this case, paths are still relative to the original input
* location, which is preserved in the properties.
*/
const auto* properties = this->GetProperties();
if (properties != nullptr)
properties->GetStringProperty("MITK.IO.reader.inputlocation", inputLocation);
- imagePath = std::filesystem::path(inputLocation).remove_filename() / imagePath;
+ imagePath = fs::path(inputLocation).remove_filename() / imagePath;
}
- if (!std::filesystem::exists(imagePath))
+ if (!fs::exists(imagePath))
mitkThrow() << "Referenced image \"" << imagePath << "\" in task " << i << " does not exist!";
if (!segmentationTaskList->HasResult(i))
mitkThrow() << "Task " << i << " must contain \"Result\"!";
}
}
catch (const nlohmann::json::type_error& e)
{
mitkThrow() << e.what();
}
std::vector<BaseData::Pointer> result;
result.push_back(segmentationTaskList.GetPointer());
return result;
}
void mitk::SegmentationTaskListIO::Write()
{
auto segmentationTaskList = dynamic_cast<const SegmentationTaskList*>(this->GetInput());
if (nullptr == segmentationTaskList)
mitkThrow() << "Invalid input for writing!";
if (segmentationTaskList->GetNumberOfTasks() == 0)
mitkThrow() << "No tasks found!";
auto* stream = this->GetOutputStream();
std::ofstream fileStream;
if (nullptr == stream)
{
auto filename = this->GetOutputLocation();
if (filename.empty())
mitkThrow() << "Neither an output stream nor an output filename was specified!";
fileStream.open(filename);
if (!fileStream.is_open())
mitkThrow() << "Could not open file \"" << filename << "\" for writing!";
stream = &fileStream;
}
if (!stream->good())
mitkThrow() << "Stream for writing is not good!";
nlohmann::ordered_json json = {
{ "FileFormat", "MITK Segmentation Task List" },
{ "Version", 1 },
{ "Name", segmentationTaskList->GetProperty("name")->GetValueAsString() }
};
nlohmann::json defaults = segmentationTaskList->GetDefaults();
if (!defaults.is_null())
json["Defaults"] = defaults;
nlohmann::json tasks;
for (const auto& task : *segmentationTaskList)
tasks.push_back(task);
json["Tasks"] = tasks;
*stream << std::setw(2) << json << std::endl;
}
mitk::SegmentationTaskListIO* mitk::SegmentationTaskListIO::IOClone() const
{
return new SegmentationTaskListIO(*this);
}
diff --git a/Modules/Multilabel/files.cmake b/Modules/Multilabel/files.cmake
index cc5f29c1e5..f05c0cb0d0 100644
--- a/Modules/Multilabel/files.cmake
+++ b/Modules/Multilabel/files.cmake
@@ -1,23 +1,24 @@
set(CPP_FILES
mitkLabel.cpp
+ mitkLabelHighlightGuard.cpp
mitkLabelSetImage.cpp
mitkLabelSetImageConverter.cpp
mitkLabelSetImageSource.cpp
mitkLabelSetImageHelper.cpp
mitkLabelSetImageSurfaceStampFilter.cpp
mitkLabelSetImageToSurfaceFilter.cpp
mitkLabelSetImageToSurfaceThreadedFilter.cpp
mitkLabelSetImageVtkMapper2D.cpp
mitkMultilabelObjectFactory.cpp
mitkMultiLabelIOHelper.cpp
mitkMultiLabelEvents.cpp
mitkMultiLabelPredicateHelper.cpp
mitkDICOMSegmentationPropertyHelper.cpp
mitkDICOMSegmentationConstants.cpp
mitkSegmentationTaskList.cpp
mitkMultiLabelSegmentationVtkMapper3D.cpp
)
set(RESOURCE_FILES
)
diff --git a/Modules/Multilabel/mitkLabel.h b/Modules/Multilabel/mitkLabel.h
index 649affabc6..03bcec65fe 100644
--- a/Modules/Multilabel/mitkLabel.h
+++ b/Modules/Multilabel/mitkLabel.h
@@ -1,115 +1,115 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkLabel_h
#define mitkLabel_h
#include "MitkMultilabelExports.h"
#include <mitkColorProperty.h>
#include <mitkPropertyList.h>
#include <mitkPoint.h>
#include <mitkVector.h>
namespace mitk
{
//##
//##Documentation
//## @brief A data structure describing a label.
//## @ingroup Data
//##
class MITKMULTILABEL_EXPORT Label : public PropertyList
{
public:
mitkClassMacro(Label, mitk::PropertyList);
typedef unsigned short PixelType;
itkNewMacro(Self);
mitkNewMacro2Param(Self, PixelType, const std::string&);
/// The maximum value a label can get: Since the value is of type unsigned short MAX_LABEL_VALUE = 65535
static const PixelType MAX_LABEL_VALUE;
//** Value indicating pixels that are not labeled at all.*/
static constexpr PixelType UNLABELED_VALUE = 0;
void SetLocked(bool locked);
bool GetLocked() const;
void SetVisible(bool visible);
bool GetVisible() const;
void SetOpacity(float opacity);
float GetOpacity() const;
void SetName(const std::string &name);
std::string GetName() const;
std::string GetTrackingID() const;
void SetCenterOfMassIndex(const mitk::Point3D &center);
mitk::Point3D GetCenterOfMassIndex() const;
void SetCenterOfMassCoordinates(const mitk::Point3D &center);
mitk::Point3D GetCenterOfMassCoordinates() const;
void SetColor(const mitk::Color &);
const mitk::Color &GetColor() const;
void SetValue(PixelType pixelValue);
PixelType GetValue() const;
void SetLayer(unsigned int layer);
unsigned int GetLayer() const;
void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override;
using itk::Object::Modified;
void Modified() { Superclass::Modified(); }
Label();
Label(PixelType value, const std::string& name);
~Label() override;
protected:
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
Label(const Label &other);
private:
itk::LightObject::Pointer InternalClone() const override;
};
using LabelVector = std::vector<Label::Pointer>;
using ConstLabelVector = std::vector<Label::ConstPointer>;
/**
- * @brief Equal A function comparing two labels for beeing equal in data
+ * @brief Equal A function comparing two labels for being equal in data
*
* @ingroup MITKTestingAPI
*
* Following aspects are tested for equality:
* - Lebel equality via Equal-PropetyList
*
* @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
*/
MITKMULTILABEL_EXPORT bool Equal(const mitk::Label &leftHandSide,
const mitk::Label &rightHandSide,
ScalarType eps,
bool verbose);
} // namespace mitk
#endif
diff --git a/Modules/Multilabel/mitkLabelHighlightGuard.cpp b/Modules/Multilabel/mitkLabelHighlightGuard.cpp
new file mode 100644
index 0000000000..7c0f0aef7e
--- /dev/null
+++ b/Modules/Multilabel/mitkLabelHighlightGuard.cpp
@@ -0,0 +1,95 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+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 <mitkLabelHighlightGuard.h>
+
+#include <mitkExceptionMacro.h>
+#include <mitkProperties.h>
+#include <mitkRenderingManager.h>
+#include <mitkVectorProperty.h>
+
+
+void mitk::LabelHighlightGuard::SetSegmentationNode(DataNode* node)
+{
+ auto ownNode = m_Node.Lock();
+ if (node != ownNode)
+ {
+ UpdateNode(ownNode, {}, false);
+ m_Node = node;
+ m_Labels.clear();
+ UpdateNode(node, m_Labels, m_HighlightInvisible);
+ mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+ }
+};
+
+mitk::DataNode::Pointer mitk::LabelHighlightGuard::GetSegmentationNode() const
+{
+ return m_Node.Lock();
+}
+
+void mitk::LabelHighlightGuard::SetHighlightedLabels(LabelSetImage::LabelValueVectorType labels)
+{
+ auto ownNode = m_Node.Lock();
+ if (ownNode.IsNotNull() && labels != m_Labels)
+ {
+ m_Labels = labels;
+ UpdateNode(ownNode, m_Labels, m_HighlightInvisible);
+ mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+ }
+}
+
+void mitk::LabelHighlightGuard::SetHighlightInvisibleLabels(bool highlightInvisible)
+{
+ auto ownNode = m_Node.Lock();
+ if (highlightInvisible == m_HighlightInvisible)
+ return;
+
+ m_HighlightInvisible = highlightInvisible;
+
+ if (ownNode.IsNotNull() && !m_Labels.empty())
+ {
+ UpdateNode(ownNode, m_Labels, m_HighlightInvisible);
+ mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+ }
+}
+
+mitk::LabelHighlightGuard::~LabelHighlightGuard()
+{
+ this->SetSegmentationNode(nullptr);
+}
+
+void mitk::LabelHighlightGuard::UpdateNode(DataNode* node, LabelSetImage::LabelValueVectorType labels, bool highlightInvisible)
+{
+ if (nullptr == node)
+ return;
+
+ mitk::IntVectorProperty::Pointer labelProperty = dynamic_cast<mitk::IntVectorProperty*>(node->GetNonConstProperty(PROPERTY_NAME_LABELS_HIGHLIGHTED()));
+ if (nullptr == labelProperty)
+ {
+ labelProperty = mitk::IntVectorProperty::New();
+ node->SetProperty(PROPERTY_NAME_LABELS_HIGHLIGHTED(), labelProperty);
+ }
+
+ mitk::IntVectorProperty::VectorType intValues(labels.begin(), labels.end());
+ labelProperty->SetValue(intValues);
+ labelProperty->Modified(); //see T30386; needed because VectorProperty::SetValue does currently trigger no modified
+
+ mitk::BoolProperty::Pointer invisibleProperty = dynamic_cast<mitk::BoolProperty*>(node->GetNonConstProperty(PROPERTY_NAME_HIGHLIGHT_INVISIBLE()));
+ if (nullptr == invisibleProperty)
+ {
+ invisibleProperty = mitk::BoolProperty::New();
+ node->SetProperty(PROPERTY_NAME_HIGHLIGHT_INVISIBLE(), invisibleProperty);
+ }
+
+ invisibleProperty->SetValue(highlightInvisible);
+ invisibleProperty->Modified(); //see T30386; needed because VectorProperty::SetValue does currently trigger no modified
+}
diff --git a/Modules/Multilabel/mitkLabelHighlightGuard.h b/Modules/Multilabel/mitkLabelHighlightGuard.h
new file mode 100644
index 0000000000..d8db916500
--- /dev/null
+++ b/Modules/Multilabel/mitkLabelHighlightGuard.h
@@ -0,0 +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.
+
+============================================================================*/
+
+#ifndef mitkLabelHighlightGuard_h
+#define mitkLabelHighlightGuard_h
+
+#include <MitkMultilabelExports.h>
+
+#include <mitkDataNode.h>
+#include <mitkLabelSetImage.h>
+
+namespace mitk
+{
+ /** Helper class that allows to manage the node properties for label highlighting.
+ * Using the class ensures that the highlighting will always be removed if needed:
+ * (a) the destructor of the guard is called, or (b) the node changes (highlight
+ * of former node will be removed). The guard also manages the triggering the
+ * RenderWindowManager, if need, to refresh the render windows.
+ */
+ class MITKMULTILABEL_EXPORT LabelHighlightGuard
+ {
+ public:
+ void SetSegmentationNode(DataNode* node);
+ DataNode::Pointer GetSegmentationNode() const;
+
+ void SetHighlightedLabels(LabelSetImage::LabelValueVectorType labels);
+ void SetHighlightInvisibleLabels(bool highlightInvisible);
+
+ ~LabelHighlightGuard();
+
+ constexpr static const char* PROPERTY_NAME_LABELS_HIGHLIGHTED()
+ {
+ return "org.mitk.multilabel.labels.highlighted";
+ };
+
+ constexpr static const char* PROPERTY_NAME_HIGHLIGHT_INVISIBLE()
+ {
+ return "org.mitk.multilabel.highlight_invisible";
+ };
+
+ protected:
+ static void UpdateNode(DataNode* node, LabelSetImage::LabelValueVectorType labels, bool highlightInvisible);
+ LabelSetImage::LabelValueVectorType m_Labels = {};
+ bool m_HighlightInvisible = false;
+ WeakPointer<DataNode> m_Node;
+ };
+}
+
+#endif
diff --git a/Modules/Multilabel/mitkLabelSetImage.cpp b/Modules/Multilabel/mitkLabelSetImage.cpp
index faaf396b89..2510ef44ad 100644
--- a/Modules/Multilabel/mitkLabelSetImage.cpp
+++ b/Modules/Multilabel/mitkLabelSetImage.cpp
@@ -1,1739 +1,1755 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkLabelSetImage.h"
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <mitkImagePixelWriteAccessor.h>
#include <mitkPadImageFilter.h>
#include <mitkDICOMSegmentationPropertyHelper.h>
#include <mitkDICOMQIPropertyHelper.h>
#include <mitkNodePredicateGeometry.h>
#include <itkLabelGeometryImageFilter.h>
#include <itkCommand.h>
#include <itkBinaryFunctorImageFilter.h>
namespace mitk
{
template <typename ImageType>
void ClearBufferProcessing(ImageType* itkImage)
{
itkImage->FillBuffer(0);
}
void ClearImageBuffer(mitk::Image* image)
{
if (image->GetDimension() == 4)
{ //remark: this extra branch was added, because LabelSetImage instances can be
//dynamic (4D), but AccessByItk by support only supports 2D and 3D.
//The option to change the CMake default dimensions for AccessByItk was
//dropped (for details see discussion in T28756)
AccessFixedDimensionByItk(image, ClearBufferProcessing, 4);
}
else
{
AccessByItk(image, ClearBufferProcessing);
}
}
}
const mitk::LabelSetImage::LabelValueType mitk::LabelSetImage::UNLABELED_VALUE = 0;
mitk::LabelSetImage::LabelSetImage()
: mitk::Image(), m_ActiveLabelValue(0), m_UnlabeledLabelLock(false), m_ActiveLayer(0), m_activeLayerInvalid(false)
{
m_LookupTable = mitk::LookupTable::New();
m_LookupTable->SetType(mitk::LookupTable::MULTILABEL);
// Add some DICOM Tags as properties to segmentation image
DICOMSegmentationPropertyHelper::DeriveDICOMSegmentationProperties(this);
}
mitk::LabelSetImage::LabelSetImage(const mitk::LabelSetImage &other)
: Image(other),
m_ActiveLabelValue(other.m_ActiveLabelValue),
m_LookupTable(other.m_LookupTable->Clone()),
m_UnlabeledLabelLock(other.m_UnlabeledLabelLock),
m_ActiveLayer(other.GetActiveLayer()),
m_activeLayerInvalid(false)
{
GroupIndexType i = 0;
for (auto groupImage : other.m_LayerContainer)
{
this->AddLayer(groupImage->Clone(), other.GetConstLabelsByValue(other.GetLabelValuesByGroup(i)));
i++;
}
m_Groups = other.m_Groups;
// Add some DICOM Tags as properties to segmentation image
DICOMSegmentationPropertyHelper::DeriveDICOMSegmentationProperties(this);
}
void mitk::LabelSetImage::Initialize(const mitk::Image *other)
{
mitk::PixelType pixelType(mitk::MakeScalarPixelType<LabelSetImage::PixelType>());
if (other->GetDimension() == 2)
{
const unsigned int dimensions[] = {other->GetDimension(0), other->GetDimension(1), 1};
Superclass::Initialize(pixelType, 3, dimensions);
}
else
{
Superclass::Initialize(pixelType, other->GetDimension(), other->GetDimensions());
}
auto originalGeometry = other->GetTimeGeometry()->Clone();
this->SetTimeGeometry(originalGeometry);
// initialize image memory to zero
ClearImageBuffer(this);
// Transfer some general DICOM properties from the source image to derived image (e.g. Patient information,...)
DICOMQIPropertyHelper::DeriveDICOMSourceProperties(other, this);
- // Add a inital LabelSet ans corresponding image data to the stack
+ // Add an initial LabelSet and corresponding image data to the stack
if (this->GetNumberOfLayers() == 0)
{
AddLayer();
}
}
mitk::LabelSetImage::~LabelSetImage()
{
for (auto [value, label] : m_LabelMap)
{
+ (void)value; // Prevent unused variable error in older compilers
this->ReleaseLabel(label);
}
m_LabelMap.clear();
}
unsigned int mitk::LabelSetImage::GetActiveLayer() const
{
if (m_LayerContainer.size() == 0) mitkThrow() << "Cannot return active layer index. No layer is available.";
return m_ActiveLayer;
}
unsigned int mitk::LabelSetImage::GetNumberOfLayers() const
{
return m_LayerContainer.size();
}
void mitk::LabelSetImage::RemoveGroup(GroupIndexType indexToDelete)
{
if (!this->ExistGroup(indexToDelete)) mitkThrow() << "Cannot remove group. Group does not exist. Invalid group index: "<<indexToDelete;
const auto activeIndex = GetActiveLayer();
auto newActiveIndex = activeIndex;
auto newActiveIndexBeforeDeletion = activeIndex;
//determine new active group index (after the group will be removed);
if (indexToDelete < activeIndex)
{ //lower the index because position in m_LayerContainer etc has changed
newActiveIndex = activeIndex-1;
}
else if (indexToDelete == activeIndex)
{
if (this->GetNumberOfLayers() == 1)
{ //last layer is about to be deleted
newActiveIndex = 0;
}
else
{
//we have to add/subtract one more because we have not removed the layer yet, thus the group count is to 1 high.
newActiveIndex = indexToDelete+1 < GetNumberOfLayers() ? indexToDelete : GetNumberOfLayers() - 2;
newActiveIndexBeforeDeletion = indexToDelete + 1 < GetNumberOfLayers() ? indexToDelete+1 : indexToDelete -1;
}
}
if (activeIndex == indexToDelete)
{
// we are deleting the active layer, it should not be copied back into the vector
m_activeLayerInvalid = true;
//copy the image content of the upcoming new active layer;
SetActiveLayer(newActiveIndexBeforeDeletion);
}
auto relevantLabels = m_GroupToLabelMap[indexToDelete];
{
std::lock_guard<std::shared_mutex> guard(m_LabelNGroupMapsMutex);
// remove labels of group
for (auto labelValue : relevantLabels)
{
auto label = m_LabelMap[labelValue];
this->ReleaseLabel(label);
m_LabelToGroupMap.erase(labelValue);
m_LabelMap.erase(labelValue);
this->InvokeEvent(LabelRemovedEvent(labelValue));
}
// remove the group entries in the maps and the image.
m_Groups.erase(m_Groups.begin() + indexToDelete);
m_GroupToLabelMap.erase(m_GroupToLabelMap.begin() + indexToDelete);
m_LayerContainer.erase(m_LayerContainer.begin() + indexToDelete);
}
//update old indexes in m_GroupToLabelMap to new layer indexes
for (auto& element : m_LabelToGroupMap)
{
if (element.second > indexToDelete) element.second = element.second -1;
}
//correct active layer index
m_ActiveLayer = newActiveIndex;
this->InvokeEvent(LabelsChangedEvent(relevantLabels));
this->InvokeEvent(GroupRemovedEvent(indexToDelete));
this->Modified();
}
mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::ExtractLabelValuesFromLabelVector(const LabelVectorType& labels)
{
LabelValueVectorType result;
for (auto label : labels)
{
result.emplace_back(label->GetValue());
}
return result;
}
mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::ExtractLabelValuesFromLabelVector(const ConstLabelVectorType& labels)
{
LabelValueVectorType result;
for (auto label : labels)
{
result.emplace_back(label->GetValue());
}
return result;
}
mitk::LabelSetImage::ConstLabelVectorType mitk::LabelSetImage::ConvertLabelVectorConst(const LabelVectorType& labels)
{
ConstLabelVectorType result(labels.begin(), labels.end());
return result;
};
const mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetAllLabelValues() const
{
LabelValueVectorType result;
for (auto [value, label] : m_LabelMap)
{
+ (void)label; // Prevent unused variable error in older compilers
result.emplace_back(value);
}
return result;
}
mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetUsedLabelValues() const
{
LabelValueVectorType result = { UNLABELED_VALUE };
for (auto [value, label] : m_LabelMap)
{
+ (void)label; // Prevent unused variable error in older compilers
result.emplace_back(value);
}
return result;
}
mitk::LabelSetImage::GroupIndexType mitk::LabelSetImage::AddLayer(ConstLabelVector labels)
{
mitk::Image::Pointer newImage = mitk::Image::New();
newImage->Initialize(this->GetPixelType(),
this->GetDimension(),
this->GetDimensions(),
this->GetImageDescriptor()->GetNumberOfChannels());
newImage->SetTimeGeometry(this->GetTimeGeometry()->Clone());
ClearImageBuffer(newImage);
return this->AddLayer(newImage, labels);
}
mitk::LabelSetImage::GroupIndexType mitk::LabelSetImage::AddLayer(mitk::Image* layerImage, ConstLabelVector labels)
{
GroupIndexType newGroupID = m_Groups.size();
if (nullptr == layerImage)
mitkThrow() << "Cannot add group. Passed group image is nullptr.";
bool equalGeometries = Equal(
*(this->GetTimeGeometry()),
*(layerImage->GetTimeGeometry()),
NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION,
NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION,
false);
if (!equalGeometries)
mitkThrow() << "Cannot add group. Passed group image has not the same geometry like segmentation.";
if (layerImage->GetPixelType() != MakePixelType<LabelValueType, LabelValueType, 1>())
mitkThrow() << "Cannot add group. Passed group image has incorrect pixel type. Only LabelValueType is supported. Invalid pixel type: "<< layerImage->GetPixelType().GetTypeAsString();
// push a new working image for the new layer
m_LayerContainer.push_back(layerImage);
m_Groups.push_back("");
m_GroupToLabelMap.push_back({});
for (auto label : labels)
{
if (m_LabelMap.end() != m_LabelMap.find(label->GetValue()))
{
mitkThrow() << "Cannot add layer. Labels that should be added with layer use at least one label value that is already in use. Conflicted label value: " << label->GetValue();
}
auto labelClone = label->Clone();
DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(labelClone);
this->AddLabelToMap(labelClone->GetValue(), labelClone, newGroupID);
this->RegisterLabel(labelClone);
}
this->Modified();
this->InvokeEvent(GroupAddedEvent(newGroupID));
return newGroupID;
}
void mitk::LabelSetImage::ReplaceGroupLabels(const GroupIndexType groupID, const ConstLabelVectorType& labelSet)
{
if (m_LayerContainer.size() <= groupID)
{
mitkThrow() << "Trying to replace labels of non-existing group. Invalid group id: "<<groupID;
}
//remove old group labels
LabelValueVectorType oldLabels;
{
std::lock_guard<std::shared_mutex> guard(m_LabelNGroupMapsMutex);
oldLabels = this->m_GroupToLabelMap[groupID];
for (auto labelID : oldLabels)
{
this->RemoveLabelFromMap(labelID);
this->InvokeEvent(LabelRemovedEvent(labelID));
}
}
this->InvokeEvent(LabelsChangedEvent(oldLabels));
this->InvokeEvent(GroupModifiedEvent(groupID));
//add new labels to group
for (auto label : labelSet)
{
this->AddLabel(label->Clone(), groupID, true, false);
}
}
void mitk::LabelSetImage::ReplaceGroupLabels(const GroupIndexType groupID, const LabelVectorType& labelSet)
{
return ReplaceGroupLabels(groupID, ConvertLabelVectorConst(labelSet));
}
mitk::Image* mitk::LabelSetImage::GetGroupImage(GroupIndexType groupID)
{
if (!this->ExistGroup(groupID)) mitkThrow() << "Error, cannot return group image. Group ID is invalid. Invalid ID: " << groupID;
return groupID == this->GetActiveLayer() ? this : m_LayerContainer[groupID];
}
const mitk::Image* mitk::LabelSetImage::GetGroupImage(GroupIndexType groupID) const
{
if (!this->ExistGroup(groupID)) mitkThrow() << "Error, cannot return group image. Group ID is invalid. Invalid ID: " << groupID;
return groupID == this->GetActiveLayer() ? this : m_LayerContainer.at(groupID).GetPointer();
}
const mitk::Image* mitk::LabelSetImage::GetGroupImageWorkaround(GroupIndexType groupID) const
{
if (!this->ExistGroup(groupID))
mitkThrow() << "Error, cannot return group image. Group ID is invalid. Invalid ID: " << groupID;
if (groupID == this->GetActiveLayer() && this->GetMTime()> m_LayerContainer[groupID]->GetMTime())
{ //we have to transfer the content first into the group image
if (4 == this->GetDimension())
{
AccessFixedDimensionByItk_n(this, ImageToLayerContainerProcessing, 4, (groupID));
}
else
{
AccessByItk_1(this, ImageToLayerContainerProcessing, groupID);
}
}
return m_LayerContainer[groupID].GetPointer();
}
const std::string& mitk::LabelSetImage::GetGroupName(GroupIndexType groupID) const
{
if (!this->ExistGroup(groupID))
mitkThrow() << "Error, cannot return group name. Group ID is invalid. Invalid ID: " << groupID;
return m_Groups[groupID];
}
void mitk::LabelSetImage::SetGroupName(GroupIndexType groupID, const std::string& name)
{
if (!this->ExistGroup(groupID))
mitkThrow() << "Error, cannot set group name. Group ID is invalid. Invalid ID: " << groupID;
m_Groups[groupID] = name;
this->InvokeEvent(GroupModifiedEvent(groupID));
}
void mitk::LabelSetImage::SetActiveLayer(unsigned int layer)
{
try
{
if (4 == this->GetDimension())
{
if ((layer != GetActiveLayer() || m_activeLayerInvalid) && (layer < this->GetNumberOfLayers()))
{
BeforeChangeLayerEvent.Send();
if (m_activeLayerInvalid)
{
// We should not write the invalid layer back to the vector
m_activeLayerInvalid = false;
}
else
{
AccessFixedDimensionByItk_n(this, ImageToLayerContainerProcessing, 4, (GetActiveLayer()));
}
m_ActiveLayer = layer;
AccessFixedDimensionByItk_n(this, LayerContainerToImageProcessing, 4, (GetActiveLayer()));
AfterChangeLayerEvent.Send();
}
}
else
{
if ((layer != GetActiveLayer() || m_activeLayerInvalid) && (layer < this->GetNumberOfLayers()))
{
BeforeChangeLayerEvent.Send();
if (m_activeLayerInvalid)
{
// We should not write the invalid layer back to the vector
m_activeLayerInvalid = false;
}
else
{
AccessByItk_1(this, ImageToLayerContainerProcessing, GetActiveLayer());
}
m_ActiveLayer = layer;
AccessByItk_1(this, LayerContainerToImageProcessing, GetActiveLayer());
AfterChangeLayerEvent.Send();
}
}
}
catch (itk::ExceptionObject &e)
{
mitkThrow() << e.GetDescription();
}
this->Modified();
}
void mitk::LabelSetImage::SetActiveLabel(LabelValueType label)
{
m_ActiveLabelValue = label;
if (label != UNLABELED_VALUE)
{
auto groupID = this->GetGroupIndexOfLabel(label);
if (groupID!=this->GetActiveLayer()) this->SetActiveLayer(groupID);
}
Modified();
}
void mitk::LabelSetImage::ClearBuffer()
{
try
{
ClearImageBuffer(this);
this->Modified();
}
catch (itk::ExceptionObject &e)
{
mitkThrow() << e.GetDescription();
}
}
void mitk::LabelSetImage::MergeLabel(PixelType pixelValue, PixelType sourcePixelValue)
{
try
{
AccessByItk_2(this, MergeLabelProcessing, pixelValue, sourcePixelValue);
}
catch (itk::ExceptionObject &e)
{
mitkThrow() << e.GetDescription();
}
this->SetActiveLabel(pixelValue);
this->InvokeEvent(LabelModifiedEvent(sourcePixelValue));
this->InvokeEvent(LabelModifiedEvent(pixelValue));
this->InvokeEvent(LabelsChangedEvent({ sourcePixelValue, pixelValue }));
Modified();
}
void mitk::LabelSetImage::MergeLabels(PixelType pixelValue, const std::vector<PixelType>& vectorOfSourcePixelValues)
{
try
{
for (unsigned int idx = 0; idx < vectorOfSourcePixelValues.size(); idx++)
{
AccessByItk_2(this, MergeLabelProcessing, pixelValue, vectorOfSourcePixelValues[idx]);
this->InvokeEvent(LabelModifiedEvent(vectorOfSourcePixelValues[idx]));
}
}
catch (itk::ExceptionObject &e)
{
mitkThrow() << e.GetDescription();
}
this->SetActiveLabel(pixelValue);
this->InvokeEvent(LabelModifiedEvent(pixelValue));
auto modifiedValues = vectorOfSourcePixelValues;
modifiedValues.push_back(pixelValue);
this->InvokeEvent(LabelsChangedEvent(modifiedValues));
Modified();
}
void mitk::LabelSetImage::RemoveLabel(LabelValueType pixelValue)
{
GroupIndexType groupID = 0;
{
std::lock_guard<std::shared_mutex> guard(m_LabelNGroupMapsMutex);
if (m_LabelMap.find(pixelValue) == m_LabelMap.end()) return;
groupID = this->GetGroupIndexOfLabel(pixelValue);
//first erase the pixel content (also triggers a LabelModified event)
this->EraseLabel(pixelValue);
this->RemoveLabelFromMap(pixelValue);
if (m_ActiveLabelValue == pixelValue)
{
this->SetActiveLabel(0);
}
}
this->InvokeEvent(LabelRemovedEvent(pixelValue));
this->InvokeEvent(LabelsChangedEvent({ pixelValue }));
this->InvokeEvent(GroupModifiedEvent(groupID));
}
void mitk::LabelSetImage::RemoveLabelFromMap(LabelValueType pixelValue)
{
if (m_LabelMap.find(pixelValue) == m_LabelMap.end()) mitkThrow()<<"Invalid state of instance. RemoveLabelFromMap was called for unknown label id. invalid label id: "<<pixelValue;
auto groupID = this->GetGroupIndexOfLabel(pixelValue);
this->ReleaseLabel(m_LabelMap[pixelValue]);
//now remove the label entry itself
m_LabelMap.erase(pixelValue);
m_LabelToGroupMap.erase(pixelValue);
auto labelsInGroup = m_GroupToLabelMap[groupID];
labelsInGroup.erase(std::remove(labelsInGroup.begin(), labelsInGroup.end(), pixelValue), labelsInGroup.end());
m_GroupToLabelMap[groupID] = labelsInGroup;
}
void mitk::LabelSetImage::RemoveLabels(const LabelValueVectorType& vectorOfLabelPixelValues)
{
for (const auto labelValue : vectorOfLabelPixelValues)
{
this->RemoveLabel(labelValue);
}
this->InvokeEvent(LabelsChangedEvent(vectorOfLabelPixelValues));
}
void mitk::LabelSetImage::EraseLabel(LabelValueType pixelValue)
{
try
{
auto groupID = this->GetGroupIndexOfLabel(pixelValue);
mitk::Image* groupImage = this->GetGroupImage(groupID);
if (4 == this->GetDimension())
{
AccessFixedDimensionByItk_1(groupImage, EraseLabelProcessing, 4, pixelValue);
}
else
{
AccessByItk_1(groupImage, EraseLabelProcessing, pixelValue);
}
+ groupImage->Modified();
}
catch (const itk::ExceptionObject& e)
{
mitkThrow() << e.GetDescription();
}
this->InvokeEvent(LabelModifiedEvent(pixelValue));
this->InvokeEvent(LabelsChangedEvent({ pixelValue }));
Modified();
}
void mitk::LabelSetImage::EraseLabels(const LabelValueVectorType& labelValues)
{
for (auto labelValue : labelValues)
{
this->EraseLabel(labelValue);
}
}
mitk::LabelSetImage::LabelValueType mitk::LabelSetImage::GetUnusedLabelValue() const
{
auto usedValues = this->GetUsedLabelValues();
return usedValues.back() + 1;
}
mitk::Label* mitk::LabelSetImage::AddLabel(mitk::Label* label, GroupIndexType groupID, bool addAsClone, bool correctLabelValue)
{
if (nullptr == label) mitkThrow() << "Invalid use of AddLabel. label is not valid.";
mitk::Label::Pointer newLabel = label;
{
std::lock_guard<std::shared_mutex> guard(m_LabelNGroupMapsMutex);
unsigned int max_size = mitk::Label::MAX_LABEL_VALUE + 1;
if (m_LayerContainer.size() >= max_size)
return nullptr;
if (addAsClone) newLabel = label->Clone();
auto pixelValue = newLabel->GetValue();
auto usedValues = this->GetUsedLabelValues();
auto finding = std::find(usedValues.begin(), usedValues.end(), pixelValue);
if (!usedValues.empty() && usedValues.end() != finding)
{
if (correctLabelValue)
{
pixelValue = this->GetUnusedLabelValue();
newLabel->SetValue(pixelValue);
}
else
{
mitkThrow() << "Cannot add label due to conflicting label value that already exists in the MultiLabelSegmentation. Conflicting label value: " << pixelValue;
}
}
// add DICOM information of the label
DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(newLabel);
this->AddLabelToMap(pixelValue, newLabel, groupID);
this->RegisterLabel(newLabel);
}
this->InvokeEvent(LabelAddedEvent(newLabel->GetValue()));
m_ActiveLabelValue = newLabel->GetValue();
this->Modified();
return newLabel;
}
mitk::Label* mitk::LabelSetImage::AddLabelWithContent(Label* label, const Image* labelContent, GroupIndexType groupID, LabelValueType contentLabelValue, bool addAsClone, bool correctLabelValue)
{
if (nullptr == labelContent) mitkThrow() << "Invalid use of AddLabel. labelContent is not valid.";
if (!Equal(*(this->GetTimeGeometry()), *(labelContent->GetTimeGeometry()), mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION))
mitkThrow() << "Invalid use of AddLabel. labelContent has not the same geometry like the segmentation.";
auto newLabel = this->AddLabel(label, groupID, addAsClone, correctLabelValue);
mitk::TransferLabelContent(labelContent, this->GetGroupImage(groupID), this->GetConstLabelsByValue(this->GetLabelValuesByGroup(groupID)),
mitk::LabelSetImage::UNLABELED_VALUE, mitk::LabelSetImage::UNLABELED_VALUE, false, { {contentLabelValue, newLabel->GetValue()}},
mitk::MultiLabelSegmentation::MergeStyle::Replace, mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
this->Modified();
return newLabel;
}
mitk::Label* mitk::LabelSetImage::AddLabel(const std::string& name, const mitk::Color& color, GroupIndexType groupID)
{
mitk::Label::Pointer newLabel = mitk::Label::New();
newLabel->SetName(name);
newLabel->SetColor(color);
return AddLabel(newLabel,groupID,false);
}
void mitk::LabelSetImage::RenameLabel(LabelValueType pixelValue, const std::string& name, const mitk::Color& color)
{
std::shared_lock<std::shared_mutex> guard(m_LabelNGroupMapsMutex);
- mitk::Label* label = GetLabel(pixelValue);
- if (nullptr == label) mitkThrow() << "Cannot rename label.Unknown label value provided. Unknown label value:" << pixelValue;
+ auto label = GetLabel(pixelValue);
+ if (label.IsNull()) mitkThrow() << "Cannot rename label. Unknown label value provided. Unknown label value:" << pixelValue;
label->SetName(name);
label->SetColor(color);
this->UpdateLookupTable(pixelValue);
+ m_LookupTable->Modified();
+
// change DICOM information of the label
DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(label);
}
mitk::Label *mitk::LabelSetImage::GetActiveLabel()
{
if (m_ActiveLabelValue == UNLABELED_VALUE) return nullptr;
auto finding = m_LabelMap.find(m_ActiveLabelValue);
return finding == m_LabelMap.end() ? nullptr : finding->second;
}
const mitk::Label* mitk::LabelSetImage::GetActiveLabel() const
{
if (m_ActiveLabelValue == UNLABELED_VALUE) return nullptr;
auto finding = m_LabelMap.find(m_ActiveLabelValue);
return finding == m_LabelMap.end() ? nullptr : finding->second;
}
void mitk::LabelSetImage::UpdateCenterOfMass(PixelType pixelValue)
{
if (4 == this->GetDimension())
{
AccessFixedDimensionByItk_1(this->GetGroupImage(this->GetGroupIndexOfLabel(pixelValue)), CalculateCenterOfMassProcessing, 4, pixelValue);
}
else
{
AccessByItk_1(this->GetGroupImage(this->GetGroupIndexOfLabel(pixelValue)), CalculateCenterOfMassProcessing, pixelValue);
}
}
void mitk::LabelSetImage::SetLookupTable(mitk::LookupTable* lut)
{
m_LookupTable = lut;
this->Modified();
}
void mitk::LabelSetImage::UpdateLookupTable(PixelType pixelValue)
{
- const mitk::Color& color = this->GetLabel(pixelValue)->GetColor();
+ auto label = this->GetLabel(pixelValue);
+ if (label.IsNull()) mitkThrow() << "Cannot update lookup table. Unknown label value provided. Unknown label value:" << pixelValue;
+
+ const mitk::Color& color = label->GetColor();
double rgba[4];
m_LookupTable->GetTableValue(static_cast<int>(pixelValue), rgba);
rgba[0] = color.GetRed();
rgba[1] = color.GetGreen();
rgba[2] = color.GetBlue();
- if (GetLabel(pixelValue)->GetVisible())
- rgba[3] = GetLabel(pixelValue)->GetOpacity();
+ if (label->GetVisible())
+ rgba[3] = label->GetOpacity();
else
rgba[3] = 0.0;
m_LookupTable->SetTableValue(static_cast<int>(pixelValue), rgba);
}
unsigned int mitk::LabelSetImage::GetNumberOfLabels(unsigned int layer) const
{
if (layer >= m_Groups.size()) mitkThrow() << "Cannot get number of labels in group. Group is unknown. Invalid index:" << layer;
return m_GroupToLabelMap[layer].size();
}
unsigned int mitk::LabelSetImage::GetTotalNumberOfLabels() const
{
return m_LabelMap.size();
}
void mitk::LabelSetImage::MaskStamp(mitk::Image *mask, bool forceOverwrite)
{
try
{
mitk::PadImageFilter::Pointer padImageFilter = mitk::PadImageFilter::New();
padImageFilter->SetInput(0, mask);
padImageFilter->SetInput(1, this);
padImageFilter->SetPadConstant(0);
padImageFilter->SetBinaryFilter(false);
padImageFilter->SetLowerThreshold(0);
padImageFilter->SetUpperThreshold(1);
padImageFilter->Update();
mitk::Image::Pointer paddedMask = padImageFilter->GetOutput();
if (paddedMask.IsNull())
return;
AccessByItk_2(this, MaskStampProcessing, paddedMask, forceOverwrite);
}
catch (...)
{
mitkThrow() << "Could not stamp the provided mask on the selected label.";
}
}
void mitk::LabelSetImage::InitializeByLabeledImage(mitk::Image::Pointer image)
{
if (image.IsNull() || image->IsEmpty() || !image->IsInitialized())
mitkThrow() << "Invalid labeled image.";
try
{
this->Initialize(image);
unsigned int byteSize = sizeof(LabelSetImage::PixelType);
for (unsigned int dim = 0; dim < image->GetDimension(); ++dim)
{
byteSize *= image->GetDimension(dim);
}
mitk::ImageWriteAccessor *accessor = new mitk::ImageWriteAccessor(static_cast<mitk::Image *>(this));
memset(accessor->GetData(), 0, byteSize);
delete accessor;
auto geometry = image->GetTimeGeometry()->Clone();
this->SetTimeGeometry(geometry);
if (image->GetDimension() == 3)
{
AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing, 3);
}
else if (image->GetDimension() == 4)
{
AccessTwoImagesFixedDimensionByItk(this, image, InitializeByLabeledImageProcessing, 4);
}
else
{
mitkThrow() << image->GetDimension() << "-dimensional label set images not yet supported";
}
}
catch (Exception& e)
{
mitkReThrow(e) << "Could not initialize by provided labeled image.";
}
catch (...)
{
mitkThrow() << "Could not initialize by provided labeled image due to unknown error.";
}
this->Modified();
}
template <typename LabelSetImageType, typename ImageType>
void mitk::LabelSetImage::InitializeByLabeledImageProcessing(LabelSetImageType *labelSetImage, ImageType *image)
{
typedef itk::ImageRegionConstIteratorWithIndex<ImageType> SourceIteratorType;
typedef itk::ImageRegionIterator<LabelSetImageType> TargetIteratorType;
TargetIteratorType targetIter(labelSetImage, labelSetImage->GetRequestedRegion());
targetIter.GoToBegin();
SourceIteratorType sourceIter(image, image->GetRequestedRegion());
sourceIter.GoToBegin();
while (!sourceIter.IsAtEnd())
{
const auto originalSourceValue = sourceIter.Get();
const auto sourceValue = static_cast<PixelType>(originalSourceValue);
if (originalSourceValue > mitk::Label::MAX_LABEL_VALUE)
{
mitkThrow() << "Cannot initialize MultiLabelSegmentation by image. Image contains a pixel value that exceeds the label value range. Invalid pixel value:" << originalSourceValue;
}
targetIter.Set(sourceValue);
if (LabelSetImage::UNLABELED_VALUE!=sourceValue && !this->ExistLabel(sourceValue))
{
if (this->GetTotalNumberOfLabels() >= mitk::Label::MAX_LABEL_VALUE)
{
mitkThrow() << "Cannot initialize MultiLabelSegmentation by image. Image contains to many labels.";
}
std::stringstream name;
name << "object-" << sourceValue;
double rgba[4];
this->GetLookupTable()->GetTableValue(sourceValue, rgba);
mitk::Color color;
color.SetRed(rgba[0]);
color.SetGreen(rgba[1]);
color.SetBlue(rgba[2]);
auto label = mitk::Label::New();
label->SetName(name.str().c_str());
label->SetColor(color);
label->SetOpacity(rgba[3]);
label->SetValue(sourceValue);
this->AddLabel(label,0,false);
}
++sourceIter;
++targetIter;
}
}
template <typename ImageType>
void mitk::LabelSetImage::MaskStampProcessing(ImageType *itkImage, mitk::Image *mask, bool forceOverwrite)
{
typename ImageType::Pointer itkMask;
mitk::CastToItkImage(mask, itkMask);
typedef itk::ImageRegionConstIterator<ImageType> SourceIteratorType;
typedef itk::ImageRegionIterator<ImageType> TargetIteratorType;
SourceIteratorType sourceIter(itkMask, itkMask->GetLargestPossibleRegion());
sourceIter.GoToBegin();
TargetIteratorType targetIter(itkImage, itkImage->GetLargestPossibleRegion());
targetIter.GoToBegin();
const auto activeLabel = this->GetActiveLabel()->GetValue();
while (!sourceIter.IsAtEnd())
{
PixelType sourceValue = sourceIter.Get();
PixelType targetValue = targetIter.Get();
if ((sourceValue != UNLABELED_VALUE) &&
(forceOverwrite || !this->IsLabelLocked(targetValue))) // skip unlabeled pixels and locked labels
{
targetIter.Set(activeLabel);
}
++sourceIter;
++targetIter;
}
this->Modified();
}
template <typename ImageType>
void mitk::LabelSetImage::CalculateCenterOfMassProcessing(ImageType *itkImage, LabelValueType pixelValue)
{
if (ImageType::GetImageDimension() != 3)
{
return;
}
auto labelGeometryFilter = itk::LabelGeometryImageFilter<ImageType>::New();
labelGeometryFilter->SetInput(itkImage);
labelGeometryFilter->Update();
auto centroid = labelGeometryFilter->GetCentroid(pixelValue);
mitk::Point3D pos;
pos[0] = centroid[0];
pos[1] = centroid[1];
pos[2] = centroid[2];
- this->GetLabel(pixelValue)->SetCenterOfMassIndex(pos);
- this->GetSlicedGeometry()->IndexToWorld(pos, pos);
- this->GetLabel(pixelValue)->SetCenterOfMassCoordinates(pos);
+ auto label = this->GetLabel(pixelValue);
+ if (label.IsNotNull())
+ {
+ label->SetCenterOfMassIndex(pos);
+ this->GetSlicedGeometry()->IndexToWorld(pos, pos);
+ label->SetCenterOfMassCoordinates(pos);
+ }
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::LabelSetImage::LayerContainerToImageProcessing(itk::Image<TPixel, VImageDimension> *target,
unsigned int layer)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typename ImageType::Pointer itkSource;
// mitk::CastToItkImage(m_LayerContainer[layer], itkSource);
itkSource = ImageToItkImage<TPixel, VImageDimension>(m_LayerContainer[layer]);
typedef itk::ImageRegionConstIterator<ImageType> SourceIteratorType;
typedef itk::ImageRegionIterator<ImageType> TargetIteratorType;
SourceIteratorType sourceIter(itkSource, itkSource->GetLargestPossibleRegion());
sourceIter.GoToBegin();
TargetIteratorType targetIter(target, target->GetLargestPossibleRegion());
targetIter.GoToBegin();
while (!sourceIter.IsAtEnd())
{
targetIter.Set(sourceIter.Get());
++sourceIter;
++targetIter;
}
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::LabelSetImage::ImageToLayerContainerProcessing(const itk::Image<TPixel, VImageDimension> *source,
unsigned int layer) const
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typename ImageType::Pointer itkTarget;
// mitk::CastToItkImage(m_LayerContainer[layer], itkTarget);
itkTarget = ImageToItkImage<TPixel, VImageDimension>(m_LayerContainer[layer]);
typedef itk::ImageRegionConstIterator<ImageType> SourceIteratorType;
typedef itk::ImageRegionIterator<ImageType> TargetIteratorType;
SourceIteratorType sourceIter(source, source->GetLargestPossibleRegion());
sourceIter.GoToBegin();
TargetIteratorType targetIter(itkTarget, itkTarget->GetLargestPossibleRegion());
targetIter.GoToBegin();
while (!sourceIter.IsAtEnd())
{
targetIter.Set(sourceIter.Get());
++sourceIter;
++targetIter;
}
m_LayerContainer[layer]->Modified();
}
template <typename ImageType>
void mitk::LabelSetImage::EraseLabelProcessing(ImageType *itkImage, PixelType pixelValue)
{
typedef itk::ImageRegionIterator<ImageType> IteratorType;
IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion());
iter.GoToBegin();
while (!iter.IsAtEnd())
{
PixelType value = iter.Get();
if (value == pixelValue)
{
iter.Set(0);
}
++iter;
}
}
template <typename ImageType>
void mitk::LabelSetImage::MergeLabelProcessing(ImageType *itkImage, PixelType pixelValue, PixelType index)
{
typedef itk::ImageRegionIterator<ImageType> IteratorType;
IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion());
iter.GoToBegin();
while (!iter.IsAtEnd())
{
if (iter.Get() == index)
{
iter.Set(pixelValue);
}
++iter;
}
}
void mitk::LabelSetImage::AddLabelToMap(LabelValueType labelValue, mitk::Label* label, GroupIndexType groupID)
{
if (m_LabelMap.find(labelValue)!=m_LabelMap.end())
mitkThrow() << "Segmentation is in an invalid state: Label value collision. A label was added with a LabelValue already in use. LabelValue: " << labelValue;
if (!this->ExistGroup(groupID))
mitkThrow() << "Cannot add label. Defined group is unknown. Invalid group index: " << groupID;
m_LabelMap[labelValue] = label;
m_LabelToGroupMap[labelValue] = groupID;
auto groupFinding = std::find(m_GroupToLabelMap[groupID].begin(), m_GroupToLabelMap[groupID].end(), labelValue);
if (groupFinding == m_GroupToLabelMap[groupID].end())
{
m_GroupToLabelMap[groupID].push_back(labelValue);
}
}
void mitk::LabelSetImage::RegisterLabel(mitk::Label* label)
{
if (nullptr == label) mitkThrow() << "Invalid call of RegisterLabel with a nullptr.";
UpdateLookupTable(label->GetValue());
+ m_LookupTable->Modified();
auto command = itk::MemberCommand<LabelSetImage>::New();
command->SetCallbackFunction(this, &LabelSetImage::OnLabelModified);
m_LabelModEventGuardMap.emplace(label->GetValue(), ITKEventObserverGuard(label, itk::ModifiedEvent(), command));
}
void mitk::LabelSetImage::ReleaseLabel(Label* label)
{
if (nullptr == label) mitkThrow() << "Invalid call of ReleaseLabel with a nullptr.";
m_LabelModEventGuardMap.erase(label->GetValue());
}
void mitk::LabelSetImage::ApplyToLabels(const LabelValueVectorType& values, std::function<void(Label*)>&& lambda)
{
auto labels = this->GetLabelsByValue(values);
std::for_each(labels.begin(), labels.end(), lambda);
this->InvokeEvent(LabelsChangedEvent(values));
}
void mitk::LabelSetImage::VisitLabels(const LabelValueVectorType& values, std::function<void(const Label*)>&& lambda) const
{
auto labels = this->GetConstLabelsByValue(values);
std::for_each(labels.begin(), labels.end(), lambda);
}
void mitk::LabelSetImage::OnLabelModified(const Object* sender, const itk::EventObject&)
{
auto label = dynamic_cast<const Label*>(sender);
if (nullptr == label)
mitkThrow() << "LabelSet is in wrong state. LabelModified event is not send by a label instance.";
Superclass::Modified();
this->InvokeEvent(LabelModifiedEvent(label->GetValue()));
}
bool mitk::LabelSetImage::ExistLabel(LabelValueType value) const
{
auto finding = m_LabelMap.find(value);
return m_LabelMap.end() != finding;
}
bool mitk::LabelSetImage::ExistLabel(LabelValueType value, GroupIndexType groupIndex) const
{
auto finding = m_LabelToGroupMap.find(value);
if (m_LabelToGroupMap.end() != finding)
{
return finding->second == groupIndex;
}
return false;
}
bool mitk::LabelSetImage::ExistGroup(GroupIndexType index) const
{
return index < m_LayerContainer.size();
}
mitk::LabelSetImage::GroupIndexType mitk::LabelSetImage::GetGroupIndexOfLabel(LabelValueType value) const
{
auto finding = m_LabelToGroupMap.find(value);
if (m_LabelToGroupMap.end() == finding)
{
mitkThrow()<< "Cannot deduce group index. Passed label value does not exist. Value: "<< value;
}
return finding->second;
}
-const mitk::Label* mitk::LabelSetImage::GetLabel(LabelValueType value) const
+mitk::Label::ConstPointer mitk::LabelSetImage::GetLabel(LabelValueType value) const
{
auto finding = m_LabelMap.find(value);
if (m_LabelMap.end() != finding)
{
return finding->second;
}
return nullptr;
};
-mitk::Label* mitk::LabelSetImage::GetLabel(LabelValueType value)
+mitk::Label::Pointer mitk::LabelSetImage::GetLabel(LabelValueType value)
{
auto finding = m_LabelMap.find(value);
if (m_LabelMap.end() != finding)
{
return finding->second;
}
return nullptr;
};
bool mitk::LabelSetImage::IsLabelLocked(LabelValueType value) const
{
if (value == UNLABELED_VALUE)
{
return m_UnlabeledLabelLock;
}
const auto label = this->GetLabel(value);
return label->GetLocked();
}
const mitk::LabelSetImage::ConstLabelVectorType mitk::LabelSetImage::GetLabels() const
{
ConstLabelVectorType result;
for (auto [value, label] : m_LabelMap)
{
+ (void)value; // Prevent unused variable error in older compilers
result.emplace_back(label);
}
return result;
}
const mitk::LabelSetImage::LabelVectorType mitk::LabelSetImage::GetLabels()
{
LabelVectorType result;
for (auto [value, label] : m_LabelMap)
{
+ (void)value; // Prevent unused variable error in older compilers
result.emplace_back(label);
}
return result;
}
const mitk::LabelSetImage::LabelVectorType mitk::LabelSetImage::GetLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing)
{
LabelVectorType result;
for (const auto& labelValue : labelValues)
{
- auto* label = this->GetLabel(labelValue);
+ Label::Pointer label = this->GetLabel(labelValue);
- if (label != nullptr)
+ if (label.IsNotNull())
{
result.emplace_back(label);
}
else if (!ignoreMissing) mitkThrow() << "Error cannot get labels by Value. At least one passed value is unknown. Unknown value: " << labelValue;
}
return result;
}
const mitk::LabelSetImage::ConstLabelVectorType mitk::LabelSetImage::GetConstLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing) const
{
ConstLabelVectorType result;
for (const auto& labelValue : labelValues)
{
- const auto* label = this->GetLabel(labelValue);
+ Label::ConstPointer label = this->GetLabel(labelValue);
- if (label != nullptr)
+ if (label.IsNotNull())
{
result.emplace_back(label);
}
else if (!ignoreMissing) mitkThrow() << "Error cannot get labels by Value. At least one passed value is unknown. Unknown value: " << labelValue;
}
return result;
}
const mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetLabelValuesByGroup(GroupIndexType index) const
{
if (!this->ExistGroup(index))
mitkThrow() << "Cannot get labels of an invalid group. Invalid group index: " << index;
return m_GroupToLabelMap[index];
}
const mitk::LabelSetImage::LabelValueVectorType mitk::LabelSetImage::GetLabelValuesByName(GroupIndexType index, const std::string_view name) const
{
LabelValueVectorType result;
auto searchName = [&result, name](const Label* l) { if(l->GetName() == name) result.push_back(l->GetValue()); };
this->VisitLabels(this->GetLabelValuesByGroup(index), searchName);
return result;
}
std::vector<std::string> mitk::LabelSetImage::GetLabelClassNames() const
{
std::set<std::string> names;
auto searchName = [&names](const Label* l) { names.emplace(l->GetName()); };
this->VisitLabels(this->GetAllLabelValues(), searchName);
return std::vector<std::string>(names.begin(), names.end());
}
std::vector<std::string> mitk::LabelSetImage::GetLabelClassNamesByGroup(GroupIndexType index) const
{
std::set<std::string> names;
auto searchName = [&names](const Label* l) { names.emplace(l->GetName()); };
this->VisitLabels(this->GetLabelValuesByGroup(index), searchName);
return std::vector<std::string>(names.begin(), names.end());
}
void mitk::LabelSetImage::SetAllLabelsVisible(bool visible)
{
auto setVisibility = [visible,this](Label* l)
{
l->SetVisible(visible);
this->UpdateLookupTable(l->GetValue());
};
this->ApplyToLabels(this->GetAllLabelValues(), setVisibility);
this->m_LookupTable->Modified();
}
void mitk::LabelSetImage::SetAllLabelsVisibleByGroup(GroupIndexType group, bool visible)
{
auto setVisibility = [visible, this](Label* l)
{
l->SetVisible(visible);
this->UpdateLookupTable(l->GetValue());
};
this->ApplyToLabels(this->GetLabelValuesByGroup(group), setVisibility);
this->m_LookupTable->Modified();
}
void mitk::LabelSetImage::SetAllLabelsVisibleByName(GroupIndexType group, const std::string_view name, bool visible)
{
auto setVisibility = [visible, this](Label* l)
{
l->SetVisible(visible);
this->UpdateLookupTable(l->GetValue());
};
this->ApplyToLabels(this->GetLabelValuesByName(group, name), setVisibility);
this->m_LookupTable->Modified();
}
void mitk::LabelSetImage::SetAllLabelsLocked(bool locked)
{
auto setLock = [locked](Label* l) { l->SetLocked(locked); };
this->ApplyToLabels(this->GetAllLabelValues(), setLock);
}
void mitk::LabelSetImage::SetAllLabelsLockedByGroup(GroupIndexType group, bool locked)
{
auto setLock = [locked](Label* l) { l->SetLocked(locked); };
this->ApplyToLabels(this->GetLabelValuesByGroup(group), setLock);
}
void mitk::LabelSetImage::SetAllLabelsLockedByName(GroupIndexType group, const std::string_view name, bool locked)
{
auto setLock = [locked](Label* l) { l->SetLocked(locked); };
this->ApplyToLabels(this->GetLabelValuesByName(group, name), setLock);
}
bool mitk::Equal(const mitk::LabelSetImage &leftHandSide,
const mitk::LabelSetImage &rightHandSide,
ScalarType eps,
bool verbose)
{
bool returnValue = true;
/* LabelSetImage members */
MITK_INFO(verbose) << "--- LabelSetImage Equal ---";
// m_LookupTable;
const mitk::LookupTable* lhsLUT = leftHandSide.GetLookupTable();
const mitk::LookupTable* rhsLUT = rightHandSide.GetLookupTable();
returnValue = *lhsLUT == *rhsLUT;
if (!returnValue)
{
MITK_INFO(verbose) << "Lookup tables not equal.";
return returnValue;
;
}
// number layers
returnValue = leftHandSide.GetNumberOfLayers() == rightHandSide.GetNumberOfLayers();
if (!returnValue)
{
MITK_INFO(verbose) << "Number of layers not equal.";
return false;
}
// total number labels
returnValue = leftHandSide.GetTotalNumberOfLabels() == rightHandSide.GetTotalNumberOfLabels();
if (!returnValue)
{
MITK_INFO(verbose) << "Total number of labels not equal.";
return false;
}
// active layer
returnValue = leftHandSide.GetActiveLayer() == rightHandSide.GetActiveLayer();
if (!returnValue)
{
MITK_INFO(verbose) << "Active layer not equal.";
return false;
}
if (4 == leftHandSide.GetDimension())
{
MITK_INFO(verbose) << "Can not compare image data for 4D images - skipping check.";
}
else
{
// working image data
returnValue = mitk::Equal((const mitk::Image &)leftHandSide, (const mitk::Image &)rightHandSide, eps, verbose);
if (!returnValue)
{
MITK_INFO(verbose) << "Working image data not equal.";
return false;
}
}
if (leftHandSide.GetTotalNumberOfLabels() != rightHandSide.GetTotalNumberOfLabels())
{
MITK_INFO(verbose) << "Number of labels are not equal.";
return false;
}
for (unsigned int layerIndex = 0; layerIndex < leftHandSide.GetNumberOfLayers(); layerIndex++)
{
if (4 == leftHandSide.GetDimension())
{
MITK_INFO(verbose) << "Can not compare image data for 4D images - skipping check.";
}
else
{
// layer image data
returnValue =
mitk::Equal(*leftHandSide.GetGroupImage(layerIndex), *rightHandSide.GetGroupImage(layerIndex), eps, verbose);
if (!returnValue)
{
MITK_INFO(verbose) << "Layer image data not equal.";
return false;
}
}
// label data
auto leftLabelsInGroup = leftHandSide.GetLabelValuesByGroup(layerIndex);
auto rightLabelsInGroup = rightHandSide.GetLabelValuesByGroup(layerIndex);
if (leftLabelsInGroup.size()!=rightLabelsInGroup.size())
{
MITK_INFO(verbose) << "Number of layer labels is not equal. Invalid layer:" <<layerIndex;
return false;
}
for (ConstLabelVector::size_type index = 0; index < leftLabelsInGroup.size(); ++index)
{
if (!mitk::Equal(*(leftHandSide.GetLabel(leftLabelsInGroup[index])), *(rightHandSide.GetLabel(rightLabelsInGroup[index])),eps,verbose))
{
MITK_INFO(verbose) << "At least one label in layer is not equal. Invalid layer:" << layerIndex;
return false;
}
}
}
return returnValue;
}
bool mitk::Equal(const mitk::LabelSetImage::ConstLabelVectorType& leftHandSide,
const mitk::LabelSetImage::ConstLabelVectorType& rightHandSide, ScalarType eps, bool verbose)
{
bool returnValue = true;
// container size;
returnValue = leftHandSide.size() == rightHandSide.size();
if (!returnValue)
{
MITK_INFO(verbose) << "Number of labels not equal.";
return returnValue;
;
}
// m_LabelContainer;
auto lhsit = leftHandSide.begin();
auto rhsit = rightHandSide.begin();
for (; lhsit != leftHandSide.end(); ++lhsit, ++rhsit)
{
returnValue = mitk::Equal(**rhsit, **lhsit,eps,verbose);
if (!returnValue)
{
MITK_INFO(verbose) << "Label in label container not equal.";
return returnValue;
;
}
}
return returnValue;
}
/**Helper function to convert a vector of labels into a label map
* @pre every label in the vector has a unique value.*/
using ConstLabelMapType = std::map<mitk::LabelSetImage::LabelValueType, mitk::Label::ConstPointer>;
ConstLabelMapType ConvertLabelVectorToMap(const mitk::ConstLabelVector& labelV)
{
ConstLabelMapType result;
for (auto label : labelV)
{
const auto value = label->GetValue();
auto finding = result.find(value);
if (finding != result.end()) mitkThrow() << "Operation failed. Cannot convert label vector into label map, because at least one label value is not unique. Violating label value: " << value;
result.insert(std::make_pair(value, label));
}
return result;
}
/** Functor class that implements the label transfer and is used in conjunction with the itk::BinaryFunctorImageFilter.
* For details regarding the usage of the filter and the functor patterns, please see info of itk::BinaryFunctorImageFilter.
*/
template <class TDestinationPixel, class TSourcePixel, class TOutputpixel>
class LabelTransferFunctor
{
public:
LabelTransferFunctor() {};
LabelTransferFunctor(const ConstLabelMapType& destinationLabels, mitk::Label::PixelType sourceBackground,
mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked,
mitk::Label::PixelType sourceLabel, mitk::Label::PixelType newDestinationLabel, mitk::MultiLabelSegmentation::MergeStyle mergeStyle,
mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle) :
m_DestinationLabels(destinationLabels), m_SourceBackground(sourceBackground),
m_DestinationBackground(destinationBackground), m_DestinationBackgroundLocked(destinationBackgroundLocked),
m_SourceLabel(sourceLabel), m_NewDestinationLabel(newDestinationLabel), m_MergeStyle(mergeStyle), m_OverwriteStyle(overwriteStyle)
{
};
~LabelTransferFunctor() {};
bool operator!=(const LabelTransferFunctor& other)const
{
return !(*this == other);
}
bool operator==(const LabelTransferFunctor& other) const
{
return this->m_SourceBackground == other.m_SourceBackground &&
this->m_DestinationBackground == other.m_DestinationBackground &&
this->m_DestinationBackgroundLocked == other.m_DestinationBackgroundLocked &&
this->m_SourceLabel == other.m_SourceLabel &&
this->m_NewDestinationLabel == other.m_NewDestinationLabel &&
this->m_MergeStyle == other.m_MergeStyle &&
this->m_OverwriteStyle == other.m_OverwriteStyle &&
this->m_DestinationLabels == other.m_DestinationLabels;
}
LabelTransferFunctor& operator=(const LabelTransferFunctor& other)
{
this->m_DestinationLabels = other.m_DestinationLabels;
this->m_SourceBackground = other.m_SourceBackground;
this->m_DestinationBackground = other.m_DestinationBackground;
this->m_DestinationBackgroundLocked = other.m_DestinationBackgroundLocked;
this->m_SourceLabel = other.m_SourceLabel;
this->m_NewDestinationLabel = other.m_NewDestinationLabel;
this->m_MergeStyle = other.m_MergeStyle;
this->m_OverwriteStyle = other.m_OverwriteStyle;
return *this;
}
inline TOutputpixel operator()(const TDestinationPixel& existingDestinationValue, const TSourcePixel& existingSourceValue)
{
if (existingSourceValue == this->m_SourceLabel)
{
if (mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks == this->m_OverwriteStyle)
{
return this->m_NewDestinationLabel;
}
else
{
if (existingDestinationValue == m_DestinationBackground)
{
if (!m_DestinationBackgroundLocked)
{
return this->m_NewDestinationLabel;
}
}
else
{
auto labelFinding = this->m_DestinationLabels.find(existingDestinationValue);
if (labelFinding==this->m_DestinationLabels.end() || !labelFinding->second->GetLocked())
{
return this->m_NewDestinationLabel;
}
}
}
}
else if (mitk::MultiLabelSegmentation::MergeStyle::Replace == this->m_MergeStyle
&& existingSourceValue == this->m_SourceBackground
&& existingDestinationValue == this->m_NewDestinationLabel
&& (mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks == this->m_OverwriteStyle
|| !this->m_DestinationBackgroundLocked))
{
return this->m_DestinationBackground;
}
return existingDestinationValue;
}
private:
ConstLabelMapType m_DestinationLabels;
mitk::Label::PixelType m_SourceBackground = 0;
mitk::Label::PixelType m_DestinationBackground = 0;
bool m_DestinationBackgroundLocked = false;
mitk::Label::PixelType m_SourceLabel = 1;
mitk::Label::PixelType m_NewDestinationLabel = 1;
mitk::MultiLabelSegmentation::MergeStyle m_MergeStyle = mitk::MultiLabelSegmentation::MergeStyle::Replace;
mitk::MultiLabelSegmentation::OverwriteStyle m_OverwriteStyle = mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks;
};
/**Helper function used by TransferLabelContentAtTimeStep to allow the templating over different image dimensions in conjunction of AccessFixedPixelTypeByItk_n.*/
template<unsigned int VImageDimension>
void TransferLabelContentAtTimeStepHelper(const itk::Image<mitk::Label::PixelType, VImageDimension>* itkSourceImage, mitk::Image* destinationImage,
const mitk::ConstLabelVector& destinationLabels, mitk::Label::PixelType sourceBackground, mitk::Label::PixelType destinationBackground,
bool destinationBackgroundLocked, mitk::Label::PixelType sourceLabel, mitk::Label::PixelType newDestinationLabel, mitk::MultiLabelSegmentation::MergeStyle mergeStyle, mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle)
{
typedef itk::Image<mitk::Label::PixelType, VImageDimension> ContentImageType;
typename ContentImageType::Pointer itkDestinationImage;
mitk::CastToItkImage(destinationImage, itkDestinationImage);
auto sourceRegion = itkSourceImage->GetLargestPossibleRegion();
auto relevantRegion = itkDestinationImage->GetLargestPossibleRegion();
bool overlapping = relevantRegion.Crop(sourceRegion);
if (!overlapping)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage and destinationImage seem to have no overlapping image region.";
}
typedef LabelTransferFunctor <mitk::Label::PixelType, mitk::Label::PixelType, mitk::Label::PixelType> LabelTransferFunctorType;
typedef itk::BinaryFunctorImageFilter<ContentImageType, ContentImageType, ContentImageType, LabelTransferFunctorType> FilterType;
LabelTransferFunctorType transferFunctor(ConvertLabelVectorToMap(destinationLabels), sourceBackground, destinationBackground,
destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeStyle, overwriteStyle);
auto transferFilter = FilterType::New();
transferFilter->SetFunctor(transferFunctor);
transferFilter->InPlaceOn();
transferFilter->SetInput1(itkDestinationImage);
transferFilter->SetInput2(itkSourceImage);
transferFilter->GetOutput()->SetRequestedRegion(relevantRegion);
transferFilter->Update();
}
void mitk::TransferLabelContentAtTimeStep(
const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabels, const TimeStepType timeStep, mitk::Label::PixelType sourceBackground,
mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, LabelValueMappingVector labelMapping,
MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
{
if (nullptr == sourceImage)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage must not be null.";
}
if (nullptr == destinationImage)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; destinationImage must not be null.";
}
if (sourceImage == destinationImage && labelMapping.size() > 1)
{
MITK_DEBUG << "Warning. Using TransferLabelContentAtTimeStep or TransferLabelContent with equal source and destination and more then on label to transfer, can lead to wrong results. Please see documentation and verify that the usage is OK.";
}
Image::ConstPointer sourceImageAtTimeStep = SelectImageByTimeStep(sourceImage, timeStep);
Image::Pointer destinationImageAtTimeStep = SelectImageByTimeStep(destinationImage, timeStep);
if (nullptr == sourceImageAtTimeStep)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage does not have the requested time step: " << timeStep;
}
if (nullptr == destinationImageAtTimeStep)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; destinationImage does not have the requested time step: " << timeStep;
}
if (!Equal(*(sourceImageAtTimeStep->GetGeometry()), *(destinationImageAtTimeStep->GetGeometry()), mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION))
{
- if (IsSubGeometry(*(sourceImageAtTimeStep->GetGeometry()), *(destinationImageAtTimeStep->GetGeometry()), mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION))
+ if (IsSubGeometry(*(sourceImageAtTimeStep->GetGeometry()), *(destinationImageAtTimeStep->GetGeometry()), mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true))
{
//we have to pad the source image
//because ImageToImageFilters always check for origin matching even if
//the requested output region is fitting :(
auto padFilter = mitk::PadImageFilter::New();
padFilter->SetInput(0, sourceImageAtTimeStep);
padFilter->SetInput(1, destinationImageAtTimeStep);
padFilter->SetPadConstant(Label::UNLABELED_VALUE);
padFilter->SetBinaryFilter(false);
padFilter->Update();
sourceImageAtTimeStep = padFilter->GetOutput();
}
else
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; source image has neither the same geometry than destination image nor has the source image a sub geometry.";
}
}
auto destLabelMap = ConvertLabelVectorToMap(destinationLabels);
for (const auto& [sourceLabel, newDestinationLabel] : labelMapping)
{
if (LabelSetImage::UNLABELED_VALUE!=newDestinationLabel && destLabelMap.end() == destLabelMap.find(newDestinationLabel))
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep. Defined destination label does not exist in destinationImage. newDestinationLabel: " << newDestinationLabel;
}
AccessFixedPixelTypeByItk_n(sourceImageAtTimeStep, TransferLabelContentAtTimeStepHelper, (Label::PixelType), (destinationImageAtTimeStep, destinationLabels, sourceBackground, destinationBackground, destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeStyle, overwriteStlye));
}
destinationImage->Modified();
}
void mitk::TransferLabelContent(
const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabels, mitk::Label::PixelType sourceBackground,
mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, LabelValueMappingVector labelMapping,
MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
{
if (nullptr == sourceImage)
{
mitkThrow() << "Invalid call of TransferLabelContent; sourceImage must not be null.";
}
if (nullptr == destinationImage)
{
mitkThrow() << "Invalid call of TransferLabelContent; destinationImage must not be null.";
}
const auto sourceTimeStepCount = sourceImage->GetTimeGeometry()->CountTimeSteps();
if (sourceTimeStepCount != destinationImage->GetTimeGeometry()->CountTimeSteps())
{
mitkThrow() << "Invalid call of TransferLabelContent; mismatch between images in number of time steps.";
}
for (mitk::TimeStepType i = 0; i < sourceTimeStepCount; ++i)
{
TransferLabelContentAtTimeStep(sourceImage, destinationImage, destinationLabels, i, sourceBackground,
destinationBackground, destinationBackgroundLocked, labelMapping, mergeStyle, overwriteStlye);
}
}
void mitk::TransferLabelContentAtTimeStep(
const LabelSetImage* sourceImage, LabelSetImage* destinationImage, const TimeStepType timeStep,
LabelValueMappingVector labelMapping,
MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
{
if (nullptr == sourceImage)
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep; sourceImage must not be null.";
}
auto destinationLabels = destinationImage->GetConstLabelsByValue(destinationImage->GetLabelValuesByGroup(destinationImage->GetActiveLayer()));
for (const auto& mappingElement : labelMapping)
{
if (LabelSetImage::UNLABELED_VALUE != mappingElement.first && !sourceImage->ExistLabel(mappingElement.first, sourceImage->GetActiveLayer()))
{
mitkThrow() << "Invalid call of TransferLabelContentAtTimeStep. Defined source label does not exist in sourceImage. SourceLabel: " << mappingElement.first;
}
}
TransferLabelContentAtTimeStep(sourceImage, destinationImage, destinationLabels, timeStep, LabelSetImage::UNLABELED_VALUE, LabelSetImage::UNLABELED_VALUE, destinationImage->GetUnlabeledLabelLock(),
labelMapping, mergeStyle, overwriteStlye);
}
void mitk::TransferLabelContent(
const LabelSetImage* sourceImage, LabelSetImage* destinationImage,
LabelValueMappingVector labelMapping,
MultiLabelSegmentation::MergeStyle mergeStyle, MultiLabelSegmentation::OverwriteStyle overwriteStlye)
{
if (nullptr == sourceImage)
{
mitkThrow() << "Invalid call of TransferLabelContent; sourceImage must not be null.";
}
if (nullptr == destinationImage)
{
mitkThrow() << "Invalid call of TransferLabelContent; destinationImage must not be null.";
}
const auto sourceTimeStepCount = sourceImage->GetTimeGeometry()->CountTimeSteps();
if (sourceTimeStepCount != destinationImage->GetTimeGeometry()->CountTimeSteps())
{
mitkThrow() << "Invalid call of TransferLabelContent; images have no equal number of time steps.";
}
for (mitk::TimeStepType i = 0; i < sourceTimeStepCount; ++i)
{
TransferLabelContentAtTimeStep(sourceImage, destinationImage, i, labelMapping, mergeStyle, overwriteStlye);
}
}
diff --git a/Modules/Multilabel/mitkLabelSetImage.h b/Modules/Multilabel/mitkLabelSetImage.h
index 2003399661..997a859e44 100644
--- a/Modules/Multilabel/mitkLabelSetImage.h
+++ b/Modules/Multilabel/mitkLabelSetImage.h
@@ -1,706 +1,711 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkLabelSetImage_h
#define mitkLabelSetImage_h
#include <shared_mutex>
#include <mitkImage.h>
#include <mitkLabel.h>
#include <mitkLookupTable.h>
#include <mitkMultiLabelEvents.h>
#include <mitkMessage.h>
#include <mitkITKEventObserverGuard.h>
#include <MitkMultilabelExports.h>
namespace mitk
{
/** @brief LabelSetImage class for handling labels and layers in a segmentation session.
*
* Events that are potentially send by the class in regard to groups or labels:
* - LabelAddedEvent is emitted whenever a new label has been added.
* - LabelModifiedEvent is emitted whenever a label has been modified.
* - LabelRemovedEvent is emitted whenever a label has been removed.
* - LabelsChangedEvent is emitted when labels are changed (added, removed, modified). In difference to the other label events LabelsChanged is send only *one time* after the modification of the
* MultiLableImage instance is finished. So e.g. even if 4 labels are changed by a merge operation, this event will
* only be sent once (compared to LabelRemoved or LabelModified).
* - GroupAddedEvent is emitted whenever a new group has been added.
* - GroupModifiedEvent is emitted whenever a group has been modified.
* - GroupRemovedEvent is emitted whenever a label has been removed.
*
* @ingroup Data
*/
class MITKMULTILABEL_EXPORT LabelSetImage : public Image
{
public:
/**
* \brief BeforeChangeLayerEvent (e.g. used for GUI integration)
* As soon as active labelset should be changed, the signal emits.
* Emitted by SetActiveLayer(int layer);
*/
Message<> BeforeChangeLayerEvent;
/**
* \brief AfterchangeLayerEvent (e.g. used for GUI integration)
* As soon as active labelset was changed, the signal emits.
* Emitted by SetActiveLayer(int layer);
*/
Message<> AfterChangeLayerEvent;
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
// FUTURE MultiLabelSegmentation:
// Section that already contains declarations used in the new class.
// So this part of the interface will stay after refactoring towards
// the new MultiLabelSegmentation class (see T28524). This section was introduced
// because some of the planned features are already urgently needed.
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
mitkClassMacro(LabelSetImage, Image);
itkNewMacro(Self);
typedef mitk::Label::PixelType PixelType;
using GroupIndexType = std::size_t;
using LabelValueType = mitk::Label::PixelType;
using ConstLabelVectorType = ConstLabelVector;
using LabelVectorType = LabelVector;
using LabelValueVectorType = std::vector<LabelValueType>;
const static LabelValueType UNLABELED_VALUE;
/** \brief Adds a label instance to a group of the multi label image.
* @remark By default, if the pixel value of the label is already used in the image, the label
* will get a new none conflicting value assigned. This can be controlled by correctLabelValue.
* @param label Instance of an label that should be added or used as template
* @param groupID The id of the group the label should be added to.
* @param addAsClone Flag that controls, if the passed instance should be added (false; the image will then take ownership,
* be aware that e.g. event observers will be added)
* a clone of the instance (true).
* @param correctLabelValue Flag that controls, if the value of the passed label should be correct, if this value is already used in
* the multi label image. True: Conflicting values will be corrected, be assigning a none conflicting value. False: If the value is conflicting
* an exception will be thrown.
* @return Instance of the label as it was added to the label set.
* @pre label must point to a valid instance.
* @pre If correctLabelValue==false, label value must be non conflicting.
* @pre groupID must indicate an existing group.
*/
mitk::Label* AddLabel(Label* label, GroupIndexType groupID, bool addAsClone = true, bool correctLabelValue = true);
/** \brief Adds a label instance to a group of the multi label image including its pixel content.
* @remark By default, if the pixel value of the label is already used in the image, the label
* will get a new none conflicting value assigned. This can be controlled by correctLabelValue.
* @param label Instance of a label that should be added or used as template
* @param groupID The id of the group the label should be added to.
* @param labelContent Pointer to an image that contains the pixel content of the label that should be added.
* @param contentLabelValue Pixel value in the content image that indicates the label (may not be the same like the label value
* used in the segmentation after addition).
* @param addAsClone Flag that controls, if the passed instance should be added (false; the image will then take ownership,
* be aware that e.g. event observers will be added)
* a clone of the instance (true).
* @param correctLabelValue Flag that controls, if the value of the passed label should be corrected, if this value is already used in
* the multi label image. True: Conflicting values will be corrected, by assigning a none conflicting value. False: If the value is conflicting
* an exception will be thrown.
* @return Instance of the label as it was added to the label set.
* @pre label must point to a valid instance.
* @pre If correctLabelValue==false, label value must be non conflicting.
* @pre groupID must indicate an existing group.
* @pre labelContent must point to a valid image that has the same geometry like the segmentation.
*/
mitk::Label* AddLabelWithContent(Label* label, const Image* labelContent, GroupIndexType groupID, LabelValueType contentLabelValue, bool addAsClone = true, bool correctLabelValue = true);
/** \brief Adds a new label to a group of the image by providing name and color.
* @param name (Class) name of the label instance that should be added.
* @param color Color of the new label instance.
* @param groupID The id of the group the label should be added to.
* @return Instance of the label as it was added to the label set.
* @pre groupID must indicate an existing group.
*/
mitk::Label* AddLabel(const std::string& name, const Color& color, GroupIndexType groupID);
/** \brief allows to adapt name and color of a certain label
* @param labelValue Value of the label that should be changed
* @param name New name for the label
* @param color New color for the label
* @pre Indicated label value must exist.
*/
void RenameLabel(LabelValueType labelValue, const std::string& name, const Color& color);
/**
* @brief Removes the label with the given value.
* The label is removed from the labelset and
* the pixel with the value of the label are set to UNLABELED_VALUE.
* @param labelValue the pixel value of the label to be removed. If the label is unknown,
* the method will return without doing anything.
*/
void RemoveLabel(LabelValueType labelValue);
/**
* @brief Removes labels from the mitk::MultiLabelSegmentation.
* The label is removed from the labelset and
* the pixel with the value of the label are set to UNLABELED_VALUE.
* If a label value does not exist, it will be ignored.
* @param vectorOfLabelPixelValues a list of labels to be removed
*/
void RemoveLabels(const LabelValueVectorType& vectorOfLabelPixelValues);
/**
* @brief Erases the label with the given value from the labelset image.
* The label itself will not be erased from the respective mitk::LabelSet. In order to
* remove the label itself use mitk::LabelSetImage::RemoveLabels()
* @param labelValue the pixel value of the label that will be erased from the labelset image
* @pre labelValue must exist.
*/
void EraseLabel(LabelValueType labelValue);
/**
* @brief Erases a list of labels with the given values from the labelset image.
* @param labelValues the list of pixel values of the labels
* that will be erased from the labelset image
* @pre label values must exist
*/
void EraseLabels(const LabelValueVectorType& labelValues);
/**
* @brief Removes a whole group including all its labels.
* @remark with removing a group all groups with greater index will be re-indexed to
* close the gap. Hence externally stored spatial group indices may become invalid.
* @param group Group index of the spatial group that should be removed. If the spatial group does not exist, an
* exception will be raised.
* @pre group index must be valid.
*/
void RemoveGroup(GroupIndexType group);
/** \brief Returns true if the value exists in the MultiLabelSegmentation instance*/
bool ExistLabel(LabelValueType value) const;
/**
* @brief Checks if a label belongs in a certain spatial group
* @param value the label value
- * @param groupIndex Index of the spacial group which should be checked for the label
+ * @param groupIndex Index of the spatial group which should be checked for the label
* @return true if the label exists otherwise false
*/
bool ExistLabel(LabelValueType value, GroupIndexType groupIndex) const;
/**
* @brief Returns true if the spatial group exists in the MultiLabelSegmentation instance.
*
* @param index Group index of the group that should be checked for existence.
*/
bool ExistGroup(GroupIndexType index) const;
/** Returns the group id of the based label value.
* @pre label value must exists.
*/
GroupIndexType GetGroupIndexOfLabel(LabelValueType value) const;
/**
* @brief Returns the mitk::Label with the given value.
* @param value the pixel value of the label
- * @return the label instance if defined in the segmentation, otherwise nullptr.
+ * @return smart pointer to the label instance if defined in the segmentation, otherwise nullptr.
+ * @remark The label is returned as smart pointer, because the MultiLabelSegmentation instance
+ * gives no guarantee how long the label instance will be a valid label of the segmentation.
+ * If you hold the label instance for a longer time, you must expect that it is not valid anymore
+ * (Either because the label id was removed or the label instance was replaced). It is valid as long
+ * it points to the same label instance like a recent GetLabel() call.
*/
- const mitk::Label* GetLabel(LabelValueType value) const;
- mitk::Label* GetLabel(LabelValueType value);
+ mitk::Label::ConstPointer GetLabel(LabelValueType value) const;
+ mitk::Label::Pointer GetLabel(LabelValueType value);
/** Returns a vector with pointers to all labels currently defined in the MultiLabelSegmentation
instance.*/
const ConstLabelVectorType GetLabels() const;
const LabelVectorType GetLabels();
/** Returns a vector of all label values currently defined in the MultiLabelSegmentation
instance.*/
const LabelValueVectorType GetAllLabelValues() const;
/** @brief Returns a vector with pointers to all labels in the MultiLabelSegmentation indicated
* by the passed label value vector.
* @param labelValues Vector of values of labels that should be returned.
* @param ignoreMissing If true (default), unknown labels Will be skipped in the result. If false,
* an exception will be raised, if a label is requested.
*/
const LabelVectorType GetLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing = true);
/** @brief Returns a vector with const pointers to all labels in the MultiLabelSegmentation indicated
* by the passed label value vector.
* For details see GetLabelsByValue();
*/
const ConstLabelVectorType GetConstLabelsByValue(const LabelValueVectorType& labelValues, bool ignoreMissing = false) const;
/** Helper function that can be used to extract a vector of label values of a vector of label instance pointers.*/
static LabelValueVectorType ExtractLabelValuesFromLabelVector(const ConstLabelVectorType& labels);
/** Helper function that can be used to extract a vector of label values are vector of label instances.*/
static LabelValueVectorType ExtractLabelValuesFromLabelVector(const LabelVectorType& labels);
/** Helper function that converts a given vector of label instance pointers into a vector of const pointers.*/
static ConstLabelVectorType ConvertLabelVectorConst(const LabelVectorType& labels);
/**
* @brief Returns a vector of all label values located on the specified group.
* @param index the index of the group for which the vector of labels should be retrieved.
* If an invalid index is passed an exception will be raised.
* @return the respective vector of label values.
* @pre group index must exist.
*/
const LabelValueVectorType GetLabelValuesByGroup(GroupIndexType index) const;
/**
* @brief Returns a vector of all label values located on the specified group having a certain name.
* @param index the index of the group for which the vector of labels should be retrieved.
* If an invalid index is passed an exception will be raised.
* @param name Name of the label instances one is looking for.
* @return the respective vector of label values.
* @pre group index must exist.
*/
const LabelValueVectorType GetLabelValuesByName(GroupIndexType index, const std::string_view name) const;
/**
* Returns a vector with (class) names of all label instances used in the segmentation (over all groups)
*/
std::vector<std::string> GetLabelClassNames() const;
/**
* Returns a vector with (class) names of all label instances present in a certain group.
* @param index ID of the group, for which the label class names should be returned
* @pre Indicated group must exist. */
std::vector<std::string> GetLabelClassNamesByGroup(GroupIndexType index) const;
/** Helper that returns an unused label value, that could be used e.g. if one wants to define a label externally
* before adding it.
* @return A label value currently not in use.
* @remark is no unused label value can be provided an exception will be thrown.*/
LabelValueType GetUnusedLabelValue() const;
itkGetConstMacro(UnlabeledLabelLock, bool);
itkSetMacro(UnlabeledLabelLock, bool);
itkBooleanMacro(UnlabeledLabelLock);
/** Set the visibility of all label instances accordingly to the passed state.
*/
void SetAllLabelsVisible(bool visible);
/** Set the visibility of all label instances in a group accordingly to the passed state.
* @pre The specified group must exist.
*/
void SetAllLabelsVisibleByGroup(GroupIndexType group, bool visible);
/** Set the visibility of all label instances In a group with a given class name
* accordingly to the passed state.
* @pre The specified group must exist.
*/
void SetAllLabelsVisibleByName(GroupIndexType group, const std::string_view name, bool visible);
/** Returns the lock state of the label (including UnlabeledLabel value).
@pre Requested label does exist.*/
bool IsLabelLocked(LabelValueType value) const;
/** Set the lock state of all label instances accordingly to the passed state.
*/
void SetAllLabelsLocked(bool locked);
/** Set the lock state of all label instances in a group accordingly to the passed state.
* @pre The specified group must exist.
*/
void SetAllLabelsLockedByGroup(GroupIndexType group, bool locked);
/** Set the lock state of all label instances In a group with a given class name
* accordingly to the passed state.
* @pre The specified group must exist.
*/
void SetAllLabelsLockedByName(GroupIndexType group, const std::string_view name, bool locked);
/**
* \brief Replaces the labels of a group with a given vector of labels.
*
* @remark The passed label instances will be cloned before added to ensure clear ownership
* of the new labels.
* @remark The pixel content of the old labels will not be removed.
* @param groupID The index of the group that should have its labels replaced
* @param newLabels The vector of new labels
* @pre Group that should be replaced must exist.
* @pre new label values must not be used in other groups.
*/
void ReplaceGroupLabels(const GroupIndexType groupID, const ConstLabelVectorType& newLabels);
void ReplaceGroupLabels(const GroupIndexType groupID, const LabelVectorType& newLabels);
/** Returns the pointer to the image that contains the labeling of the indicate group.
*@pre groupID must reference an existing group.*/
mitk::Image* GetGroupImage(GroupIndexType groupID);
/** Returns the pointer to the image that contains the labeling of the indicate group.
*@pre groupID must reference an existing group.*/
const mitk::Image* GetGroupImage(GroupIndexType groupID) const;
/** Returns the name of the indicated group. String may be empty if no name was defined.
* Remark: The name neither is guaranteed to be defined nor that it is unique. Use the index
* to uniquely refer to a group.
*@pre groupID must reference an existing group.*/
const std::string& GetGroupName(GroupIndexType groupID) const;
/** Set the name of a group.
*@pre groupID must reference an existing group.*/
void SetGroupName(GroupIndexType groupID, const std::string& name);
itkGetModifiableObjectMacro(LookupTable, mitk::LookupTable);
void SetLookupTable(LookupTable* lut);
/** Updates the lookup table for a label indicated by the passed label value using the color of the label.
* @pre labelValue must exist.
*/
void UpdateLookupTable(PixelType pixelValue);
protected:
void OnLabelModified(const Object* sender, const itk::EventObject&);
/** Helper to ensure that the maps are correctly populated for a new label instance.*/
void AddLabelToMap(LabelValueType labelValue, Label* label, GroupIndexType groupID);
void RemoveLabelFromMap(LabelValueType labelValue);
/** Helper to ensure label events are correctly connected and lookup table is updated for a new label instance.*/
void RegisterLabel(Label* label);
/** Helper to ensure label events are unregistered.*/
void ReleaseLabel(Label* label);
/** Helper class used internally to apply lambda functions to the labels specified by the passed label value vector.
*/
void ApplyToLabels(const LabelValueVectorType& values, std::function<void(Label*)>&& lambda);
/** Helper class used internally to for visiting the labels specified by the passed label value vector
* with the lambda function.
*/
void VisitLabels(const LabelValueVectorType& values, std::function<void(const Label*)>&& lambda) const;
LabelValueType m_ActiveLabelValue;
private:
using LabelMapType = std::map<LabelValueType, Label::Pointer>;
/** Dictionary that holds all known labels (label value is the key).*/
LabelMapType m_LabelMap;
using GroupNameVectorType = std::vector<std::string>;
/** Vector storing the names of all groups. If a group has no user name defined, string is empty.*/
GroupNameVectorType m_Groups;
/**This type is internally used to track which label is currently
* associated with which layer.*/
using GroupToLabelMapType = std::vector<LabelValueVectorType>;
/* Dictionary that maps between group id (key) and label values in the group (vector of label value).*/
GroupToLabelMapType m_GroupToLabelMap;
using LabelToGroupMapType = std::map<LabelValueType, GroupIndexType>;
/* Dictionary that maps between label value (key) and group id (value)*/
LabelToGroupMapType m_LabelToGroupMap;
using LabelEventGuardMapType = std::map<LabelValueType, ITKEventObserverGuard>;
LabelEventGuardMapType m_LabelModEventGuardMap;
LookupTable::Pointer m_LookupTable;
/** Indicates if the MultiLabelSegmentation allows to overwrite unlabeled pixels in normal pixel manipulation operations (e.g. TransferLabelConent).*/
bool m_UnlabeledLabelLock;
/** Mutex used to secure manipulations of the internal state of label and group maps.*/
std::shared_mutex m_LabelNGroupMapsMutex;
public:
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
// END FUTURE MultiLabelSegmentation
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
/** DON'T USE. WORKAROUND method that is used until the rework is finished to
ensure always getting a group image and not this.
@warning Don't use. This method is going to be removed as soon as T30194 is
solved.*/
const mitk::Image* GetGroupImageWorkaround(GroupIndexType groupID) const;
/**
* \brief */
void UpdateCenterOfMass(PixelType pixelValue);
/**
* @brief Initialize an empty mitk::LabelSetImage using the information
* of an mitk::Image
* @param image the image which is used for initializing the mitk::LabelSetImage
*/
using mitk::Image::Initialize;
void Initialize(const mitk::Image *image) override;
/**
* \brief removes all pixel content form the active layer.*/
void ClearBuffer();
/**
* @brief Merges the mitk::Label with a given target value with the active label
*
* @param pixelValue the value of the label that should be the new merged label
* @param sourcePixelValue the value of the label that should be merged into the specified one
*/
void MergeLabel(PixelType pixelValue, PixelType sourcePixelValue);
/**
* @brief Merges a list of mitk::Labels with the mitk::Label that has a specific value
*
* @param pixelValue the value of the label that should be the new merged label
* @param vectorOfSourcePixelValues the list of label values that should be merge into the specified one
*/
void MergeLabels(PixelType pixelValue, const std::vector<PixelType>& vectorOfSourcePixelValues);
/**
* @brief Gets the ID of the currently active layer
* @return the ID of the active layer
* @pre at least on group must exist.
*/
unsigned int GetActiveLayer() const;
Label* GetActiveLabel();
const Label* GetActiveLabel() const;
/**
* @brief Get the number of all existing mitk::Labels for a given layer
* @param layer the layer ID for which the active mitk::Labels should be retrieved
* @return the number of all existing mitk::Labels for the given layer
*/
unsigned int GetNumberOfLabels(unsigned int layer) const;
/**
* @brief Returns the number of all labels summed up across all layers
* @return the overall number of labels across all layers
*/
unsigned int GetTotalNumberOfLabels() const;
/** @brief Initialize a new mitk::LabelSetImage by a given image.
* For all distinct pixel values of the parameter image new labels will
* be created. If the number of distinct pixel values exceeds mitk::Label::MAX_LABEL_VALUE
* an exception will be raised.
* @param image the image which is used for initialization
*/
void InitializeByLabeledImage(mitk::Image::Pointer image);
void MaskStamp(mitk::Image *mask, bool forceOverwrite);
void SetActiveLayer(unsigned int layer);
void SetActiveLabel(LabelValueType label);
unsigned int GetNumberOfLayers() const;
/**
* \brief Adds a new layer to the LabelSetImage. The new layer will be set as the active one.
* \param labels Labels that will be added to the new layer if provided
* \return the layer ID of the new layer
*/
GroupIndexType AddLayer(ConstLabelVector labels = {});
/**
* \brief Adds a layer based on a provided mitk::Image.
* \param layerImage is added to the vector of label images
* \param labels labels that will be cloned and added to the new layer if provided
* \return the layer ID of the new layer
* \pre layerImage must be valid instance
* \pre layerImage needs to have the same geometry then the segmentation
* \pre layerImage must have the pixel value equal to LabelValueType.
*/
GroupIndexType AddLayer(mitk::Image* layerImage, ConstLabelVector labels = {});
protected:
mitkCloneMacro(Self);
LabelSetImage();
LabelSetImage(const LabelSetImage &other);
~LabelSetImage() override;
template <typename TPixel, unsigned int VImageDimension>
void LayerContainerToImageProcessing(itk::Image<TPixel, VImageDimension> *source, unsigned int layer);
template <typename TPixel, unsigned int VImageDimension>
void ImageToLayerContainerProcessing(const itk::Image<TPixel, VImageDimension> *source, unsigned int layer) const;
template <typename ImageType>
void CalculateCenterOfMassProcessing(ImageType *input, LabelValueType index);
template <typename ImageType>
void EraseLabelProcessing(ImageType *input, PixelType index);
template <typename ImageType>
void MergeLabelProcessing(ImageType *input, PixelType pixelValue, PixelType index);
template <typename ImageType>
void MaskStampProcessing(ImageType *input, mitk::Image *mask, bool forceOverwrite);
template <typename LabelSetImageType, typename ImageType>
void InitializeByLabeledImageProcessing(LabelSetImageType *input, ImageType *other);
/** helper needed for ensuring unique values.
returns a sorted list of all labels (including the value for Unlabeled pixels..*/
LabelValueVectorType GetUsedLabelValues() const;
std::vector<Image::Pointer> m_LayerContainer;
int m_ActiveLayer;
bool m_activeLayerInvalid;
};
/**
- * @brief Equal A function comparing two label set images for beeing equal in meta- and imagedata
+ * @brief Equal A function comparing two label set images for being equal in meta- and imagedata
*
* @ingroup MITKTestingAPI
*
* Following aspects are tested for equality:
* - LabelSetImage members
* - working image data
* - layer image data
* - labels in label set
*
* @param rightHandSide An image to be compared
* @param leftHandSide An image to be compared
* @param eps Tolerance 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
*/
MITKMULTILABEL_EXPORT bool Equal(const mitk::LabelSetImage &leftHandSide,
const mitk::LabelSetImage &rightHandSide,
ScalarType eps,
bool verbose);
/**
* @brief Equal A function comparing two vectors of labels for being equal in data
*
* @ingroup MITKTestingAPI
*
* Following aspects are tested for equality:
* - Labels in vector
*
* @param rightHandSide An vector of labels to be compared
* @param leftHandSide An vector of labels to be compared
* @param eps Tolerance 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
*/
MITKMULTILABEL_EXPORT bool Equal(const mitk::LabelSetImage::ConstLabelVectorType& leftHandSide,
const mitk::LabelSetImage::ConstLabelVectorType& rightHandSide,
ScalarType eps,
bool verbose);
/** temporary namespace that is used until the new class MultiLabelSegmentation is
introduced. It allows to already introduce/use some upcoming definitions, while
refactoring code.*/
namespace MultiLabelSegmentation
{
enum class MergeStyle
{
Replace, //The old label content of a label value will be replaced by its new label content.
//Therefore pixels that are labeled might become unlabeled again.
//(This means that a lock of the value is also ignored).
Merge //The union of old and new label content will be generated.
};
enum class OverwriteStyle
{
RegardLocks, //Locked labels in the same spatial group will not be overwritten/changed.
IgnoreLocks //Label locks in the same spatial group will be ignored, so these labels might be changed.
};
}
using LabelValueMappingVector = std::vector < std::pair<Label::PixelType, Label::PixelType> >;
/**Helper function that transfers pixels of the specified source label from source image to the destination image by using
a specified destination label for a specific time step. Function processes the whole image volume of the specified time step.
@remark in its current implementation the function only transfers contents of the active layer of the passed LabelSetImages.
@remark the function assumes that it is only called with source and destination image of same geometry.
@remark CAUTION: The function is not save if sourceImage and destinationImage are the same instance and more than one label is transferred,
because the changes are made in-place for performance reasons in multiple passes. If a mapped value A equals an "old value"
that occurs later in the mapping, one ends up with a wrong transfer, as a pixel would be first mapped to A and then later again, because
it is also an "old" value in the mapping table.
@param sourceImage Pointer to the LabelSetImage which active layer should be used as source for the transfer.
@param destinationImage Pointer to the LabelSetImage which active layer should be used as destination for the transfer.
@param labelMapping Map that encodes the mappings of all label pixel transfers that should be done. First element is the
label in the source image. The second element is the label that transferred pixels should become in the destination image.
- The order in which the labels will be transfered is the same order of elements in the labelMapping.
+ The order in which the labels will be transferred is the same order of elements in the labelMapping.
If you use a heterogeneous label mapping (e.g. (1,2); so changing the label while transferring), keep in mind that
for the MergeStyle and OverwriteStyle only the destination label (second element) is relevant (e.g. what should be
altered with MergeStyle Replace).
@param mergeStyle indicates how the transfer should be done (merge or replace). For more details see documentation of
MultiLabelSegmentation::MergeStyle.
@param overwriteStlye indicates if label locks in the destination image should be regarded or not. For more details see
documentation of MultiLabelSegmentation::OverwriteStyle.
@param timeStep indicate the time step that should be transferred.
@pre sourceImage and destinationImage must be valid
@pre sourceImage and destinationImage must contain the indicated timeStep
@pre sourceImage must contain all indicated sourceLabels in its active layer.
@pre destinationImage must contain all indicated destinationLabels in its active layer.*/
MITKMULTILABEL_EXPORT void TransferLabelContentAtTimeStep(const LabelSetImage* sourceImage, LabelSetImage* destinationImage,
const TimeStepType timeStep, LabelValueMappingVector labelMapping = { {1,1} },
MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace,
MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks);
/**Helper function that transfers pixels of the specified source label from source image to the destination image by using
a specified destination label. Function processes the whole image volume for all time steps.
For more details please see TransferLabelContentAtTimeStep for LabelSetImages.
@sa TransferLabelContentAtTimeStep*/
MITKMULTILABEL_EXPORT void TransferLabelContent(const LabelSetImage* sourceImage, LabelSetImage* destinationImage, LabelValueMappingVector labelMapping = { {1,1} },
MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace,
MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks);
/**Helper function that transfers pixels of the specified source label from source image to the destination image by using
a specified destination label for a specific time step. Function processes the whole image volume of the specified time step.
@remark the function assumes that it is only called with source and destination image of same geometry.
@remark CAUTION: The function is not save, if sourceImage and destinationImage are the same instance and you transfer more then one
label, because the changes are made in-place for performance reasons but not in one pass. If a mapped value A equals a "old value"
that is later in the mapping, one ends up with a wrong transfer, as a pixel would be first mapped to A and then latter again, because
it is also an "old" value in the mapping table.
@param sourceImage Pointer to the image that should be used as source for the transfer.
@param destinationImage Pointer to the image that should be used as destination for the transfer.
@param destinationLabelVector Reference to the vector of labels (incl. lock states) in the destination image. Unknown pixel
values in the destinationImage will be assumed to be unlocked.
@param sourceBackground Value indicating the background in the source image.
@param destinationBackground Value indicating the background in the destination image.
@param destinationBackgroundLocked Value indicating the lock state of the background in the destination image.
@param labelMapping Map that encodes the mappings of all label pixel transfers that should be done. First element is the
label in the source image. The second element is the label that transferred pixels should become in the destination image.
- The order in which the labels will be transfered is the same order of elements in the labelMapping.
+ The order in which the labels will be transferred is the same order of elements in the labelMapping.
If you use a heterogeneous label mapping (e.g. (1,2); so changing the label while transferring), keep in mind that
for the MergeStyle and OverwriteStyle only the destination label (second element) is relevant (e.g. what should be
altered with MergeStyle Replace).
@param mergeStyle indicates how the transfer should be done (merge or replace). For more details see documentation of
MultiLabelSegmentation::MergeStyle.
@param overwriteStlye indicates if label locks in the destination image should be regarded or not. For more details see
documentation of MultiLabelSegmentation::OverwriteStyle.
@param timeStep indicate the time step that should be transferred.
@pre sourceImage, destinationImage and destinationLabelVector must be valid
@pre sourceImage and destinationImage must contain the indicated timeStep
@pre destinationLabelVector must contain all indicated destinationLabels for mapping.*/
MITKMULTILABEL_EXPORT void TransferLabelContentAtTimeStep(const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabelVector,
const TimeStepType timeStep, mitk::Label::PixelType sourceBackground = LabelSetImage::UNLABELED_VALUE,
mitk::Label::PixelType destinationBackground = LabelSetImage::UNLABELED_VALUE,
bool destinationBackgroundLocked = false,
LabelValueMappingVector labelMapping = { {1,1} },
MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace,
MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks);
/**Helper function that transfers pixels of the specified source label from source image to the destination image by using
a specified destination label. Function processes the whole image volume for all time steps.
For more details please see TransferLabelContentAtTimeStep.
@sa TransferLabelContentAtTimeStep*/
MITKMULTILABEL_EXPORT void TransferLabelContent(const Image* sourceImage, Image* destinationImage, const mitk::ConstLabelVector& destinationLabelVector,
mitk::Label::PixelType sourceBackground = LabelSetImage::UNLABELED_VALUE,
mitk::Label::PixelType destinationBackground = LabelSetImage::UNLABELED_VALUE,
bool destinationBackgroundLocked = false,
LabelValueMappingVector labelMapping = { {1,1} },
MultiLabelSegmentation::MergeStyle mergeStyle = MultiLabelSegmentation::MergeStyle::Replace,
MultiLabelSegmentation::OverwriteStyle overwriteStlye = MultiLabelSegmentation::OverwriteStyle::RegardLocks);
} // namespace mitk
#endif
diff --git a/Modules/Multilabel/mitkLabelSetImageConverter.h b/Modules/Multilabel/mitkLabelSetImageConverter.h
index 349560bc4e..6b712a3f80 100644
--- a/Modules/Multilabel/mitkLabelSetImageConverter.h
+++ b/Modules/Multilabel/mitkLabelSetImageConverter.h
@@ -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.
============================================================================*/
#ifndef mitkLabelSetImageConverter_h
#define mitkLabelSetImageConverter_h
#include <mitkLabelSetImage.h>
namespace mitk
{
/**
* \brief Convert mitk::LabelSetImage to mitk::Image (itk::VectorImage)
*/
MITKMULTILABEL_EXPORT Image::Pointer ConvertLabelSetImageToImage(LabelSetImage::ConstPointer labelSetImage);
/**
* \brief Convert mitk::Image to mitk::LabelSetImage, templating and differentation between itk::Image and
* itk::VectorImage is internal
*/
MITKMULTILABEL_EXPORT LabelSetImage::Pointer ConvertImageToLabelSetImage(Image::Pointer image);
MITKMULTILABEL_EXPORT LabelSetImage::Pointer ConvertImageVectorToLabelSetImage(const std::vector<mitk::Image::Pointer>& images, const TimeGeometry* timeGeometry);
MITKMULTILABEL_EXPORT std::vector<mitk::Image::Pointer> SplitVectorImage(const Image* vecImage);
/** Function takes a vector of labels and transfers all labels as clones with adapted label values to the result vector.
The values will be adapted according to the provided mapping (key is the old value, value the new).
- @remark: Only labels will be transfered, nothing else. So things like message observers or m_ReservedLabelValuesFunctor must be copied explicitly.*/
+ @remark: Only labels will be transferred, nothing else. So things like message observers or m_ReservedLabelValuesFunctor must be copied explicitly.*/
MITKMULTILABEL_EXPORT LabelSetImage::LabelVectorType GenerateLabelSetWithMappedValues(const LabelSetImage::ConstLabelVectorType&, LabelValueMappingVector labelMapping);
MITKMULTILABEL_EXPORT Image::Pointer ConvertImageToGroupImage(const Image* inputImage, mitk::LabelSetImage::LabelValueVectorType& foundLabels);
MITKMULTILABEL_EXPORT bool CheckForLabelValueConflictsAndResolve(const mitk::LabelSetImage::LabelValueVectorType& newValues, mitk::LabelSetImage::LabelValueVectorType& usedLabelValues, mitk::LabelSetImage::LabelValueVectorType& correctedLabelValues);
/** Function creates a binary mask representing only the specified label of the multi label segmentation.
* @param segmentation Pointer to the segmentation that is the source for the mask.
* @param labelValue the label that should be extracted.
* @param createBinaryMap indicates if the label pixels should be indicated by the value 1 (createBinaryMap==true) or by the value of the label
* (createBinaryMap==false).
* @pre segmentation must point to a valid instance.
* @pre labelValue must exist in segmentation.*/
MITKMULTILABEL_EXPORT Image::Pointer CreateLabelMask(const LabelSetImage* segmentation, LabelSetImage::LabelValueType labelValue, bool createBinaryMap = true);
/** Function creates a map of all label classes in a specified group.
* @param segmentation Pointer to the segmentation that is the source for the map.
* @param groupID the group that should be used.
* @param selectedLabels The selected labels that should be represented in the class map. This is meant as white list, therefore only
* label values listed in the list are used. Invalid label values (not existing in the group) will be ignored.
* @return Returns a pair where first is the pointer to the created map image and second is the look up table that indicated
* the pixel value of each found class in the map.
* @pre segmentation must point to a valid instance.
* @pre groupID must exist in segmentation.*/
using IDToLabelClassNameMapType = std::map<LabelSetImage::LabelValueType, std::string>;
MITKMULTILABEL_EXPORT std::pair<Image::Pointer, IDToLabelClassNameMapType> CreateLabelClassMap(const LabelSetImage* segmentation, LabelSetImage::GroupIndexType groupID, const LabelSetImage::LabelValueVectorType& selectedLabels);
/** Function creates a map of all label classes in a specified group.
* @overload
* This version always uses all labels of a group.
* @param segmentation Pointer to the segmentation that is the source for the map.
* @param groupID the group that should be used.
* @return Returns a pair where first is the pointer to the created map image and second is the look up table that indicated
* the pixel value of each found class in the map.
* @pre segmentation must point to a valid instance.
* @pre groupID must exist in segmentation.*/
MITKMULTILABEL_EXPORT std::pair<Image::Pointer, IDToLabelClassNameMapType> CreateLabelClassMap(const LabelSetImage* segmentation, LabelSetImage::GroupIndexType groupID);
}
#endif
diff --git a/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp b/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp
index 4bcf1430d9..6e593a5d32 100644
--- a/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp
+++ b/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp
@@ -1,108 +1,109 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkLabelSetImageSurfaceStampFilter.h"
#include "mitkImageAccessByItk.h"
#include "mitkImageCast.h"
#include <mitkLabelSetImage.h>
#include <mitkLabelSetImage.h>
#include <mitkSurface.h>
#include <mitkSurfaceToImageFilter.h>
mitk::LabelSetImageSurfaceStampFilter::LabelSetImageSurfaceStampFilter() : m_ForceOverwrite(false)
{
this->SetNumberOfIndexedInputs(1);
this->SetNumberOfRequiredInputs(1);
}
mitk::LabelSetImageSurfaceStampFilter::~LabelSetImageSurfaceStampFilter()
{
}
void mitk::LabelSetImageSurfaceStampFilter::GenerateData()
{
// GenerateOutputInformation();
this->SetNthOutput(0, this->GetInput(0));
mitk::Image::Pointer inputImage = this->GetInput(0);
if (m_Surface.IsNull())
{
MITK_ERROR << "Input surface is nullptr.";
return;
}
mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New();
surfaceToImageFilter->MakeOutputBinaryOn();
surfaceToImageFilter->SetInput(m_Surface);
surfaceToImageFilter->SetImage(inputImage);
surfaceToImageFilter->Update();
mitk::Image::Pointer resultImage = surfaceToImageFilter->GetOutput();
AccessByItk_1(inputImage, ItkImageProcessing, resultImage);
inputImage->DisconnectPipeline();
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::LabelSetImageSurfaceStampFilter::ItkImageProcessing(itk::Image<TPixel, VImageDimension> *itkImage,
mitk::Image::Pointer resultImage)
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
- mitk::LabelSetImage::Pointer LabelSetInputImage = dynamic_cast<LabelSetImage *>(GetInput());
+ const mitk::LabelSetImage* labelSetInputImage = dynamic_cast<LabelSetImage *>(GetInput());
try
{
typename ImageType::Pointer itkResultImage = ImageType::New();
mitk::CastToItkImage(resultImage, itkResultImage);
typedef itk::ImageRegionConstIterator<ImageType> SourceIteratorType;
typedef itk::ImageRegionIterator<ImageType> TargetIteratorType;
SourceIteratorType sourceIter(itkResultImage, itkResultImage->GetLargestPossibleRegion());
sourceIter.GoToBegin();
TargetIteratorType targetIter(itkImage, itkImage->GetLargestPossibleRegion());
targetIter.GoToBegin();
- int activeLabel = LabelSetInputImage->GetActiveLabel()->GetValue();
+ int activeLabel = labelSetInputImage->GetActiveLabel()->GetValue();
while (!sourceIter.IsAtEnd())
{
auto sourceValue = static_cast<int>(sourceIter.Get());
auto targetValue = static_cast<int>(targetIter.Get());
+ auto label = labelSetInputImage->GetLabel(targetValue);
if ((sourceValue != LabelSetImage::UNLABELED_VALUE) &&
(m_ForceOverwrite ||
- !LabelSetInputImage->GetLabel(targetValue)->GetLocked())) // skip unlabled pixels and locked labels
+ label.IsNull() || !label->GetLocked())) // skip unlabeled source pixels and locked target labels
{
targetIter.Set(activeLabel);
}
++sourceIter;
++targetIter;
}
}
catch (itk::ExceptionObject &e)
{
mitkThrow() << e.GetDescription();
}
this->Modified();
}
void mitk::LabelSetImageSurfaceStampFilter::GenerateOutputInformation()
{
mitk::Image::Pointer inputImage = (mitk::Image *)this->GetInput();
mitk::Image::Pointer output = this->GetOutput();
itkDebugMacro(<< "GenerateOutputInformation()");
if (inputImage.IsNull())
return;
}
diff --git a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp
index 70893567b1..d54225b144 100644
--- a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp
+++ b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp
@@ -1,764 +1,760 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkLabelSetImageVtkMapper2D.h"
// MITK
#include <mitkAbstractTransformGeometry.h>
#include <mitkDataNode.h>
#include <mitkImageSliceSelector.h>
#include <mitkPlaneGeometry.h>
#include <mitkProperties.h>
#include <mitkVectorProperty.h>
+#include <mitkLabelHighlightGuard.h>
// MITK Rendering
#include "vtkNeverTranslucentTexture.h"
// VTK
#include <vtkCamera.h>
#include <vtkImageData.h>
#include <vtkImageReslice.h>
#include <vtkLookupTable.h>
#include <vtkPlaneSource.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkImageMapToColors.h>
namespace
{
itk::ModifiedTimeType PropertyTimeStampIsNewer(const mitk::IPropertyProvider* provider, mitk::BaseRenderer* renderer, const std::string& propName, itk::ModifiedTimeType refMT)
{
const std::string context = renderer != nullptr ? renderer->GetName() : "";
auto prop = provider->GetConstProperty(propName, context);
if (prop != nullptr)
{
return prop->GetTimeStamp() > refMT;
}
return false;
}
}
mitk::LabelSetImageVtkMapper2D::LabelSetImageVtkMapper2D()
{
}
mitk::LabelSetImageVtkMapper2D::~LabelSetImageVtkMapper2D()
{
}
vtkProp *mitk::LabelSetImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer)
{
// return the actor corresponding to the renderer
return m_LSH.GetLocalStorage(renderer)->m_Actors;
}
mitk::LabelSetImageVtkMapper2D::LocalStorage *mitk::LabelSetImageVtkMapper2D::GetLocalStorage(
mitk::BaseRenderer *renderer)
{
return m_LSH.GetLocalStorage(renderer);
}
void mitk::LabelSetImageVtkMapper2D::GenerateLookupTable(mitk::BaseRenderer* renderer)
{
LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer);
mitk::DataNode* node = this->GetDataNode();
auto* image = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
assert(image && image->IsInitialized());
localStorage->m_LabelLookupTable = image->GetLookupTable()->Clone();
const auto labelValues = image->GetAllLabelValues();
- std::string propertyName = "org.mitk.multilabel.labels.highlighted";
+ mitk::IntVectorProperty::Pointer prop = dynamic_cast<mitk::IntVectorProperty*>(node->GetNonConstProperty(LabelHighlightGuard::PROPERTY_NAME_LABELS_HIGHLIGHTED()));
- mitk::IntVectorProperty::Pointer prop = dynamic_cast<mitk::IntVectorProperty*>(node->GetNonConstProperty(propertyName));
if (nullptr != prop)
{
const auto highlightedLabelValues = prop->GetValue();
+ mitk::BoolProperty::Pointer boolProp = dynamic_cast<mitk::BoolProperty*>(node->GetNonConstProperty(LabelHighlightGuard::PROPERTY_NAME_HIGHLIGHT_INVISIBLE()));
+ bool higlightInvisible = boolProp.IsNull() ? false : boolProp->GetValue();
if (!highlightedLabelValues.empty())
{
auto lookUpTable = localStorage->m_LabelLookupTable->GetVtkLookupTable();
auto highlightEnd = highlightedLabelValues.cend();
double rgba[4];
for (const auto& value : labelValues)
{
lookUpTable->GetTableValue(value, rgba);
if (highlightEnd == std::find(highlightedLabelValues.begin(), highlightedLabelValues.end(), value))
{ //make all none highlighted values more transparent
rgba[3] *= 0.3;
}
else
{
- if (rgba[3] != 0)
- { //if highlighted values are visible set them to opaque to pop out
+ if (higlightInvisible || rgba[3] != 0 )
+ {
rgba[3] = 1.;
}
- else
- { //if highlighted values are invisible the opacity is increased a bit
- //to give a visual hint that the are highlighted but also invisible.
- //e.g. needed to see a difference if you change the visibility of
- //a highlighted label in the MultiLabelInspector
- rgba[3] = 0.4;
- }
}
lookUpTable->SetTableValue(value, rgba);
}
localStorage->m_LabelLookupTable->Modified();
}
}
}
namespace
{
std::vector<mitk::LabelSetImage::GroupIndexType> GetOutdatedGroups(const mitk::LabelSetImageVtkMapper2D::LocalStorage* ls, const mitk::LabelSetImage* seg)
{
const auto nrOfGroups = seg->GetNumberOfLayers();
std::vector<mitk::LabelSetImage::GroupIndexType> result;
for (mitk::LabelSetImage::GroupIndexType groupID = 0; groupID < nrOfGroups; ++groupID)
{
const auto groupImage = seg->GetGroupImage(groupID);
if (groupImage->GetMTime() > ls->m_LastDataUpdateTime
|| groupImage->GetPipelineMTime() > ls->m_LastDataUpdateTime
|| ls->m_GroupImageIDs.size() <= groupID
|| groupImage != ls->m_GroupImageIDs[groupID])
{
result.push_back(groupID);
}
}
return result;
}
}
void mitk::LabelSetImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
mitk::DataNode *node = this->GetDataNode();
auto *segmentation = dynamic_cast<mitk::LabelSetImage *>(node->GetData());
assert(segmentation && segmentation->IsInitialized());
bool isLookupModified = localStorage->m_LabelLookupTable.IsNull() ||
(localStorage->m_LabelLookupTable->GetMTime() < segmentation->GetLookupTable()->GetMTime()) ||
PropertyTimeStampIsNewer(node, renderer, "org.mitk.multilabel.labels.highlighted", localStorage->m_LabelLookupTable->GetMTime()) ||
+ PropertyTimeStampIsNewer(node, renderer, "org.mitk.multilabel.highlight_invisible", localStorage->m_LabelLookupTable->GetMTime()) ||
PropertyTimeStampIsNewer(node, renderer, "opacity", localStorage->m_LabelLookupTable->GetMTime());
if (isLookupModified)
{
this->GenerateLookupTable(renderer);
}
bool isGeometryModified = (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) ||
(localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime());
bool rendererGeometryIsValid = true;
// check if there is a valid worldGeometry
if (isGeometryModified)
{
const PlaneGeometry* worldGeometry = renderer->GetCurrentWorldPlaneGeometry();
rendererGeometryIsValid = worldGeometry != nullptr
&& worldGeometry->IsValid()
&& worldGeometry->HasReferenceGeometry();
isGeometryModified = rendererGeometryIsValid
&& localStorage->m_WorldPlane.IsNotNull() && !Equal(*worldGeometry, *(localStorage->m_WorldPlane.GetPointer()));
localStorage->m_WorldPlane = rendererGeometryIsValid ? worldGeometry->Clone() : nullptr;
}
const bool hasValidContent = rendererGeometryIsValid && RenderingGeometryIntersectsImage(localStorage->m_WorldPlane, segmentation->GetSlicedGeometry());
const bool contentBecameValid = hasValidContent && !localStorage->m_HasValidContent;
const bool contentBecameInvalid = !hasValidContent && localStorage->m_HasValidContent;
if (contentBecameInvalid)
{
// 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
for (unsigned int lidx = 0; lidx < localStorage->m_NumberOfLayers; ++lidx)
{
localStorage->m_ReslicedImageVector[lidx] = nullptr;
localStorage->m_LayerMapperVector[lidx]->SetInputData(localStorage->m_EmptyPolyData);
localStorage->m_OutlineActor->SetVisibility(false);
localStorage->m_OutlineShadowActor->SetVisibility(false);
}
localStorage->m_LastDataUpdateTime.Modified();
}
localStorage->m_HasValidContent = hasValidContent;
if (!hasValidContent)
{
// early out if there is no intersection of the current rendering geometry
// and the geometry of the image that is to be rendered.
return;
}
std::vector<mitk::LabelSetImage::GroupIndexType> outdatedGroups;
auto currentTimestep = this->GetTimestep();
if (isGeometryModified || contentBecameValid || localStorage->m_LastTimeStep!= currentTimestep)
{
//if geometry is outdated or we have valid content again
// -> all groups need regeneration
outdatedGroups.resize(segmentation->GetNumberOfLayers());
std::iota(outdatedGroups.begin(), outdatedGroups.end(), 0);
}
else
{
outdatedGroups = GetOutdatedGroups(localStorage, segmentation);
}
if (!outdatedGroups.empty())
{
this->GenerateImageSlice(renderer, outdatedGroups);
}
localStorage->m_LastTimeStep = currentTimestep;
float opacity = 1.0f;
node->GetOpacity(opacity, renderer, "opacity");
if (isLookupModified)
{
//if lookup table is modified all groups need a new color mapping
outdatedGroups.resize(segmentation->GetNumberOfLayers());
std::iota(outdatedGroups.begin(), outdatedGroups.end(), 0);
}
for (const auto groupID: outdatedGroups)
{
localStorage->m_LayerImageMapToColors[groupID]->SetLookupTable(localStorage->m_LabelLookupTable->GetVtkLookupTable());
localStorage->m_LayerImageMapToColors[groupID]->SetInputData(localStorage->m_ReslicedImageVector[groupID]);
localStorage->m_LayerImageMapToColors[groupID]->Update();
// check for texture interpolation property
bool textureInterpolation = false;
node->GetBoolProperty("texture interpolation", textureInterpolation, renderer);
// set the interpolation modus according to the property
localStorage->m_LayerTextureVector[groupID]->SetInterpolate(textureInterpolation);
localStorage->m_LayerTextureVector[groupID]->SetInputConnection(
localStorage->m_LayerImageMapToColors[groupID]->GetOutputPort());
this->TransformActor(renderer);
// set the plane as input for the mapper
localStorage->m_LayerMapperVector[groupID]->SetInputConnection(localStorage->m_Plane->GetOutputPort());
// set the texture for the actor
localStorage->m_LayerActorVector[groupID]->SetTexture(localStorage->m_LayerTextureVector[groupID]);
localStorage->m_LayerActorVector[groupID]->GetProperty()->SetOpacity(opacity);
}
auto activeLayer = segmentation->GetActiveLayer();
bool activeGroupIsOutdated = std::find(outdatedGroups.begin(), outdatedGroups.end(), activeLayer) != outdatedGroups.end();
if (activeGroupIsOutdated
|| PropertyTimeStampIsNewer(node, renderer, "opacity", localStorage->m_LastActiveLabelUpdateTime.GetMTime())
|| PropertyTimeStampIsNewer(node, renderer, "labelset.contour.active", localStorage->m_LastActiveLabelUpdateTime.GetMTime())
|| PropertyTimeStampIsNewer(node, renderer, "labelset.contour.width", localStorage->m_LastActiveLabelUpdateTime.GetMTime())
)
{
this->GenerateActiveLabelOutline(renderer);
}
}
void mitk::LabelSetImageVtkMapper2D::GenerateImageSlice(mitk::BaseRenderer* renderer, const std::vector<mitk::LabelSetImage::GroupIndexType>& outdatedGroupIDs)
{
LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer);
mitk::DataNode* node = this->GetDataNode();
auto* segmentation = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
assert(segmentation && segmentation->IsInitialized());
segmentation->Update();
const auto numberOfLayers = segmentation->GetNumberOfLayers();
if (numberOfLayers != localStorage->m_NumberOfLayers)
{
if (numberOfLayers > localStorage->m_NumberOfLayers)
{
for (unsigned int lidx = localStorage->m_NumberOfLayers; lidx < numberOfLayers; ++lidx)
{
localStorage->m_GroupImageIDs.push_back(nullptr);
localStorage->m_ReslicedImageVector.push_back(vtkSmartPointer<vtkImageData>::New());
localStorage->m_ReslicerVector.push_back(mitk::ExtractSliceFilter::New());
localStorage->m_LayerTextureVector.push_back(vtkSmartPointer<vtkNeverTranslucentTexture>::New());
localStorage->m_LayerMapperVector.push_back(vtkSmartPointer<vtkPolyDataMapper>::New());
localStorage->m_LayerActorVector.push_back(vtkSmartPointer<vtkActor>::New());
localStorage->m_LayerImageMapToColors.push_back(vtkSmartPointer<vtkImageMapToColors>::New());
// do not repeat the texture (the image)
localStorage->m_LayerTextureVector[lidx]->RepeatOff();
// set corresponding mappers for the actors
localStorage->m_LayerActorVector[lidx]->SetMapper(localStorage->m_LayerMapperVector[lidx]);
}
}
else
{
localStorage->m_GroupImageIDs.resize(numberOfLayers);
localStorage->m_ReslicedImageVector.resize(numberOfLayers);
localStorage->m_ReslicerVector.resize(numberOfLayers);
localStorage->m_LayerTextureVector.resize(numberOfLayers);
localStorage->m_LayerMapperVector.resize(numberOfLayers);
localStorage->m_LayerActorVector.resize(numberOfLayers);
localStorage->m_LayerImageMapToColors.resize(numberOfLayers);
}
localStorage->m_NumberOfLayers = numberOfLayers;
localStorage->m_Actors = vtkSmartPointer<vtkPropAssembly>::New();
for (unsigned int lidx = 0; lidx < numberOfLayers; ++lidx)
{
localStorage->m_Actors->AddPart(localStorage->m_LayerActorVector[lidx]);
}
localStorage->m_Actors->AddPart(localStorage->m_OutlineShadowActor);
localStorage->m_Actors->AddPart(localStorage->m_OutlineActor);
}
for (const auto groupID : outdatedGroupIDs)
{
const auto groupImage = segmentation->GetGroupImage(groupID);
localStorage->m_GroupImageIDs[groupID] = groupImage;
localStorage->m_ReslicerVector[groupID]->SetInput(groupImage);
localStorage->m_ReslicerVector[groupID]->SetWorldGeometry(localStorage->m_WorldPlane);
localStorage->m_ReslicerVector[groupID]->SetTimeStep(this->GetTimestep());
// set the transformation of the image to adapt reslice axis
localStorage->m_ReslicerVector[groupID]->SetResliceTransformByGeometry(
groupImage->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep()));
// is the geometry of the slice based on the image image or the worldgeometry?
bool inPlaneResampleExtentByGeometry = false;
node->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer);
localStorage->m_ReslicerVector[groupID]->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry);
localStorage->m_ReslicerVector[groupID]->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST);
localStorage->m_ReslicerVector[groupID]->SetVtkOutputRequest(true);
// this is needed when thick mode was enabled before. These variables have to be reset to default values
localStorage->m_ReslicerVector[groupID]->SetOutputDimensionality(2);
localStorage->m_ReslicerVector[groupID]->SetOutputSpacingZDirection(1.0);
localStorage->m_ReslicerVector[groupID]->SetOutputExtentZDirection(0, 0);
// Bounds information for reslicing (only required if reference geometry is present)
// this used for generating a vtkPLaneSource with the right size
double sliceBounds[6];
sliceBounds[0] = 0.0;
sliceBounds[1] = 0.0;
sliceBounds[2] = 0.0;
sliceBounds[3] = 0.0;
sliceBounds[4] = 0.0;
sliceBounds[5] = 0.0;
localStorage->m_ReslicerVector[groupID]->GetClippedPlaneBounds(sliceBounds);
// setup the textured plane
this->GeneratePlane(renderer, sliceBounds);
// get the spacing of the slice
localStorage->m_mmPerPixel = localStorage->m_ReslicerVector[groupID]->GetOutputSpacing();
localStorage->m_ReslicerVector[groupID]->Modified();
// start the pipeline with updating the largest possible, needed if the geometry of the image has changed
localStorage->m_ReslicerVector[groupID]->UpdateLargestPossibleRegion();
localStorage->m_ReslicedImageVector[groupID] = localStorage->m_ReslicerVector[groupID]->GetVtkOutput();
}
localStorage->m_LastDataUpdateTime.Modified();
}
void mitk::LabelSetImageVtkMapper2D::GenerateActiveLabelOutline(mitk::BaseRenderer* renderer)
{
LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer);
mitk::DataNode* node = this->GetDataNode();
auto* image = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
int activeLayer = image->GetActiveLayer();
float opacity = 1.0f;
node->GetOpacity(opacity, renderer, "opacity");
mitk::Label* activeLabel = image->GetActiveLabel();
bool contourActive = false;
node->GetBoolProperty("labelset.contour.active", contourActive, renderer);
if (nullptr != activeLabel && contourActive && activeLabel->GetVisible())
{
//generate contours/outlines
localStorage->m_OutlinePolyData =
this->CreateOutlinePolyData(renderer, localStorage->m_ReslicedImageVector[activeLayer], activeLabel->GetValue());
localStorage->m_OutlineActor->SetVisibility(true);
localStorage->m_OutlineShadowActor->SetVisibility(true);
const mitk::Color& color = activeLabel->GetColor();
localStorage->m_OutlineActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue());
localStorage->m_OutlineShadowActor->GetProperty()->SetColor(0, 0, 0);
float contourWidth(2.0);
node->GetFloatProperty("labelset.contour.width", contourWidth, renderer);
localStorage->m_OutlineActor->GetProperty()->SetLineWidth(contourWidth);
localStorage->m_OutlineShadowActor->GetProperty()->SetLineWidth(contourWidth * 1.5);
localStorage->m_OutlineActor->GetProperty()->SetOpacity(opacity);
localStorage->m_OutlineShadowActor->GetProperty()->SetOpacity(opacity);
localStorage->m_OutlineMapper->SetInputData(localStorage->m_OutlinePolyData);
}
else
{
localStorage->m_OutlineActor->SetVisibility(false);
localStorage->m_OutlineShadowActor->SetVisibility(false);
}
localStorage->m_LastActiveLabelUpdateTime.Modified();
}
bool mitk::LabelSetImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry,
const BaseGeometry *imageGeometry) const
{
// 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;
}
vtkSmartPointer<vtkPolyData> mitk::LabelSetImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer,
vtkImageData *image,
int pixelValue)
{
LocalStorage *localStorage = this->GetLocalStorage(renderer);
// get the min and max index values of each direction
int *extent = image->GetExtent();
int xMin = extent[0];
int xMax = extent[1];
int yMin = extent[2];
int yMax = extent[3];
int *dims = image->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 = this->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<mitk::Label::PixelType *>(image->GetScalarPointer());
while (y <= yMax)
{
// if the current pixel value is set to something
if ((currentPixel) && (*currentPixel == pixelValue))
{
// 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) != pixelValue)
{ // 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) != pixelValue)
{ // 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) != pixelValue)
{ // 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) != pixelValue)
{ // 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::LabelSetImageVtkMapper2D::Update(mitk::BaseRenderer *renderer)
{
bool visible = true;
const DataNode *node = this->GetDataNode();
node->GetVisibility(visible, renderer, "visible");
if (!visible)
return;
auto *image = dynamic_cast<mitk::LabelSetImage *>(node->GetData());
if (image == nullptr || image->IsInitialized() == false)
return;
// Calculate time step of the image data for the specified renderer (integer value)
this->CalculateTimeStep(renderer);
// Check if time step is valid
const TimeGeometry *dataTimeGeometry = image->GetTimeGeometry();
if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) ||
(!dataTimeGeometry->IsValidTimeStep(this->GetTimestep())))
{
return;
}
image->UpdateOutputInformation();
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
// check if something important has changed and we need to re-render
if (localStorage->m_LabelLookupTable.IsNull() ||
(localStorage->m_LabelLookupTable->GetMTime() < image->GetLookupTable()->GetMTime()) ||
(localStorage->m_LastDataUpdateTime < image->GetMTime()) ||
(localStorage->m_LastDataUpdateTime < image->GetPipelineMTime()) ||
(localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) ||
(localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) ||
(localStorage->m_LastPropertyUpdateTime < node->GetPropertyList()->GetMTime()) ||
(localStorage->m_LastPropertyUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ||
(localStorage->m_LastPropertyUpdateTime < image->GetPropertyList()->GetMTime()))
{
this->GenerateDataForRenderer(renderer);
localStorage->m_LastPropertyUpdateTime.Modified();
}
else if ((localStorage->m_LastPropertyUpdateTime < node->GetPropertyList()->GetMTime()) ||
(localStorage->m_LastPropertyUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ||
(localStorage->m_LastPropertyUpdateTime < image->GetPropertyList()->GetMTime()))
{
this->GenerateDataForRenderer(renderer);
localStorage->m_LastPropertyUpdateTime.Modified();
}
}
// set the two points defining the textured plane according to the dimension and spacing
void mitk::LabelSetImageVtkMapper2D::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 sagittal) 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::LabelSetImageVtkMapper2D::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;
}
void mitk::LabelSetImageVtkMapper2D::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 sagittal
vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
vtkSmartPointer<vtkMatrix4x4> matrix = localStorage->m_ReslicerVector[0]->GetResliceAxes(); // same for all layers
trans->SetMatrix(matrix);
for (unsigned int lidx = 0; lidx < localStorage->m_NumberOfLayers; ++lidx)
{
// transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or sagittal)
localStorage->m_LayerActorVector[lidx]->SetUserTransform(trans);
// transform the origin to center based coordinates, because MITK is center based.
localStorage->m_LayerActorVector[lidx]->SetPosition(
-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0);
}
// same for outline actor
localStorage->m_OutlineActor->SetUserTransform(trans);
localStorage->m_OutlineActor->SetPosition(
-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0);
// same for outline shadow actor
localStorage->m_OutlineShadowActor->SetUserTransform(trans);
localStorage->m_OutlineShadowActor->SetPosition(
-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0);
}
void mitk::LabelSetImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node,
mitk::BaseRenderer *renderer,
bool overwrite)
{
// add/replace the following properties
node->SetProperty("opacity", FloatProperty::New(1.0f), renderer);
node->SetProperty("binary", BoolProperty::New(false), renderer);
node->SetProperty("labelset.contour.active", BoolProperty::New(true), renderer);
node->SetProperty("labelset.contour.width", FloatProperty::New(2.0), renderer);
Superclass::SetDefaultProperties(node, renderer, overwrite);
}
mitk::LabelSetImageVtkMapper2D::LocalStorage::~LocalStorage()
{
}
mitk::LabelSetImageVtkMapper2D::LocalStorage::LocalStorage()
{
// Do as much actions as possible in here to avoid double executions.
m_Plane = vtkSmartPointer<vtkPlaneSource>::New();
m_Actors = vtkSmartPointer<vtkPropAssembly>::New();
m_OutlinePolyData = vtkSmartPointer<vtkPolyData>::New();
m_EmptyPolyData = vtkSmartPointer<vtkPolyData>::New();
m_OutlineActor = vtkSmartPointer<vtkActor>::New();
m_OutlineMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
m_OutlineShadowActor = vtkSmartPointer<vtkActor>::New();
m_HasValidContent = false;
m_NumberOfLayers = 0;
m_mmPerPixel = nullptr;
m_LastTimeStep = 0;
m_OutlineActor->SetMapper(m_OutlineMapper);
m_OutlineShadowActor->SetMapper(m_OutlineMapper);
m_OutlineActor->SetVisibility(false);
m_OutlineShadowActor->SetVisibility(false);
}
diff --git a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h
index 4defba4876..4dc7bdff26 100644
--- a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h
+++ b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.h
@@ -1,233 +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 mitkLabelSetImageVtkMapper2D_h
#define mitkLabelSetImageVtkMapper2D_h
// MITK
#include "MitkMultilabelExports.h"
#include "mitkCommon.h"
// MITK Rendering
#include "mitkBaseRenderer.h"
#include "mitkExtractSliceFilter.h"
#include "mitkLabelSetImage.h"
#include "mitkVtkMapper.h"
// VTK
#include <vtkSmartPointer.h>
class vtkActor;
class vtkPolyDataMapper;
class vtkPlaneSource;
class vtkImageData;
class vtkLookupTable;
class vtkImageReslice;
class vtkPoints;
class vtkMitkThickSlicesFilter;
class vtkPolyData;
class vtkNeverTranslucentTexture;
class vtkImageMapToColors;
namespace mitk
{
/** \brief Mapper to resample and display 2D slices of a 3D labelset image.
*
* Properties that can be set for labelset images and influence this mapper are:
*
* - \b "labelset.contour.active": (BoolProperty) whether to show only the active label as a contour or not
* - \b "labelset.contour.width": (FloatProperty) line width of the contour
* The default properties are:
* - \b "labelset.contour.active", mitk::BoolProperty::New( true ), renderer, overwrite )
* - \b "labelset.contour.width", mitk::FloatProperty::New( 2.0 ), renderer, overwrite )
* \ingroup Mapper
*/
class MITKMULTILABEL_EXPORT LabelSetImageVtkMapper2D : public VtkMapper
{
public:
/** Standard class typedefs. */
mitkClassMacro(LabelSetImageVtkMapper2D, VtkMapper);
/** Method for creation through the object factory. */
itkNewMacro(Self);
/** \brief Get the Image to map */
const mitk::Image *GetInput(void);
/** \brief Checks whether this mapper needs to update itself and generate
* data. */
void Update(mitk::BaseRenderer *renderer) override;
//### methods of MITK-VTK rendering pipeline
vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override;
//### end of methods of MITK-VTK rendering pipeline
/** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */
/**
* To render axial, coronal, and sagittal, the mapper is called three times.
* For performance reasons, the corresponding data for each view is saved in the
* internal helper class LocalStorage. This allows rendering n views with just
* 1 mitkMapper using n vtkMapper.
* */
class MITKMULTILABEL_EXPORT LocalStorage : public mitk::Mapper::BaseLocalStorage
{
public:
vtkSmartPointer<vtkPropAssembly> m_Actors;
/** Vector containing the pointer of the currently used group images.
* IMPORTANT: This member must not be used to access any data.
* Its purpose is to allow checking if the order of the groups has changed
* in order to adapt the pipe line accordingly*/
std::vector<const Image*> m_GroupImageIDs;
std::vector<vtkSmartPointer<vtkActor>> m_LayerActorVector;
std::vector<vtkSmartPointer<vtkPolyDataMapper>> m_LayerMapperVector;
std::vector<vtkSmartPointer<vtkImageData>> m_ReslicedImageVector;
std::vector<vtkSmartPointer<vtkImageMapToColors>> m_LayerImageMapToColors;
std::vector<vtkSmartPointer<vtkNeverTranslucentTexture>> m_LayerTextureVector;
vtkSmartPointer<vtkPolyData> m_EmptyPolyData;
vtkSmartPointer<vtkPlaneSource> m_Plane;
std::vector<mitk::ExtractSliceFilter::Pointer> m_ReslicerVector;
vtkSmartPointer<vtkPolyData> m_OutlinePolyData;
/** \brief An actor for the outline */
vtkSmartPointer<vtkActor> m_OutlineActor;
/** \brief An actor for the outline shadow*/
vtkSmartPointer<vtkActor> m_OutlineShadowActor;
/** \brief A mapper for the outline */
vtkSmartPointer<vtkPolyDataMapper> m_OutlineMapper;
/** \brief Timestamp of last update of stored data. */
itk::TimeStamp m_LastDataUpdateTime;
/** \brief Timestamp of last update of a property. */
itk::TimeStamp m_LastPropertyUpdateTime;
/** \brief Timestamp of last update of a property. */
itk::TimeStamp m_LastActiveLabelUpdateTime;
/** \brief mmPerPixel relation between pixel and mm. (World spacing).*/
mitk::ScalarType *m_mmPerPixel;
/** look up table for label colors. */
mitk::LookupTable::Pointer m_LabelLookupTable;
mitk::PlaneGeometry::Pointer m_WorldPlane;
bool m_HasValidContent;
mitk::TimeStepType m_LastTimeStep;
unsigned int m_NumberOfLayers;
/** \brief Default constructor of the local storage. */
LocalStorage();
/** \brief Default destructor of the local storage. */
~LocalStorage() override;
};
/** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */
mitk::LocalStorageHandler<LocalStorage> m_LSH;
/** \brief Get the LocalStorage corresponding to the current renderer. */
LocalStorage *GetLocalStorage(mitk::BaseRenderer *renderer);
/** \brief Set the default properties for general image rendering. */
static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false);
/** \brief This method switches between different rendering modes (e.g. use a lookup table or a transfer function).
* Detailed documentation about the modes can be found here: \link mitk::RenderingModeProperty \endlink
*/
void ApplyRenderingMode(mitk::BaseRenderer *renderer);
protected:
/** \brief Transforms the actor to the actual position in 3D.
* \param renderer The current renderer corresponding to the render window.
*/
void TransformActor(mitk::BaseRenderer *renderer);
/** \brief Generates a plane according to the size of the resliced image in milimeters.
*
* In VTK a vtkPlaneSource is defined through three points. The origin and two
* points defining the axes of the plane (see VTK documentation). The origin is
* set to (xMin; yMin; Z), where xMin and yMin are the minimal bounds of the
* resliced image in space. Z is relevant for blending and the layer property.
* The center of the plane (C) is also the center of the view plane (cf. the image above).
*
* \note For the standard MITK view with three 2D render windows showing three
* different slices, three such planes are generated. All these planes are generated
* in the XY-plane (even if they depict a YZ-slice of the volume).
*
*/
void GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]);
/** \brief Generates a vtkPolyData object containing the outline of a given binary slice.
\param renderer Pointer to the renderer containing the needed information
\param image
\param pixelValue
\note This code is based on code from the iil library.
*/
vtkSmartPointer<vtkPolyData> CreateOutlinePolyData(mitk::BaseRenderer *renderer,
vtkImageData *image,
int pixelValue = 1);
/** Default constructor */
LabelSetImageVtkMapper2D();
/** Default deconstructor */
~LabelSetImageVtkMapper2D() override;
/** \brief Does the actual resampling, without rendering the image yet.
* All the data is generated inside this method. The vtkProp (or Actor)
* is filled with content (i.e. the resliced image).
*
* After generation, a 4x4 transformation matrix(t) of the current slice is obtained
* from the vtkResliceImage object via GetReslicesAxis(). This matrix is
* applied to each textured plane (actor->SetUserTransform(t)) to transform everything
* to the actual 3D position (cf. the following image).
*
* \image html cameraPositioning3D.png
*
*/
void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override;
void GenerateImageSlice(mitk::BaseRenderer* renderer, const std::vector<mitk::LabelSetImage::GroupIndexType>& outdatedGroupIDs);
void GenerateActiveLabelOutline(mitk::BaseRenderer* renderer);
/** \brief Generates the look up table that should be used.
*/
void GenerateLookupTable(mitk::BaseRenderer* renderer);
/** \brief This method uses the vtkCamera clipping range and the layer property
- * to calcualte the depth of the object (e.g. image or contour). The depth is used
+ * to calculate the depth of the object (e.g. image or contour). The depth is used
* to keep the correct order for the final VTK rendering.*/
float CalculateLayerDepth(mitk::BaseRenderer *renderer);
/**
* \brief Calculates whether the given rendering geometry intersects the
* given SlicedGeometry3D.
*
* This method checks if the given Geometry2D intersects the given
* SlicedGeometry3D. It calculates the distance of the Geometry2D to all
* 8 cornerpoints of the SlicedGeometry3D. If all distances have the same
* sign (all positive or all negative) there is no intersection.
* If the distances have different sign, there is an intersection.
**/
bool RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, const BaseGeometry* imageGeometry) const;
};
} // namespace mitk
#endif
diff --git a/Modules/Multilabel/mitkMultiLabelIOHelper.h b/Modules/Multilabel/mitkMultiLabelIOHelper.h
index 0345aa2fcb..f1d2c0c221 100644
--- a/Modules/Multilabel/mitkMultiLabelIOHelper.h
+++ b/Modules/Multilabel/mitkMultiLabelIOHelper.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 mitkMultiLabelIOHelper_h
#define mitkMultiLabelIOHelper_h
#include <mitkLabel.h>
#include <itkSmartPointer.h>
#include <nlohmann/json.hpp>
#include <MitkMultilabelExports.h>
namespace tinyxml2
{
class XMLDocument;
class XMLElement;
}
namespace itk
{
class MetaDataDictionary;
}
namespace mitk
{
class LabelSetImage;
const constexpr char* const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type";
const constexpr char* const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints";
const constexpr char* const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type";
const constexpr char* const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints";
const constexpr char* const PROPERTY_KEY_UID = "org_mitk_uid";
/**
* @brief The MultiLabelIOHelper is a static helper class that supports serialization of mitk::LabelSetImage
*
* This class provides static functions for converting mitk::Label into XML and also allows the serialization
* of mitk::LabelSet as presets
*/
class MITKMULTILABEL_EXPORT MultiLabelIOHelper
{
public:
/**
* @brief Saves the mitk::LabelSet configuration of inputImage to presetFilename.
* The preset is stored as "*.lsetp"
* @param presetFilename the filename including the filesystem path
* @param inputImage the input image from which the preset should be generated
* @return true if the serialization was successful and false otherwise
*/
static bool SaveLabelSetImagePreset(const std::string &presetFilename,
const mitk::LabelSetImage *inputImage);
/**
* @brief Loads an existing preset for a mitk::LabelSetImage from presetFilename and applies it to inputImage
* @param presetFilename the filename of the preset including the filesystem path
* @param inputImage the image to which the loaded preset will be applied
* @return true if the deserilization was successful and false otherwise
*/
static bool LoadLabelSetImagePreset(const std::string &presetFilename,
mitk::LabelSetImage *inputImage);
/**
* @brief Creates a mitk::Label from an XML element
* @param labelElem the xml element from which a mitk::Label will be created
* @return the created mitk::Label
*/
static itk::SmartPointer<mitk::Label> LoadLabelFromXMLDocument(const tinyxml2::XMLElement *labelElem);
/**
* @brief Creates an XML element from a mitk::Label
* @param doc
* @param label the mitk::Label from which the xml element will be created
* @return the created XML element
*/
static tinyxml2::XMLElement *GetLabelAsXMLElement(tinyxml2::XMLDocument &doc, const Label *label);
/**
- * @brief Since a mitk::Label is basically a mitk::PropertyList this function coverts the label's properties into
+ * @brief Since a mitk::Label is basically a mitk::PropertyList this function converts the label's properties into
* XML
* @param doc
* @param key the property's key which will be used in the XML element
* @param property the mitk::BaseProperty that should be converted
* @return the created XML element
*/
static tinyxml2::XMLElement *PropertyToXMLElement(tinyxml2::XMLDocument& doc, const std::string &key, const BaseProperty *property);
/**
- * @brief Since a mitk::Label is basically a mitk::PropertyList this function coverts a XML element into a property
+ * @brief Since a mitk::Label is basically a mitk::PropertyList this function converts an XML element into a property
* @param key the property's key
* @param prop the mitk::BaseProperty that will be created
* @param elem the XML elem from which the property will be created
* @return true if the conversion was successful and false otherwise
*/
static bool PropertyFromXMLElement(std::string &key, itk::SmartPointer<mitk::BaseProperty> &prop, const tinyxml2::XMLElement *elem);
/** Helper that extracts the value of a key in a meta dictionary as int.
* If the key does not exist 0 is returned.*/
static int GetIntByKey(const itk::MetaDataDictionary& dic, const std::string& key);
/** Helper that extracts the value of a key in a meta dictionary as string.
* If the key does not exist an empty string is returned.*/
static std::string GetStringByKey(const itk::MetaDataDictionary& dic, const std::string& key);
static nlohmann::json SerializeMultLabelGroupsToJSON(const mitk::LabelSetImage* inputImage);
static std::vector<LabelVector> DeserializeMultiLabelGroupsFromJSON(const nlohmann::json& listOfLabelSets);
static nlohmann::json SerializeLabelToJSON(const Label* label);
static mitk::Label::Pointer DeserializeLabelFromJSON(const nlohmann::json& labelJson);
private:
MultiLabelIOHelper();
};
}
#endif
diff --git a/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp b/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp
index 27a5d47030..4d51a291b7 100644
--- a/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp
+++ b/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp
@@ -1,350 +1,346 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkMultiLabelSegmentationVtkMapper3D.h"
// MITK
#include <mitkDataNode.h>
#include <mitkProperties.h>
#include <mitkVectorProperty.h>
+#include <mitkLabelHighlightGuard.h>
// MITK Rendering
// VTK
#include <vtkImageData.h>
#include <vtkLookupTable.h>
#include <vtkVolume.h>
#include <vtkSmartVolumeMapper.h>
#include <vtkVolumeProperty.h>
#include <vtkSmartPointer.h>
#include <vtkColorTransferFunction.h>
#include <vtkPiecewiseFunction.h>
#include <vtkProperty.h>
namespace
{
itk::ModifiedTimeType PropertyTimeStampIsNewer(const mitk::IPropertyProvider* provider, mitk::BaseRenderer* renderer, const std::string& propName, itk::ModifiedTimeType refMT)
{
const std::string context = renderer != nullptr ? renderer->GetName() : "";
auto prop = provider->GetConstProperty(propName, context);
if (prop != nullptr)
{
return prop->GetTimeStamp() > refMT;
}
return false;
}
}
mitk::MultiLabelSegmentationVtkMapper3D::MultiLabelSegmentationVtkMapper3D()
{
}
mitk::MultiLabelSegmentationVtkMapper3D::~MultiLabelSegmentationVtkMapper3D()
{
}
vtkProp *mitk::MultiLabelSegmentationVtkMapper3D::GetVtkProp(mitk::BaseRenderer *renderer)
{
// return the actor corresponding to the renderer
return m_LSH.GetLocalStorage(renderer)->m_Actors;
}
mitk::MultiLabelSegmentationVtkMapper3D::LocalStorage *mitk::MultiLabelSegmentationVtkMapper3D::GetLocalStorage(
mitk::BaseRenderer *renderer)
{
return m_LSH.GetLocalStorage(renderer);
}
void mitk::MultiLabelSegmentationVtkMapper3D::GenerateLookupTable(mitk::BaseRenderer* renderer)
{
LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer);
mitk::DataNode* node = this->GetDataNode();
auto* image = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
assert(image && image->IsInitialized());
localStorage->m_LabelLookupTable = image->GetLookupTable()->Clone();
auto lookUpTable = localStorage->m_LabelLookupTable->GetVtkLookupTable();
const auto labelValues = image->GetAllLabelValues();
- std::string propertyName = "org.mitk.multilabel.labels.highlighted";
+ mitk::IntVectorProperty::Pointer prop = dynamic_cast<mitk::IntVectorProperty*>(node->GetNonConstProperty(LabelHighlightGuard::PROPERTY_NAME_LABELS_HIGHLIGHTED()));
- mitk::IntVectorProperty::Pointer prop = dynamic_cast<mitk::IntVectorProperty*>(node->GetNonConstProperty(propertyName));
if (nullptr != prop)
{
const auto highlightedLabelValues = prop->GetValue();
+ mitk::BoolProperty::Pointer boolProp = dynamic_cast<mitk::BoolProperty*>(node->GetNonConstProperty(LabelHighlightGuard::PROPERTY_NAME_HIGHLIGHT_INVISIBLE()));
+ bool higlightInvisible = boolProp.IsNull() ? false : boolProp->GetValue();
if (!highlightedLabelValues.empty())
{
auto highlightEnd = highlightedLabelValues.cend();
double rgba[4];
for (const auto& value : labelValues)
{
lookUpTable->GetTableValue(value, rgba);
if (highlightEnd == std::find(highlightedLabelValues.begin(), highlightedLabelValues.end(), value))
{ //make all none highlighted values more transparent
rgba[3] *= 0.05;
}
else
{
- if (rgba[3] != 0)
- { //if highlighted values are visible set them to opaque to pop out
+ if (higlightInvisible || rgba[3] != 0)
+ {
rgba[3] = 1.;
}
- else
- { //if highlighted values are invisible the opacity is increased a bit
- //to give a visual hint that the are highlighted but also invisible.
- //e.g. needed to see a difference if you change the visibility of
- //a highlighted label in the MultiLabelInspector
- rgba[3] = 0.2;
- }
}
lookUpTable->SetTableValue(value, rgba);
}
localStorage->m_LabelLookupTable->Modified(); // need to call modified, since LookupTableProperty seems to be unchanged so no widget-update is
// executed
}
}
const auto nrOfGroups = image->GetNumberOfLayers();
for (unsigned int groupID = 0; groupID < nrOfGroups; ++groupID)
{
localStorage->m_TransferFunctions[groupID] = vtkSmartPointer<vtkColorTransferFunction>::New();
localStorage->m_OpacityTransferFunctions[groupID] = vtkSmartPointer<vtkPiecewiseFunction>::New();
localStorage->m_TransferFunctions[groupID]->AddRGBPoint(0, 0., 0., 1.);
localStorage->m_OpacityTransferFunctions[groupID]->AddPoint(0, 0.);
for (const auto& value : image->GetLabelValuesByGroup(groupID))
{
double* color = lookUpTable->GetTableValue(value);
localStorage->m_TransferFunctions[groupID]->AddRGBPoint(value, color[0], color[1], color[2]);
localStorage->m_OpacityTransferFunctions[groupID]->AddPoint(value, color[3]);
}
}
}
namespace
{
std::vector<mitk::LabelSetImage::GroupIndexType> GetOutdatedGroups(const mitk::MultiLabelSegmentationVtkMapper3D::LocalStorage* ls, const mitk::LabelSetImage* seg)
{
const auto nrOfGroups = seg->GetNumberOfLayers();
std::vector<mitk::LabelSetImage::GroupIndexType> result;
for (mitk::LabelSetImage::GroupIndexType groupID = 0; groupID < nrOfGroups; ++groupID)
{
const auto groupImage = seg->GetGroupImage(groupID);
if (groupImage->GetMTime() > ls->m_LastDataUpdateTime
|| groupImage->GetPipelineMTime() > ls->m_LastDataUpdateTime
|| ls->m_GroupImageIDs.size() <= groupID
|| groupImage != ls->m_GroupImageIDs[groupID])
{
result.push_back(groupID);
}
}
return result;
}
}
void mitk::MultiLabelSegmentationVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer)
{
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
mitk::DataNode *node = this->GetDataNode();
auto *image = dynamic_cast<mitk::LabelSetImage *>(node->GetData());
assert(image && image->IsInitialized());
bool isLookupModified = localStorage->m_LabelLookupTable.IsNull() ||
(localStorage->m_LabelLookupTable->GetMTime() < image->GetLookupTable()->GetMTime()) ||
PropertyTimeStampIsNewer(node, renderer, "org.mitk.multilabel.labels.highlighted", localStorage->m_LabelLookupTable->GetMTime()) ||
+ PropertyTimeStampIsNewer(node, renderer, "org.mitk.multilabel.highlight_invisible", localStorage->m_LabelLookupTable->GetMTime()) ||
PropertyTimeStampIsNewer(node, renderer, "opacity", localStorage->m_LabelLookupTable->GetMTime());
auto outdatedGroups = GetOutdatedGroups(localStorage, image);
bool isGeometryModified = (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) ||
(localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime());
if (isGeometryModified)
{
//if geometry is outdated all groups need regeneration
outdatedGroups.resize(image->GetNumberOfLayers());
std::iota(outdatedGroups.begin(), outdatedGroups.end(), 0);
}
if (!outdatedGroups.empty())
{
auto hasValidContent = this->GenerateVolumeMapping(renderer, outdatedGroups);
if (!hasValidContent) return;
}
if (isLookupModified)
{
this->GenerateLookupTable(renderer);
}
if (isLookupModified)
{
//if lookup table is modified all groups need a new color mapping
outdatedGroups.resize(image->GetNumberOfLayers());
std::iota(outdatedGroups.begin(), outdatedGroups.end(), 0);
}
for (const auto groupID : outdatedGroups)
{
localStorage->m_LayerVolumes[groupID]->GetProperty()->SetColor(localStorage->m_TransferFunctions[groupID]);
localStorage->m_LayerVolumes[groupID]->GetProperty()->SetScalarOpacity(localStorage->m_OpacityTransferFunctions[groupID]);
localStorage->m_LayerVolumes[groupID]->Update();
}
}
bool mitk::MultiLabelSegmentationVtkMapper3D::GenerateVolumeMapping(mitk::BaseRenderer* renderer, const std::vector<mitk::LabelSetImage::GroupIndexType>& outdatedGroupIDs)
{
LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer);
mitk::DataNode* node = this->GetDataNode();
auto* image = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
assert(image && image->IsInitialized());
image->Update();
const auto numberOfGroups = image->GetNumberOfLayers();
if (numberOfGroups != localStorage->m_NumberOfGroups)
{
if (numberOfGroups > localStorage->m_NumberOfGroups)
{
for (unsigned int groupID = localStorage->m_NumberOfGroups; groupID < numberOfGroups; ++groupID)
{
localStorage->m_GroupImageIDs.push_back(nullptr);
localStorage->m_LayerImages.push_back(vtkSmartPointer<vtkImageData>::New());
localStorage->m_LayerVolumeMappers.push_back(vtkSmartPointer<vtkSmartVolumeMapper>::New());
localStorage->m_LayerVolumes.push_back(vtkSmartPointer<vtkVolume>::New());
localStorage->m_TransferFunctions.push_back(vtkSmartPointer<vtkColorTransferFunction>::New());
localStorage->m_OpacityTransferFunctions.push_back(vtkSmartPointer<vtkPiecewiseFunction>::New());
}
}
else
{
localStorage->m_GroupImageIDs.resize(numberOfGroups);
localStorage->m_LayerImages.resize(numberOfGroups);
localStorage->m_LayerVolumeMappers.resize(numberOfGroups);
localStorage->m_LayerVolumes.resize(numberOfGroups);
localStorage->m_TransferFunctions.resize(numberOfGroups);
localStorage->m_OpacityTransferFunctions.resize(numberOfGroups);
}
localStorage->m_NumberOfGroups = numberOfGroups;
localStorage->m_Actors = vtkSmartPointer<vtkPropAssembly>::New();
for (unsigned int groupID = 0; groupID < numberOfGroups; ++groupID)
{
localStorage->m_Actors->AddPart(localStorage->m_LayerVolumes[groupID]);
}
}
for (const auto groupID : outdatedGroupIDs)
{
const auto groupImage = image->GetGroupImage(groupID);
localStorage->m_GroupImageIDs[groupID] = groupImage;
localStorage->m_LayerImages[groupID] = groupImage->GetVtkImageData(this->GetTimestep());
//need to recreate the volumeMapper because otherwise label data was still rendered even
//if a label was removed. There must be a cleaner way to do it. Exchanging the whole mapper
//is a ugly workaround for now.
localStorage->m_LayerVolumeMappers[groupID] = vtkSmartPointer<vtkSmartVolumeMapper>::New();
localStorage->m_LayerVolumeMappers[groupID]->SetInputData(localStorage->m_LayerImages[groupID]);
localStorage->m_LayerVolumes[groupID]->GetProperty()->ShadeOn();
localStorage->m_LayerVolumes[groupID]->GetProperty()->SetDiffuse(1.0);
localStorage->m_LayerVolumes[groupID]->GetProperty()->SetAmbient(0.4);
localStorage->m_LayerVolumes[groupID]->GetProperty()->SetSpecular(0.2);
localStorage->m_LayerVolumes[groupID]->GetProperty()->SetInterpolationTypeToNearest();
localStorage->m_LayerVolumes[groupID]->SetMapper(localStorage->m_LayerVolumeMappers[groupID]);
}
localStorage->m_LastDataUpdateTime.Modified();
return true;
}
void mitk::MultiLabelSegmentationVtkMapper3D::Update(mitk::BaseRenderer *renderer)
{
bool visible = true;
bool has3Dvisualize = true;
const DataNode *node = this->GetDataNode();
node->GetVisibility(visible, renderer, "visible");
node->GetBoolProperty("multilabel.3D.visualize", has3Dvisualize, renderer);
if (!visible || !has3Dvisualize)
return;
auto *image = dynamic_cast<mitk::LabelSetImage *>(node->GetData());
if (image == nullptr || image->IsInitialized() == false)
return;
// Calculate time step of the image data for the specified renderer (integer value)
this->CalculateTimeStep(renderer);
// Check if time step is valid
const TimeGeometry *dataTimeGeometry = image->GetTimeGeometry();
if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) ||
(!dataTimeGeometry->IsValidTimeStep(this->GetTimestep())))
{
return;
}
image->UpdateOutputInformation();
LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer);
// check if something important has changed and we need to re-render
if (localStorage->m_LabelLookupTable.IsNull() ||
(localStorage->m_LabelLookupTable->GetMTime() < image->GetLookupTable()->GetMTime()) ||
(localStorage->m_LastDataUpdateTime < image->GetMTime()) ||
(localStorage->m_LastDataUpdateTime < image->GetPipelineMTime()) ||
(localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) ||
(localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) ||
(localStorage->m_LastPropertyUpdateTime < node->GetPropertyList()->GetMTime()) ||
(localStorage->m_LastPropertyUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ||
(localStorage->m_LastPropertyUpdateTime < image->GetPropertyList()->GetMTime()))
{
this->GenerateDataForRenderer(renderer);
localStorage->m_LastPropertyUpdateTime.Modified();
}
}
void mitk::MultiLabelSegmentationVtkMapper3D::SetDefaultProperties(mitk::DataNode *node,
mitk::BaseRenderer *renderer,
bool overwrite)
{
Superclass::SetDefaultProperties(node, renderer, overwrite);
// add/replace the following properties
node->SetProperty("multilabel.3D.visualize", BoolProperty::New(false), renderer);
}
mitk::MultiLabelSegmentationVtkMapper3D::LocalStorage::~LocalStorage()
{
}
mitk::MultiLabelSegmentationVtkMapper3D::LocalStorage::LocalStorage()
{
// Do as much actions as possible in here to avoid double executions.
m_Actors = vtkSmartPointer<vtkPropAssembly>::New();
m_NumberOfGroups = 0;
}
diff --git a/Modules/Multilabel/mitkSegmentationTaskList.cpp b/Modules/Multilabel/mitkSegmentationTaskList.cpp
index 1755207f6a..97523730e6 100644
--- a/Modules/Multilabel/mitkSegmentationTaskList.cpp
+++ b/Modules/Multilabel/mitkSegmentationTaskList.cpp
@@ -1,196 +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.
============================================================================*/
#include "mitkSegmentationTaskList.h"
#include <mitkIOUtil.h>
#include <mitkProperties.h>
mitk::SegmentationTaskList::Task::Task()
: m_Defaults(nullptr)
{
}
mitk::SegmentationTaskList::Task::~Task()
{
}
void mitk::SegmentationTaskList::Task::SetDefaults(const Task* defaults)
{
m_Defaults = defaults;
}
mitk::SegmentationTaskList::SegmentationTaskList()
{
// A base data cannot be serialized if empty. To be not considered empty its
// geometry must consist of at least one time step. However, a segmentation
- // task would then appear as invisible spacial object in a scene. This can
+ // task would then appear as invisible spatial object in a scene. This can
// be prevented by excluding it from the scene's bounding box calculations.
this->GetTimeGeometry()->Expand(1);
this->SetProperty("includeInBoundingBox", BoolProperty::New(false));
}
mitk::SegmentationTaskList::SegmentationTaskList(const Self& other)
: BaseData(other)
{
}
mitk::SegmentationTaskList::~SegmentationTaskList()
{
}
size_t mitk::SegmentationTaskList::GetNumberOfTasks() const
{
return m_Tasks.size();
}
size_t mitk::SegmentationTaskList::AddTask(const Task& subtask)
{
m_Tasks.push_back(subtask);
m_Tasks.back().SetDefaults(&m_Defaults);
return m_Tasks.size() - 1;
}
const mitk::SegmentationTaskList::Task* mitk::SegmentationTaskList::GetTask(size_t index) const
{
return &m_Tasks.at(index);
}
mitk::SegmentationTaskList::Task* mitk::SegmentationTaskList::GetTask(size_t index)
{
return &m_Tasks.at(index);
}
const mitk::SegmentationTaskList::Task& mitk::SegmentationTaskList::GetDefaults() const
{
return m_Defaults;
}
void mitk::SegmentationTaskList::SetDefaults(const Task& defaults)
{
m_Defaults = defaults;
for (auto& subtask : m_Tasks)
subtask.SetDefaults(&m_Defaults);
}
bool mitk::SegmentationTaskList::IsDone() const
{
for (size_t i = 0; i < m_Tasks.size(); ++i)
{
if (!this->IsDone(i))
return false;
}
return true;
}
bool mitk::SegmentationTaskList::IsDone(size_t index) const
{
- return std::filesystem::exists(this->GetAbsolutePath(m_Tasks.at(index).GetResult()));
+ return fs::exists(this->GetAbsolutePath(m_Tasks.at(index).GetResult()));
}
-std::filesystem::path mitk::SegmentationTaskList::GetInputLocation() const
+fs::path mitk::SegmentationTaskList::GetInputLocation() const
{
std::string inputLocation;
this->GetPropertyList()->GetStringProperty("MITK.IO.reader.inputlocation", inputLocation);
return !inputLocation.empty()
- ? std::filesystem::path(inputLocation).lexically_normal()
- : std::filesystem::path();
+#ifdef MITK_HAS_FILESYSTEM
+ ? fs::path(inputLocation).lexically_normal()
+#else
+ ? fs::path(inputLocation)
+#endif
+ : fs::path();
}
-std::filesystem::path mitk::SegmentationTaskList::GetBasePath() const
+fs::path mitk::SegmentationTaskList::GetBasePath() const
{
return this->GetInputLocation().remove_filename();
}
-std::filesystem::path mitk::SegmentationTaskList::GetAbsolutePath(const std::filesystem::path& path) const
+fs::path mitk::SegmentationTaskList::GetAbsolutePath(const fs::path& path) const
{
if (path.empty())
return path;
+#ifdef MITK_HAS_FILESYSTEM
auto normalizedPath = path.lexically_normal();
+#else
+ auto normalizedPath = path;
+#endif
return !normalizedPath.is_absolute()
? this->GetBasePath() / normalizedPath
: normalizedPath;
}
-std::filesystem::path mitk::SegmentationTaskList::GetInterimPath(const std::filesystem::path& path) const
+fs::path mitk::SegmentationTaskList::GetInterimPath(const fs::path& path) const
{
if (path.empty() || !path.has_filename())
return path;
auto interimPath = path;
return interimPath.replace_extension(".interim" + path.extension().string());
}
void mitk::SegmentationTaskList::SaveTask(size_t index, const BaseData* segmentation, bool saveAsInterimResult)
{
if (segmentation == nullptr)
return;
auto path = this->GetAbsolutePath(this->GetResult(index));
auto interimPath = this->GetInterimPath(path);
- if (std::filesystem::exists(path))
+ if (fs::exists(path))
saveAsInterimResult = false;
IOUtil::Save(segmentation, saveAsInterimResult
? interimPath.string()
: path.string());
- if (!saveAsInterimResult && std::filesystem::exists(interimPath))
+ if (!saveAsInterimResult && fs::exists(interimPath))
{
std::error_code ec;
- std::filesystem::remove(interimPath, ec);
+ fs::remove(interimPath, ec);
}
}
std::vector<mitk::SegmentationTaskList::Task>::const_iterator mitk::SegmentationTaskList::begin() const
{
return m_Tasks.begin();
}
std::vector<mitk::SegmentationTaskList::Task>::const_iterator mitk::SegmentationTaskList::end() const
{
return m_Tasks.end();
}
std::vector<mitk::SegmentationTaskList::Task>::iterator mitk::SegmentationTaskList::begin()
{
return m_Tasks.begin();
}
std::vector<mitk::SegmentationTaskList::Task>::iterator mitk::SegmentationTaskList::end()
{
return m_Tasks.end();
}
void mitk::SegmentationTaskList::SetRequestedRegionToLargestPossibleRegion()
{
}
bool mitk::SegmentationTaskList::RequestedRegionIsOutsideOfTheBufferedRegion()
{
return false;
}
bool mitk::SegmentationTaskList::VerifyRequestedRegion()
{
return true;
}
void mitk::SegmentationTaskList::SetRequestedRegion(const itk::DataObject*)
{
}
diff --git a/Modules/Multilabel/mitkSegmentationTaskList.h b/Modules/Multilabel/mitkSegmentationTaskList.h
index d57dfd555e..9a90f1210d 100644
--- a/Modules/Multilabel/mitkSegmentationTaskList.h
+++ b/Modules/Multilabel/mitkSegmentationTaskList.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 mitkSegmentationTaskList_h
#define mitkSegmentationTaskList_h
#include <mitkBaseData.h>
#include <mitkSegmentationTaskListMacros.h>
#include <MitkMultilabelExports.h>
-#include <filesystem>
+#include <mitkFileSystem.h>
#include <optional>
namespace mitk
{
/** \brief A list of segmentation tasks.
*
* See \ref MITKSegmentationTaskListsPage for more information.
*/
class MITKMULTILABEL_EXPORT SegmentationTaskList : public BaseData
{
public:
class MITKMULTILABEL_EXPORT Task
{
public:
Task();
~Task();
void SetDefaults(const Task* defaults);
mitkSegmentationTaskValueMacro(std::string, Name)
mitkSegmentationTaskValueMacro(std::string, Description)
- mitkSegmentationTaskValueMacro(std::filesystem::path, Image)
- mitkSegmentationTaskValueMacro(std::filesystem::path, Segmentation)
+ mitkSegmentationTaskValueMacro(fs::path, Image)
+ mitkSegmentationTaskValueMacro(fs::path, Segmentation)
mitkSegmentationTaskValueMacro(std::string, LabelName)
- mitkSegmentationTaskValueMacro(std::filesystem::path, LabelNameSuggestions)
- mitkSegmentationTaskValueMacro(std::filesystem::path, Preset)
- mitkSegmentationTaskValueMacro(std::filesystem::path, Result)
+ mitkSegmentationTaskValueMacro(fs::path, LabelNameSuggestions)
+ mitkSegmentationTaskValueMacro(fs::path, Preset)
+ mitkSegmentationTaskValueMacro(fs::path, Result)
mitkSegmentationTaskValueMacro(bool, Dynamic)
private:
const Task* m_Defaults;
};
mitkClassMacro(SegmentationTaskList, BaseData)
itkFactorylessNewMacro(Self)
itkCloneMacro(Self)
mitkSegmentationTaskListValueMacro(std::string, Name)
mitkSegmentationTaskListValueMacro(std::string, Description)
- mitkSegmentationTaskListValueMacro(std::filesystem::path, Image)
- mitkSegmentationTaskListValueMacro(std::filesystem::path, Segmentation)
+ mitkSegmentationTaskListValueMacro(fs::path, Image)
+ mitkSegmentationTaskListValueMacro(fs::path, Segmentation)
mitkSegmentationTaskListValueMacro(std::string, LabelName)
- mitkSegmentationTaskListValueMacro(std::filesystem::path, LabelNameSuggestions)
- mitkSegmentationTaskListValueMacro(std::filesystem::path, Preset)
- mitkSegmentationTaskListValueMacro(std::filesystem::path, Result)
+ mitkSegmentationTaskListValueMacro(fs::path, LabelNameSuggestions)
+ mitkSegmentationTaskListValueMacro(fs::path, Preset)
+ mitkSegmentationTaskListValueMacro(fs::path, Result)
mitkSegmentationTaskListValueMacro(bool, Dynamic)
size_t GetNumberOfTasks() const;
size_t AddTask(const Task& subtask);
const Task* GetTask(size_t index) const;
Task* GetTask(size_t index);
const Task& GetDefaults() const;
void SetDefaults(const Task& defaults);
bool IsDone() const;
bool IsDone(size_t index) const;
- std::filesystem::path GetInputLocation() const;
- std::filesystem::path GetBasePath() const;
- std::filesystem::path GetAbsolutePath(const std::filesystem::path& path) const;
- std::filesystem::path GetInterimPath(const std::filesystem::path& path) const;
+ fs::path GetInputLocation() const;
+ fs::path GetBasePath() const;
+ fs::path GetAbsolutePath(const fs::path& path) const;
+ fs::path GetInterimPath(const fs::path& path) const;
void SaveTask(size_t index, const BaseData* segmentation, bool saveAsInterimResult = false);
std::vector<Task>::const_iterator begin() const;
std::vector<Task>::const_iterator end() const;
std::vector<Task>::iterator begin();
std::vector<Task>::iterator end();
void SetRequestedRegionToLargestPossibleRegion() override;
bool RequestedRegionIsOutsideOfTheBufferedRegion() override;
bool VerifyRequestedRegion() override;
void SetRequestedRegion(const itk::DataObject*) override;
protected:
mitkCloneMacro(Self)
SegmentationTaskList();
SegmentationTaskList(const Self& other);
~SegmentationTaskList() override;
private:
Task m_Defaults;
std::vector<Task> m_Tasks;
};
}
#endif
diff --git a/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp b/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp
index d871108746..31a8b46481 100644
--- a/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp
+++ b/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp
@@ -1,890 +1,890 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.
============================================================================*/
// std includes
#include <string>
// itk includes
#include "itksys/SystemTools.hxx"
// CTK includes
#include "mitkCommandLineParser.h"
// MITK includes
#include <mitkIOUtil.h>
#include <mitkPreferenceListReaderOptionsFunctor.h>
#include <mitkImageTimeSelector.h>
#include <mitkImageCast.h>
#include <mitkPixelBasedParameterFitImageGenerator.h>
#include <mitkROIBasedParameterFitImageGenerator.h>
#include <mitkModelFitInfo.h>
#include <mitkModelFitCmdAppsHelper.h>
#include <mitkMaskedDynamicImageStatisticsGenerator.h>
#include <mitkLevenbergMarquardtModelFitFunctor.h>
#include <mitkNormalizedSumOfSquaredDifferencesFitCostFunction.h>
#include <mitkExtractTimeGrid.h>
#include <mitkAterialInputFunctionGenerator.h>
#include <mitkModelFitResultHelper.h>
#include <mitkDescriptivePharmacokineticBrixModelParameterizer.h>
#include <mitkDescriptivePharmacokineticBrixModelValueBasedParameterizer.h>
#include <mitkDescriptivePharmacokineticBrixModelFactory.h>
#include <mitkExtendedToftsModelParameterizer.h>
#include <mitkExtendedToftsModelFactory.h>
#include <mitkTwoCompartmentExchangeModelParameterizer.h>
#include <mitkTwoCompartmentExchangeModelFactory.h>
#include <mitkThreeStepLinearModelParameterizer.h>
#include <mitkThreeStepLinearModelFactory.h>
#include <mitkTwoStepLinearModelParameterizer.h>
#include <mitkTwoStepLinearModelFactory.h>
#include <mitkModelFactoryBase.h>
std::string inFilename;
std::string outFileName;
std::string maskFileName;
std::string aifMaskFileName;
std::string aifImageFileName;
mitk::Image::Pointer image;
mitk::Image::Pointer mask;
mitk::Image::Pointer aifImage;
mitk::Image::Pointer aifMask;
bool useConstraints(false);
bool verbose(false);
bool roibased(false);
bool preview(false);
std::string modelName;
float aifHematocritLevel(0);
float brixInjectionTime(0);
const std::string MODEL_NAME_2SL = "2SL";
const std::string MODEL_NAME_3SL = "3SL";
const std::string MODEL_NAME_descriptive = "descriptive";
const std::string MODEL_NAME_tofts = "tofts";
const std::string MODEL_NAME_2CX = "2CX";
void onFitEvent(::itk::Object* caller, const itk::EventObject & event, void* /*data*/)
{
itk::ProgressEvent progressEvent;
if (progressEvent.CheckEvent(&event))
{
mitk::ParameterFitImageGeneratorBase* castedReporter = dynamic_cast<mitk::ParameterFitImageGeneratorBase*>(caller);
std::cout <<castedReporter->GetProgress()*100 << "% ";
}
}
void setupParser(mitkCommandLineParser& parser)
{
// set general information about your MiniApp
parser.setCategory("Dynamic Data Analysis Tools");
parser.setTitle("MR Perfusion");
parser.setDescription("MiniApp that allows to fit MRI perfusion models and generates the according parameter maps. IMPORTANT!!!: The app assumes that the input images (signal and AIF) are concentration images. If your images do not hold this assumption, convert the image date before using this app (e.g. by using the signal-to-concentration-converter mini app.");
parser.setContributor("DKFZ MIC");
//! [create parser]
//! [add arguments]
// how should arguments be prefixed
parser.setArgumentPrefix("--", "-");
// add each argument, unless specified otherwise each argument is optional
// see mitkCommandLineParser::addArgument for more information
parser.beginGroup("Model parameters");
parser.addArgument(
"model", "l", mitkCommandLineParser::String, "Model function", "Model that should be used to fit the concentration signal. Options are: \""+MODEL_NAME_descriptive+"\" (descriptive pharmacokinetic Brix model),\"" + MODEL_NAME_2SL + "\" (two step linear model),\""+MODEL_NAME_3SL+"\" (three step linear model), \""+MODEL_NAME_tofts+"\" (extended tofts model) or \""+MODEL_NAME_2CX+"\" (two compartment exchange model).", us::Any(std::string(MODEL_NAME_tofts)));
parser.addArgument(
"injectiontime", "j", mitkCommandLineParser::Float, "Injection time [min]", "Injection time of the bolus. This information is needed for the descriptive pharmacokinetic Brix model.", us::Any());
parser.endGroup();
parser.beginGroup("Required I/O parameters");
parser.addArgument(
"input", "i", mitkCommandLineParser::File, "Input file", "input 3D+t image file", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output",
"o",
mitkCommandLineParser::File,
"Output file template",
"where to save the output parameter images. The specified path will be used as template to determine the format (via extension) and the name \"root\". For each parameter a suffix will be added to the name.",
us::Any(),
false, false, false, mitkCommandLineParser::Output);
parser.endGroup();
parser.beginGroup("AIF parameters");
parser.addArgument(
"aifmask", "n", mitkCommandLineParser::File, "AIF mask file", "Mask that defines the spatial image region that should be used as AIF for models that need one. Must have the same geometry as the AIF input image!", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument(
- "aifimage", "a", mitkCommandLineParser::File, "AIF image file", "3D+t image that defines the image that containes the AIF signal. If this flag is not set and the model needs a AIF, the CLI will assume that the AIF is encoded in the normal image. Must have the same geometry as the AIF mask!", us::Any(), true, false, false, mitkCommandLineParser::Input);
+ "aifimage", "a", mitkCommandLineParser::File, "AIF image file", "3D+t image that defines the image that contains the AIF signal. If this flag is not set and the model needs a AIF, the CLI will assume that the AIF is encoded in the normal image. Must have the same geometry as the AIF mask!", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument(
"hematocrit", "h", mitkCommandLineParser::Float, "Hematocrit Level", "Value needed for correct AIF computation. Only needed if model needs an AIF. Default value is 0.45.", us::Any(0.45));
parser.endGroup();
parser.beginGroup("Optional parameters");
parser.addArgument(
"mask", "m", mitkCommandLineParser::File, "Mask file", "Mask that defines the spatial image region that should be fitted. Must have the same geometry as the input image!", us::Any(), true, false, false, mitkCommandLineParser::Input);
parser.addArgument(
"verbose", "v", mitkCommandLineParser::Bool, "Verbose Output", "Whether to produce verbose output");
parser.addArgument(
- "roibased", "r", mitkCommandLineParser::Bool, "Roi based fitting", "Will compute a mean intesity signal over the ROI before fitting it. If this mode is used a mask must be specified.");
+ "roibased", "r", mitkCommandLineParser::Bool, "Roi based fitting", "Will compute a mean intensity signal over the ROI before fitting it. If this mode is used a mask must be specified.");
parser.addArgument(
- "constraints", "c", mitkCommandLineParser::Bool, "Constraints", "Indicates if constraints should be used for the fitting (if flag is set the default contraints will be used.).", us::Any(false));
+ "constraints", "c", mitkCommandLineParser::Bool, "Constraints", "Indicates if constraints should be used for the fitting (if flag is set the default constraints will be used.).", us::Any(false));
parser.addArgument(
"preview", "p", mitkCommandLineParser::Bool, "Preview outputs", "The application previews the outputs (filename, type) it would produce with the current settings.");
parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text");
parser.endGroup();
//! [add arguments]
}
bool configureApplicationSettings(std::map<std::string, us::Any> parsedArgs)
{
if (parsedArgs.size() == 0)
return false;
// parse, cast and set required arguments
modelName = MODEL_NAME_tofts;
if (parsedArgs.count("model"))
{
modelName = us::any_cast<std::string>(parsedArgs["model"]);
}
inFilename = us::any_cast<std::string>(parsedArgs["input"]);
outFileName = us::any_cast<std::string>(parsedArgs["output"]);
if (parsedArgs.count("mask"))
{
maskFileName = us::any_cast<std::string>(parsedArgs["mask"]);
}
if (parsedArgs.count("aifimage"))
{
aifImageFileName = us::any_cast<std::string>(parsedArgs["aifimage"]);
}
if (parsedArgs.count("aifmask"))
{
aifMaskFileName = us::any_cast<std::string>(parsedArgs["aifmask"]);
}
verbose = false;
if (parsedArgs.count("verbose"))
{
verbose = us::any_cast<bool>(parsedArgs["verbose"]);
}
preview = false;
if (parsedArgs.count("preview"))
{
preview = us::any_cast<bool>(parsedArgs["preview"]);
}
roibased = false;
if (parsedArgs.count("roibased"))
{
roibased = us::any_cast<bool>(parsedArgs["roibased"]);
}
useConstraints = false;
if (parsedArgs.count("constraints"))
{
useConstraints = us::any_cast<bool>(parsedArgs["constraints"]);
}
aifHematocritLevel = 0.45;
if (parsedArgs.count("hematocrit"))
{
aifHematocritLevel = us::any_cast<float>(parsedArgs["hematocrit"]);
}
brixInjectionTime = 0.0;
if (parsedArgs.count("injectiontime"))
{
brixInjectionTime = us::any_cast<float>(parsedArgs["injectiontime"]);
}
return true;
}
mitk::ModelFitFunctorBase::Pointer createDefaultFitFunctor(
const mitk::ModelParameterizerBase* parameterizer, const mitk::ModelFactoryBase* modelFactory)
{
mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor =
mitk::LevenbergMarquardtModelFitFunctor::New();
mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::Pointer chi2 =
mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::New();
fitFunctor->RegisterEvaluationParameter("Chi^2", chi2);
if (useConstraints)
{
fitFunctor->SetConstraintChecker(modelFactory->CreateDefaultConstraints().GetPointer());
}
mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel();
::itk::LevenbergMarquardtOptimizer::ScalesType scales;
scales.SetSize(refModel->GetNumberOfParameters());
scales.Fill(1.0);
fitFunctor->SetScales(scales);
fitFunctor->SetDebugParameterMaps(true);
return fitFunctor.GetPointer();
}
/**Helper that ensures that the mask (if it exists) is always 3D image. If the mask is originally an 4D image, the first
time step will be used.*/
mitk::Image::Pointer getMask3D()
{
mitk::Image::Pointer result;
if (mask.IsNotNull())
{
result = mask;
//mask settings
if (mask->GetTimeSteps() > 1)
{
MITK_INFO << "Selected mask has multiple timesteps. Only use first timestep to mask model fit.";
mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New();
maskedImageTimeSelector->SetInput(mask);
maskedImageTimeSelector->SetTimeNr(0);
maskedImageTimeSelector->UpdateLargestPossibleRegion();
result = maskedImageTimeSelector->GetOutput();
}
}
return result;
}
void getAIF(mitk::AIFBasedModelBase::AterialInputFunctionType& aif,
mitk::AIFBasedModelBase::AterialInputFunctionType& aifTimeGrid)
{
if (aifMask.IsNotNull())
{
aif.clear();
aifTimeGrid.clear();
mitk::AterialInputFunctionGenerator::Pointer aifGenerator =
mitk::AterialInputFunctionGenerator::New();
//Hematocrit level
aifGenerator->SetHCL(aifHematocritLevel);
std::cout << "AIF hematocrit level: " << aifHematocritLevel << std::endl;
mitk::Image::Pointer selectedAIFMask = aifMask;
//mask settings
if (aifMask->GetTimeSteps() > 1)
{
MITK_INFO << "Selected AIF mask has multiple timesteps. Only use first timestep to mask model fit.";
mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New();
maskedImageTimeSelector->SetInput(aifMask);
maskedImageTimeSelector->SetTimeNr(0);
maskedImageTimeSelector->UpdateLargestPossibleRegion();
aifMask = maskedImageTimeSelector->GetOutput();
}
aifGenerator->SetMask(aifMask);
mitk::Image::Pointer selectedAIFImage = image;
//image settings
if (aifImage.IsNotNull())
{
selectedAIFImage = aifImage;
}
aifGenerator->SetDynamicImage(selectedAIFImage);
aif = aifGenerator->GetAterialInputFunction();
aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid();
}
else
{
mitkThrow() << "Cannot generate AIF. AIF mask was not specified or correctly loaded.";
}
}
void generateDescriptiveBrixModel_PixelBased(mitk::modelFit::ModelFitInfo::Pointer&
modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::PixelBasedParameterFitImageGenerator::New();
mitk::DescriptivePharmacokineticBrixModelParameterizer::Pointer modelParameterizer =
mitk::DescriptivePharmacokineticBrixModelParameterizer::New();
mitk::Image::Pointer mask3D = getMask3D();
//Model configuration (static parameters) can be done now
modelParameterizer->SetTau(brixInjectionTime);
std::cout << "Injection time [min]: " << brixInjectionTime << std::endl;
mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput(image);
imageTimeSelector->SetTimeNr(0);
imageTimeSelector->UpdateLargestPossibleRegion();
mitk::DescriptivePharmacokineticBrixModelParameterizer::BaseImageType::Pointer baseImage;
mitk::CastToItkImage(imageTimeSelector->GetOutput(), baseImage);
modelParameterizer->SetBaseImage(baseImage);
//Specify fitting strategy and criterion parameters
mitk::ModelFactoryBase::Pointer factory = mitk::DescriptivePharmacokineticBrixModelFactory::New().GetPointer();
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
std::string roiUID = "";
if (mask3D.IsNotNull())
{
fitGenerator->SetMask(mask3D);
roiUID = mask->GetUID();
}
fitGenerator->SetDynamicImage(image);
fitGenerator->SetFitFunctor(fitFunctor);
generator = fitGenerator.GetPointer();
//Create model info
modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
image, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), roiUID);
}
void generateDescriptiveBrixModel_ROIBased(mitk::modelFit::ModelFitInfo::Pointer&
modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::Image::Pointer mask3D = getMask3D();
if (mask3D.IsNull())
{
return;
}
mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::ROIBasedParameterFitImageGenerator::New();
mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::Pointer modelParameterizer =
mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::New();
//Compute ROI signal
mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator =
mitk::MaskedDynamicImageStatisticsGenerator::New();
signalGenerator->SetMask(mask3D);
signalGenerator->SetDynamicImage(image);
signalGenerator->Generate();
mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean();
//Model configuration (static parameters) can be done now
modelParameterizer->SetTau(brixInjectionTime);
std::cout << "Injection time [min]: " << brixInjectionTime << std::endl;
modelParameterizer->SetBaseValue(roiSignal[0]);
//Specify fitting strategy and criterion parameters
mitk::ModelFactoryBase::Pointer factory = mitk::DescriptivePharmacokineticBrixModelFactory::New().GetPointer();
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
fitGenerator->SetMask(mask3D);
fitGenerator->SetFitFunctor(fitFunctor);
fitGenerator->SetSignal(roiSignal);
fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image));
generator = fitGenerator.GetPointer();
std::string roiUID = mask->GetUID();
//Create model info
modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
image, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), roiUID);
mitk::ScalarListLookupTable::ValueType infoSignal;
for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos =
roiSignal.begin(); pos != roiSignal.end(); ++pos)
{
infoSignal.push_back(*pos);
}
modelFitInfo->inputData.SetTableValue("ROI", infoSignal);
}
template <typename TParameterizer, typename TFactory>
void GenerateLinearModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer&
modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::PixelBasedParameterFitImageGenerator::New();
typename TParameterizer::Pointer modelParameterizer = TParameterizer::New();
mitk::Image::Pointer mask3D = getMask3D();
//Specify fitting strategy and criterion parameters
mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer();
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
std::string roiUID = "";
if (mask3D.IsNotNull())
{
fitGenerator->SetMask(mask3D);
roiUID = mask->GetUID();
}
fitGenerator->SetDynamicImage(image);
fitGenerator->SetFitFunctor(fitFunctor);
generator = fitGenerator.GetPointer();
//Create model info
modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
image, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), roiUID);
}
template <typename TParameterizer, typename TFactory>
void GenerateLinearModelFit_ROIBased(mitk::modelFit::ModelFitInfo::Pointer&
modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::Image::Pointer mask3D = getMask3D();
if (mask3D.IsNull())
{
return;
}
mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::ROIBasedParameterFitImageGenerator::New();
typename TParameterizer::Pointer modelParameterizer = TParameterizer::New();
//Compute ROI signal
mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator =
mitk::MaskedDynamicImageStatisticsGenerator::New();
signalGenerator->SetMask(mask3D);
signalGenerator->SetDynamicImage(image);
signalGenerator->Generate();
mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean();
//Specify fitting strategy and criterion parameters
mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer();
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
fitGenerator->SetMask(mask3D);
fitGenerator->SetFitFunctor(fitFunctor);
fitGenerator->SetSignal(roiSignal);
fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image));
generator = fitGenerator.GetPointer();
std::string roiUID = mask->GetUID();
//Create model info
modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
image, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), roiUID);
mitk::ScalarListLookupTable::ValueType infoSignal;
for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos =
roiSignal.begin(); pos != roiSignal.end(); ++pos)
{
infoSignal.push_back(*pos);
}
modelFitInfo->inputData.SetTableValue("ROI", infoSignal);
}
template <typename TParameterizer, typename TFactory>
void generateAIFbasedModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer&
modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::PixelBasedParameterFitImageGenerator::New();
typename TParameterizer::Pointer modelParameterizer =
TParameterizer::New();
mitk::AIFBasedModelBase::AterialInputFunctionType aif;
mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid;
getAIF(aif, aifTimeGrid);
modelParameterizer->SetAIF(aif);
modelParameterizer->SetAIFTimeGrid(aifTimeGrid);
mitk::Image::Pointer mask3D = getMask3D();
//Specify fitting strategy and criterion parameters
mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer();
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
std::string roiUID = "";
if (mask3D.IsNotNull())
{
fitGenerator->SetMask(mask3D);
roiUID = mask->GetUID();
}
fitGenerator->SetDynamicImage(image);
fitGenerator->SetFitFunctor(fitFunctor);
generator = fitGenerator.GetPointer();
//Create model info
modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
image, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(),
roiUID);
mitk::ScalarListLookupTable::ValueType infoSignal;
for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos =
aif.begin(); pos != aif.end(); ++pos)
{
infoSignal.push_back(*pos);
}
modelFitInfo->inputData.SetTableValue("AIF", infoSignal);
}
template <typename TParameterizer, typename TFactory>
void generateAIFbasedModelFit_ROIBased(
mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo,
mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
mitk::Image::Pointer mask3D = getMask3D();
if (mask3D.IsNull())
{
return;
}
mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator =
mitk::ROIBasedParameterFitImageGenerator::New();
typename TParameterizer::Pointer modelParameterizer =
TParameterizer::New();
mitk::AIFBasedModelBase::AterialInputFunctionType aif;
mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid;
getAIF(aif, aifTimeGrid);
modelParameterizer->SetAIF(aif);
modelParameterizer->SetAIFTimeGrid(aifTimeGrid);
//Compute ROI signal
mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator =
mitk::MaskedDynamicImageStatisticsGenerator::New();
signalGenerator->SetMask(mask3D);
signalGenerator->SetDynamicImage(image);
signalGenerator->Generate();
mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean();
//Specify fitting strategy and criterion parameters
mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer();
mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory);
//Parametrize fit generator
fitGenerator->SetModelParameterizer(modelParameterizer);
fitGenerator->SetMask(mask3D);
fitGenerator->SetFitFunctor(fitFunctor);
fitGenerator->SetSignal(roiSignal);
fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image));
generator = fitGenerator.GetPointer();
std::string roiUID = mask->GetUID();
//Create model info
modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer,
image, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(),
roiUID);
mitk::ScalarListLookupTable::ValueType infoSignal;
for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos =
roiSignal.begin(); pos != roiSignal.end(); ++pos)
{
infoSignal.push_back(*pos);
}
modelFitInfo->inputData.SetTableValue("ROI", infoSignal);
infoSignal.clear();
for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos =
aif.begin(); pos != aif.end(); ++pos)
{
infoSignal.push_back(*pos);
}
modelFitInfo->inputData.SetTableValue("AIF", infoSignal);
}
void storeResultImage(const std::string& name, mitk::Image* image, mitk::modelFit::Parameter::Type nodeType, const mitk::modelFit::ModelFitInfo* modelFitInfo)
{
mitk::modelFit::SetModelFitDataProperties(image, name, nodeType, modelFitInfo);
std::string ext = ::itksys::SystemTools::GetFilenameLastExtension(outFileName);
std::string dir = itksys::SystemTools::GetFilenamePath(outFileName);
dir = itksys::SystemTools::ConvertToOutputPath(dir);
std::string rootName = itksys::SystemTools::GetFilenameWithoutLastExtension(outFileName);
std::string fileName = rootName + "_" + name + ext;
std::vector<std::string> pathElements;
pathElements.push_back(dir);
pathElements.push_back(fileName);
std::string fullOutPath = itksys::SystemTools::ConvertToOutputPath(dir + "/" + fileName);
mitk::IOUtil::Save(image, fullOutPath);
std::cout << "Store result (parameter: "<<name<<"): " << fullOutPath << std::endl;
}
void createFitGenerator(mitk::modelFit::ModelFitInfo::Pointer& fitSession, mitk::ParameterFitImageGeneratorBase::Pointer& generator)
{
bool isDescBrixFactory = modelName == MODEL_NAME_descriptive;
bool isToftsFactory = modelName == MODEL_NAME_tofts;
bool is2CXMFactory = modelName == MODEL_NAME_2CX;
bool is3SLFactory = modelName == MODEL_NAME_3SL;
bool is2SLFactory = modelName == MODEL_NAME_2SL;
if (isDescBrixFactory)
{
std::cout << "Model: descriptive pharmacokinetic brix model" << std::endl;
if (!roibased)
{
generateDescriptiveBrixModel_PixelBased(fitSession, generator);
}
else
{
generateDescriptiveBrixModel_ROIBased(fitSession, generator);
}
}
else if (is3SLFactory)
{
std::cout << "Model: three step linear model" << std::endl;
if (!roibased)
{
GenerateLinearModelFit_PixelBased<mitk::ThreeStepLinearModelParameterizer, mitk::ThreeStepLinearModelFactory>(fitSession, generator);
}
else
{
GenerateLinearModelFit_ROIBased<mitk::ThreeStepLinearModelParameterizer, mitk::ThreeStepLinearModelFactory>(fitSession, generator);
}
}
else if (is2SLFactory)
{
std::cout << "Model: two step linear model" << std::endl;
if (!roibased)
{
GenerateLinearModelFit_PixelBased<mitk::TwoStepLinearModelParameterizer, mitk::TwoStepLinearModelFactory>(fitSession, generator);
}
else
{
GenerateLinearModelFit_ROIBased<mitk::TwoStepLinearModelParameterizer, mitk::TwoStepLinearModelFactory>(fitSession, generator);
}
}
else if (isToftsFactory)
{
std::cout << "Model: extended tofts model" << std::endl;
if (!roibased)
{
generateAIFbasedModelFit_PixelBased<mitk::ExtendedToftsModelParameterizer, mitk::ExtendedToftsModelFactory>(fitSession, generator);
}
else
{
generateAIFbasedModelFit_ROIBased<mitk::ExtendedToftsModelParameterizer, mitk::ExtendedToftsModelFactory>(fitSession, generator);
}
}
else if (is2CXMFactory)
{
std::cout << "Model: two compartment exchange model" << std::endl;
if (!roibased)
{
generateAIFbasedModelFit_PixelBased<mitk::TwoCompartmentExchangeModelParameterizer, mitk::TwoCompartmentExchangeModelFactory>(fitSession, generator);
}
else
{
generateAIFbasedModelFit_ROIBased<mitk::TwoCompartmentExchangeModelParameterizer, mitk::TwoCompartmentExchangeModelFactory>(fitSession, generator);
}
}
else
{
std::cerr << "ERROR. Model flag is unknown. Given flag: " << modelName << std::endl;
}
}
void doFitting()
{
mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr;
mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr;
::itk::CStyleCommand::Pointer command = ::itk::CStyleCommand::New();
command->SetCallback(onFitEvent);
createFitGenerator(fitSession, generator);
if (generator.IsNotNull() )
{
std::cout << "Started fitting process..." << std::endl;
generator->AddObserver(::itk::AnyEvent(), command);
generator->Generate();
std::cout << std::endl << "Finished fitting process" << std::endl;
mitk::storeModelFitGeneratorResults(outFileName, generator, fitSession);
}
else
{
mitkThrow() << "Fitting error! Could not initialize fitting job.";
}
}
void doPreview()
{
mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr;
mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr;
createFitGenerator(fitSession, generator);
if (generator.IsNotNull())
{
mitk::previewModelFitGeneratorResults(outFileName, generator);
}
else
{
mitkThrow() << "Fitting error! Could not initialize fitting job.";
}
}
int main(int argc, char* argv[])
{
mitkCommandLineParser parser;
setupParser(parser);
const std::map<std::string, us::Any>& parsedArgs = parser.parseArguments(argc, argv);
mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (autoselect)" }, { "" });
if (!configureApplicationSettings(parsedArgs))
{
return EXIT_FAILURE;
};
// Show a help message
if (parsedArgs.count("help") || parsedArgs.count("h"))
{
std::cout << parser.helpText();
return EXIT_SUCCESS;
}
//! [do processing]
try
{
image = mitk::IOUtil::Load<mitk::Image>(inFilename, &readerFilterFunctor);
std::cout << "Input: " << inFilename << std::endl;
if (!maskFileName.empty())
{
mask = mitk::IOUtil::Load<mitk::Image>(maskFileName, &readerFilterFunctor);
std::cout << "Mask: " << maskFileName << std::endl;
}
else
{
std::cout << "Mask: none" << std::endl;
}
if (modelName != MODEL_NAME_descriptive && modelName != MODEL_NAME_3SL && MODEL_NAME_2SL != modelName)
{
if (!aifMaskFileName.empty())
{
aifMask = mitk::IOUtil::Load<mitk::Image>(aifMaskFileName, &readerFilterFunctor);
std::cout << "AIF mask: " << aifMaskFileName << std::endl;
}
else
{
- mitkThrow() << "Error. Cannot fit. Choosen model needs an AIF. Please specify AIF mask (--aifmask).";
+ mitkThrow() << "Error. Cannot fit. Chosen model needs an AIF. Please specify AIF mask (--aifmask).";
}
if (!aifImageFileName.empty())
{
aifImage = mitk::IOUtil::Load<mitk::Image>(aifImageFileName, &readerFilterFunctor);
std::cout << "AIF image: " << aifImageFileName << std::endl;
}
else
{
std::cout << "AIF image: none (using signal image)" << std::endl;
}
}
if (roibased && mask.IsNull())
{
mitkThrow() << "Error. Cannot fit. Please specify mask if you select roi based fitting.";
}
std::cout << "Style: ";
if (roibased)
{
std::cout << "ROI based";
}
else
{
std::cout << "pixel based";
}
std::cout << std::endl;
if (preview)
{
doPreview();
}
else
{
doFitting();
}
std::cout << "Processing finished." << std::endl;
return EXIT_SUCCESS;
}
catch (const itk::ExceptionObject& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
MITK_ERROR << "Unexpected error encountered.";
return EXIT_FAILURE;
}
}
diff --git a/Modules/Pharmacokinetics/include/mitkAIFBasedModelBase.h b/Modules/Pharmacokinetics/include/mitkAIFBasedModelBase.h
index 0a9a18b411..694f23f81e 100644
--- a/Modules/Pharmacokinetics/include/mitkAIFBasedModelBase.h
+++ b/Modules/Pharmacokinetics/include/mitkAIFBasedModelBase.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 mitkAIFBasedModelBase_h
#define mitkAIFBasedModelBase_h
#include "MitkPharmacokineticsExports.h"
#include "mitkModelBase.h"
#include "itkArray2D.h"
namespace mitk
{
/** \class AIFBasedModelBase
* \brief Base Class for all physiological perfusion models using an Aterial Input Function
* All AIF based models come with an array of AIF values and the corresponding TimeGrid ( AIF(t))
* This class provides functions for setting the AIF Values and optionally a specific AIF TimeGrid.
* It also provides a method for interpolation of the AIF source array to a specified Timegrid that differs from
* AIFTimeGrid. The AIF must be set with an itk::Array. If no AIFTimeGrid is specified with the Setter, it is assumed
* that the AIFTimeGrid is the same as the ModelTimegrid (e.g. AIF is derived from data set to be fitted). In this
* case, AIFvalues must have the same length as ModelTimeGrid, otherwise an exception is generated*/
class MITKPHARMACOKINETICS_EXPORT AIFBasedModelBase : public mitk::ModelBase
{
public:
typedef AIFBasedModelBase Self;
typedef ModelBase Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
/** Run-time type information (and related methods). */
- itkTypeMacro(PhysiologciModelBase, AIFBasedModelBase);
+ itkTypeMacro(AIFBasedModelBase, ModelBase);
static const std::string NAME_STATIC_PARAMETER_AIF;
static const std::string NAME_STATIC_PARAMETER_AIFTimeGrid;
static const std::string UNIT_STATIC_PARAMETER_AIF;
static const std::string UNIT_STATIC_PARAMETER_AIFTimeGrid;
static const unsigned int NUMBER_OF_STATIC_PARAMETERS;
static const std::string X_AXIS_NAME;
static const std::string X_AXIS_UNIT;
static const std::string Y_AXIS_NAME;
static const std::string Y_AXIS_UNIT;
/** Typedef for Aterial InputFunction AIF(t)*/
typedef itk::Array<double> AterialInputFunctionType;
itkGetConstReferenceMacro(AterialInputFunctionValues, AterialInputFunctionType);
itkGetConstReferenceMacro(AterialInputFunctionTimeGrid, TimeGridType);
itkSetMacro(AterialInputFunctionValues, AterialInputFunctionType);
itkSetMacro(AterialInputFunctionTimeGrid, TimeGridType);
std::string GetXAxisName() const override;
std::string GetXAxisUnit() const override;
std::string GetYAxisName() const override;
std::string GetYAxisUnit() const override;
/** Returns the TimeGrid used for the AIF. Either the externally set AIF time grid
* or the time grid of the model if nothing is set.*/
const TimeGridType& GetCurrentAterialInputFunctionTimeGrid() const;
/** Returns the Aterial Input function matching currentTimeGrid
* The original values are interpolated to the passed TimeGrid
* if currentTimeGrid.Size() = 0 , the Original AIF will be returned*/
const AterialInputFunctionType GetAterialInputFunction(TimeGridType currentTimeGrid) const;
ParameterNamesType GetStaticParameterNames() const override;
ParametersSizeType GetNumberOfStaticParameters() const override;
ParamterUnitMapType GetStaticParameterUnits() const override;
protected:
AIFBasedModelBase();
~AIFBasedModelBase() override;
/** Reimplementation that checks if AIF and timegrid settings are valid.
* @param [out] error Set internally to indicate the error reason if method returns false. Is used by GetSignal() for the
* exception comment.
* @return Returns true if the model is valid and can compute a signal. Otherwise it returns false.*/
bool ValidateModel(std::string& error) const override;
void PrintSelf(std::ostream& os, ::itk::Indent indent) const override;
void SetStaticParameter(const ParameterNameType& name,
const StaticParameterValuesType& values) override;
StaticParameterValuesType GetStaticParameterValue(const ParameterNameType& name) const
override;
TimeGridType m_AterialInputFunctionTimeGrid;
AterialInputFunctionType m_AterialInputFunctionValues;
private:
//No copy constructor allowed
AIFBasedModelBase(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/Pharmacokinetics/include/mitkAterialInputFunctionGenerator.h b/Modules/Pharmacokinetics/include/mitkAterialInputFunctionGenerator.h
index b38ee0fb54..44eea51db6 100644
--- a/Modules/Pharmacokinetics/include/mitkAterialInputFunctionGenerator.h
+++ b/Modules/Pharmacokinetics/include/mitkAterialInputFunctionGenerator.h
@@ -1,107 +1,107 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkAterialInputFunctionGenerator_h
#define mitkAterialInputFunctionGenerator_h
#include <mitkImage.h>
#include "mitkAIFBasedModelBase.h"
#include "MitkPharmacokineticsExports.h"
namespace mitk
{
/** \class AterialInputFunctionGenerator
* \brief Compute the Aterial Input Function from a given dynamic image and a mask defining the tumour supplying artery
*
* The AterialInputFunctionGenerator takes a given 4D dynamic image and a corresponding mask and returns an array of the averaged values and
* an array of the corresponding TimeGrid
* within the mask over time. No conversion is performed, so conversion from signal to concentration has to be performed in advanced
* and the resulting image is fed into the Generator.
- * The generator checks wether both image and mask are set and passes them to the itkMaskedNaryStatisticsImageFilter and the mitkExtractTimeGrid, to
+ * The generator checks whether both image and mask are set and passes them to the itkMaskedNaryStatisticsImageFilter and the mitkExtractTimeGrid, to
* calculate the mean of every time slice within the ROI and extract the corresponding time grid from the date set.
*/
class MITKPHARMACOKINETICS_EXPORT AterialInputFunctionGenerator : public itk::Object
{
public:
mitkClassMacroItkParent(AterialInputFunctionGenerator, itk::Object);
itkNewMacro(Self);
/** @brief Setter and Getter for Input Image for calculation of AIF, already converted to concentrations
* Getter calls CheckValidInputs() and CalculateAIFAndGetResult() if HasOutdatedResults() is true */
itkSetConstObjectMacro(DynamicImage, Image);
itkGetConstObjectMacro(DynamicImage, Image);
/** @brief Setter and Getter for mask defining the tumour feeding atery
* Getter calls CheckValidInputs() and CalculateAIFAndGetResult() if HasOutdatedResults() is true */
itkSetConstObjectMacro(Mask, Image);
itkGetConstObjectMacro(Mask, Image);
/** @brief Setter and Getter for the hematocritlevel, important for conversion to plasma curve*/
itkSetMacro(HCL, double);
itkGetConstReferenceMacro(HCL, double);
//Common Value for Hematocrit level is 0.45
static const double DEFAULT_HEMATOCRIT_LEVEL;
void SetDefaultHematocritLevel()
{
this->m_HCL = DEFAULT_HEMATOCRIT_LEVEL;
};
double GetDefaultHematocritLevel()
{
return DEFAULT_HEMATOCRIT_LEVEL;
}
AIFBasedModelBase::AterialInputFunctionType GetAterialInputFunction();
ModelBase::TimeGridType GetAterialInputFunctionTimeGrid();
protected:
AterialInputFunctionGenerator()
{
m_Mask = nullptr;
m_DynamicImage = nullptr;
this->SetDefaultHematocritLevel();
};
~AterialInputFunctionGenerator() override {};
//template <typename TPixel, unsigned int VDim>
//void DoCalculateAIF(itk::Image<TPixel, VDim>* image);
/** @brief Passes m_DynamicImage and m_Mask to the itkMaskedNaryStatisticsImageFilter and mitkExtractTimeGrid
* and inserts the result into m_AIFValues and m_AIFTimeGrid and modiefies the Timestamp*/
virtual void CalculateAIFAndGetResult();
/** @brief Makes sure that m_DynamicImage and m_Mask are set */
virtual void CheckValidInputs() const;
bool HasOutdatedResults();
itk::TimeStamp m_GenerationTimeStamp;
private:
Image::ConstPointer m_DynamicImage;
Image::ConstPointer m_Mask;
AIFBasedModelBase::AterialInputFunctionType m_AIFValues;
ModelBase::TimeGridType m_AIFTimeGrid;
double m_HCL;
};
}
#endif
diff --git a/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h b/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h
index 1889ef0280..c052b4af86 100644
--- a/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h
+++ b/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h
@@ -1,167 +1,167 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkConcentrationCurveGenerator_h
#define mitkConcentrationCurveGenerator_h
#include <mitkImage.h>
#include <itkBinaryFunctorImageFilter.h>
#include "mitkConvertToConcentrationAbsoluteFunctor.h"
#include "mitkConvertToConcentrationRelativeFunctor.h"
#include "MitkPharmacokineticsExports.h"
namespace mitk {
/** \class ConcentrationCurveGenerator
* \brief Converts a given 4D mitk::Image with MR signal values into a 4D mitk::Image with corresponding contrast agent concentration values
*
* From a given 4D image, the Generator takes the 3D image of the first time point as baseline image. It then loops over all time steps, casts
* the current 3D image to itk and passes it to the ConvertToconcentrationFunctor. The returned 3D image has now values of concentration type and is stored at its timepoint
* in the return image.
*/
class MITKPHARMACOKINETICS_EXPORT ConcentrationCurveGenerator : public itk::Object
{
public:
mitkClassMacroItkParent(ConcentrationCurveGenerator, itk::Object);
itkNewMacro(Self);
//typedef itk::Image<double,3> ImageType;
typedef itk::Image<double,3> ConvertedImageType;
/** Getter and Setter for 4D mitk::Image*/
itkSetConstObjectMacro(DynamicImage,Image);
itkGetConstObjectMacro(DynamicImage,Image);
- /** Parameters Relevant for conversion Calculation; Have to be Set externally (Sequence Dependend)*/
+ /** Parameters Relevant for conversion Calculation; Have to be Set externally (Sequence Dependent)*/
itkSetMacro(RelaxationTime, double);
itkGetConstReferenceMacro(RelaxationTime, double);
itkSetMacro(Relaxivity, double);
itkGetConstReferenceMacro(Relaxivity, double);
itkSetMacro(RecoveryTime, double);
itkGetConstReferenceMacro(RecoveryTime, double);
itkSetMacro(RepetitionTime, double);
itkGetConstReferenceMacro(RepetitionTime, double);
itkSetMacro(FlipAngle, double);
itkGetConstReferenceMacro(FlipAngle, double);
itkSetMacro(FlipAnglePDW, double);
itkGetConstReferenceMacro(FlipAnglePDW, double);
itkSetMacro(Factor, double);
itkGetConstReferenceMacro(Factor, double);
/** Getter and Setter for PDW Map image*/
itkSetConstObjectMacro(PDWImage,Image);
itkGetConstObjectMacro(PDWImage,Image);
itkSetMacro(T2Factor, double);
itkGetConstReferenceMacro(T2Factor, double);
itkSetMacro(T2EchoTime, double);
itkGetConstReferenceMacro(T2EchoTime, double);
/** @brief Calls Convert and returns the 4D mitk::image in Concentration units*/
itkSetMacro(BaselineStartTimeStep, unsigned int);
itkGetConstReferenceMacro(BaselineStartTimeStep, unsigned int);
itkSetMacro(BaselineEndTimeStep, unsigned int);
itkGetConstReferenceMacro(BaselineEndTimeStep, unsigned int);
itkSetMacro(isTurboFlashSequence,bool);
itkGetConstReferenceMacro(isTurboFlashSequence,bool);
itkSetMacro(AbsoluteSignalEnhancement,bool);
itkGetConstReferenceMacro(AbsoluteSignalEnhancement,bool);
itkSetMacro(RelativeSignalEnhancement,bool);
itkGetConstReferenceMacro(RelativeSignalEnhancement,bool);
itkSetMacro(UsingT1Map,bool);
itkGetConstReferenceMacro(UsingT1Map,bool);
itkSetMacro(isT2weightedImage,bool);
itkGetConstReferenceMacro(isT2weightedImage,bool);
Image::Pointer GetConvertedImage();
protected:
ConcentrationCurveGenerator();
~ConcentrationCurveGenerator() override;
template<class TPixel_input, class TPixel_baseline>
mitk::Image::Pointer convertToConcentration(const itk::Image<TPixel_input, 3> *itkInputImage, const itk::Image<TPixel_baseline, 3> *itkBaselineImage);
/** Calls ConvertToconcentrationFunctor for passed 3D itk::image*/
mitk::Image::Pointer ConvertSignalToConcentrationCurve(const mitk::Image* inputImage, const mitk::Image* baselineImage);
/** @brief Takes the 3D image of the first timepoint to set as baseline image*/
void PrepareBaselineImage();
template<class TPixel>
void CalculateAverageBaselineImage(const itk::Image<TPixel,4> *itkBaselineImage);
/** @brief loops over all timepoints, casts the current timepoint 3D mitk::image to itk and passes it to ConvertSignalToConcentrationCurve */
virtual void Convert();
private:
Image::ConstPointer m_DynamicImage;
Image::ConstPointer m_BaselineImage;
Image::ConstPointer m_PDWImage;
Image::Pointer m_ConvertSignalToConcentrationCurve_OutputImage;
Image::Pointer m_ConvertedImage;
bool m_isT2weightedImage;
bool m_isTurboFlashSequence;
bool m_AbsoluteSignalEnhancement;
bool m_RelativeSignalEnhancement;
bool m_UsingT1Map;
double m_Factor;
//=Recovery Time
double m_RecoveryTime;
//=Repetition Time TR
double m_RepetitionTime;
//= pre-CA T1 time
double m_RelaxationTime;
//= contrast agent relaxivity
double m_Relaxivity;
double m_FlipAngle;
double m_FlipAnglePDW;
double m_T2Factor;
double m_T2EchoTime;
// The baseline image is averaged from the signal within time step range [m_BaselineStartTimeStep, m_BaselineEndTimeStep].
// m_BaselineStartTimeStep is the first time frame, that is included into the baseline averaging (starting with 0).
unsigned int m_BaselineStartTimeStep;
// m_BaselinStopTimeStep is the last time frame, that is included into the baseline averaging.
unsigned int m_BaselineEndTimeStep;
};
}
#endif
diff --git a/Modules/Pharmacokinetics/include/mitkConvolutionHelper.h b/Modules/Pharmacokinetics/include/mitkConvolutionHelper.h
index 5bc53dfc27..b459cf9b06 100644
--- a/Modules/Pharmacokinetics/include/mitkConvolutionHelper.h
+++ b/Modules/Pharmacokinetics/include/mitkConvolutionHelper.h
@@ -1,154 +1,154 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkConvolutionHelper_h
#define mitkConvolutionHelper_h
#include "itkArray.h"
#include "mitkAIFBasedModelBase.h"
#include <iostream>
#include "MitkPharmacokineticsExports.h"
namespace mitk {
/** @namespace convolution
* @brief Helper for itk implementation of vnl fourier transformation
- * This namespace provides functions for the preperation of vnl_fft_1d, including a wrapper
+ * This namespace provides functions for the preparation of vnl_fft_1d, including a wrapper
* for wrapping the convolution kernel (turning it inside out) and a function for zeropadding
* to avoid convolution artefacts. */
namespace convolution {
/** Some typedefs concerning data structures needed for vnl_fft_1d, which has vnl_vector< vcl_complex< double > >
* as output typ of the forward transformation fwd_transform. Input is of type vnl_vector< vcl_complex< T > >
* but since itk::Array is derived from vnl_vector, this works as well*/
/** @brief Function that wraps the kernel */
inline itk::Array<double> wrap1d(itk::Array<double> kernel)
{
int dim = kernel.GetNumberOfElements();
itk::Array<double> wrappedKernel(dim);
wrappedKernel.fill(0.);
for(int i=0; i< dim; ++i)
{
wrappedKernel.SetElement(i, kernel.GetElement((i+(dim/2))%dim));
}
return wrappedKernel;
}
- /** @brief Fuction for zeropadding (adding zeros) of an Array/vnl_vector, so that is has size paddedDimensions
+ /** @brief Function for zeropadding (adding zeros) of an Array/vnl_vector, so that is has size paddedDimensions
* @param unpaddedSpectrum
* @param paddedDimension Dimensions that the Array should have after padding (convolution dimensions)
* \remark dim = Dimensions of padded image --> PaddedDimension
* \remark m dimensions of larger image
* \remark n dimensions of image to be padded --> InitialDimension*/
inline itk::Array<double> zeropadding1d(itk::Array<double> unpaddedSpectrum, int paddedDimension)
{
int initialDimension = unpaddedSpectrum.GetNumberOfElements();
itk::Array<double> paddedSpectrum(paddedDimension);
paddedSpectrum.fill(0.);
if(paddedDimension > initialDimension)
{
unsigned int padding = paddedDimension - initialDimension;
for(int i=0; i<initialDimension ;++i)
{
paddedSpectrum.SetElement(i+padding/2, unpaddedSpectrum.GetElement(i));
}
}
return paddedSpectrum;
}
/** @brief Follow up function after back transformation from fourier space bwd_transform.
* removes padding and scales (transformed values have to be divided by transformation dimensions) */
inline itk::Array<double> unpadAndScale(itk::Array<double> convolutionResult, int initialDimension)
{
int transformationDimension = convolutionResult.size();
unsigned int padding = transformationDimension - initialDimension;
itk::Array<double> scaledResult(initialDimension);
scaledResult.fill(0.0);
for(int i = 0; i<initialDimension; ++i)
{
double value = convolutionResult(i+padding/2) / transformationDimension;
scaledResult.SetElement(i,value);
}
return scaledResult;
}
- /** @brief Convinience function for preparing 2 array for convolution with each other.
+ /** @brief Convenience function for preparing 2 array for convolution with each other.
* Takes both arrays of type itk::Array, zeropadds them to the sum of their sizes and wraps
* the one specified as kernel. Returns them as vnl_vector<vcl_complex<double> >, ready to
* be entered in fwd_transform*/
inline void prepareConvolution(const itk::Array<double>& kernel, const itk::Array<double>& spectrum, itk::Array<double>& preparedKernel, itk::Array<double>& preparedSpectrum ){
int convolutionDimensions = kernel.GetSize() + spectrum.GetSize();
// itk::Array<double> paddedKernel = zeropadding1d(kernel,convolutionDimensions);
preparedKernel=zeropadding1d(kernel,convolutionDimensions);
preparedSpectrum = zeropadding1d(spectrum,convolutionDimensions);
// preparedKernel = wrap1d(paddedKernel);
}
}
inline itk::Array<double> convoluteAIFWithExponential(mitk::ModelBase::TimeGridType timeGrid, mitk::AIFBasedModelBase::AterialInputFunctionType aif, double lambda)
{
/** @brief Iterative Formula to Convolve aif(t) with an exponential Residuefunction R(t) = exp(lambda*t)
**/
typedef itk::Array<double> ConvolutionResultType;
ConvolutionResultType convolution(timeGrid.GetSize());
convolution.fill(0.0);
convolution(0) = 0;
for(unsigned int i = 0; i< (timeGrid.GetSize()-1); ++i)
{
double dt = timeGrid(i+1) - timeGrid(i);
double m = (aif(i+1) - aif(i))/dt;
double edt = exp(-lambda *dt);
convolution(i+1) =edt * convolution(i)
+ (aif(i) - m*timeGrid(i))/lambda * (1 - edt )
+ m/(lambda * lambda) * ((lambda * timeGrid(i+1) - 1) - edt*(lambda*timeGrid(i) -1));
}
return convolution;
}
inline itk::Array<double> convoluteAIFWithConstant(mitk::ModelBase::TimeGridType timeGrid, mitk::AIFBasedModelBase::AterialInputFunctionType aif, double constant)
{
/** @brief Iterative Formula to Convolve aif(t) with a constant value by linear interpolation of the Aif between sampling points
**/
typedef itk::Array<double> ConvolutionResultType;
ConvolutionResultType convolution(timeGrid.GetSize());
convolution.fill(0.0);
convolution(0) = 0;
for(unsigned int i = 0; i< (timeGrid.GetSize()-1); ++i)
{
double dt = timeGrid(i+1) - timeGrid(i);
double m = (aif(i+1) - aif(i))/dt;
convolution(i+1) = convolution(i) + constant * (aif(i)*dt + m*timeGrid(i)*dt + m/2*(timeGrid(i+1)*timeGrid(i+1) - timeGrid(i)*timeGrid(i)));
}
return convolution;
}
}
#endif
diff --git a/Modules/Pharmacokinetics/include/mitkCurveDescriptionParameterBase.h b/Modules/Pharmacokinetics/include/mitkCurveDescriptionParameterBase.h
index 20945815cb..28dcceee4e 100644
--- a/Modules/Pharmacokinetics/include/mitkCurveDescriptionParameterBase.h
+++ b/Modules/Pharmacokinetics/include/mitkCurveDescriptionParameterBase.h
@@ -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.
============================================================================*/
#ifndef mitkCurveDescriptionParameterBase_h
#define mitkCurveDescriptionParameterBase_h
#include <iostream>
#include <itkArray.h>
#include <itkArray2D.h>
#include <itkObject.h>
#include <mitkModelBase.h>
#include "MitkPharmacokineticsExports.h"
namespace mitk
{
/** Base class for functor that compute descriptive values for
a curve (e.g. like Area under the Curve, Time to peek, maximum,...)
@remark The derived classes must be implemented thread safe because GetCurveDescriptionParameter()
and GetDescriptionParameterName() of one instance may be called in
multi-threaded context (e.g. DescriptionParameterImageGeneratorBase
and derived classes). */
class MITKPHARMACOKINETICS_EXPORT CurveDescriptionParameterBase : public itk::Object
{
public:
typedef CurveDescriptionParameterBase Self;
typedef itk::Object Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkTypeMacro(CurveDescriptionParameterBase, itk::Object);
typedef itk::Array<double> CurveType;
typedef itk::Array<double> CurveGridType;
typedef double CurveDescriptionParameterResultType;
typedef std::string CurveDescriptionParameterNameType;
typedef std::vector<CurveDescriptionParameterResultType> DescriptionParameterResultsType;
typedef std::vector<CurveDescriptionParameterNameType> DescriptionParameterNamesType;
/** Returns the concrete description values for a curve.
* @pre Curve value vector and curve grid must have the same size*/
DescriptionParameterResultsType GetCurveDescriptionParameter(const CurveType& curve, const CurveGridType& grid) const;
- /**Return the names of all descrition values that will be computed by the class.
+ /**Return the names of all description values that will be computed by the class.
* @post The order of names equales the order of the results of GetCurveDescriptionParameter().*/
virtual DescriptionParameterNamesType GetDescriptionParameterName() const = 0 ;
protected:
/** Slot to implement the computation of the descriptor values.*/
virtual DescriptionParameterResultsType ComputeCurveDescriptionParameter(const CurveType& curve, const CurveGridType& grid) const = 0;
CurveDescriptionParameterBase();
~CurveDescriptionParameterBase() override;
private:
//No copy constructor allowed
CurveDescriptionParameterBase(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/Pharmacokinetics/include/mitkCurveParameterFunctor.h b/Modules/Pharmacokinetics/include/mitkCurveParameterFunctor.h
index 9c91dc86c4..246f78f8e0 100644
--- a/Modules/Pharmacokinetics/include/mitkCurveParameterFunctor.h
+++ b/Modules/Pharmacokinetics/include/mitkCurveParameterFunctor.h
@@ -1,73 +1,73 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkCurveParameterFunctor_h
#define mitkCurveParameterFunctor_h
#include "mitkCurveDescriptionParameterBase.h"
#include "mitkSimpleFunctorBase.h"
#include "MitkPharmacokineticsExports.h"
namespace mitk
{
/**Functor for the curve description values by using the itkMulitOutputNaryImageFilter.
* You may register any number of CurveDescriptionParamterBase instances to the functor.
* The Functor will compute all values.
* @warning the functor must be threadsafe and so must be the registered CurveDescriptionParamterBase instances.*/
class MITKPHARMACOKINETICS_EXPORT CurveParameterFunctor : public SimpleFunctorBase
{
public:
typedef CurveParameterFunctor Self;
typedef itk::Object Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
itkFactorylessNewMacro(Self);
itkTypeMacro(CurveParameterFunctor, SimpleFunctorBase);
typedef CurveDescriptionParameterBase::CurveDescriptionParameterNameType ParameterNameType;
typedef CurveDescriptionParameterBase::DescriptionParameterNamesType ParameterNamesType;
using GridArrayType = SimpleFunctorBase::GridArrayType;
SimpleFunctorBase::OutputPixelVectorType Compute(const InputPixelVectorType & value) const override;
unsigned int GetNumberOfOutputs() const override;
GridArrayType GetGrid() const override;
itkSetMacro(Grid, GridArrayType);
ParameterNamesType GetDescriptionParameterNames() const;
- /**@warning Teh function is currently not thread safe.
+ /**@warning The function is currently not thread safe.
@todo reimplement with shareable lock to allow other class methods to be used parallel but lock this one exclusively.*/
void ResetDescriptionParameters();
- /**@warning Teh function is currently not thread safe.
+ /**@warning The function is currently not thread safe.
@todo reimplement with shareable lock to allow other class methods to be used parallel but lock this one exclusively.*/
void RegisterDescriptionParameter(const ParameterNameType& parameterName, CurveDescriptionParameterBase* parameterFunction);
- /**@warning Teh function is currently not thread safe.
+ /**@warning The function is currently not thread safe.
@todo reimplement with shareable lock to allow other class methods to be used parallel but lock this one exclusively.*/
const CurveDescriptionParameterBase* GetDescriptionParameterFunction(const ParameterNameType& parameterName) const;
protected:
CurveParameterFunctor();
~CurveParameterFunctor() override;
private:
typedef std::map<ParameterNameType, CurveDescriptionParameterBase::Pointer> DescriptionParameterMapType;
DescriptionParameterMapType m_DescriptorMap;
GridArrayType m_Grid;
};
}
#endif
diff --git a/Modules/Pharmacokinetics/include/mitkDescriptivePharmacokineticBrixModelParameterizer.h b/Modules/Pharmacokinetics/include/mitkDescriptivePharmacokineticBrixModelParameterizer.h
index 5b3f3d45c3..956a75ced3 100644
--- a/Modules/Pharmacokinetics/include/mitkDescriptivePharmacokineticBrixModelParameterizer.h
+++ b/Modules/Pharmacokinetics/include/mitkDescriptivePharmacokineticBrixModelParameterizer.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 mitkDescriptivePharmacokineticBrixModelParameterizer_h
#define mitkDescriptivePharmacokineticBrixModelParameterizer_h
#include "mitkConcreteModelParameterizerBase.h"
#include "mitkDescriptivePharmacokineticBrixModel.h"
namespace mitk
{
/** Parameterizer for the DescriptivePharmacokineticBrixModel that use an image
for initializing the model. This parameterizer is amongst others used for pixel based fiting
strategies.
@sa DescriptivePharmacokineticBrixModelParameterizer*/
class MITKPHARMACOKINETICS_EXPORT DescriptivePharmacokineticBrixModelParameterizer : public
ConcreteModelParameterizerBase<mitk::DescriptivePharmacokineticBrixModel>
{
public:
typedef DescriptivePharmacokineticBrixModelParameterizer Self;
typedef ConcreteModelParameterizerBase<mitk::DescriptivePharmacokineticBrixModel> Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
- itkTypeMacro(ConcreteModelParameterizerBase, ModelParameterizerBase);
+ itkTypeMacro(DescriptivePharmacokineticBrixModelParameterizer, ConcreteModelParameterizerBase);
itkNewMacro(Self);
typedef Superclass::ModelBaseType ModelBaseType;
typedef Superclass::ModelBasePointer ModelBasePointer;
typedef Superclass::ModelType ModelType;
typedef Superclass::ModelPointer ModelPointer;
typedef Superclass::StaticParameterValueType StaticParameterValueType;
typedef Superclass::StaticParameterValuesType StaticParameterValuesType;
typedef Superclass::StaticParameterMapType StaticParameterMapType;
typedef itk::Image<double, 3> BaseImageType;
typedef Superclass::IndexType IndexType;
itkSetMacro(Tau, double);
itkGetConstReferenceMacro(Tau, double);
itkSetConstObjectMacro(BaseImage, BaseImageType);
itkGetConstObjectMacro(BaseImage, BaseImageType);
/* Returns the global static parameters for the model.
* @remark this default implementation assumes no global static parameters exist.
* Thus an empty map is returned.*/
StaticParameterMapType GetGlobalStaticParameters() const override;
/* Returns the local static parameters for the model at the given index.
* @remark this default implementation assumes no local static parameters exist.
* Thus an empty map is returned.*/
StaticParameterMapType GetLocalStaticParameters(const IndexType& currentPosition) const override;
/** This function returns the default parameterization (e.g. initial parametrization for fitting)
defined by the model developer for for the given model.*/
ParametersType GetDefaultInitialParameterization() const override;
protected:
DescriptivePharmacokineticBrixModelParameterizer();
~DescriptivePharmacokineticBrixModelParameterizer() override;
/**injection time Tau in minutes [min]*/
double m_Tau;
- /**Pointer to the image that containes the values of the first timestep
+ /**Pointer to the image that contains the values of the first timestep
(base value of the series that should be modelled)*/
BaseImageType::ConstPointer m_BaseImage;
private:
//No copy constructor allowed
DescriptivePharmacokineticBrixModelParameterizer(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/Pharmacokinetics/include/mitkDescriptivePharmacokineticBrixModelValueBasedParameterizer.h b/Modules/Pharmacokinetics/include/mitkDescriptivePharmacokineticBrixModelValueBasedParameterizer.h
index de5c46e154..36d975a28d 100644
--- a/Modules/Pharmacokinetics/include/mitkDescriptivePharmacokineticBrixModelValueBasedParameterizer.h
+++ b/Modules/Pharmacokinetics/include/mitkDescriptivePharmacokineticBrixModelValueBasedParameterizer.h
@@ -1,90 +1,90 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkDescriptivePharmacokineticBrixModelValueBasedParameterizer_h
#define mitkDescriptivePharmacokineticBrixModelValueBasedParameterizer_h
#include "mitkConcreteModelParameterizerBase.h"
namespace mitk
{
/** Parameterizer for the DescriptivePharmacokineticBrixModel that don't use an image
for initializing the model but a single signal value. This parameterizer is amongst
others used for ROI based fiting strategies where no complete image is needed/used.
@sa DescriptivePharmacokineticBrixModelParameterizer*/
class MITKPHARMACOKINETICS_EXPORT DescriptivePharmacokineticBrixModelValueBasedParameterizer : public
ConcreteModelParameterizerBase<mitk::DescriptivePharmacokineticBrixModel>
{
public:
typedef DescriptivePharmacokineticBrixModelValueBasedParameterizer Self;
typedef ConcreteModelParameterizerBase<mitk::DescriptivePharmacokineticBrixModel> Superclass;
typedef itk::SmartPointer< Self > Pointer;
typedef itk::SmartPointer< const Self > ConstPointer;
- itkTypeMacro(ConcreteModelParameterizerBase, ModelParameterizerBase);
+ itkTypeMacro(DescriptivePharmacokineticBrixModelValueBasedParameterizer, ConcreteModelParameterizerBase);
itkNewMacro(Self);
typedef Superclass::ModelBaseType ModelBaseType;
typedef Superclass::ModelBasePointer ModelBasePointer;
typedef Superclass::ModelType ModelType;
typedef Superclass::ModelPointer ModelPointer;
typedef Superclass::StaticParameterValueType StaticParameterValueType;
typedef Superclass::StaticParameterValuesType StaticParameterValuesType;
typedef Superclass::StaticParameterMapType StaticParameterMapType;
typedef itk::Image<double, 3> BaseImageType;
typedef Superclass::IndexType IndexType;
itkSetMacro(Tau, double);
itkGetConstReferenceMacro(Tau, double);
itkSetMacro(BaseValue, double);
itkGetConstReferenceMacro(BaseValue, double);
/* Returns the global static parameters for the model.
* @remark this default implementation assumes no global static parameters exist.
* Thus an empty map is returned.*/
StaticParameterMapType GetGlobalStaticParameters() const override;
/* Returns the local static parameters for the model at the given index.
* @remark this default implementation assumes no local static parameters exist.
* Thus an empty map is returned.*/
StaticParameterMapType GetLocalStaticParameters(const IndexType& currentPosition) const override;
/** This function returns the default parameterization (e.g. initial parametrization for fitting)
defined by the model developer for for the given model.*/
ParametersType GetDefaultInitialParameterization() const override;
protected:
DescriptivePharmacokineticBrixModelValueBasedParameterizer();
~DescriptivePharmacokineticBrixModelValueBasedParameterizer() override;
/**injection time Tau in minutes [min]*/
double m_Tau;
/** Contains the base value that should be used by the model.*/
double m_BaseValue;
private:
//No copy constructor allowed
DescriptivePharmacokineticBrixModelValueBasedParameterizer(const Self& source);
void operator=(const Self&); //purposely not implemented
};
}
#endif
diff --git a/Modules/Pharmacokinetics/src/Models/mitkDescriptivePharmacokineticBrixModelFactory.cpp b/Modules/Pharmacokinetics/src/Models/mitkDescriptivePharmacokineticBrixModelFactory.cpp
index 14be13fc5b..8a43391814 100644
--- a/Modules/Pharmacokinetics/src/Models/mitkDescriptivePharmacokineticBrixModelFactory.cpp
+++ b/Modules/Pharmacokinetics/src/Models/mitkDescriptivePharmacokineticBrixModelFactory.cpp
@@ -1,90 +1,90 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkImageTimeSelector.h>
#include <mitkImageCast.h>
#include "mitkDescriptivePharmacokineticBrixModelFactory.h"
#include "mitkDescriptivePharmacokineticBrixModelParameterizer.h"
#include "mitkDescriptivePharmacokineticBrixModelValueBasedParameterizer.h"
mitk::DescriptivePharmacokineticBrixModelFactory::DescriptivePharmacokineticBrixModelFactory()
{
};
mitk::DescriptivePharmacokineticBrixModelFactory::~DescriptivePharmacokineticBrixModelFactory()
{
};
mitk::ModelParameterizerBase::ParametersType
mitk::DescriptivePharmacokineticBrixModelFactory::GetDefaultInitialParameterization() const
{
return DescriptivePharmacokineticBrixModelParameterizer::New()->GetDefaultInitialParameterization();
};
mitk::ModelParameterizerBase::Pointer
mitk::DescriptivePharmacokineticBrixModelFactory::DoCreateParameterizer(
const mitk::modelFit::ModelFitInfo* fit) const
{
mitk::ModelParameterizerBase::Pointer result;
if (fit->fitType == ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED())
{
DescriptivePharmacokineticBrixModelParameterizer::Pointer modelParameterizer =
DescriptivePharmacokineticBrixModelParameterizer::New();
modelFit::StaticParameterMap::ValueType tau = fit->staticParamMap.Get(
DescriptivePharmacokineticBrixModel::NAME_STATIC_PARAMETER_tau);
modelParameterizer->SetTau(tau[0]);
mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New();
imageTimeSelector->SetInput(fit->inputImage);
imageTimeSelector->SetTimeNr(0);
imageTimeSelector->UpdateLargestPossibleRegion();
mitk::DescriptivePharmacokineticBrixModelParameterizer::BaseImageType::Pointer baseImage;
mitk::CastToItkImage(imageTimeSelector->GetOutput(), baseImage);
modelParameterizer->SetBaseImage(baseImage);
result = modelParameterizer.GetPointer();
}
else if (fit->fitType == ModelFitConstants::FIT_TYPE_VALUE_ROIBASED())
{
DescriptivePharmacokineticBrixModelValueBasedParameterizer::Pointer modelParameterizer =
DescriptivePharmacokineticBrixModelValueBasedParameterizer::New();
modelFit::StaticParameterMap::ValueType tau = fit->staticParamMap.Get(
DescriptivePharmacokineticBrixModel::NAME_STATIC_PARAMETER_tau);
modelParameterizer->SetTau(tau[0]);
if (!fit->inputData.ValueExists("ROI"))
{
mitkThrow() <<
- "Cannot generate parameterizer for fit of type ROIbased. Input data with the lable \"ROI\" is missing in fit.";
+ "Cannot generate parameterizer for fit of type ROIbased. Input data with the label \"ROI\" is missing in fit.";
}
ScalarListLookupTable::ValueType signal = fit->inputData.GetTableValue("ROI");
if (signal.empty())
{
mitkThrow() <<
- "Cannot generate parameterizer for fit of type ROIbased. Input data with the lable \"ROI\" is invalid: No values available.";
+ "Cannot generate parameterizer for fit of type ROIbased. Input data with the label \"ROI\" is invalid: No values available.";
}
modelParameterizer->SetBaseValue(signal[0]);
result = modelParameterizer.GetPointer();
}
return result;
};
diff --git a/Modules/PharmacokineticsUI/Qmitk/QmitkDescriptionParameterBackgroundJob.cpp b/Modules/PharmacokineticsUI/Qmitk/QmitkDescriptionParameterBackgroundJob.cpp
index bd0a16a8d3..3f73a89946 100644
--- a/Modules/PharmacokineticsUI/Qmitk/QmitkDescriptionParameterBackgroundJob.cpp
+++ b/Modules/PharmacokineticsUI/Qmitk/QmitkDescriptionParameterBackgroundJob.cpp
@@ -1,123 +1,123 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkDescriptionParameterBackgroundJob.h"
#include "mitkModelFitInfo.h"
void DescriptionParameterBackgroundJob::OnComputeEvent(::itk::Object* caller,
const itk::EventObject& event)
{
itk::ProgressEvent progressEvent;
itk::InitializeEvent initEvent;
itk::StartEvent startEvent;
itk::EndEvent endEvent;
if (progressEvent.CheckEvent(&event))
{
mitk::DescriptionParameterImageGeneratorBase* castedReporter =
dynamic_cast<mitk::DescriptionParameterImageGeneratorBase*>(caller);
emit JobProgress(castedReporter->GetProgress());
}
else if (initEvent.CheckEvent(&event))
{
emit JobStatusChanged(QString("Initializing description parameter generator"));
}
else if (startEvent.CheckEvent(&event))
{
emit JobStatusChanged(QString("Started parameter computation process."));
}
else if (endEvent.CheckEvent(&event))
{
emit JobStatusChanged(QString("Finished parameter computation process."));
}
}
DescriptionParameterBackgroundJob::
DescriptionParameterBackgroundJob(mitk::DescriptionParameterImageGeneratorBase* generator,
mitk::DataNode* parentNode)
{
if (!generator)
{
mitkThrow() << "Cannot create description parameter background job. Passed fit generator is NULL.";
}
m_Generator = generator;
m_ParentNode = parentNode;
m_spCommand = ::itk::MemberCommand<DescriptionParameterBackgroundJob>::New();
m_spCommand->SetCallbackFunction(this, &DescriptionParameterBackgroundJob::OnComputeEvent);
m_ObserverID = m_Generator->AddObserver(::itk::AnyEvent(), m_spCommand);
};
mitk::DataNode*
DescriptionParameterBackgroundJob::
GetParentNode() const
{
return m_ParentNode;
};
DescriptionParameterBackgroundJob::
~DescriptionParameterBackgroundJob()
{
m_Generator->RemoveObserver(m_ObserverID);
};
mitk::modelFit::ModelFitResultNodeVectorType DescriptionParameterBackgroundJob::CreateResultNodes(
const mitk::DescriptionParameterImageGeneratorBase::ParameterImageMapType& paramimages)
{
mitk::modelFit::ModelFitResultNodeVectorType results;
for (const auto &image : paramimages)
{
if (image.second.IsNull())
{
mitkThrow() << "Cannot generate result node. Passed parameterImage is null. parameter name: " <<
image.first;
}
mitk::DataNode::Pointer result = mitk::DataNode::New();
result->SetData(image.second);
result->SetName(image.first);
result->SetVisibility(true);
results.push_back(result);
}
return results;
};
void
DescriptionParameterBackgroundJob::
run()
{
try
{
emit JobStatusChanged(QString("Started session..."));
m_Generator->Generate();
emit JobStatusChanged(QString("Generate result nodes."));
m_Results = CreateResultNodes(m_Generator->GetParameterImages());
emit ResultsAreAvailable(m_Results, this);
}
catch (::std::exception& e)
{
emit Error(QString("Error while processing data. Details: ") + QString::fromLatin1(e.what()));
}
catch (...)
{
- emit Error(QString("Unkown error when processing the data."));
+ emit Error(QString("Unknown error when processing the data."));
}
emit Finished();
};
diff --git a/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.cpp b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.cpp
index 8407bb074e..5f6a7ba2c5 100644
--- a/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.cpp
+++ b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.cpp
@@ -1,562 +1,562 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkPlanarFigureIO.h>
#include "mitkCustomMimeType.h"
#include "mitkIOMimeTypes.h"
#include "mitkExceptionMacro.h"
#include "mitkPlanarAngle.h"
#include "mitkPlanarArrow.h"
#include "mitkPlanarBezierCurve.h"
#include "mitkPlanarCircle.h"
#include "mitkPlanarCross.h"
#include "mitkPlanarDoubleEllipse.h"
#include "mitkPlanarEllipse.h"
#include "mitkPlanarFourPointAngle.h"
#include "mitkPlanarLine.h"
#include "mitkPlanarPolygon.h"
#include "mitkPlanarRectangle.h"
#include "mitkPlanarSubdivisionPolygon.h"
#include "mitkPlaneGeometry.h"
#include "mitkBasePropertySerializer.h"
#include <mitkLocaleSwitch.h>
#include <tinyxml2.h>
namespace mitk
{
PlanarFigureIO::PlanarFigureIO()
: AbstractFileIO(PlanarFigure::GetStaticNameOfClass())
{
std::string category = "MITK PlanarFigure File";
CustomMimeType customMimeType;
customMimeType.SetCategory(category);
customMimeType.AddExtension("pf");
this->AbstractFileIOWriter::SetMimeType(customMimeType);
this->AbstractFileIOWriter::SetDescription(category);
customMimeType.AddExtension("pf");
customMimeType.AddExtension("PF");
this->AbstractFileIOReader::SetMimeType(customMimeType);
this->AbstractFileIOReader::SetDescription(category);
AbstractFileWriter::SetRanking(10);
AbstractFileReader::SetRanking(10);
this->RegisterService();
}
IFileIO::ConfidenceLevel PlanarFigureIO::GetWriterConfidenceLevel() const
{
if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported)
return Unsupported;
const auto *input = static_cast<const PlanarFigure *>(this->GetInput());
if (input != nullptr)
return Supported;
else
return Unsupported;
}
void PlanarFigureIO::Write()
{
this->ValidateOutputLocation();
mitk::LocaleSwitch localeSwitch("C");
tinyxml2::XMLDocument document;
document.InsertEndChild(document.NewDeclaration());
auto *version = document.NewElement("Version");
version->SetAttribute("Writer", __FILE__);
version->SetAttribute("CVSRevision", "$Revision: 17055 $");
version->SetAttribute("FileVersion", 1);
document.InsertEndChild(version);
auto pf = dynamic_cast<const PlanarFigure*>(this->GetInput());
if (pf == nullptr)
{
mitkThrow() << "Try to safe a BaseData instance as PlanarFigure. That is not a planar figure. This should not happen and is a violated precondition. Please check the program logic.";
}
auto *pfElement = document.NewElement("PlanarFigure");
pfElement->SetAttribute("type", pf->GetNameOfClass());
document.InsertEndChild(pfElement);
// Serialize property list of PlanarFigure
mitk::PropertyList::Pointer propertyList = pf->GetPropertyList();
mitk::PropertyList::PropertyMap::const_iterator it;
for (it = propertyList->GetMap()->begin(); it != propertyList->GetMap()->end(); ++it)
{
- // Create seralizer for this property
+ // Create serializer for this property
const mitk::BaseProperty* prop = it->second;
std::string serializerName = std::string(prop->GetNameOfClass()) + "Serializer";
std::list<itk::LightObject::Pointer> allSerializers =
itk::ObjectFactoryBase::CreateAllInstance(serializerName.c_str());
if (allSerializers.size() != 1)
{
// No or too many serializer(s) found, skip this property
continue;
}
auto* serializer =
dynamic_cast<mitk::BasePropertySerializer*>(allSerializers.begin()->GetPointer());
if (serializer == nullptr)
{
// Serializer not valid; skip this property
}
auto *keyElement = document.NewElement("property");
keyElement->SetAttribute("key", it->first.c_str());
keyElement->SetAttribute("type", prop->GetNameOfClass());
serializer->SetProperty(prop);
tinyxml2::XMLElement* valueElement = nullptr;
try
{
valueElement = serializer->Serialize(document);
}
catch (...)
{
}
if (valueElement == nullptr)
{
// Serialization failed; skip this property
continue;
}
// Add value to property element
keyElement->InsertEndChild(valueElement);
// Append serialized property to property list
pfElement->InsertEndChild(keyElement);
}
// Serialize control points of PlanarFigure
auto *controlPointsElement = document.NewElement("ControlPoints");
pfElement->InsertEndChild(controlPointsElement);
for (unsigned int i = 0; i < pf->GetNumberOfControlPoints(); i++)
{
auto *vElement = document.NewElement("Vertex");
vElement->SetAttribute("id", i);
vElement->SetAttribute("x", pf->GetControlPoint(i)[0]);
vElement->SetAttribute("y", pf->GetControlPoint(i)[1]);
controlPointsElement->InsertEndChild(vElement);
}
auto *geoElement = document.NewElement("Geometry");
const auto* planeGeo = dynamic_cast<const PlaneGeometry*>(pf->GetPlaneGeometry());
if (planeGeo != nullptr)
{
// Write parameters of IndexToWorldTransform of the PlaneGeometry
typedef mitk::Geometry3D::TransformType TransformType;
const TransformType* affineGeometry = planeGeo->GetIndexToWorldTransform();
const TransformType::ParametersType& parameters = affineGeometry->GetParameters();
auto *vElement = document.NewElement("transformParam");
for (unsigned int i = 0; i < affineGeometry->GetNumberOfParameters(); ++i)
{
std::stringstream paramName;
paramName << "param" << i;
vElement->SetAttribute(paramName.str().c_str(), parameters.GetElement(i));
}
geoElement->InsertEndChild(vElement);
// Write bounds of the PlaneGeometry
typedef mitk::Geometry3D::BoundsArrayType BoundsArrayType;
const BoundsArrayType& bounds = planeGeo->GetBounds();
vElement = document.NewElement("boundsParam");
for (unsigned int i = 0; i < 6; ++i)
{
std::stringstream boundName;
boundName << "bound" << i;
vElement->SetAttribute(boundName.str().c_str(), bounds.GetElement(i));
}
geoElement->InsertEndChild(vElement);
// Write spacing and origin of the PlaneGeometry
Vector3D spacing = planeGeo->GetSpacing();
Point3D origin = planeGeo->GetOrigin();
geoElement->InsertEndChild(this->CreateXMLVectorElement(document, "Spacing", spacing));
geoElement->InsertEndChild(this->CreateXMLVectorElement(document, "Origin", origin));
pfElement->InsertEndChild(geoElement);
}
if (this->GetOutputStream() != nullptr)
{
tinyxml2::XMLPrinter printer;
document.Print(&printer);
*(this->GetOutputStream()) << printer.CStr();
}
else
{
if (document.SaveFile(this->GetOutputLocation().c_str()) != tinyxml2::XML_SUCCESS)
{
MITK_ERROR << "Could not write planar figures to " << this->GetOutputLocation() << "\nTinyXML reports '" << document.ErrorStr()
<< "'";
throw std::ios_base::failure("Error during writing of planar figure xml file.");
}
}
}
tinyxml2::XMLElement* mitk::PlanarFigureIO::CreateXMLVectorElement(tinyxml2::XMLDocument& doc, const char* name, itk::FixedArray<mitk::ScalarType, 3> v)
{
auto vElement = doc.NewElement(name);
vElement->SetAttribute("x", v.GetElement(0));
vElement->SetAttribute("y", v.GetElement(1));
vElement->SetAttribute("z", v.GetElement(2));
return vElement;
}
IFileIO::ConfidenceLevel PlanarFigureIO::GetReaderConfidenceLevel() const
{
if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported)
return Unsupported;
return Supported;
//Remark: The original reader code assumed that al pf files can be read.
//So no content checking was done. Thus was not implemented while refactoring
//to services yet. But I think it would make sense.
}
std::vector<BaseData::Pointer> PlanarFigureIO::DoRead()
{
mitk::LocaleSwitch localeSwitch("C");
std::vector<BaseData::Pointer> results;
tinyxml2::XMLDocument document;
if (this->GetInputStream() != nullptr)
{
std::string s(std::istreambuf_iterator<char>(*(this->GetInputStream())), {});
document.Parse(s.c_str());
//Remark: didn't use *(this->GetInputStream()) >> document;
//because our PlanarFigure files version 1 are illformed (multiple top level elements)
- //and therefor tinyxml does not read them completly when streamed directly.
+ //and therefor tinyxml does not read them completely when streamed directly.
//only the first (version element) is read.
}
else
{
if (tinyxml2::XML_SUCCESS != document.LoadFile(this->GetInputLocation().c_str()))
{
MITK_ERROR << "Could not open/read/parse " << this->GetInputLocation() << ". TinyXML reports: '" << document.ErrorStr() << "'.";
return {};
}
}
int fileVersion = 1;
auto* versionObject = document.FirstChildElement("Version");
if (versionObject != nullptr)
{
if (versionObject->QueryIntAttribute("FileVersion", &fileVersion) != tinyxml2::XML_SUCCESS)
{
MITK_WARN << this->GetInputLocation() << " does not contain version information! Trying version 1 format." << std::endl;
}
}
else
{
MITK_WARN << this->GetInputLocation() << " does not contain version information! Trying version 1 format." << std::endl;
}
if (fileVersion !=
1) // add file version selection and version specific file parsing here, if newer file versions are created
{
MITK_WARN << "File version > 1 is not supported by this reader.";
return {};
}
/* file version 1 reader code */
for (auto* pfElement = document.FirstChildElement("PlanarFigure"); pfElement != nullptr;
pfElement = pfElement->NextSiblingElement("PlanarFigure"))
{
const char* typeC = pfElement->Attribute("type");
std::string type = nullptr != typeC
? typeC
: "";
mitk::PlanarFigure::Pointer planarFigure = nullptr;
if (type == "PlanarAngle")
{
planarFigure = mitk::PlanarAngle::New();
}
else if (type == "PlanarCircle")
{
planarFigure = mitk::PlanarCircle::New();
}
else if (type == "PlanarEllipse")
{
planarFigure = mitk::PlanarEllipse::New();
}
else if (type == "PlanarCross")
{
planarFigure = mitk::PlanarCross::New();
}
else if (type == "PlanarFourPointAngle")
{
planarFigure = mitk::PlanarFourPointAngle::New();
}
else if (type == "PlanarLine")
{
planarFigure = mitk::PlanarLine::New();
}
else if (type == "PlanarPolygon")
{
planarFigure = mitk::PlanarPolygon::New();
}
else if (type == "PlanarSubdivisionPolygon")
{
planarFigure = mitk::PlanarSubdivisionPolygon::New();
}
else if (type == "PlanarRectangle")
{
planarFigure = mitk::PlanarRectangle::New();
}
else if (type == "PlanarArrow")
{
planarFigure = mitk::PlanarArrow::New();
}
else if (type == "PlanarDoubleEllipse")
{
planarFigure = mitk::PlanarDoubleEllipse::New();
}
else if (type == "PlanarBezierCurve")
{
planarFigure = mitk::PlanarBezierCurve::New();
}
else
{
// unknown type
MITK_WARN << "encountered unknown planar figure type '" << type << "'. Skipping this element.";
continue;
}
// Read properties of the planar figure
for (auto* propertyElement = pfElement->FirstChildElement("property"); propertyElement != nullptr;
propertyElement = propertyElement->NextSiblingElement("property"))
{
const char* keya = propertyElement->Attribute("key");
const std::string key(keya ? keya : "");
const char* typea = propertyElement->Attribute("type");
const std::string type(typea ? typea : "");
// hand propertyElement to specific reader
std::stringstream propertyDeserializerClassName;
propertyDeserializerClassName << type << "Serializer";
const std::list<itk::LightObject::Pointer> readers =
itk::ObjectFactoryBase::CreateAllInstance(propertyDeserializerClassName.str().c_str());
if (readers.size() < 1)
{
MITK_ERROR << "No property reader found for " << type;
}
if (readers.size() > 1)
{
MITK_WARN << "Multiple property readers found for " << type << ". Using arbitrary first one.";
}
for (auto iter = readers.cbegin(); iter != readers.cend(); ++iter)
{
if (auto* reader = dynamic_cast<BasePropertySerializer*>(iter->GetPointer()))
{
const BaseProperty::Pointer property = reader->Deserialize(propertyElement->FirstChildElement());
if (property.IsNotNull())
{
planarFigure->GetPropertyList()->ReplaceProperty(key, property);
}
else
{
MITK_ERROR << "There were errors while loading property '" << key << "' of type " << type
<< ". Your data may be corrupted";
}
break;
}
}
}
// If we load a planarFigure, it has definitely been placed correctly.
// If we do not set this property here, we cannot load old planarFigures
// without messing up the interaction (PF-Interactor needs this property.
planarFigure->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
// Which features (length or circumference etc) a figure has is decided by whether it is closed or not
// the function SetClosed has to be called in case of PlanarPolygons to ensure they hold the correct feature
auto* planarPolygon = dynamic_cast<PlanarPolygon*>(planarFigure.GetPointer());
if (planarPolygon != nullptr)
{
bool isClosed = false;
planarFigure->GetPropertyList()->GetBoolProperty("closed", isClosed);
planarPolygon->SetClosed(isClosed);
}
// Read geometry of containing plane
auto* geoElement = pfElement->FirstChildElement("Geometry");
if (geoElement != nullptr)
{
try
{
// Create plane geometry
mitk::PlaneGeometry::Pointer planeGeo = mitk::PlaneGeometry::New();
// Extract and set plane transform parameters
const DoubleList transformList =
this->GetDoubleAttributeListFromXMLNode(geoElement->FirstChildElement("transformParam"), "param", 12);
typedef mitk::BaseGeometry::TransformType TransformType;
TransformType::ParametersType parameters;
parameters.SetSize(12);
unsigned int i;
DoubleList::const_iterator it;
for (it = transformList.cbegin(), i = 0; it != transformList.cend(); ++it, ++i)
{
parameters.SetElement(i, *it);
}
typedef mitk::BaseGeometry::TransformType TransformType;
TransformType::Pointer affineGeometry = TransformType::New();
affineGeometry->SetParameters(parameters);
planeGeo->SetIndexToWorldTransform(affineGeometry);
// Extract and set plane bounds
const DoubleList boundsList =
this->GetDoubleAttributeListFromXMLNode(geoElement->FirstChildElement("boundsParam"), "bound", 6);
typedef mitk::BaseGeometry::BoundsArrayType BoundsArrayType;
BoundsArrayType bounds;
for (it = boundsList.cbegin(), i = 0; it != boundsList.cend(); ++it, ++i)
{
bounds[i] = *it;
}
planeGeo->SetBounds(bounds);
// Extract and set spacing and origin
const Vector3D spacing = this->GetVectorFromXMLNode(geoElement->FirstChildElement("Spacing"));
planeGeo->SetSpacing(spacing);
const Point3D origin = this->GetPointFromXMLNode(geoElement->FirstChildElement("Origin"));
planeGeo->SetOrigin(origin);
planarFigure->SetPlaneGeometry(planeGeo);
}
catch (...)
{
}
}
auto* cpElement = pfElement->FirstChildElement("ControlPoints");
bool first = true;
if (cpElement != nullptr)
for (auto* vertElement = cpElement->FirstChildElement("Vertex"); vertElement != nullptr;
vertElement = vertElement->NextSiblingElement("Vertex"))
{
int id = 0;
mitk::Point2D::ValueType x = 0.0;
mitk::Point2D::ValueType y = 0.0;
if (vertElement->QueryIntAttribute("id", &id) != tinyxml2::XML_SUCCESS)
return{}; // TODO: can we do a better error handling?
if (vertElement->QueryDoubleAttribute("x", &x) != tinyxml2::XML_SUCCESS)
return{}; // TODO: can we do a better error handling?
if (vertElement->QueryDoubleAttribute("y", &y) != tinyxml2::XML_SUCCESS)
return{}; // TODO: can we do a better error handling?
Point2D p;
p.SetElement(0, x);
p.SetElement(1, y);
if (first == true) // needed to set m_FigurePlaced to true
{
planarFigure->PlaceFigure(p);
first = false;
}
planarFigure->SetControlPoint(id, p, true);
}
// Calculate feature quantities of this PlanarFigure
planarFigure->EvaluateFeatures();
// Make sure that no control point is currently selected
planarFigure->DeselectControlPoint();
if (planarFigure.IsNotNull())
{
results.emplace_back(planarFigure);
}
}
return results;
}
mitk::PlanarFigureIO::DoubleList mitk::PlanarFigureIO::GetDoubleAttributeListFromXMLNode(
const tinyxml2::XMLElement* e, const char* attributeNameBase, unsigned int count)
{
DoubleList list;
if (e == nullptr)
throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling?
for (unsigned int i = 0; i < count; ++i)
{
mitk::ScalarType p(-1.0);
std::stringstream attributeName;
attributeName << attributeNameBase << i;
if (e->QueryDoubleAttribute(attributeName.str().c_str(), &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
list.push_back(p);
}
return list;
}
mitk::Point3D mitk::PlanarFigureIO::GetPointFromXMLNode(const tinyxml2::XMLElement* e)
{
if (e == nullptr)
throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling?
mitk::Point3D point;
mitk::ScalarType p(-1.0);
if (e->QueryDoubleAttribute("x", &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
point.SetElement(0, p);
if (e->QueryDoubleAttribute("y", &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
point.SetElement(1, p);
if (e->QueryDoubleAttribute("z", &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
point.SetElement(2, p);
return point;
}
mitk::Vector3D mitk::PlanarFigureIO::GetVectorFromXMLNode(const tinyxml2::XMLElement* e)
{
if (e == nullptr)
throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling?
mitk::Vector3D vector;
mitk::ScalarType p(-1.0);
if (e->QueryDoubleAttribute("x", &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
vector.SetElement(0, p);
if (e->QueryDoubleAttribute("y", &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
vector.SetElement(1, p);
if (e->QueryDoubleAttribute("z", &p) != tinyxml2::XML_SUCCESS)
throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
vector.SetElement(2, p);
return vector;
}
PlanarFigureIO *PlanarFigureIO::IOClone() const { return new PlanarFigureIO(*this); }
} // namespace
diff --git a/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.h b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.h
index 0c7c08f292..395f05b730 100644
--- a/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.h
+++ b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.h
@@ -1,97 +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 mitkPlanarFigureIO_h
#define mitkPlanarFigureIO_h
#include <mitkAbstractFileIO.h>
#include <mitkPlanarFigure.h>
namespace tinyxml2
{
class XMLDocument;
class XMLElement;
}
namespace mitk
{
/**
* Reads/Writes a PlanarFigure to a file
* @ingroup Process
*/
class PlanarFigureIO : public mitk::AbstractFileIO
{
public:
typedef mitk::PlanarFigure InputType;
PlanarFigureIO();
// -------------- AbstractFileReader -------------
using AbstractFileReader::Read;
ConfidenceLevel GetReaderConfidenceLevel() const override;
// -------------- AbstractFileWriter -------------
void Write() override;
ConfidenceLevel GetWriterConfidenceLevel() const override;
protected:
/**
* @brief Reads a number of mitk::PlanarFigures from the file system
* @return a vector of mitk::PlanarFigures
- * @throws throws an mitk::Exception if an error ocurrs during parsing the nrrd header
+ * @throws throws an mitk::Exception if an error occurs during parsing the nrrd header
*/
std::vector<itk::SmartPointer<BaseData>> DoRead() override;
using DoubleList = std::list<double>;
/**
* \brief parses the element for the attributes name0 to nameN, where "name" and the number of attributes
* to read are passed as argument. Returns a list of double vales.
* \param[in] e the XML element that will be parsed
* \param[in] attributeNameBase the basic name of the parameters
* \param[in] count the number of parameters
* \return returns a mitk::Point3D with the values x,y,z
*/
DoubleList GetDoubleAttributeListFromXMLNode(const tinyxml2::XMLElement* e, const char* attributeNameBase, unsigned int count);
/**
* \brief parses the element for the attributes x,y,z and returns a mitk::Vector3D filled with these values
* \param[in] e the XML element that will be parsed
* \return returns a mitk::Vector3D with the values x,y,z
*/
static mitk::Vector3D GetVectorFromXMLNode(const tinyxml2::XMLElement* e);
/**
* \brief parses the element for the attributes x,y,z and returns a mitk::Point3D filled with these values
* \param[in] e the XML element that will be parsed
* \return returns a mitk::Point3D with the values x,y,z
*/
static mitk::Point3D GetPointFromXMLNode(const tinyxml2::XMLElement* e);
/**Documentation
* \brief creates a TinyXML element that contains x, y, and z values
*
* \param[in] doc
* \param[in] name the name of the XML element
* \param[in] v the vector or point that contains the x, y and z values
* \return returns a XML element named name and three attributes x, y and z.
*/
static tinyxml2::XMLElement* CreateXMLVectorElement(tinyxml2::XMLDocument& doc, const char* name, itk::FixedArray<mitk::ScalarType, 3> v);
private:
PlanarFigureIO *IOClone() const override;
};
} // end of namespace mitk
#endif
diff --git a/Modules/PlanarFigure/include/mitkImageToPlanarFigureFilter.h b/Modules/PlanarFigure/include/mitkImageToPlanarFigureFilter.h
index a3cd22f028..fb4e62a52a 100644
--- a/Modules/PlanarFigure/include/mitkImageToPlanarFigureFilter.h
+++ b/Modules/PlanarFigure/include/mitkImageToPlanarFigureFilter.h
@@ -1,74 +1,74 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkImageToPlanarFigureFilter_h
#define mitkImageToPlanarFigureFilter_h
#include "mitkCommon.h"
#include "mitkImage.h"
#include "mitkPlanarFigureSource.h"
namespace mitk
{
//##Documentation
//## @brief Superclass of all classes having one or more Images as input and
//## generating PlanarFigures as output
//## @ingroup MitkPlanarFigureModule
class MITKPLANARFIGURE_EXPORT ImageToPlanarFigureFilter : public PlanarFigureSource
{
public:
mitkClassMacro(ImageToPlanarFigureFilter, PlanarFigureSource);
// itkFactorylessNewMacro(Self)
// itkCloneMacro(Self)
/** Some convenient typedefs. */
typedef mitk::Image InputImageType;
typedef InputImageType::Pointer InputImagePointer;
typedef InputImageType::ConstPointer InputImageConstPointer;
typedef SlicedData::RegionType InputImageRegionType;
/** Set/Get the image input of this process object. */
using Superclass::SetInput;
virtual void SetInput(const InputImageType *image);
virtual void SetInput(unsigned int, const InputImageType *image);
const InputImageType *GetInput(void);
const InputImageType *GetInput(unsigned int idx);
protected:
ImageToPlanarFigureFilter();
~ImageToPlanarFigureFilter() override;
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
/** What is the input requested region that is required to produce the
* output requested region? The base assumption for image processing
* filters is that the input requested region can be set to match the
* output requested region. If a filter requires more input (for instance
* a filter that uses neighborhoods needs more input than output to avoid
* introducing artificial boundary conditions) or less input (for instance
* a magnify filter) will have to override this method. In doing so, it
* should call its superclass' implementation as its first step. Note that
* this imaging filters operate differently than the classes to this
- * point in the class hierachy. Up till now, the base assumption has been
+ * point in the class hierarchy. Up till now, the base assumption has been
* that the largest possible region will be requested of the input.
*
* \sa ProcessObject::GenerateInputRequestedRegion(),
* ImageSource::GenerateInputRequestedRegion() */
void GenerateInputRequestedRegion() override;
private:
void operator=(const Self &); // purposely not implemented
};
} // namespace mitk
#endif
diff --git a/Modules/PlanarFigure/include/mitkPlanarFigure.h b/Modules/PlanarFigure/include/mitkPlanarFigure.h
index d4ba5a9ca8..a5f5c355db 100644
--- a/Modules/PlanarFigure/include/mitkPlanarFigure.h
+++ b/Modules/PlanarFigure/include/mitkPlanarFigure.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 mitkPlanarFigure_h
#define mitkPlanarFigure_h
#include "mitkBaseData.h"
#include "mitkCommon.h"
#include <MitkPlanarFigureExports.h>
#include <deque>
namespace mitk
{
class PlaneGeometry;
/**
* \brief Base-class for geometric planar (2D) figures, such as
* lines, circles, rectangles, polygons, etc.
*
* \warning Currently does not support time-resolved data handling
*
* Behavior and appearance of PlanarFigures are controlled by various properties; for a detailed
* list of appearance properties see mitk::PlanarFigureMapper2D
*
* The following properties control general PlanarFigure behavior:
*
* <ul>
* <li>"selected": true if the planar figure is selected
* <li>"planarfigure.ishovering": true if the mouse "hovers" over the planar figure
* <li>"planarfigure.iseditable": true if the planar figure can be edited (otherwise,
* it can only be picked/selected, but its control points cannot be edited); default is true
* <li>"planarfigure.isextendable": true if new control points can be inserted into the list of control points;
* default is false
* </ul>
*
*
* TODO: Implement local 2D transform (including center of rotation...)
*
*/
class MITKPLANARFIGURE_EXPORT PlanarFigure : public BaseData
{
public:
mitkClassMacro(PlanarFigure, BaseData);
itkCloneMacro(Self);
typedef Point2D PolyLineElement;
typedef itk::VectorContainer<unsigned long, bool> BoolContainerType;
typedef std::deque<Point2D> ControlPointListType;
typedef std::vector<PolyLineElement> PolyLineType;
/** \brief Sets the 2D geometry on which this figure will be placed.
*
* In most cases, this is a Geometry already owned by another object, e.g.
* describing the slice of the image on which measurements will be
* performed.
*/
virtual void SetPlaneGeometry(mitk::PlaneGeometry *geometry);
/** \brief Returns (previously set) 2D geometry of this figure. */
virtual const PlaneGeometry *GetPlaneGeometry() const;
/** \brief True if the planar figure is closed.
*
* Default is false. The "closed" boolean property must be set in sub-classes. */
virtual bool IsClosed() const;
/** \brief True if the planar figure has been placed (and can be
* displayed/interacted with). */
virtual bool IsPlaced() const { return m_FigurePlaced; };
/** \brief Place figure at the given point (in 2D index coordinates) onto
* the given 2D geometry.
*
* By default, the first two control points of the figure are set to the
* passed point. Further points can be set via AddControlPoint(), if the
* current number of control points is below the maximum number of control
* points.
*
* Can be re-implemented in sub-classes as needed.
*/
virtual void PlaceFigure(const Point2D &point);
/**
* \brief Adds / inserts new control-points
*
* This method adds a new control-point with the coordinates defined by point at the given index.
* If 'index' == -1 or index is greater than the number of control-points the new point is appended
* to the back of the list of control points.
* If a control-point already exists for 'index', an additional point is inserted at that position.
* It is not possible to add more points if the maximum number of control-points (GetMaximumNumberOfControlPoints())
* has been reached.
*/
virtual bool AddControlPoint(const Point2D &point, int index = -1);
virtual bool SetControlPoint(unsigned int index, const Point2D &point, bool createIfDoesNotExist = false);
virtual bool SetCurrentControlPoint(const Point2D &point);
/** \brief Returns the current number of 2D control points defining this figure. */
unsigned int GetNumberOfControlPoints() const;
/** \brief Returns the minimum number of control points needed to represent
* this figure.
*
* Must be implemented in sub-classes.
*/
virtual unsigned int GetMinimumNumberOfControlPoints() const = 0;
/** \brief Returns the maximum number of control points allowed for
* this figure (e.g. 3 for triangles).
*
* Must be implemented in sub-classes.
*/
virtual unsigned int GetMaximumNumberOfControlPoints() const = 0;
/** \brief Selects currently active control points. */
virtual bool SelectControlPoint(unsigned int index);
/** \brief Deselect control point; no control point active. */
virtual bool DeselectControlPoint();
/** \brief Return currently selected control point. */
virtual int GetSelectedControlPoint() const { return m_SelectedControlPoint; }
/** \brief Returns specified control point in 2D world coordinates. */
Point2D GetControlPoint(unsigned int index) const;
/**
* \brief Returns the id of the control-point that corresponds to the given
* polyline-point.
*/
virtual int GetControlPointForPolylinePoint(int indexOfPolylinePoint, int polyLineIndex) const;
/** \brief Returns specified control point in world coordinates. */
Point3D GetWorldControlPoint(unsigned int index) const;
/** \brief Returns the polyline representing the planar figure
* (for rendering, measurements, etc.). */
const PolyLineType GetPolyLine(unsigned int index);
/** \brief Returns the polyline representing the planar figure
- * (for rendering, measurments, etc.). */
+ * (for rendering, measurements, etc.). */
const PolyLineType GetPolyLine(unsigned int index) const;
/** \brief Returns the polyline that should be drawn the same size at every scale
* (for text, angles, etc.). */
const PolyLineType GetHelperPolyLine(unsigned int index, double mmPerDisplayUnit, unsigned int displayHeight);
/** \brief Sets the position of the PreviewControlPoint. Automatically sets it visible.*/
void SetPreviewControlPoint(const Point2D &point);
/** \brief Marks the PreviewControlPoint as invisible.*/
void ResetPreviewContolPoint();
/** \brief Returns whether or not the PreviewControlPoint is visible.*/
bool IsPreviewControlPointVisible() const;
/** \brief Returns the coordinates of the PreviewControlPoint. */
Point2D GetPreviewControlPoint() const;
/** \brief Returns the number of features available for this PlanarFigure
* (such as, radius, area, ...). */
virtual unsigned int GetNumberOfFeatures() const;
/** \brief Returns the name (identifier) of the specified features. */
const char *GetFeatureName(unsigned int index) const;
/** \brief Returns the physical unit of the specified features. */
const char *GetFeatureUnit(unsigned int index) const;
/** Returns quantity of the specified feature (e.g., length, radius,
* area, ... ) */
double GetQuantity(unsigned int index) const;
/** \brief Returns true if the feature with the specified index exists and
* is active (an inactive feature may e.g. be the area of a non-closed
* polygon. */
bool IsFeatureActive(unsigned int index) const;
/** \brief Returns true if the feature with the specified index exists and is set visible */
bool IsFeatureVisible(unsigned int index) const;
/** \brief Defines if the feature with the specified index will be shown as an
* Annotation in the RenderWindow */
void SetFeatureVisible(unsigned int index, bool visible);
/** \brief Calculates quantities of all features of this planar figure. */
virtual void EvaluateFeatures();
/** \brief Intherited from parent */
void UpdateOutputInformation() override;
/** \brief Intherited from parent */
void SetRequestedRegionToLargestPossibleRegion() override;
/** \brief Intherited from parent */
bool RequestedRegionIsOutsideOfTheBufferedRegion() override;
/** \brief Intherited from parent */
bool VerifyRequestedRegion() override;
/** \brief Intherited from parent */
void SetRequestedRegion(const itk::DataObject *data) override;
/** \brief Returns the current number of polylines */
virtual unsigned short GetPolyLinesSize();
/** \brief Returns the current number of helperpolylines */
virtual unsigned short GetHelperPolyLinesSize() const;
/** \brief Returns whether a helper polyline should be painted or not */
virtual bool IsHelperToBePainted(unsigned int index) const;
/** \brief Returns true if the planar figure is reset to "add points" mode
* when a point is selected.
*
* Default return value is false. Subclasses can overwrite this method and
* execute any reset / initialization statements required. */
virtual bool ResetOnPointSelect();
virtual bool ResetOnPointSelectNeeded() const;
/** \brief removes the point with the given index from the list of controlpoints. */
virtual void RemoveControlPoint(unsigned int index);
/** \brief Removes last control point */
virtual void RemoveLastControlPoint();
/** \brief Allow sub-classes to apply constraints on control points.
*
* Sub-classes can define spatial constraints to certain control points by
* overwriting this method and returning a constrained point. By default,
* the points are constrained by the image bounds. */
virtual Point2D ApplyControlPointConstraints(unsigned int /*index*/, const Point2D &point);
/**
* \brief Compare two PlanarFigure objects
* Note: all subclasses have to implement the method on their own.
*/
virtual bool Equals(const mitk::PlanarFigure &other) const;
/** \brief Set the initial number of control points of the planar figure */
void ResetNumberOfControlPoints(int numberOfControlPoints);
protected:
PlanarFigure();
PlanarFigure(const Self &other);
/** Adds feature (e.g., circumference, radius, angle, ...) to feature vector
* of a planar figure object and returns integer ID for the feature element.
* Should be called in sub-class constructors. */
virtual unsigned int AddFeature(const char *featureName, const char *unitName);
/** Sets the name of the specified feature. INTERNAL METHOD. */
void SetFeatureName(unsigned int index, const char *featureName);
/** Sets the physical unit of the specified feature. INTERNAL METHOD. */
void SetFeatureUnit(unsigned int index, const char *unitName);
/** Sets quantity of the specified feature. INTERNAL METHOD. */
void SetQuantity(unsigned int index, double quantity);
/** Sets the specified feature as active. INTERAL METHOD. */
void ActivateFeature(unsigned int index);
/** Sets the specified feature as active. INTERAL METHOD. */
void DeactivateFeature(unsigned int index);
/** \brief Generates the poly-line representation of the planar figure.
* Must be implemented in sub-classes. */
virtual void GeneratePolyLine() = 0;
/** \brief Generates the poly-lines that should be drawn the same size regardless of zoom.
* Must be implemented in sub-classes. */
virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight) = 0;
/** \brief Calculates quantities of all features of this planar figure.
* Must be implemented in sub-classes. */
virtual void EvaluateFeaturesInternal() = 0;
/** \brief Initializes the TimeGeometry describing the (time-resolved)
* geometry of this figure. Note that each time step holds one PlaneGeometry.
*/
void InitializeTimeGeometry(unsigned int timeSteps = 1) override;
/** \brief defines the number of PolyLines that will be available */
void SetNumberOfPolyLines(unsigned int numberOfPolyLines);
/** \brief Append a point to the PolyLine # index */
void AppendPointToPolyLine(unsigned int index, PolyLineElement element);
/** \brief clears the list of PolyLines. Call before re-calculating a new Polyline. */
void ClearPolyLines();
/** \brief defines the number of HelperPolyLines that will be available */
void SetNumberOfHelperPolyLines(unsigned int numberOfHelperPolyLines);
/** \brief Append a point to the HelperPolyLine # index */
void AppendPointToHelperPolyLine(unsigned int index, PolyLineElement element);
/** \brief clears the list of HelperPolyLines. Call before re-calculating a new HelperPolyline. */
void ClearHelperPolyLines();
void PrintSelf(std::ostream &os, itk::Indent indent) const override;
ControlPointListType m_ControlPoints;
unsigned int m_NumberOfControlPoints;
// Currently selected control point; -1 means no point selected
int m_SelectedControlPoint;
std::vector<PolyLineType> m_PolyLines;
std::vector<PolyLineType> m_HelperPolyLines;
BoolContainerType::Pointer m_HelperPolyLinesToBePainted;
- // this point is used to store the coordiantes an additional 'ControlPoint' that is rendered
+ // this point is used to store the coordinates an additional 'ControlPoint' that is rendered
// when the mouse cursor is above the figure (and not a control-point) and when the
// property 'planarfigure.isextendable' is set to true
Point2D m_PreviewControlPoint;
bool m_PreviewControlPointVisible;
bool m_FigurePlaced;
private:
// not implemented to prevent PlanarFigure::New() calls which would create an itk::Object.
static Pointer New();
struct Feature
{
Feature(const char *name, const char *unit) : Name(name), Unit(unit), Quantity(0.0), Active(true), Visible(true)
{
}
std::string Name;
std::string Unit;
double Quantity;
bool Active;
bool Visible;
};
itk::LightObject::Pointer InternalClone() const override = 0;
bool m_PolyLineUpToDate;
bool m_HelperLinesUpToDate;
bool m_FeaturesUpToDate;
// Vector of features available for this geometric figure
typedef std::vector<Feature> FeatureVectorType;
FeatureVectorType m_Features;
unsigned long m_FeaturesMTime;
// this pair is used to store the mmInDisplayUnits (m_DisplaySize.first) and the displayHeight
// (m_DisplaySize.second)
// that the helperPolyLines have been calculated for.
// It's used to determine whether or not GetHelperPolyLine() needs to recalculate the HelperPolyLines.
std::pair<double, unsigned int> m_DisplaySize;
};
MITKPLANARFIGURE_EXPORT bool Equal(const mitk::PlanarFigure &leftHandSide,
const mitk::PlanarFigure &rightHandSide,
ScalarType eps,
bool verbose);
} // namespace mitk
#endif
diff --git a/Modules/PlanarFigure/include/mitkPlanarFigureSource.h b/Modules/PlanarFigure/include/mitkPlanarFigureSource.h
index 24c3f99b9f..1404755900 100644
--- a/Modules/PlanarFigure/include/mitkPlanarFigureSource.h
+++ b/Modules/PlanarFigure/include/mitkPlanarFigureSource.h
@@ -1,74 +1,74 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkPlanarFigureSource_h
#define mitkPlanarFigureSource_h
#include "mitkBaseDataSource.h"
#include "mitkCommon.h"
#include "mitkPlanarFigure.h"
#include <MitkPlanarFigureExports.h>
namespace mitk
{
/**
* @brief Base class for all filters which have an object of type
* mitk::PlanarFigure as output
*
* Base class for all filters which have an object of type mitk::PlanarFigure
* as output. mitk::PlanarFigureSources do not provide support
* for streaming, that is, that the requested region is always the largest
* possible region.
* @ingroup MitkPlanarFigureModule
*/
class MITKPLANARFIGURE_EXPORT PlanarFigureSource : public mitk::BaseDataSource
{
public:
mitkClassMacro(PlanarFigureSource, BaseDataSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef mitk::PlanarFigure OutputType;
typedef OutputType::Pointer OutputTypePointer;
typedef itk::DataObject::Pointer DataObjectPointer;
mitkBaseDataSourceGetOutputDeclarations
/**
* 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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
/**
* Generates the input requested region simply by calling the equivalent
* method of the superclass.
*/
void GenerateInputRequestedRegion() override;
protected:
PlanarFigureSource();
~PlanarFigureSource() override;
};
} // namespace mitk
#endif
diff --git a/Modules/PlanarFigure/src/DataManagement/mitkPlanarPolygon.cpp b/Modules/PlanarFigure/src/DataManagement/mitkPlanarPolygon.cpp
index b0222f645e..77cc54a45c 100644
--- a/Modules/PlanarFigure/src/DataManagement/mitkPlanarPolygon.cpp
+++ b/Modules/PlanarFigure/src/DataManagement/mitkPlanarPolygon.cpp
@@ -1,271 +1,271 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkPlanarPolygon.h"
#include "mitkPlaneGeometry.h"
#include "mitkProperties.h"
// stl related includes
#include <algorithm>
mitk::PlanarPolygon::PlanarPolygon()
: FEATURE_ID_CIRCUMFERENCE(this->AddFeature("Circumference", "mm")), FEATURE_ID_AREA(this->AddFeature("Area", "mm2"))
{
// Polygon has at least two control points
this->ResetNumberOfControlPoints(2);
this->SetNumberOfPolyLines(1);
// Polygon is closed by default
this->SetProperty("closed", mitk::BoolProperty::New(true));
this->SetProperty("subdivision", mitk::BoolProperty::New(false));
}
void mitk::PlanarPolygon::SetClosed(bool closed)
{
this->SetProperty("closed", mitk::BoolProperty::New(closed));
if (!closed)
{
// For non-closed polygons: use "Length" as feature name; disable area
this->SetFeatureName(FEATURE_ID_CIRCUMFERENCE, "Length");
this->DeactivateFeature(FEATURE_ID_AREA);
}
else
{
// For closed polygons: use "Circumference" as feature name; enable area
this->SetFeatureName(FEATURE_ID_CIRCUMFERENCE, "Circumference");
this->ActivateFeature(FEATURE_ID_AREA);
}
this->Modified();
}
void mitk::PlanarPolygon::GeneratePolyLine()
{
this->ClearPolyLines();
for (ControlPointListType::size_type i = 0; i < m_ControlPoints.size(); ++i)
this->AppendPointToPolyLine(0, this->GetControlPoint(i));
}
void mitk::PlanarPolygon::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/)
{
// A polygon does not require helper objects
}
void mitk::PlanarPolygon::EvaluateFeaturesInternal()
{
// Calculate circumference
double circumference = 0.0;
unsigned int i, j;
const PolyLineType polyLine = m_PolyLines[0];
if (polyLine.empty())
return;
for (i = 0; i < (polyLine.size() - 1); ++i)
{
circumference += static_cast<Point2D>(polyLine[i]).EuclideanDistanceTo(static_cast<Point2D>(polyLine[i + 1]));
}
if (this->IsClosed())
{
circumference += static_cast<Point2D>(polyLine[i]).EuclideanDistanceTo(static_cast<Point2D>(polyLine.front()));
}
this->SetQuantity(FEATURE_ID_CIRCUMFERENCE, circumference);
// Calculate polygon area (if closed)
double area = 0.0;
bool intersection = false;
if (this->IsClosed() && (this->GetPlaneGeometry() != nullptr))
{
// does PlanarPolygon overlap/intersect itself?
const unsigned int numberOfPoints = polyLine.size();
if (numberOfPoints >= 4)
{
for (i = 0; i < (numberOfPoints - 1); ++i)
{
// line 1
const Point2D p0 = polyLine[i];
const Point2D p1 = polyLine[i + 1];
// check for intersection with all other lines
for (j = i + 1; j < (numberOfPoints - 1); ++j)
{
const Point2D p2 = polyLine[j];
const Point2D p3 = polyLine[j + 1];
intersection = CheckForLineIntersection(p0, p1, p2, p3);
if (intersection)
break;
}
if (intersection)
break; // only because the inner loop might have changed "intersection"
// last line from p_x to p_0
const Point2D p2 = polyLine.front();
const Point2D p3 = polyLine.back();
intersection = CheckForLineIntersection(p0, p1, p2, p3);
if (intersection)
break;
}
}
// calculate area
for (i = 0; i < polyLine.size(); ++i)
{
const Point2D p0 = polyLine[i];
const Point2D p1 = polyLine[(i + 1) % polyLine.size()];
area += p0[0] * p1[1] - p1[0] * p0[1];
}
area /= 2.0;
}
- // set area if appropiate (i.e. closed and not intersected)
+ // set area if appropriate (i.e. closed and not intersected)
if (this->IsClosed() && !intersection)
{
SetQuantity(FEATURE_ID_AREA, fabs(area));
this->ActivateFeature(FEATURE_ID_AREA);
}
else
{
SetQuantity(FEATURE_ID_AREA, 0);
this->DeactivateFeature(FEATURE_ID_AREA);
}
}
void mitk::PlanarPolygon::PrintSelf(std::ostream &os, itk::Indent indent) const
{
Superclass::PrintSelf(os, indent);
if (this->IsClosed())
os << indent << "Polygon is closed\n";
else
os << indent << "Polygon is not closed\n";
}
// based on
// https://flassari.is/2008/11/line-line-intersection-in-cplusplus/
bool mitk::PlanarPolygon::CheckForLineIntersection(const mitk::Point2D &p1,
const mitk::Point2D &p2,
const mitk::Point2D &p3,
const mitk::Point2D &p4,
Point2D &intersection) const
{
// do not check for intersections with control points
if (p1 == p2 || p1 == p3 || p1 == p4 || p2 == p3 || p2 == p4 || p3 == p4)
return false;
// Store the values for fast access and easy
// equations-to-code conversion
const double x1 = p1[0], x2 = p2[0], x3 = p3[0], x4 = p4[0];
const double y1 = p1[1], y2 = p2[1], y3 = p3[1], y4 = p4[1];
const double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
// If d is zero, there is no intersection
// if (d < mitk::eps) return false;
if (d == 0)
return false;
// Get the x and y
const double pre = (x1 * y2 - y1 * x2);
const double post = (x3 * y4 - y3 * x4);
const double x = (pre * (x3 - x4) - (x1 - x2) * post) / d;
const double y = (pre * (y3 - y4) - (y1 - y2) * post) / d;
double tolerance = 0.001;
// Check if the x coordinates are within both lines, including tolerance
if (x < (std::min(x1, x2) - tolerance) || x > (std::max(x1, x2) + tolerance) || x < (std::min(x3, x4) - tolerance) ||
x > (std::max(x3, x4) + tolerance))
{
return false;
}
// Check if the y coordinates are within both lines, including tolerance
if (y < (std::min(y1, y2) - tolerance) || y > (std::max(y1, y2) + tolerance) || y < (std::min(y3, y4) - tolerance) ||
y > (std::max(y3, y4) + tolerance))
{
return false;
}
// point of intersection
Point2D ret;
ret[0] = x;
ret[1] = y;
intersection = ret;
return true;
}
bool mitk::PlanarPolygon::CheckForLineIntersection(const mitk::Point2D &p1,
const mitk::Point2D &p2,
const mitk::Point2D &p3,
const mitk::Point2D &p4) const
{
mitk::Point2D intersection;
return mitk::PlanarPolygon::CheckForLineIntersection(p1, p2, p3, p4, intersection);
}
std::vector<mitk::Point2D> mitk::PlanarPolygon::CheckForLineIntersection(const mitk::Point2D &p1,
const mitk::Point2D &p2) const
{
std::vector<mitk::Point2D> intersectionList;
ControlPointListType polyLinePoints;
const PolyLineType tempList = m_PolyLines[0];
for (auto iter = tempList.cbegin(); iter != tempList.cend(); ++iter)
{
polyLinePoints.push_back(*iter);
}
for (ControlPointListType::size_type i = 0; i < polyLinePoints.size() - 1; i++)
{
const mitk::Point2D pnt1 = polyLinePoints[i];
const mitk::Point2D pnt2 = polyLinePoints[i + 1];
mitk::Point2D intersection;
if (mitk::PlanarPolygon::CheckForLineIntersection(p1, p2, pnt1, pnt2, intersection))
{
intersectionList.push_back(intersection);
}
}
if (this->IsClosed())
{
mitk::Point2D intersection;
const mitk::Point2D lastControlPoint = polyLinePoints.back();
const mitk::Point2D firstControlPoint = polyLinePoints.front();
if (mitk::PlanarPolygon::CheckForLineIntersection(lastControlPoint, firstControlPoint, p1, p2, intersection))
{
intersectionList.push_back(intersection);
}
}
return intersectionList;
}
bool mitk::PlanarPolygon::Equals(const mitk::PlanarFigure &other) const
{
const auto *otherPolygon = dynamic_cast<const mitk::PlanarPolygon *>(&other);
if (otherPolygon)
{
return Superclass::Equals(other);
}
else
{
return false;
}
}
diff --git a/Modules/Python/autoload/PythonService/mitkPythonService.cpp b/Modules/Python/autoload/PythonService/mitkPythonService.cpp
index a8df58ec85..636530ec4a 100644
--- a/Modules/Python/autoload/PythonService/mitkPythonService.cpp
+++ b/Modules/Python/autoload/PythonService/mitkPythonService.cpp
@@ -1,744 +1,744 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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_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();
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.
+ // ToDo: Check if this is a column 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::IOPixelEnum::SCALAR )
{
if( pixelType.GetComponentType() == itk::IOComponentEnum::DOUBLE ) {
npy_type = NPY_DOUBLE;
sitk_type = "sitkFloat64";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::FLOAT ) {
npy_type = NPY_FLOAT;
sitk_type = "sitkFloat32";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::SHORT) {
npy_type = NPY_SHORT;
sitk_type = "sitkInt16";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::CHAR ) {
npy_type = NPY_BYTE;
sitk_type = "sitkInt8";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::INT ) {
npy_type = NPY_INT;
sitk_type = "sitkInt32";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::LONG ) {
npy_type = NPY_LONG;
sitk_type = "sitkInt64";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::UCHAR ) {
npy_type = NPY_UBYTE;
sitk_type = "sitkUInt8";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::UINT ) {
npy_type = NPY_UINT;
sitk_type = "sitkUInt32";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::ULONG ) {
npy_type = NPY_LONG;
sitk_type = "sitkUInt64";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::USHORT ) {
npy_type = NPY_USHORT;
sitk_type = "sitkUInt16";
}
}
else if ( ioPixelType == itk::IOPixelEnum::VECTOR ||
ioPixelType == itk::IOPixelEnum::RGB ||
ioPixelType == itk::IOPixelEnum::RGBA
)
{
if( pixelType.GetComponentType() == itk::IOComponentEnum::DOUBLE ) {
npy_type = NPY_DOUBLE;
sitk_type = "sitkVectorFloat64";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::FLOAT ) {
npy_type = NPY_FLOAT;
sitk_type = "sitkVectorFloat32";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::SHORT) {
npy_type = NPY_SHORT;
sitk_type = "sitkVectorInt16";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::CHAR ) {
npy_type = NPY_BYTE;
sitk_type = "sitkVectorInt8";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::INT ) {
npy_type = NPY_INT;
sitk_type = "sitkVectorInt32";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::LONG ) {
npy_type = NPY_LONG;
sitk_type = "sitkVectorInt64";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::UCHAR ) {
npy_type = NPY_UBYTE;
sitk_type = "sitkVectorUInt8";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::UINT ) {
npy_type = NPY_UINT;
sitk_type = "sitkVectorUInt32";
} else if( pixelType.GetComponentType() == itk::IOComponentEnum::ULONG ) {
npy_type = NPY_LONG;
sitk_type = "sitkVectorUInt64";
} 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;
}
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::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/Python/documentation/mitkPython.dox b/Modules/Python/documentation/mitkPython.dox
index 08ad7e4ef1..e55e2da394 100644
--- a/Modules/Python/documentation/mitkPython.dox
+++ b/Modules/Python/documentation/mitkPython.dox
@@ -1,46 +1,46 @@
/**
\page mitkPython_Overview Python Module
\section python_sec1 Brief description
The MITK Python Module provides a service class to interactively run python code (passed as C++ strings) and
evaluate the results. Furthermore the service class offers means to convert an MITK Image to an ITK image in their wrapped python environment.
<strong>Thus, one can process MITK images with Python Code from the ITK wrapping system</strong>.
Furthermore one can convert an mitk::Surface to a vtkPolyData in its Python environment.<br />
Under the hood, the MITK build system takes care that the wrapping build process for SimpleITK/VTK is correctly initiated and all paths are correctly set within MITK code.
To use the features of the different toolkits make sure they are enabled during the superbuild process.
\section python_sec2 Build Instructions
Have a look at \ref python_sec3 on how to build MITK-Python with Qt6.
The following CMake build options are available:
<ul>
<li> MITK_USE_Python3
</ul>
\subsection python_ssec1 MITK_USE_Python3
MITK_USE_Python3 enables the python wrapping in MITK. When the option is activated
the build of the additional dependency SimpleITK is also enabled. The default behaviour is to use the python runtime from the system is used.
Only Python 3.x is supported.
The user can also specify it's own runtime by modifying the variables added by the
FindPythonLib.cmake script. <strong>Note:</strong> A Python runtime with numpy is needed to use the MITK Python wrapping.
When using this options all additional libraries installed in the python runtime will be available within the MITK-Python console.
-\section python_sec3 Suported Data Types
+\section python_sec3 Supported Data Types
The following data types in MITK are supported in the MITK Python Wrapping:
<ul>
<li> Image
<li> Surface
</ul>
\subsection python_ssec4 Image
Mitk Images can be transferred to python. The images are copied in-memory and
transferred as a numpy array to Python and vice versa. The MITK python wrapping creates a SimpleITK image
using the numpy array with the properties of the MITK Image.
\subsection python_ssec5 Surface
Surfaces within mitk can be transferred as a vtkPolyData Object to Python.
The surfaces are fully memory mapped. When changing a python wrapped surface
the original object is also modified on the C++ side of MITK.
*/
diff --git a/Modules/Python/documentation/slides.tex b/Modules/Python/documentation/slides.tex
index 5be2291ea0..1e5fc442c8 100644
--- a/Modules/Python/documentation/slides.tex
+++ b/Modules/Python/documentation/slides.tex
@@ -1,44 +1,44 @@
\documentclass{beamer}
\usepackage[utf8]{inputenc}
\usepackage{listings}
\usetheme{Warsaw}
\title[The MITK Python Module]{The MITK Python Module\\A short introduction}
\author{Michael Müller}
\institute{mitk.org}
\date{\today}
\begin{document}
\begin{frame}
\titlepage
\end{frame}
\begin{frame}{Problem}
Playing around with ITK/VTK/OpenCV functions in C++ is
\center{\textbf{time consuming}}
\\(setup project, write code, compile, adapt parameters, recompile, execute, do it again, do it again, Visual Studio crashes, do it again...)
\end{frame}
\begin{frame}{Aims}
\begin{itemize}
\item Use Python as an interpreted language to interactively use and test ITK/VTK/OpenCV functions \pause
\item Provide a one click super-build integration \pause
-\item Provide a service class to programatically execute and evaluate Python code \pause
+\item Provide a service class to programmatically execute and evaluate Python code \pause
\item Create a GUI to interactively work with Python within the MITK workbench
\end{itemize}
\end{frame}
\begin{frame}{The Python Module}
\textbf{Central class: IPythonService}\\
Most important functions:\\
\end{frame}
\begin{frame}{The Python View}
write motivation here
\end{frame}
\begin{frame}{Installation}
write motivation here
\end{frame}
\end{document}
diff --git a/Modules/Python/mitkIPythonService.h b/Modules/Python/mitkIPythonService.h
index 874f8e5629..f465cf846d 100644
--- a/Modules/Python/mitkIPythonService.h
+++ b/Modules/Python/mitkIPythonService.h
@@ -1,142 +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 mitkIPythonService_h
#define mitkIPythonService_h
// mitk
#include <mitkImage.h>
#include <mitkSurface.h>
#include <MitkPythonExports.h>
//for microservices
#include <mitkServiceInterface.h>
#include <vector>
class ctkAbstractPythonManager;
namespace mitk
{
///
/// describes a python variable (data container)
/// \see IPythonService::GetVariableStack()
///
struct PythonVariable
{
std::string m_Name;
std::string m_Type;
std::string m_Value;
};
///
/// a PythonCommandObserver gets informed as soon as a python command was issued
/// \see IPythonService::AddPythonCommandObserver()
///
class PythonCommandObserver
{
public:
virtual void CommandExecuted(const std::string& pythonCommand) = 0;
};
///
/// The central service for issuing Python Code
/// The class also enables to transfer mitk images to python as itk::Image and vice versa
/// \see IPythonService::GetVariableStack()
///
class MITKPYTHON_EXPORT IPythonService
{
public:
///
/// Constant representing a single line command
/// \see IPythonService::Execute()
static const int SINGLE_LINE_COMMAND = 0;
///
- /// Constant representing a command in which the commands are seperated by new lines, i.e. "\\n"
+ /// Constant representing a command in which the commands are separated by new lines, i.e. "\\n"
/// \see IPythonService::Execute()
static const int MULTI_LINE_COMMAND = 1;
///
/// Constant representing a single line command x which is run as "eval(x)"
/// \see IPythonService::Execute()
static const int EVAL_COMMAND = 2;
///
/// Executes a python command.
/// \return A variant containing the return value as string of the python code (if any)
virtual std::string Execute( const std::string& pythonCommand, int commandType = SINGLE_LINE_COMMAND ) = 0;
///
/// Executes a python script.
virtual void ExecuteScript( const std::string& pathToPythonScript ) = 0;
///
/// \return true if the last call to Execute...() resulted in an error, false otherwise
virtual bool PythonErrorOccured() const = 0;
///
/// \return The list of variables in the __main__ namespace
virtual std::vector<PythonVariable> GetVariableStack() const = 0;
///
/// \return true if a variable with this name is defined in the __main__ namespace, false otherwise
virtual bool DoesVariableExist(const std::string& name) const = 0;
///
/// \return value of variable with this name as string, empty string if variable does not exist
virtual std::string GetVariable(const std::string& name) const = 0;
///
/// adds a command observer which is informed after a command was issued with "Execute"
virtual void AddPythonCommandObserver( PythonCommandObserver* observer ) = 0;
///
/// removes a specific command observer
virtual void RemovePythonCommandObserver( PythonCommandObserver* observer ) = 0;
///
/// notify all observer. this should only be used if it can be garantueed that the
/// current python interpreter instance got another command from anywhere else
/// the the Execute() method of this service, e.g. the shell widget uses this function
/// since it does not use Execute()
virtual void NotifyObserver( const std::string& command ) = 0;
///
/// \return true, if itk wrapping is available, false otherwise
virtual bool IsSimpleItkPythonWrappingAvailable() = 0;
///
/// copies an mitk image as itk image into the python interpreter process
- /// the image will be available as "varName" in python if everythin worked
+ /// the image will be available as "varName" in python if everything worked
/// \return true if image was copied, else false
virtual bool CopyToPythonAsSimpleItkImage( mitk::Image* image, const std::string& varName ) = 0;
///
/// copies an itk image from the python process that is named "varName"
/// \return the image or 0 if copying was not possible
virtual mitk::Image::Pointer CopySimpleItkImageFromPython( const std::string& varName ) = 0;
///
/// \return true, if vtk wrapping is available, false otherwise
virtual bool IsVtkPythonWrappingAvailable() = 0;
///
/// \see CopyToPythonAsItkImage()
virtual bool CopyToPythonAsVtkPolyData( mitk::Surface* surface, const std::string& varName ) = 0;
/// \return the ctk abstract python manager instance
virtual ctkAbstractPythonManager* GetPythonManager() = 0;
///
/// nothing to do here
virtual ~IPythonService(); // leer in mitkIPythonService.cpp implementieren
// force us module loading by linking
static std::string ForceLoadModule();
virtual void AddRelativeSearchDirs(std::vector< std::string > dirs) = 0;
virtual void AddAbsoluteSearchDirs(std::vector< std::string > dirs) = 0;
};
}
MITK_DECLARE_SERVICE_INTERFACE(mitk::IPythonService, "org.mitk.services.IPythonService")
#endif
diff --git a/Modules/QtOverlays/QmitkOverlayContainerWidget.h b/Modules/QtOverlays/QmitkOverlayContainerWidget.h
index aad0d194fb..9f2165a6fe 100644
--- a/Modules/QtOverlays/QmitkOverlayContainerWidget.h
+++ b/Modules/QtOverlays/QmitkOverlayContainerWidget.h
@@ -1,48 +1,48 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitkOverlayContainerWidget_h
#define QmitkOverlayContainerWidget_h
// Qt
#include <QWidget>
#include <MitkQtOverlaysExports.h>
/**
* \class QmitkOverlayContainerWidget
* \brief Widget that overrides the paintEvent method to correctly display
* the Qt based overlays when using the system-environment variable
-* QT_DEVIDE_PIXEL_RATIO.
+* QT_DEVICE_PIXEL_RATIO.
*/
class MITKQTOVERLAYS_EXPORT QmitkOverlayContainerWidget : public QWidget
{
public:
/**
* @brief Default Constructor
**/
QmitkOverlayContainerWidget(QWidget *parent = nullptr, Qt::WindowFlags f = {});
/**
* @brief Default Destructor
**/
~QmitkOverlayContainerWidget() override;
protected:
/**
* @brief overridden version of paintEvent that correctly clears its canvas before painting.
**/
void paintEvent(QPaintEvent *event) override;
};
#endif
diff --git a/Modules/QtPython/QmitkPythonTextEditor.h b/Modules/QtPython/QmitkPythonTextEditor.h
index 9ec929f1bf..92cb0096f6 100644
--- a/Modules/QtPython/QmitkPythonTextEditor.h
+++ b/Modules/QtPython/QmitkPythonTextEditor.h
@@ -1,49 +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.
============================================================================*/
#ifndef QmitkPythonTextEditor_h
#define QmitkPythonTextEditor_h
#include <QTextEdit>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <MitkQtPythonExports.h>
struct QmitkPythonTextEditorData;
///
-/// this is a python text editor with syntax highlightning
+/// this is a python text editor with syntax highlighting
class MITKQTPYTHON_EXPORT QmitkPythonTextEditor : public QWidget
{
Q_OBJECT
public:
QmitkPythonTextEditor(QWidget *parent = nullptr);
~QmitkPythonTextEditor() override;
public slots:
void Paste(const QString& command);
protected slots:
void on_SaveScript_triggered(bool checked=false);
void on_LoadScript_triggered(bool checked=false);
void on_RunScript_triggered(bool checked=false);
protected:
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;
//bool canInsertFromMimeData( const QMimeData *source ) const;
QString ReadFile(const QString &filename);
private:
QmitkPythonTextEditorData* d;
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkAbstractNodeSelectionWidget.h b/Modules/QtWidgets/include/QmitkAbstractNodeSelectionWidget.h
index fda201043c..7abcad7a9f 100644
--- a/Modules/QtWidgets/include/QmitkAbstractNodeSelectionWidget.h
+++ b/Modules/QtWidgets/include/QmitkAbstractNodeSelectionWidget.h
@@ -1,266 +1,266 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitkAbstractNodeSelectionWidget_h
#define QmitkAbstractNodeSelectionWidget_h
#include <MitkQtWidgetsExports.h>
#include <mitkDataStorage.h>
#include <mitkWeakPointer.h>
#include <mitkNodePredicateBase.h>
#include <QWidget>
class QmitkAbstractDataStorageModel;
/**
* \class QmitkAbstractNodeSelectionWidget
* \brief Abstract base class for the selection of data from a data storage.
*/
class MITKQTWIDGETS_EXPORT QmitkAbstractNodeSelectionWidget : public QWidget
{
Q_OBJECT
public:
explicit QmitkAbstractNodeSelectionWidget(QWidget* parent = nullptr);
virtual ~QmitkAbstractNodeSelectionWidget() override;
/**
* @brief Sets the data storage that will be used / monitored by widget.
*
* @par dataStorage A pointer to the data storage to set.
*/
void SetDataStorage(mitk::DataStorage* dataStorage);
/**
* Sets the node predicate and updates the widget, according to the node predicate.
* Implement OnNodePredicateChange() for custom actualization of a derived widget class.
*
* @par nodePredicate A pointer to node predicate.
*/
void SetNodePredicate(const mitk::NodePredicateBase* nodePredicate);
const mitk::NodePredicateBase* GetNodePredicate() const;
QString GetInvalidInfo() const;
QString GetEmptyInfo() const;
QString GetPopUpTitel() const;
QString GetPopUpHint() const;
bool GetSelectionIsOptional() const;
bool GetSelectOnlyVisibleNodes() const;
using NodeList = QList<mitk::DataNode::Pointer>;
/** Other node container type often used in the code base.*/
using ConstNodeStdVector = std::vector<mitk::DataNode::ConstPointer>;
/** Returns the selected nodes, as emitted with CurrentSelectionChanged*/
NodeList GetSelectedNodes() const;
- /** Convinience method that returns the selected nodes as ConstNodeStdVector.
+ /** Convenience method that returns the selected nodes as ConstNodeStdVector.
This is a type also often used in the mitk code base.*/
ConstNodeStdVector GetSelectedNodesStdVector() const;
Q_SIGNALS:
/**
* @brief A signal that will be emitted if the selected node has changed.
*
* @par nodes A list of data nodes that are newly selected.
*/
void CurrentSelectionChanged(NodeList nodes);
public Q_SLOTS:
/**
* @brief Change the selection modus of the item view's selection model.
*
* If true, an incoming selection will be filtered (reduced) to only those nodes that are visible by the current view.
* An outgoing selection can then at most contain the filtered nodes.
* If false, the incoming non-visible selection will be stored and later added to the outgoing selection,
* to include the original selection that could not be modified.
* The part of the original selection, that is non-visible are the nodes, that do not fullfill the predicate.
*
* @par selectOnlyVisibleNodes The bool value to define the selection modus.
*/
void SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes);
/**
* @brief Transform a list of data nodes (a selection) into a model selection and set this as a new selection of the
* selection model of the private member item view.
*
* The function filters the given list of nodes according to the 'm_SelectOnlyVisibleNodes' member variable. If
* necessary, the non-visible nodes are stored. This is done if 'm_SelectOnlyVisibleNodes' is false: In this case
* the selection may be filtered and only a subset of the selected nodes may be visible and therefore (de-)selectable
* in the data storage viewer. By storing the non-visible nodes it is possible to send the new, modified selection
* but also include the selected nodes from the original selection that could not be modified (see 'SetSelectOnlyVisibleNodes').
*
* @par nodes A list of data nodes that should be newly selected.
*/
void SetCurrentSelection(NodeList selectedNodes);
/** Set the info text that should be displayed if no (valid) node is selected,
* but a selection is mandatory.
* The string can contain HTML code, if desired.
*/
void SetInvalidInfo(QString info);
/** Set the info text that should be displayed if no (valid) node is selected,
* but a selection is optional.
* The string can contain HTML code, if desired.
*/
void SetEmptyInfo(QString info);
/** Set the caption of the popup that is displayed to alter the selection.
* The string can contain HTML code, if desired.
*/
void SetPopUpTitel(QString info);
/** Set the hint text of the popup that is displayed to alter the selection.
* The string can contain HTML code, if desired.
*/
void SetPopUpHint(QString info);
/** Set the widget into an optional mode. Optional means that the selection of no valid
* node does not mean an invalid state. Thus no node is a valid "node" selection too.
*/
void SetSelectionIsOptional(bool isOptional);
protected Q_SLOTS:
/** Call to remove a node from the current selection. If the node is part of the current selection,
* this will trigger ReviseSelectionChanged(), AllowEmissionOfSelection() and if there is really a change,
* will also emit CurrentSelectionChanged.
*/
void RemoveNodeFromSelection(const mitk::DataNode* node);
protected:
/** Method is called if the display of the selected nodes should be updated (e.g. because the selection changed). */
virtual void UpdateInfo() = 0;
/** Method is called if the predicate has changed, before the selection will be updated according to the new predicate.
* The default implementation does nothing.
* @remark If you are only interested to know when the selection has changed, overwrite OnInternalSelectionChange().
*/
virtual void OnNodePredicateChanged();
- /** Method is called if the data storage has changed. The selection will be automatically be reseted afterwards.
+ /** Method is called if the data storage has changed. The selection will be automatically be reset afterwards.
* The default implementation does nothing.
*/
virtual void OnDataStorageChanged();
/** This member function will called when ever a new internal selection has been determined. This can be
* used to update the state of internal widgets. The default implementation does nothing.
*/
virtual void OnInternalSelectionChanged();
/** Method is called when a node is added to the storage. Default implementation does nothing.
* Derived widgets can override the method if they want to react on new nodes in the storage.
*/
virtual void OnNodeAddedToStorage(const mitk::DataNode* node);
/** Method is called when a node is removed from the storage. The removed node is passed as
* variable. This member is called directly before the node will be removed from the current selection.
* Default implementation does nothing.
* Derived widgets can override the method if they want to handle to-be-removed nodes before.
*/
virtual void OnNodeRemovedFromStorage(const mitk::DataNode* node);
/** Method is called when a node is modified. The modified node is passed as 'caller' variable.
* Default implementation handles changes that are related to the node predicate:
* - If the node does not fit the node predicate anymore, it will be removed.
* - If the node was part of the external selection and now fits the node predicate,
* a new selection is compiled and emitted.
* Derived widgets can override the method if they want to react on modified nodes.
*/
virtual void OnNodeModified(const itk::Object* caller, const itk::EventObject& event);
- /** Method is called if the internal selection has changed. It will call following methods, that can be overriden to change
+ /** Method is called if the internal selection has changed. It will call following methods, that can be overridden to change
* behavior in derived classes:
* - pre internal selection change: ReviseSelectionChanged()
* - post internal selection change: OnInternalSelectionChanged(), UpdateInfo() and AllowEmissionOfSelection() (via EmitSelection()).
* If the emission is needed and allowed it will also trigger the emission via EmitSelection().
*/
void HandleChangeOfInternalSelection(NodeList newInternalSelection);
/** Compiles the list of node that would be emitted. It always contains the internal selection.
* Depending on SelectOnlyVisibleNodes it also adds all external select nodes that weren't visible (failed the predicate).
*/
NodeList CompileEmitSelection() const;
/** This member function is called if the internal selection is about to be changed by the base implementation.
* This is the slot where derived classes can revise and change the internal selection before widget updates,
* signal emissions and other things are triggered. Default implementation does nothing, thus it keeps the
* passed internal selection as compiled by the base implementation.
*/
virtual void ReviseSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection);
/** This function will be called before the CurrentSelectionChanged signal is emitted. The return value indicates
* if the signal should be emitted (true = emission; false = no emission). The default implementation always
* returns true.
* @param emissionCandidates The nodes that will be emitted if the function returns true.
*/
virtual bool AllowEmissionOfSelection(const NodeList& emissionCandidates) const;
/** Checks if the new emission differs from the last emission. If this is the case and AllowEmissionOfSelection()
- * returns true the new selection will be emited.
+ * returns true the new selection will be emitted.
*/
void EmitSelection(const NodeList& emissionCandidates);
void SetCurrentInternalSelection(NodeList selectedNodes);
const NodeList& GetCurrentInternalSelection() const;
const NodeList& GetCurrentExternalSelection() const;
mitk::WeakPointer<mitk::DataStorage> m_DataStorage;
mitk::NodePredicateBase::ConstPointer m_NodePredicate;
QString m_InvalidInfo;
QString m_EmptyInfo;
QString m_PopUpTitel;
QString m_PopUpHint;
/** See documentation of SetSelectOnlyVisibleNodes for details*/
bool m_IsOptional;
/** See documentation of SetSelectionIsOptional for details*/
bool m_SelectOnlyVisibleNodes;
private:
/** Helper triggered on the storage delete event */
void SetDataStorageDeleted();
/**Member is called when a node is added to the storage.
Derived widgets can override the method OnNodeAddedToStorage if they want to react on new nodes in the storage.*/
void NodeAddedToStorage(const mitk::DataNode* node);
/**Member is called when a node is removed from the storage. It calls OnNodeRemovedFromStorage() and afterwards
it removes the removed node form the selection (if it is part of the current selection).
Derived classes can override OnNodeRemovedFromStorage() to react on the fact that a node might be removed and
their selection might change, because the removed node is part of there selection.*/
void NodeRemovedFromStorage(const mitk::DataNode* node);
void AddNodeObserver(mitk::DataNode* node);
void RemoveNodeObserver(mitk::DataNode* node);
unsigned long m_DataStorageDeletedTag;
NodeList m_CurrentInternalSelection;
NodeList m_CurrentExternalSelection;
NodeList m_LastEmission;
bool m_LastEmissionAllowance;
using NodeObserverTagMapType = std::map<const mitk::DataNode*, unsigned long>;
NodeObserverTagMapType m_NodeObserverTags;
/** Help to prevent recursions due to signal loops when emitting selections.*/
bool m_RecursionGuard;
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkDataStorageSimpleTreeModel.h b/Modules/QtWidgets/include/QmitkDataStorageSimpleTreeModel.h
index 39ca35f480..1fcfdd7429 100644
--- a/Modules/QtWidgets/include/QmitkDataStorageSimpleTreeModel.h
+++ b/Modules/QtWidgets/include/QmitkDataStorageSimpleTreeModel.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 QmitkDataStorageSimpleTreeModel_h
#define QmitkDataStorageSimpleTreeModel_h
#include <MitkQtWidgetsExports.h>
// qt widgets module
#include <QmitkAbstractDataStorageModel.h>
class QmitkDataStorageTreeModelInternalItem;
/**
* @brief The 'QmitkDataStorageSimpleTreeModel' is a basic tree model, derived from the 'QmitkAbstractDataStorageModel'.
* It provides functions to accept a data storage and a node predicate in order to customize the model data nodes.
* Furthermore it overrides the functions of 'QAbstractItemModel' to create a simple qt list model.*
* This model can be used in conjunction with a 'QmitkDataStorageSelectionConnector'.
* This model is a "light" version of the classic QmitkDataStorgageTreeModel. The differences between both are the following:
* - This class currently does not support DragNDrop.
* - This class does not have the ability to change hierarchy or changes the layer property of nodes.
* This was skipped on purpose, because that is not the job of the storage model.
* - If a tree item A is removed this class does not attach children of A to the parent of A.
* Instead the complete tree representation is updated. This was changed on purpose because otherwise the internal
* representation of the model would not reflect the data storage graph anymore.
*/
class MITKQTWIDGETS_EXPORT QmitkDataStorageSimpleTreeModel : public QmitkAbstractDataStorageModel
{
Q_OBJECT
public:
QmitkDataStorageSimpleTreeModel(QObject *parent);
~QmitkDataStorageSimpleTreeModel() override;
// override from 'QmitkAbstractDataStorageModel'
/*
* @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;
// override pure virtual from 'QAbstractItemModel'
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
protected:
using TreeItem = QmitkDataStorageTreeModelInternalItem;
private:
void UpdateModelData();
void AddNodeInternal(const mitk::DataNode *node);
mitk::DataNode *GetParentNode(const mitk::DataNode *node) const;
TreeItem *TreeItemFromIndex(const QModelIndex &index) const;
QModelIndex IndexFromTreeItem(TreeItem *item) const;
void ResetTree();
TreeItem *m_Root;
/**helper structure to check, if a tree item is really part of the model.
- Prefered over iterating over the tree by hand because we can use std::find.*/
+ Preferred over iterating over the tree by hand because we can use std::find.*/
std::list<const TreeItem*> m_TreeItems;
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkDataStorageTreeModel.h b/Modules/QtWidgets/include/QmitkDataStorageTreeModel.h
index 5cb04c5b88..f68a9773d0 100644
--- a/Modules/QtWidgets/include/QmitkDataStorageTreeModel.h
+++ b/Modules/QtWidgets/include/QmitkDataStorageTreeModel.h
@@ -1,209 +1,209 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitkDataStorageTreeModel_h
#define QmitkDataStorageTreeModel_h
#include <MitkQtWidgetsExports.h>
#include <mitkDataStorage.h>
#include <mitkNodePredicateBase.h>
#include <mitkWeakPointer.h>
#include <QAbstractListModel>
#include "QmitkCustomVariants.h"
#include "QmitkEnums.h"
#include <QList>
#include <string>
#include <vector>
class QmitkDataStorageTreeModelInternalItem;
/** \ingroup QmitkModule
@warning This class causes invalid point exception when used with invalid QModelIndex instances.
The index validation is not sufficient. This may cause unspecific crashes in situation where
this class is used multiple times or with multiple selection models. See https://phabricator.mitk.org/T24348
for more information.
*/
class MITKQTWIDGETS_EXPORT QmitkDataStorageTreeModel : public QAbstractItemModel
{
Q_OBJECT
//# CONSTANTS,TYPEDEFS
public:
static const std::string COLUMN_NAME;
static const std::string COLUMN_TYPE;
static const std::string COLUMN_VISIBILITY;
//# CTORS,DTOR
public:
QmitkDataStorageTreeModel(mitk::DataStorage *_DataStorage, bool _PlaceNewNodesOnTop = false, QObject *parent = nullptr);
~QmitkDataStorageTreeModel() override;
//# GETTER
public:
///
/// Get node at a specific model index.
/// This function is used to get a node from a QModelIndex
///
mitk::DataNode::Pointer GetNode(const QModelIndex &index) const;
///
/// Returns a copy of the node-vector that is shown by this model
///
virtual QList<mitk::DataNode::Pointer> GetNodeSet() const;
///
/// Get the DataStorage.
///
const mitk::DataStorage::Pointer GetDataStorage() const;
///
/// Get the top placement flag
///
bool GetPlaceNewNodesOnTopFlag() { return m_PlaceNewNodesOnTop; }
///
/// Set the top placement flag
///
void SetPlaceNewNodesOnTop(bool _PlaceNewNodesOnTop);
//# (Re-)implemented from QAbstractItemModel
//# Read model
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
//# hierarchical model
///
/// called whenever the model or the view needs to create a QModelIndex for a particular
/// child item (or a top-level item if parent is an invalid QModelIndex)
///
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
//# editable model
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole) override;
bool dropMimeData(
const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
Qt::DropActions supportedDropActions() const override;
Qt::DropActions supportedDragActions() const override;
QStringList mimeTypes() const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
static QMimeData *mimeDataFromModelIndexList(const QModelIndexList &indexes);
//# End of QAbstractItemModel
//# SETTER
public:
///
- /// Sets the DataStorage. The whole model will be resetted.
+ /// Sets the DataStorage. The whole model will be reset.
///
void SetDataStorage(mitk::DataStorage *_DataStorage);
///
- /// Notify that the DataStorage was deleted. The whole model will be resetted.
+ /// Notify that the DataStorage was deleted. The whole model will be reset.
///
void SetDataStorageDeleted();
///
/// Adds a node to this model.
/// If a predicate is set (not null) the node will be checked against it.The node has to have a data object (no one
/// wants to see empty nodes).
///
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);
///
- /// Sets a node to modfified. Called by the DataStorage
+ /// Sets a node to modified. Called by the DataStorage
///
virtual void SetNodeModified(const mitk::DataNode *node);
///
/// \return an index for the given datatreenode in the tree. If the node is not found
///
QModelIndex GetIndex(const mitk::DataNode *) const;
/// Set whether to allow hierarchy changes by dragging and dropping
void SetAllowHierarchyChange(bool allowHierarchyChange);
signals:
void nodeVisibilityChanged();
//# MISC
protected:
using TreeItem = QmitkDataStorageTreeModelInternalItem;
QList<TreeItem *> ToTreeItemPtrList(const QMimeData *mimeData);
QList<TreeItem *> ToTreeItemPtrList(const QByteArray &ba);
///
/// Adjusts the LayerProperty according to the nodes position
///
void AdjustLayerProperty();
///
/// invoked after m_DataStorage or m_Predicate changed
///
TreeItem *TreeItemFromIndex(const QModelIndex &index) const;
///
/// Gives a ModelIndex for the Tree Item
///
QModelIndex IndexFromTreeItem(TreeItem *) const;
///
/// Returns the first element in the nodes sources list (if available) or 0
///
mitk::DataNode *GetParentNode(const mitk::DataNode *node) const;
///
/// Adds all Childs in parent to vec. Before a child is added the function is called recursively
///
void TreeToVector(TreeItem *parent, std::vector<TreeItem *> &vec) const;
///
/// Adds all Childs in parent to vec. Before a child is added the function is called recursively
///
void TreeToNodeSet(TreeItem *parent, QList<mitk::DataNode::Pointer> &vec) const;
///
/// Update Tree Model
///
void Update();
//# ATTRIBUTES
protected:
mitk::WeakPointer<mitk::DataStorage> m_DataStorage;
mitk::NodePredicateBase::Pointer m_Predicate;
bool m_PlaceNewNodesOnTop;
TreeItem *m_Root;
/// Flag to block the data storage events if nodes are added/removed by this class.
bool m_BlockDataStorageEvents;
/// This decides whether or not it is allowed to assign a different parent to a node
/// If it is false, it is not possible to change the hierarchy of nodes by dragging
/// and dropping.
/// If it is true, dragging nodes on another node will replace all of their parents
/// with that one.
bool m_AllowHierarchyChange;
private:
void AddNodeInternal(const mitk::DataNode *);
void RemoveNodeInternal(const mitk::DataNode *);
///
/// Checks if dicom properties patient name, study names and series name exists
///
bool DicomPropertiesExists(const mitk::DataNode &) const;
unsigned long m_DataStorageDeletedTag;
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkNodeDescriptor.h b/Modules/QtWidgets/include/QmitkNodeDescriptor.h
index fcd58de33e..6b698323a5 100644
--- a/Modules/QtWidgets/include/QmitkNodeDescriptor.h
+++ b/Modules/QtWidgets/include/QmitkNodeDescriptor.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 QmitkNodeDescriptor_h
#define QmitkNodeDescriptor_h
#include <MitkQtWidgetsExports.h>
#include "mitkDataNode.h"
#include <QAction>
#include <QIcon>
#include <QList>
#include <QString>
#include <QWidgetAction>
#include <map>
#include <mitkNodePredicateBase.h>
/**
* \ingroup QmitkModule
* \brief <i>Decorator</i> class for mitk::DataNode.
*
* \sa QmitkNodeDescriptorManager
*/
class MITKQTWIDGETS_EXPORT QmitkNodeDescriptor : public QObject
{
Q_OBJECT
public:
///
/// Creates a new QmitkNodeDescriptor
///
QmitkNodeDescriptor(const QString &_ClassName,
const QString &_PathToIcon,
mitk::NodePredicateBase *_Predicate,
QObject *parent);
QmitkNodeDescriptor(const QString &_ClassName,
const QIcon &_Icon,
mitk::NodePredicateBase *_Predicate,
QObject *parent);
///
/// Deletes all actions
///
~QmitkNodeDescriptor() override;
///
/// Returns a name for this class of DataNodes (e.g. "Image", "Image Mask", etc.)
///
virtual QString GetNameOfClass() const;
///
/// Returns an Icon for this class of DataNodes
///
virtual QIcon GetIcon(const mitk::DataNode *node) const;
///
/// Returns an Icon for this class of DataNodes
///
virtual QAction *GetSeparator() const;
///
/// Check if this class describes the given node
///
virtual bool CheckNode(const mitk::DataNode *node) const;
///
/// Create and return an action with this descriptor as owner
///
virtual void AddAction(QAction *action, bool isBatchAction = true);
///
/// Remove and delete (!) an action
///
virtual void RemoveAction(QAction *_Action);
///
/// Get all actions associated with this class of nodes
///
virtual QList<QAction *> GetActions() const;
///
/// Get all actions for this descriptor class that can be executed on multiple nodes
- /// (no priot knowledge abpout the node is required)
+ /// (no prior knowledge about the node is required)
///
virtual QList<QAction *> GetBatchActions() const;
public slots:
/// Called when an action was destroyed
void ActionDestroyed(QObject *obj = nullptr);
protected:
QString m_ClassName;
QIcon m_Icon;
mitk::NodePredicateBase::Pointer m_Predicate;
QList<QAction *> m_Actions;
QList<QAction *> m_BatchActions;
QAction *m_Separator;
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkNodeSelectionButton.h b/Modules/QtWidgets/include/QmitkNodeSelectionButton.h
index 111c63a34a..03f8a21320 100644
--- a/Modules/QtWidgets/include/QmitkNodeSelectionButton.h
+++ b/Modules/QtWidgets/include/QmitkNodeSelectionButton.h
@@ -1,71 +1,71 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitkNodeSelectionButton_h
#define QmitkNodeSelectionButton_h
#include <MitkQtWidgetsExports.h>
#include <mitkDataNode.h>
#include <QPushButton>
#include <QPixmap>
/**
* @class QmitkNodeSelectionButton
* @brief Button class that can be used to display information about a given node.
* If the given node is a nullptr the node info text will be shown.
* The node info can be formatted text (e.g. HTML code; like the tooltip text).
*/
class MITKQTWIDGETS_EXPORT QmitkNodeSelectionButton : public QPushButton
{
Q_OBJECT
public:
explicit QmitkNodeSelectionButton(QWidget *parent = nullptr);
~QmitkNodeSelectionButton() override;
const mitk::DataNode* GetSelectedNode() const;
bool GetSelectionIsOptional() const;
public Q_SLOTS:
virtual void SetSelectedNode(const mitk::DataNode* node);
virtual void SetNodeInfo(QString info);
/** Set the widget into an optional mode. Optional means that the selection of no valid
node does not mean an invalid state. Thus no node is a valid "node" selection too.
The state influences if the info text is handled as an information (optional) or a
- warning (optiona==false).*/
+ warning (optional==false).*/
void SetSelectionIsOptional(bool isOptional);
protected:
void paintEvent(QPaintEvent *p) override;
void changeEvent(QEvent *event) override;
void AddNodeObserver();
void RemoveNodeObserver();
void OnNodeModified(const itk::Object * /*caller*/, const itk::EventObject &);
mitk::DataNode::ConstPointer m_SelectedNode;
QString m_Info;
bool m_OutDatedThumbNail;
QPixmap m_ThumbNail;
itk::ModifiedTimeType m_DataMTime;
itk::ModifiedTimeType m_SelectionPropMTime;
bool m_IsOptional;
unsigned long m_NodeModifiedObserverTag;
bool m_NodeObserved;
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkOverlayWidget.h b/Modules/QtWidgets/include/QmitkOverlayWidget.h
index ad63fa8e6d..0661d0576b 100644
--- a/Modules/QtWidgets/include/QmitkOverlayWidget.h
+++ b/Modules/QtWidgets/include/QmitkOverlayWidget.h
@@ -1,45 +1,45 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitkOverlayWidget_h
#define QmitkOverlayWidget_h
#include <QWidget>
#include <MitkQtWidgetsExports.h>
-/** Simple widget that can be used to achive overlays. The overlay will lie above its parent.
+/** Simple widget that can be used to achieve overlays. The overlay will lie above its parent.
* This implementation just renders a semi transparent black background. To add content to the
* overlay derive from this class.*/
class MITKQTWIDGETS_EXPORT QmitkOverlayWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY(bool transparentForMouseEvents READ isTransparentForMouseEvents WRITE setTransparentForMouseEvents)
public:
explicit QmitkOverlayWidget(QWidget* parent = nullptr);
~QmitkOverlayWidget() override;
bool isTransparentForMouseEvents() const;
void setTransparentForMouseEvents(bool transparent = true);
protected:
bool event(QEvent* e) override;
bool eventFilter(QObject* watched, QEvent* event) override;
void paintEvent(QPaintEvent* event) override;
private:
void installEventFilterOnParent();
void removeEventFilterFromParent();
};
#endif
diff --git a/Modules/QtWidgets/include/QmitkServiceListWidget.h b/Modules/QtWidgets/include/QmitkServiceListWidget.h
index ba70714b54..e9100010e8 100644
--- a/Modules/QtWidgets/include/QmitkServiceListWidget.h
+++ b/Modules/QtWidgets/include/QmitkServiceListWidget.h
@@ -1,288 +1,288 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitkServiceListWidget_h
#define QmitkServiceListWidget_h
#include "MitkQtWidgetsExports.h"
#include "ui_QmitkServiceListWidgetControls.h"
#include <vector>
// QT headers
#include <QListWidgetItem>
#include <QWidget>
// Microservices
#include "mitkServiceInterface.h"
#include "usModuleContext.h"
#include "usServiceEvent.h"
#include "usServiceReference.h"
/**
* \ingroup QmitkModule
*
* \brief This widget provides abstraction for the handling of MicroServices.
*
* Place one in your Plugin and set it to look for a certain interface.
* One can also specify a filter and / or a property to use for captioning of
* the services. It also offers functionality to signal
* ServiceEvents and to return the actual classes, so only a minimum of
* interaction with the MicroserviceInterface is required.
* To get started, just put it in your Plugin or Widget, call the Initialize
* Method and optionally connect it's signals.
* As QT limits templating possibilities, events only throw ServiceReferences.
* You can manually dereference them using TranslateServiceReference()
*/
class MITKQTWIDGETS_EXPORT QmitkServiceListWidget : public QWidget
{
// this is needed for all Qt objects that should have a MOC object (everything that derives from QObject)
Q_OBJECT
private:
us::ModuleContext *m_Context;
/** \brief a filter to further narrow down the list of results*/
std::string m_Filter;
/** \brief The name of the ServiceInterface that this class should list */
std::string m_Interface;
/** \brief The name of the ServiceProperty that will be displayed in the list to represent the service */
std::string m_NamingProperty;
/** \brief Determines if the first entry of the list should be selected automatically if no entry was selected before
* (default false). */
bool m_AutomaticallySelectFirstEntry;
public:
static const std::string VIEW_ID;
QmitkServiceListWidget(QWidget *p = nullptr, Qt::WindowFlags f1 = {});
~QmitkServiceListWidget() override;
/** \brief Set if the first entry of the list should be selected automatically if no entry was selected before. */
void SetAutomaticallySelectFirstEntry(bool automaticallySelectFirstEntry);
/** \brief This method is part of the widget an needs not to be called separately. */
virtual void CreateQtPartControl(QWidget *parent);
/** \brief This method is part of the widget an needs not to be called separately. (Creation of the connections of
* main and control widget.)*/
virtual void CreateConnections();
/**
* \brief Will return true, if a service is currently selected and false otherwise.
*
* Call this before requesting service references to avoid invalid ServiceReferences.
*/
bool GetIsServiceSelected();
/**
* \brief Returns the currently selected Service as a ServiceReference.
*
* If no Service is selected, the result will probably be a bad pointer. call GetIsServiceSelected()
* beforehand to avoid this
*/
us::ServiceReferenceU GetSelectedServiceReference();
/**
* @return Returns all service references that are displayed in this widget.
*/
std::vector<us::ServiceReferenceU> GetAllServiceReferences();
/**
* \brief Use this function to return the all listed services as a class directly.
*
* Make sure you pass the appropriate type, or else this call will fail.
* Usually, you will pass the class itself, not the SmartPointer, but the function returns a pointer.
*/
template <class T>
std::vector<T *> GetAllServices()
{
// if (this->m_Controls->m_ServiceList->currentRow()==-1) return nullptr;
std::vector<us::ServiceReferenceU> refs = GetAllServiceReferences();
std::vector<T *> result;
for (std::size_t i = 0; i < refs.size(); i++)
{
result.push_back(m_Context->GetService(us::ServiceReference<T>(refs[i])));
}
return result;
}
/**
* \brief Use this function to return the currently selected service as a class directly.
*
* Make sure you pass the appropriate type, or else this call will fail.
* Usually, you will pass the class itself, not the SmartPointer, but the function returns a pointer. Example:
* \verbatim mitk::USDevice::Pointer device = GetSelectedService<mitk::USDevice>(); \endverbatim
* @return Returns the current selected device. Returns nullptr if no device is selected.
*/
template <class T>
T *GetSelectedService()
{
if (this->m_Controls->m_ServiceList->currentRow() == -1)
return nullptr;
us::ServiceReferenceU ref = GetServiceForListItem(this->m_Controls->m_ServiceList->currentItem());
return (m_Context->GetService(us::ServiceReference<T>(ref)));
}
/**
* \brief Initializes the Widget with essential parameters.
*
* The string filter is an LDAP parsable String, compare mitk::ModuleContext for examples on filtering.
* Pass class T to tell the widget which class it should filter for - only services of this class will be listed.
* NamingProperty is a property that will be used to caption the Items in the list. If no filter is supplied, all
* matching interfaces are shown. If no namingProperty is supplied, the interfaceName will be used to caption Items in
the list.
* For example, this Initialization will filter for all USDevices that are set to active. The USDevice's model will be
used to display it in the list:
* \verbatim
std::string filter = "(&(" + us::ServiceConstants::OBJECTCLASS() + "=" +
"org.mitk.services.UltrasoundDevice)(IsActive=true))";
m_Controls.m_ActiveVideoDevices->Initialize<mitk::USDevice>(mitk::USDevice::GetPropertyKeys().US_PROPKEY_NAME
,filter);
* \endverbatim
*/
template <class T>
void Initialize(const std::string &namingProperty = static_cast<std::string>(""),
const std::string &filter = static_cast<std::string>(""))
{
std::string interfaceName(us_service_interface_iid<T>());
m_Interface = interfaceName;
InitPrivate(namingProperty, filter);
}
/**
* \brief Translates a serviceReference to a class of the given type.
*
* Use this to translate the signal's parameters. To adhere to the MicroService contract,
* only ServiceReferences stemming from the same widget should be used as parameters for this method.
* \verbatim mitk::USDevice::Pointer device = TranslateReference<mitk::USDevice>(myDeviceReference); \endverbatim
*/
template <class T>
T *TranslateReference(const us::ServiceReferenceU &reference)
{
return m_Context->GetService(us::ServiceReference<T>(reference));
}
/**
*\brief This Function listens to ServiceRegistry changes and updates the list of services accordingly.
*
- * The user of this widget does not need to call this method, it is instead used to recieve events from the module
+ * The user of this widget does not need to call this method, it is instead used to receive events from the module
*registry.
*/
void OnServiceEvent(const us::ServiceEvent event);
signals:
/**
*\brief Emitted when a new Service matching the filter is being registered.
*
* Be careful if you use a filter:
* If a device does not match the filter when registering, but modifies it's properties later to match the filter,
* then the first signal you will see this device in will be ServiceModified.
*/
void ServiceRegistered(us::ServiceReferenceU);
/**
*\brief Emitted directly before a Service matching the filter is being unregistered.
*/
void ServiceUnregistering(us::ServiceReferenceU);
/**
*\brief Emitted when a Service matching the filter changes it's properties, or when a service that formerly not
*matched the filter
* changed it's properties and now matches the filter.
*/
void ServiceModified(us::ServiceReferenceU);
/**
*\brief Emitted when a Service matching the filter changes it's properties,
*
* and the new properties make it fall trough the filter. This effectively means that
* the widget will not track the service anymore. Usually, the Service should still be useable though
*/
void ServiceModifiedEndMatch(us::ServiceReferenceU);
/**
*\brief Emitted if the user selects a Service from the list.
*
* If no service is selected, an invalid serviceReference is returned. The user can easily check for this.
* if (serviceReference) will evaluate to false, if the reference is invalid and true if valid.
*/
void ServiceSelectionChanged(us::ServiceReferenceU);
public slots:
protected slots:
/**
\brief Called, when the selection in the list of Services changes.
*/
void OnServiceSelectionChanged();
protected:
Ui::QmitkServiceListWidgetControls *m_Controls; ///< member holding the UI elements of this widget
/**
* \brief Internal structure used to link ServiceReferences to their QListWidgetItems
*/
struct ServiceListLink
{
us::ServiceReferenceU service;
QListWidgetItem *item;
};
/**
* \brief Finishes initialization after Initialize has been called.
*
* This function assumes that m_Interface is set correctly (Which Initialize does).
*/
void InitPrivate(const std::string &namingProperty, const std::string &filter);
/**
* \brief Contains a list of currently active services and their entires in the list. This is wiped with every
* ServiceRegistryEvent.
*/
std::vector<ServiceListLink> m_ListContent;
/**
* \brief Constructs a ListItem from the given service, displays it, and locally stores the service.
*/
QListWidgetItem *AddServiceToList(const us::ServiceReferenceU &serviceRef);
/**
* \brief Removes the given service from the list and cleans up. Returns true if successful, false if service was not
* found.
*/
bool RemoveServiceFromList(const us::ServiceReferenceU &serviceRef);
/**
* \brief Changes list entry of given service to match the changed service properties.
* \return true if successful, false if service was not found
*/
bool ChangeServiceOnList(const us::ServiceReferenceU &serviceRef);
/**
* \brief Returns the serviceReference corresponding to the given ListEntry or an invalid one if none was found (will
* evaluate to false in bool expressions).
*/
us::ServiceReferenceU GetServiceForListItem(QListWidgetItem *item);
/**
* \brief Returns a list of ServiceReferences matching the filter criteria by querying the service registry.
*/
std::vector<us::ServiceReferenceU> GetAllRegisteredServices();
/**
* \brief Gets string from the naming property of the service.
* \return caption string for given us::ServiceReferenceU
*/
QString CreateCaptionForService(const us::ServiceReferenceU &serviceRef);
};
#endif
diff --git a/Modules/QtWidgets/include/mitkIDataStorageInspectorProvider.h b/Modules/QtWidgets/include/mitkIDataStorageInspectorProvider.h
index f390d26c64..4765cc6467 100644
--- a/Modules/QtWidgets/include/mitkIDataStorageInspectorProvider.h
+++ b/Modules/QtWidgets/include/mitkIDataStorageInspectorProvider.h
@@ -1,73 +1,73 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkIDataStorageInspectorProvider_h
#define mitkIDataStorageInspectorProvider_h
#include <mitkServiceInterface.h>
#include <MitkQtWidgetsExports.h>
#include <QIcon>
class QmitkAbstractDataStorageInspector;
namespace mitk
{
/**
* \ingroup MicroServices_Interfaces
*
* \brief The common interface for all DataStorage inspector providers.
*
* Implementations of this interface must be registered as a service
* to make themselves available via the service registry.
*
* It is recommended to derive new implementations from QmitkDataStorageInspectorProviderBase
* which provide correct service registration semantics.
*
* \sa QmitkDataStorageInspectorProviderBase
*/
struct MITKQTWIDGETS_EXPORT IDataStorageInspectorProvider
{
virtual ~IDataStorageInspectorProvider();
/**
* \brief returns an inspector instance represented by the provider.
*/
virtual QmitkAbstractDataStorageInspector* CreateInspector() const = 0;
using InspectorIDType = std::string;
- /** Return the uniqe ID for the inspector type provided.*/
+ /** Return the unique ID for the inspector type provided.*/
virtual InspectorIDType GetInspectorID() const = 0;
/** Return the display name (e.g. used in the UI) for the inspector type provided.*/
virtual std::string GetInspectorDisplayName() const = 0;
/** Returns a description of the inspector type provided.*/
virtual std::string GetInspectorDescription() const = 0;
/** Returns the svg data of the icon of the inspector. Empty array indicates that no icon is defined.
@remark It is passed as svg file content and not as icon directly to allow later styling*/
virtual QIcon GetInspectorIcon() const = 0;
/**
* @brief Service property name for the inspector ID.
*
* The property value must be of type \c std::string.
*
* @return The property name.
*/
static std::string PROP_INSPECTOR_ID();
};
} // namespace mitk
MITK_DECLARE_SERVICE_INTERFACE(mitk::IDataStorageInspectorProvider, "org.mitk.IDataStorageInspectorProvider")
#endif
diff --git a/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp
index bdb8eaa359..d0790137b6 100644
--- a/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp
+++ b/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp
@@ -1,509 +1,509 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkDataStorageTableModel.h"
//# Own includes
#include "QmitkCustomVariants.h"
#include "QmitkEnums.h"
#include "mitkNodePredicateBase.h"
#include "mitkProperties.h"
#include "mitkRenderingManager.h"
#include <QmitkNodeDescriptorManager.h>
//# Toolkit includes
#include <QFile>
#include <QIcon>
#include <itkCommand.h>
//#CTORS/DTOR
QmitkDataStorageTableModel::QmitkDataStorageTableModel(mitk::DataStorage::Pointer _DataStorage,
mitk::NodePredicateBase *_Predicate,
QObject *parent)
: QAbstractTableModel(parent),
m_DataStorage(nullptr),
m_Predicate(nullptr),
m_BlockEvents(false),
m_SortDescending(false)
{
this->SetPredicate(_Predicate);
this->SetDataStorage(_DataStorage);
}
QmitkDataStorageTableModel::~QmitkDataStorageTableModel()
{
// set data storage 0 to remove event listeners
this->SetDataStorage(nullptr);
}
//# Public GETTER
const mitk::DataStorage::Pointer QmitkDataStorageTableModel::GetDataStorage() const
{
return m_DataStorage.Lock();
}
mitk::NodePredicateBase::Pointer QmitkDataStorageTableModel::GetPredicate() const
{
return m_Predicate;
}
mitk::DataNode::Pointer QmitkDataStorageTableModel::GetNode(const QModelIndex &index) const
{
mitk::DataNode::Pointer node;
if (index.isValid())
{
node = m_NodeSet.at(index.row());
}
return node;
}
QVariant QmitkDataStorageTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
QVariant headerData;
// show only horizontal header
if (role == Qt::DisplayRole)
{
if (orientation == Qt::Horizontal)
{
// first column: "Name"
if (section == 0)
headerData = "Name";
else if (section == 1)
headerData = "Data Type";
else if (section == 2)
headerData = "Visibility";
}
else if (orientation == Qt::Vertical)
{
// show numbers for rows
headerData = section + 1;
}
}
return headerData;
}
Qt::ItemFlags QmitkDataStorageTableModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
// name & visibility is editable
if (index.column() == 0)
{
flags |= Qt::ItemIsEditable;
}
else if (index.column() == 2)
{
flags |= Qt::ItemIsUserCheckable;
}
return flags;
}
int QmitkDataStorageTableModel::rowCount(const QModelIndex &) const
{
return m_NodeSet.size();
}
int QmitkDataStorageTableModel::columnCount(const QModelIndex &) const
{
- // show name, type and visible columnn
+ // show name, type and visible column
int columns = 3;
return columns;
}
QVariant QmitkDataStorageTableModel::data(const QModelIndex &index, int role) const
{
QVariant data;
if (index.isValid() && !m_NodeSet.empty())
{
mitk::DataNode::Pointer node = m_NodeSet.at(index.row());
std::string nodeName = node->GetName();
if (nodeName.empty())
nodeName = "unnamed";
// get name
if (index.column() == 0)
{
// get name of node (may also be edited)
if (role == Qt::DisplayRole || role == Qt::EditRole)
{
data = QString::fromStdString(nodeName);
}
else if (role == QmitkDataNodeRole)
{
data = QVariant::fromValue(node);
}
}
else if (index.column() == 1)
{
QmitkNodeDescriptor *nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(node);
// get type property of mitk::BaseData
if (role == Qt::DisplayRole)
{
data = nodeDescriptor->GetNameOfClass();
}
// show some nice icons for datatype
else if (role == Qt::DecorationRole)
{
data = nodeDescriptor->GetIcon(node);
}
}
else if (index.column() == 2)
{
// get visible property of mitk::BaseData
bool visibility = false;
if (node->GetVisibility(visibility, nullptr) && role == Qt::CheckStateRole)
{
data = (visibility ? Qt::Checked : Qt::Unchecked);
} // node->GetVisibility(visibility, 0) && role == Qt::CheckStateRole
} // index.column() == 2
} // index.isValid() && !m_NodeSet.empty()
return data;
}
//# Public SETTERS
void QmitkDataStorageTableModel::SetPredicate(mitk::NodePredicateBase *_Predicate)
{
// ensure that a new predicate is set in order to avoid unnecessary changed events
if (m_Predicate != _Predicate)
{
m_Predicate = _Predicate;
this->Reset();
}
}
void QmitkDataStorageTableModel::SetDataStorage(mitk::DataStorage::Pointer _DataStorage)
{
// only proceed if we have a new datastorage
if (m_DataStorage != _DataStorage)
{
auto dataStorage = m_DataStorage.Lock();
// if a data storage was set before remove old event listeners
if (dataStorage.IsNotNull())
{
dataStorage->AddNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkDataStorageTableModel, const mitk::DataNode *>(
this, &QmitkDataStorageTableModel::AddNode));
dataStorage->RemoveNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkDataStorageTableModel, const mitk::DataNode *>(
this, &QmitkDataStorageTableModel::RemoveNode));
}
// set new data storage
m_DataStorage = _DataStorage;
dataStorage = m_DataStorage.Lock();
// if new storage is not 0 subscribe for events
if (dataStorage.IsNotNull())
{
// subscribe for node added/removed events
dataStorage->AddNodeEvent.AddListener(
mitk::MessageDelegate1<QmitkDataStorageTableModel, const mitk::DataNode *>(
this, &QmitkDataStorageTableModel::AddNode));
dataStorage->RemoveNodeEvent.AddListener(
mitk::MessageDelegate1<QmitkDataStorageTableModel, const mitk::DataNode *>(
this, &QmitkDataStorageTableModel::RemoveNode));
}
// Reset model (even if datastorage is 0->will be checked in Reset())
this->Reset();
}
}
void QmitkDataStorageTableModel::AddNode(const mitk::DataNode *node)
{
// garantuee no recursions when a new node event is thrown
if (!m_BlockEvents)
{
// if we have a predicate, check node against predicate first
if (m_Predicate.IsNotNull() && !m_Predicate->CheckNode(node))
return;
// dont add nodes without data (formerly known as helper objects)
if (node->GetData() == nullptr)
return;
// create listener commands to listen to changes in the name or the visibility of the node
itk::MemberCommand<QmitkDataStorageTableModel>::Pointer propertyModifiedCommand =
itk::MemberCommand<QmitkDataStorageTableModel>::New();
propertyModifiedCommand->SetCallbackFunction(this, &QmitkDataStorageTableModel::PropertyModified);
mitk::BaseProperty *tempProperty = nullptr;
// add listener for properties
tempProperty = node->GetProperty("visible");
if (tempProperty)
m_VisiblePropertyModifiedObserverTags[tempProperty] =
tempProperty->AddObserver(itk::ModifiedEvent(), propertyModifiedCommand);
tempProperty = node->GetProperty("name");
if (tempProperty)
m_NamePropertyModifiedObserverTags[tempProperty] =
tempProperty->AddObserver(itk::ModifiedEvent(), propertyModifiedCommand);
// emit beginInsertRows event
beginInsertRows(QModelIndex(), m_NodeSet.size(), m_NodeSet.size());
// add node
m_NodeSet.push_back(const_cast<mitk::DataNode *>(node));
// emit endInsertRows event
endInsertRows();
}
}
void QmitkDataStorageTableModel::RemoveNode(const mitk::DataNode *node)
{
// garantuee no recursions when a new node event is thrown
if (!m_BlockEvents)
{
// find corresponding node
auto nodeIt = std::find(m_NodeSet.begin(), m_NodeSet.end(), node);
if (nodeIt != m_NodeSet.end())
{
// now: remove listeners for name property ...
mitk::BaseProperty *tempProperty = nullptr;
tempProperty = (*nodeIt)->GetProperty("visible");
if (tempProperty)
tempProperty->RemoveObserver(m_VisiblePropertyModifiedObserverTags[tempProperty]);
m_VisiblePropertyModifiedObserverTags.erase(tempProperty);
// ... and visibility property
tempProperty = (*nodeIt)->GetProperty("name");
if (tempProperty)
tempProperty->RemoveObserver(m_NamePropertyModifiedObserverTags[tempProperty]);
m_NamePropertyModifiedObserverTags.erase(tempProperty);
// get an index from iterator
int row = std::distance(m_NodeSet.begin(), nodeIt);
// emit beginRemoveRows event (QModelIndex is empty because we dont have a tree model)
this->beginRemoveRows(QModelIndex(), row, row);
// remove node
m_NodeSet.erase(nodeIt);
// emit endRemoveRows event
endRemoveRows();
}
}
}
void QmitkDataStorageTableModel::PropertyModified(const itk::Object *caller, const itk::EventObject &)
{
if (!m_BlockEvents)
{
// get modified property
const mitk::BaseProperty *modifiedProperty = dynamic_cast<const mitk::BaseProperty *>(caller);
if (modifiedProperty)
{
// find node that holds the modified property
int row = -1;
int column = -1;
std::vector<mitk::DataNode *>::iterator it;
mitk::BaseProperty *visibilityProperty = nullptr;
mitk::BaseProperty *nameProperty = nullptr;
// search for property that changed and emit datachanged on the corresponding ModelIndex
for (it = m_NodeSet.begin(); it != m_NodeSet.end(); it++)
{
// check for the visible property or the name property
visibilityProperty = (*it)->GetProperty("visible");
if (modifiedProperty == visibilityProperty)
{
column = 2;
break;
}
nameProperty = (*it)->GetProperty("name");
if (modifiedProperty == nameProperty)
{
column = 0;
break;
}
}
// if we have the property we have a valid iterator
if (it != m_NodeSet.end())
row = std::distance(m_NodeSet.begin(), it);
// now emit the dataChanged signal
QModelIndex indexOfChangedProperty = index(row, column);
emit dataChanged(indexOfChangedProperty, indexOfChangedProperty);
}
}
}
bool QmitkDataStorageTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
bool noErr = false;
if (index.isValid() && (role == Qt::EditRole || role == Qt::CheckStateRole))
{
// any change events produced here should not be caught in this class
// --> set m_BlockEvents to true
m_BlockEvents = true;
mitk::DataNode::Pointer node = m_NodeSet.at(index.row());
if (index.column() == 0)
{
node->SetName(value.toString().toStdString());
}
else if (index.column() == 2)
{
node->SetBoolProperty("visible", (value.toInt() == Qt::Checked ? true : false));
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
// inform listeners about changes
emit dataChanged(index, index);
m_BlockEvents = false;
noErr = true;
}
return noErr;
}
//#Protected SETTER
void QmitkDataStorageTableModel::Reset()
{
mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet;
// remove all nodes now (dont use iterators because removing elements
// would invalidate the iterator)
// start at the last element: first in, last out
unsigned int i = m_NodeSet.size();
while (!m_NodeSet.empty())
{
--i;
this->RemoveNode(m_NodeSet.at(i));
}
// normally now everything should be empty->just to be sure
// erase all arrays again
m_NamePropertyModifiedObserverTags.clear();
m_VisiblePropertyModifiedObserverTags.clear();
m_NodeSet.clear();
auto dataStorage = m_DataStorage.Lock();
// the whole reset depends on the fact if a data storage is set or not
if (dataStorage.IsNotNull())
{
_NodeSet = m_Predicate.IsNotNull()
? dataStorage->GetSubset(m_Predicate)
: dataStorage->GetAll();
// finally add all nodes to the model
for (auto it = _NodeSet->begin(); it != _NodeSet->end(); it++)
{
// save node
this->AddNode(*it);
}
}
}
void QmitkDataStorageTableModel::sort(int column, Qt::SortOrder order /*= Qt::AscendingOrder */)
{
bool sortDescending = (order == Qt::DescendingOrder) ? true : false;
// do not sort twice !!! (dont know why, but qt calls this func twice. STUPID!)
/*
if(sortDescending != m_SortDescending)
{*/
// m_SortDescending = sortDescending;
DataNodeCompareFunction::CompareCriteria _CompareCriteria = DataNodeCompareFunction::CompareByName;
DataNodeCompareFunction::CompareOperator _CompareOperator =
sortDescending ? DataNodeCompareFunction::Greater : DataNodeCompareFunction::Less;
if (column == 1)
_CompareCriteria = DataNodeCompareFunction::CompareByClassName;
else if (column == 2)
_CompareCriteria = DataNodeCompareFunction::CompareByVisibility;
DataNodeCompareFunction compareFunc(_CompareCriteria, _CompareOperator);
std::sort(m_NodeSet.begin(), m_NodeSet.end(), compareFunc);
QAbstractTableModel::beginResetModel();
QAbstractTableModel::endResetModel();
//}
}
std::vector<mitk::DataNode *> QmitkDataStorageTableModel::GetNodeSet() const
{
return m_NodeSet;
}
QmitkDataStorageTableModel::DataNodeCompareFunction::DataNodeCompareFunction(CompareCriteria _CompareCriteria,
CompareOperator _CompareOperator)
: m_CompareCriteria(_CompareCriteria), m_CompareOperator(_CompareOperator)
{
}
bool QmitkDataStorageTableModel::DataNodeCompareFunction::operator()(const mitk::DataNode::Pointer &_Left,
const mitk::DataNode::Pointer &_Right) const
{
switch (m_CompareCriteria)
{
case CompareByClassName:
if (m_CompareOperator == Less)
return (_Left->GetData()->GetNameOfClass() < _Right->GetData()->GetNameOfClass());
else
return (_Left->GetData()->GetNameOfClass() > _Right->GetData()->GetNameOfClass());
break;
case CompareByVisibility:
{
bool _LeftVisibility = false;
bool _RightVisibility = false;
_Left->GetVisibility(_LeftVisibility, nullptr);
_Right->GetVisibility(_RightVisibility, nullptr);
if (m_CompareOperator == Less)
return (_LeftVisibility < _RightVisibility);
else
return (_LeftVisibility > _RightVisibility);
}
break;
// CompareByName:
default:
if (m_CompareOperator == Less)
return (_Left->GetName() < _Right->GetName());
else
return (_Left->GetName() > _Right->GetName());
break;
}
}
diff --git a/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp
index b6256986a4..c3231eebcd 100644
--- a/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp
+++ b/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp
@@ -1,883 +1,883 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkNodePredicateAnd.h>
#include <mitkNodePredicateData.h>
#include <mitkNodePredicateNot.h>
#include <mitkNodePredicateOr.h>
#include <mitkNodePredicateProperty.h>
#include <mitkPlanarFigure.h>
#include <mitkProperties.h>
#include <mitkRenderingManager.h>
#include <mitkStringProperty.h>
#include <mitkPropertyNameHelper.h>
#include "QmitkDataStorageTreeModel.h"
#include "QmitkDataStorageTreeModelInternalItem.h"
#include "QmitkNodeDescriptorManager.h"
#include <QmitkCustomVariants.h>
#include <QmitkEnums.h>
#include <QmitkMimeTypes.h>
#include <QFile>
#include <QIcon>
#include <QMimeData>
#include <QTextStream>
#include <map>
#include <mitkCoreServices.h>
QmitkDataStorageTreeModel::QmitkDataStorageTreeModel(mitk::DataStorage *_DataStorage,
bool _PlaceNewNodesOnTop,
QObject *parent)
: QAbstractItemModel(parent),
m_DataStorage(nullptr),
m_PlaceNewNodesOnTop(_PlaceNewNodesOnTop),
m_Root(nullptr),
m_BlockDataStorageEvents(false),
m_AllowHierarchyChange(false)
{
this->SetDataStorage(_DataStorage);
}
QmitkDataStorageTreeModel::~QmitkDataStorageTreeModel()
{
// set data storage to 0 = remove all listeners
this->SetDataStorage(nullptr);
m_Root->Delete();
m_Root = nullptr;
}
mitk::DataNode::Pointer QmitkDataStorageTreeModel::GetNode(const QModelIndex &index) const
{
return this->TreeItemFromIndex(index)->GetDataNode();
}
const mitk::DataStorage::Pointer QmitkDataStorageTreeModel::GetDataStorage() const
{
return m_DataStorage.Lock();
}
QModelIndex QmitkDataStorageTreeModel::index(int row, int column, const QModelIndex &parent) const
{
TreeItem *parentItem;
if (!parent.isValid())
parentItem = m_Root;
else
parentItem = static_cast<TreeItem *>(parent.internalPointer());
TreeItem *childItem = parentItem->GetChild(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
int QmitkDataStorageTreeModel::rowCount(const QModelIndex &parent) const
{
TreeItem *parentTreeItem = this->TreeItemFromIndex(parent);
return parentTreeItem->GetChildCount();
}
Qt::ItemFlags QmitkDataStorageTreeModel::flags(const QModelIndex &index) const
{
if (index.isValid())
{
return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable |
Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}
else
{
return Qt::ItemIsDropEnabled;
}
}
int QmitkDataStorageTreeModel::columnCount(const QModelIndex & /* parent = QModelIndex() */) const
{
return 1;
}
QModelIndex QmitkDataStorageTreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = this->TreeItemFromIndex(index);
TreeItem *parentItem = childItem->GetParent();
if (parentItem == m_Root)
return QModelIndex();
return this->createIndex(parentItem->GetIndex(), 0, parentItem);
}
QmitkDataStorageTreeModel::TreeItem *QmitkDataStorageTreeModel::TreeItemFromIndex(const QModelIndex &index) const
{
if (index.isValid())
return static_cast<TreeItem *>(index.internalPointer());
else
return m_Root;
}
Qt::DropActions QmitkDataStorageTreeModel::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
Qt::DropActions QmitkDataStorageTreeModel::supportedDragActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
bool QmitkDataStorageTreeModel::dropMimeData(
const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent)
{
// Early exit, returning true, but not actually doing anything (ignoring data).
if (action == Qt::IgnoreAction)
{
return true;
}
// Note, we are returning true if we handled it, and false otherwise
bool returnValue = false;
if (data->hasFormat("application/x-qabstractitemmodeldatalist"))
{
returnValue = true;
// First we extract a Qlist of TreeItem* pointers.
QList<TreeItem *> listOfItemsToDrop = ToTreeItemPtrList(data);
if (listOfItemsToDrop.empty())
{
return false;
}
// Retrieve the TreeItem* where we are dropping stuff, and its parent.
TreeItem *dropItem = this->TreeItemFromIndex(parent);
TreeItem *parentItem = dropItem->GetParent();
// If item was dropped onto empty space, we select the root node
if (dropItem == m_Root)
{
parentItem = m_Root;
}
// Dragging and Dropping is only allowed within the same parent, so use the first item in list to validate.
// (otherwise, you could have a derived image such as a segmentation, and assign it to another image).
// NOTE: We are assuming the input list is valid... i.e. when it was dragged, all the items had the same parent.
// Determine whether or not the drag and drop operation is a valid one.
// Examples of invalid operations include:
// - dragging nodes with different parents
// - dragging nodes from one parent to another parent, if m_AllowHierarchyChange is false
// - dragging a node on one of its child nodes (only relevant if m_AllowHierarchyChange is true)
bool isValidDragAndDropOperation(true);
// different parents
{
TreeItem *firstParent = listOfItemsToDrop[0]->GetParent();
QList<TreeItem *>::iterator diIter;
for (diIter = listOfItemsToDrop.begin() + 1; diIter != listOfItemsToDrop.end(); diIter++)
{
if (firstParent != (*diIter)->GetParent())
{
isValidDragAndDropOperation = false;
break;
}
}
}
// dragging from one parent to another
if ((!m_AllowHierarchyChange) && isValidDragAndDropOperation)
{
if (row == -1) // drag onto a node
{
isValidDragAndDropOperation = listOfItemsToDrop[0]->GetParent() == parentItem;
}
else // drag between nodes
{
isValidDragAndDropOperation = listOfItemsToDrop[0]->GetParent() == dropItem;
}
}
// dragging on a child node of one the dragged nodes
{
QList<TreeItem *>::iterator diIter;
for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++)
{
TreeItem *tempItem = dropItem;
while (tempItem != m_Root)
{
tempItem = tempItem->GetParent();
if (tempItem == *diIter)
{
isValidDragAndDropOperation = false;
}
}
}
}
if (!isValidDragAndDropOperation)
return isValidDragAndDropOperation;
if (listOfItemsToDrop[0] != dropItem && isValidDragAndDropOperation)
{
// Retrieve the index of where we are dropping stuff.
QModelIndex parentModelIndex = this->IndexFromTreeItem(parentItem);
int dragIndex = 0;
// Iterate through the list of TreeItem (which may be at non-consecutive indexes).
QList<TreeItem *>::iterator diIter;
for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++)
{
TreeItem *itemToDrop = *diIter;
// if the item is dragged down we have to compensate its final position for the
// fact it is deleted lateron, this only applies if it is dragged within the same level
if ((itemToDrop->GetIndex() < row) && (itemToDrop->GetParent() == dropItem))
{
dragIndex = 1;
}
// Here we assume that as you remove items, one at a time, that GetIndex() will be valid.
this->beginRemoveRows(
this->IndexFromTreeItem(itemToDrop->GetParent()), itemToDrop->GetIndex(), itemToDrop->GetIndex());
itemToDrop->GetParent()->RemoveChild(itemToDrop);
this->endRemoveRows();
}
// row = -1 dropped on an item, row != -1 dropped in between two items
// Select the target index position, or put it at the end of the list.
int dropIndex = 0;
if (row != -1)
{
if (dragIndex == 0)
dropIndex = std::min(row, parentItem->GetChildCount() - 1);
else
dropIndex = std::min(row - 1, parentItem->GetChildCount() - 1);
}
else
{
dropIndex = dropItem->GetIndex();
}
QModelIndex dropItemModelIndex = this->IndexFromTreeItem(dropItem);
if ((row == -1 && dropItemModelIndex.row() == -1) || dropItemModelIndex.row() > parentItem->GetChildCount())
dropIndex = parentItem->GetChildCount() - 1;
// Now insert items again at the drop item position
if (m_AllowHierarchyChange)
{
this->beginInsertRows(dropItemModelIndex, dropIndex, dropIndex + listOfItemsToDrop.size() - 1);
}
else
{
this->beginInsertRows(parentModelIndex, dropIndex, dropIndex + listOfItemsToDrop.size() - 1);
}
for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++)
{
// dropped on node, behaviour depends on preference setting
if (m_AllowHierarchyChange)
{
auto dataStorage = m_DataStorage.Lock();
m_BlockDataStorageEvents = true;
mitk::DataNode::Pointer droppedNode = (*diIter)->GetDataNode();
mitk::DataNode *dropOntoNode = dropItem->GetDataNode();
dataStorage->Remove(droppedNode);
dataStorage->Add(droppedNode, dropOntoNode);
m_BlockDataStorageEvents = false;
dropItem->InsertChild((*diIter), dropIndex);
}
else
{
if (row == -1) // drag onto a node
{
parentItem->InsertChild((*diIter), dropIndex);
}
else // drag between nodes
{
dropItem->InsertChild((*diIter), dropIndex);
}
}
dropIndex++;
}
this->endInsertRows();
// Change Layers to match.
this->AdjustLayerProperty();
}
}
else if (data->hasFormat("application/x-mitk-datanodes"))
{
returnValue = true;
int numberOfNodesDropped = 0;
QList<mitk::DataNode *> dataNodeList = QmitkMimeTypes::ToDataNodePtrList(data);
mitk::DataNode *node = nullptr;
foreach (node, dataNodeList)
{
auto datastorage = m_DataStorage.Lock();
if (node && datastorage.IsNotNull() && !datastorage->Exists(node))
{
m_DataStorage.Lock()->Add(node);
mitk::BaseData::Pointer basedata = node->GetData();
if (basedata.IsNotNull())
{
mitk::RenderingManager::GetInstance()->InitializeViews(basedata->GetTimeGeometry());
numberOfNodesDropped++;
}
}
}
// Only do a rendering update, if we actually dropped anything.
if (numberOfNodesDropped > 0)
{
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
return returnValue;
}
QStringList QmitkDataStorageTreeModel::mimeTypes() const
{
QStringList types = QAbstractItemModel::mimeTypes();
types << "application/x-qabstractitemmodeldatalist";
types << "application/x-mitk-datanodes";
return types;
}
QMimeData *QmitkDataStorageTreeModel::mimeData(const QModelIndexList &indexes) const
{
return mimeDataFromModelIndexList(indexes);
}
QMimeData *QmitkDataStorageTreeModel::mimeDataFromModelIndexList(const QModelIndexList &indexes)
{
QMimeData *ret = new QMimeData;
QString treeItemAddresses("");
QString dataNodeAddresses("");
QByteArray baTreeItemPtrs;
QByteArray baDataNodePtrs;
QDataStream dsTreeItemPtrs(&baTreeItemPtrs, QIODevice::WriteOnly);
QDataStream dsDataNodePtrs(&baDataNodePtrs, QIODevice::WriteOnly);
for (int i = 0; i < indexes.size(); i++)
{
TreeItem *treeItem = static_cast<TreeItem *>(indexes.at(i).internalPointer());
dsTreeItemPtrs << reinterpret_cast<quintptr>(treeItem);
dsDataNodePtrs << reinterpret_cast<quintptr>(treeItem->GetDataNode().GetPointer());
// --------------- deprecated -----------------
unsigned long long treeItemAddress = reinterpret_cast<unsigned long long>(treeItem);
unsigned long long dataNodeAddress = reinterpret_cast<unsigned long long>(treeItem->GetDataNode().GetPointer());
QTextStream(&treeItemAddresses) << treeItemAddress;
QTextStream(&dataNodeAddresses) << dataNodeAddress;
if (i != indexes.size() - 1)
{
QTextStream(&treeItemAddresses) << ",";
QTextStream(&dataNodeAddresses) << ",";
}
// -------------- end deprecated -------------
}
// ------------------ deprecated -----------------
ret->setData("application/x-qabstractitemmodeldatalist", QByteArray(treeItemAddresses.toLatin1()));
ret->setData("application/x-mitk-datanodes", QByteArray(dataNodeAddresses.toLatin1()));
// --------------- end deprecated -----------------
ret->setData(QmitkMimeTypes::DataStorageTreeItemPtrs, baTreeItemPtrs);
ret->setData(QmitkMimeTypes::DataNodePtrs, baDataNodePtrs);
return ret;
}
QVariant QmitkDataStorageTreeModel::data(const QModelIndex &index, int role) const
{
mitk::DataNode *dataNode = this->TreeItemFromIndex(index)->GetDataNode();
// get name of treeItem (may also be edited)
QString nodeName = QString::fromStdString(dataNode->GetName());
if (nodeName.isEmpty())
{
nodeName = "unnamed";
}
if (role == Qt::DisplayRole)
return nodeName;
else if (role == Qt::ToolTipRole)
return nodeName;
else if (role == Qt::DecorationRole)
{
QmitkNodeDescriptor *nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode);
return nodeDescriptor->GetIcon(dataNode);
}
else if (role == Qt::CheckStateRole)
{
return dataNode->IsVisible(nullptr);
}
else if (role == QmitkDataNodeRole)
{
return QVariant::fromValue<mitk::DataNode::Pointer>(mitk::DataNode::Pointer(dataNode));
}
else if (role == QmitkDataNodeRawPointerRole)
{
return QVariant::fromValue<mitk::DataNode *>(dataNode);
}
return QVariant();
}
bool QmitkDataStorageTreeModel::DicomPropertiesExists(const mitk::DataNode &node) const
{
bool propertiesExists = false;
mitk::BaseProperty *seriesDescription_deprecated = (node.GetProperty("dicom.series.SeriesDescription"));
mitk::BaseProperty *studyDescription_deprecated = (node.GetProperty("dicom.study.StudyDescription"));
mitk::BaseProperty *patientsName_deprecated = (node.GetProperty("dicom.patient.PatientsName"));
mitk::BaseProperty *seriesDescription =
(node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x103e).c_str()));
mitk::BaseProperty *studyDescription =
(node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x1030).c_str()));
mitk::BaseProperty *patientsName = (node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str()));
if (patientsName != nullptr && studyDescription != nullptr && seriesDescription != nullptr)
{
if ((!patientsName->GetValueAsString().empty()) && (!studyDescription->GetValueAsString().empty()) &&
(!seriesDescription->GetValueAsString().empty()))
{
propertiesExists = true;
}
}
- /** Code coveres the deprecated property naming for backwards compatibility */
+ /** Code covers the deprecated property naming for backwards compatibility */
if (patientsName_deprecated != nullptr && studyDescription_deprecated != nullptr && seriesDescription_deprecated != nullptr)
{
if ((!patientsName_deprecated->GetValueAsString().empty()) &&
(!studyDescription_deprecated->GetValueAsString().empty()) &&
(!seriesDescription_deprecated->GetValueAsString().empty()))
{
propertiesExists = true;
}
}
return propertiesExists;
}
QVariant QmitkDataStorageTreeModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && m_Root)
return QString::fromStdString(m_Root->GetDataNode()->GetName());
return QVariant();
}
void QmitkDataStorageTreeModel::SetDataStorage(mitk::DataStorage *_DataStorage)
{
if (m_DataStorage != _DataStorage) // dont take the same again
{
auto dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNotNull())
{
// remove Listener for the data storage itself
dataStorage->RemoveObserver(m_DataStorageDeletedTag);
// remove listeners for the nodes
dataStorage->AddNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkDataStorageTreeModel, const mitk::DataNode *>(this,
&QmitkDataStorageTreeModel::AddNode));
dataStorage->ChangedNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkDataStorageTreeModel, const mitk::DataNode *>(
this, &QmitkDataStorageTreeModel::SetNodeModified));
dataStorage->RemoveNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkDataStorageTreeModel, const mitk::DataNode *>(
this, &QmitkDataStorageTreeModel::RemoveNode));
}
this->beginResetModel();
// take over the new data storage
m_DataStorage = _DataStorage;
// delete the old root (if necessary, create new)
if (m_Root)
m_Root->Delete();
mitk::DataNode::Pointer rootDataNode = mitk::DataNode::New();
rootDataNode->SetName("Data Manager");
m_Root = new TreeItem(rootDataNode, nullptr);
dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNotNull())
{
// add Listener for the data storage itself
auto command = itk::SimpleMemberCommand<QmitkDataStorageTreeModel>::New();
command->SetCallbackFunction(this, &QmitkDataStorageTreeModel::SetDataStorageDeleted);
m_DataStorageDeletedTag = dataStorage->AddObserver(itk::DeleteEvent(), command);
// add listeners for the nodes
dataStorage->AddNodeEvent.AddListener(mitk::MessageDelegate1<QmitkDataStorageTreeModel, const mitk::DataNode *>(
this, &QmitkDataStorageTreeModel::AddNode));
dataStorage->ChangedNodeEvent.AddListener(
mitk::MessageDelegate1<QmitkDataStorageTreeModel, const mitk::DataNode *>(
this, &QmitkDataStorageTreeModel::SetNodeModified));
dataStorage->RemoveNodeEvent.AddListener(
mitk::MessageDelegate1<QmitkDataStorageTreeModel, const mitk::DataNode *>(
this, &QmitkDataStorageTreeModel::RemoveNode));
// finally add all nodes to the model
this->Update();
}
this->endResetModel();
}
}
void QmitkDataStorageTreeModel::SetDataStorageDeleted()
{
this->SetDataStorage(nullptr);
}
void QmitkDataStorageTreeModel::AddNodeInternal(const mitk::DataNode *node)
{
auto dataStorage = m_DataStorage.Lock();
if (node == nullptr || dataStorage.IsNull() || !dataStorage->Exists(node) || m_Root->Find(node) != nullptr)
return;
// find out if we have a root node
TreeItem *parentTreeItem = m_Root;
QModelIndex index;
mitk::DataNode *parentDataNode = this->GetParentNode(node);
if (parentDataNode) // no top level data node
{
parentTreeItem = m_Root->Find(parentDataNode); // find the corresponding tree item
if (!parentTreeItem)
{
this->AddNode(parentDataNode);
parentTreeItem = m_Root->Find(parentDataNode);
if (!parentTreeItem)
return;
}
// get the index of this parent with the help of the grand parent
index = this->createIndex(parentTreeItem->GetIndex(), 0, parentTreeItem);
}
// add node
if (m_PlaceNewNodesOnTop)
{
// emit beginInsertRows event
beginInsertRows(index, 0, 0);
parentTreeItem->InsertChild(new TreeItem(const_cast<mitk::DataNode *>(node)), 0);
}
else
{
int firstRowWithASiblingBelow = 0;
int nodeLayer = -1;
node->GetIntProperty("layer", nodeLayer);
for (TreeItem* siblingTreeItem: parentTreeItem->GetChildren())
{
int siblingLayer = -1;
if (mitk::DataNode* siblingNode = siblingTreeItem->GetDataNode())
{
siblingNode->GetIntProperty("layer", siblingLayer);
}
if (nodeLayer > siblingLayer)
{
break;
}
++firstRowWithASiblingBelow;
}
beginInsertRows(index, firstRowWithASiblingBelow, firstRowWithASiblingBelow);
parentTreeItem->InsertChild(new TreeItem(const_cast<mitk::DataNode*>(node)), firstRowWithASiblingBelow);
}
// emit endInsertRows event
endInsertRows();
if(m_PlaceNewNodesOnTop)
{
this->AdjustLayerProperty();
}
}
void QmitkDataStorageTreeModel::AddNode(const mitk::DataNode *node)
{
auto dataStorage = m_DataStorage.Lock();
if (node == nullptr || m_BlockDataStorageEvents || dataStorage.IsNull() || !dataStorage->Exists(node) ||
m_Root->Find(node) != nullptr)
return;
this->AddNodeInternal(node);
}
void QmitkDataStorageTreeModel::SetPlaceNewNodesOnTop(bool _PlaceNewNodesOnTop)
{
m_PlaceNewNodesOnTop = _PlaceNewNodesOnTop;
}
void QmitkDataStorageTreeModel::RemoveNodeInternal(const mitk::DataNode *node)
{
if (!m_Root)
return;
TreeItem *treeItem = m_Root->Find(node);
if (!treeItem)
return; // return because there is no treeitem containing this node
TreeItem *parentTreeItem = treeItem->GetParent();
QModelIndex parentIndex = this->IndexFromTreeItem(parentTreeItem);
// emit beginRemoveRows event (QModelIndex is empty because we dont have a tree model)
this->beginRemoveRows(parentIndex, treeItem->GetIndex(), treeItem->GetIndex());
// remove node
std::vector<TreeItem*> children = treeItem->GetChildren();
delete treeItem;
// emit endRemoveRows event
endRemoveRows();
// move all children of deleted node into its parent
for (std::vector<TreeItem*>::iterator it = children.begin(); it != children.end(); it++)
{
// emit beginInsertRows event
beginInsertRows(parentIndex, parentTreeItem->GetChildCount(), parentTreeItem->GetChildCount());
// add nodes again
parentTreeItem->AddChild(*it);
// emit endInsertRows event
endInsertRows();
}
this->AdjustLayerProperty();
}
void QmitkDataStorageTreeModel::RemoveNode(const mitk::DataNode *node)
{
if (node == nullptr || m_BlockDataStorageEvents)
return;
this->RemoveNodeInternal(node);
}
void QmitkDataStorageTreeModel::SetNodeModified(const mitk::DataNode *node)
{
TreeItem *treeItem = m_Root->Find(node);
if (treeItem)
{
TreeItem *parentTreeItem = treeItem->GetParent();
// as the root node should not be removed one should always have a parent item
if (!parentTreeItem)
return;
QModelIndex index = this->createIndex(treeItem->GetIndex(), 0, treeItem);
// now emit the dataChanged signal
emit dataChanged(index, index);
}
}
mitk::DataNode *QmitkDataStorageTreeModel::GetParentNode(const mitk::DataNode *node) const
{
mitk::DataNode *dataNode = nullptr;
mitk::DataStorage::SetOfObjects::ConstPointer _Sources = m_DataStorage.Lock()->GetSources(node);
if (_Sources->Size() > 0)
dataNode = _Sources->front();
return dataNode;
}
bool QmitkDataStorageTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
mitk::DataNode *dataNode = this->TreeItemFromIndex(index)->GetDataNode();
if (!dataNode)
return false;
if (role == Qt::EditRole && !value.toString().isEmpty())
{
dataNode->SetName(value.toString().toStdString());
mitk::PlanarFigure *planarFigure = dynamic_cast<mitk::PlanarFigure *>(dataNode->GetData());
if (planarFigure != nullptr)
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
else if (role == Qt::CheckStateRole)
{
// Please note: value.toInt() returns 2, independentely from the actual checkstate of the index element.
// Therefore the checkstate is being estimated again here.
QVariant qcheckstate = index.data(Qt::CheckStateRole);
int checkstate = qcheckstate.toInt();
bool isVisible = bool(checkstate);
dataNode->SetVisibility(!isVisible);
emit nodeVisibilityChanged();
}
// inform listeners about changes
emit dataChanged(index, index);
return true;
}
bool QmitkDataStorageTreeModel::setHeaderData(int /*section*/,
Qt::Orientation /*orientation*/,
const QVariant & /* value */,
int /*role = Qt::EditRole*/)
{
return false;
}
void QmitkDataStorageTreeModel::AdjustLayerProperty()
{
/// transform the tree into an array and set the layer property descending
std::vector<TreeItem *> vec;
this->TreeToVector(m_Root, vec);
int i = vec.size() - 1;
for (std::vector<TreeItem *>::const_iterator it = vec.begin(); it != vec.end(); ++it)
{
mitk::DataNode::Pointer dataNode = (*it)->GetDataNode();
bool fixedLayer = false;
if (!(dataNode->GetBoolProperty("fixedLayer", fixedLayer) && fixedLayer))
dataNode->SetIntProperty("layer", i);
--i;
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkDataStorageTreeModel::TreeToVector(TreeItem *parent, std::vector<TreeItem *> &vec) const
{
TreeItem *current;
for (int i = 0; i < parent->GetChildCount(); ++i)
{
current = parent->GetChild(i);
this->TreeToVector(current, vec);
vec.push_back(current);
}
}
QModelIndex QmitkDataStorageTreeModel::IndexFromTreeItem(TreeItem *item) const
{
if (item == m_Root)
return QModelIndex();
else
return this->createIndex(item->GetIndex(), 0, item);
}
QList<mitk::DataNode::Pointer> QmitkDataStorageTreeModel::GetNodeSet() const
{
QList<mitk::DataNode::Pointer> res;
if (m_Root)
this->TreeToNodeSet(m_Root, res);
return res;
}
void QmitkDataStorageTreeModel::TreeToNodeSet(TreeItem *parent, QList<mitk::DataNode::Pointer> &vec) const
{
TreeItem *current;
for (int i = 0; i < parent->GetChildCount(); ++i)
{
current = parent->GetChild(i);
vec.push_back(current->GetDataNode());
this->TreeToNodeSet(current, vec);
}
}
QModelIndex QmitkDataStorageTreeModel::GetIndex(const mitk::DataNode *node) const
{
if (m_Root)
{
TreeItem *item = m_Root->Find(node);
if (item)
return this->IndexFromTreeItem(item);
}
return QModelIndex();
}
QList<QmitkDataStorageTreeModel::TreeItem *> QmitkDataStorageTreeModel::ToTreeItemPtrList(const QMimeData *mimeData)
{
if (mimeData == nullptr || !mimeData->hasFormat(QmitkMimeTypes::DataStorageTreeItemPtrs))
{
return QList<TreeItem *>();
}
return ToTreeItemPtrList(mimeData->data(QmitkMimeTypes::DataStorageTreeItemPtrs));
}
QList<QmitkDataStorageTreeModel::TreeItem *> QmitkDataStorageTreeModel::ToTreeItemPtrList(const QByteArray &ba)
{
QList<TreeItem *> result;
QDataStream ds(ba);
while (!ds.atEnd())
{
quintptr treeItemPtr;
ds >> treeItemPtr;
result.push_back(reinterpret_cast<TreeItem *>(treeItemPtr));
}
return result;
}
void QmitkDataStorageTreeModel::Update()
{
auto datastorage = m_DataStorage.Lock();
if (datastorage.IsNotNull())
{
mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = datastorage->GetAll();
/// Regardless the value of this preference, the new nodes must not be inserted
/// at the top now, but at the position according to their layer.
bool newNodesWereToBePlacedOnTop = m_PlaceNewNodesOnTop;
m_PlaceNewNodesOnTop = false;
for (const auto& node : *_NodeSet)
{
this->AddNodeInternal(node);
}
m_PlaceNewNodesOnTop = newNodesWereToBePlacedOnTop;
/// Adjust the layers to ensure that derived nodes are above their sources.
this->AdjustLayerProperty();
}
}
void QmitkDataStorageTreeModel::SetAllowHierarchyChange(bool allowHierarchyChange)
{
m_AllowHierarchyChange = allowHierarchyChange;
}
diff --git a/Modules/QtWidgets/src/QmitkIOUtil.cpp b/Modules/QtWidgets/src/QmitkIOUtil.cpp
index e8f59fc640..0e9e4a8418 100644
--- a/Modules/QtWidgets/src/QmitkIOUtil.cpp
+++ b/Modules/QtWidgets/src/QmitkIOUtil.cpp
@@ -1,587 +1,587 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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>
#include <QStringList>
// 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 : std::as_const(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
{
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("", Qt::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.
+ // the file already 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"
+ // Add an artificial 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/QtWidgets/src/QmitkMxNMultiWidget.cpp b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp
index ef255e9858..6431f3fab3 100644
--- a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp
+++ b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp
@@ -1,650 +1,651 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkMxNMultiWidget.h"
// mitk core
#include <mitkDisplayActionEventFunctions.h>
#include <mitkDisplayActionEventHandlerDesynchronized.h>
#include <mitkDisplayActionEventHandlerSynchronized.h>
#include <mitkNodePredicateNot.h>
#include <mitkNodePredicateAnd.h>
#include <mitkNodePredicateProperty.h>
// mitk qt widget
#include <QmitkRenderWindowUtilityWidget.h>
#include <QmitkRenderWindowWidget.h>
// qt
#include <QGridLayout>
#include <QMessageBox>
#include <QSplitter>
#include <fstream>
QmitkMxNMultiWidget::QmitkMxNMultiWidget(QWidget* parent,
Qt::WindowFlags f/* = 0*/,
const QString& multiWidgetName/* = "mxn"*/)
: QmitkAbstractMultiWidget(parent, f, multiWidgetName)
, m_SynchronizedWidgetConnector(std::make_unique<QmitkSynchronizedWidgetConnector>())
, m_CrosshairVisibility(false)
{
}
QmitkMxNMultiWidget::~QmitkMxNMultiWidget()
{
}
void QmitkMxNMultiWidget::InitializeMultiWidget()
{
SetLayout(1, 1);
SetDisplayActionEventHandler(std::make_unique<mitk::DisplayActionEventHandlerDesynchronized>());
auto displayActionEventHandler = GetDisplayActionEventHandler();
if (nullptr != displayActionEventHandler)
{
displayActionEventHandler->InitActions();
}
this->SetInitialSelection();
}
void QmitkMxNMultiWidget::Synchronize(bool synchronized)
{
if (synchronized)
{
SetDisplayActionEventHandler(std::make_unique<mitk::DisplayActionEventHandlerSynchronized>());
}
else
{
SetDisplayActionEventHandler(std::make_unique<mitk::DisplayActionEventHandlerDesynchronized>());
}
auto displayActionEventHandler = GetDisplayActionEventHandler();
if (nullptr != displayActionEventHandler)
{
displayActionEventHandler->InitActions();
}
}
QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const QString& widgetName) const
{
if ("axial" == widgetName || "sagittal" == widgetName || "coronal" == widgetName || "3d" == widgetName)
{
return GetActiveRenderWindowWidget()->GetRenderWindow();
}
return QmitkAbstractMultiWidget::GetRenderWindow(widgetName);
}
QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const mitk::AnatomicalPlane& /*orientation*/) const
{
// currently no mapping between plane orientation and render windows
// simply return the currently active render window
return GetActiveRenderWindowWidget()->GetRenderWindow();
}
void QmitkMxNMultiWidget::SetActiveRenderWindowWidget(RenderWindowWidgetPointer activeRenderWindowWidget)
{
auto currentActiveRenderWindowWidget = GetActiveRenderWindowWidget();
if (currentActiveRenderWindowWidget == activeRenderWindowWidget)
{
return;
}
// reset the decoration color of the previously active render window widget
if (nullptr != currentActiveRenderWindowWidget)
{
auto decorationColor = currentActiveRenderWindowWidget->GetDecorationColor();
QColor hexColor(decorationColor[0] * 255, decorationColor[1] * 255, decorationColor[2] * 255);
currentActiveRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid " +
hexColor.name(QColor::HexRgb) + "; }");
}
// set the new decoration color of the currently active render window widget
if (nullptr != activeRenderWindowWidget)
{
activeRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid #FF6464; }");
}
QmitkAbstractMultiWidget::SetActiveRenderWindowWidget(activeRenderWindowWidget);
}
void QmitkMxNMultiWidget::InitializeViews(const mitk::TimeGeometry* geometry, bool resetCamera)
{
auto* renderingManager = mitk::RenderingManager::GetInstance();
mitk::Point3D currentPosition = mitk::Point3D();
unsigned int imageTimeStep = 0;
if (!resetCamera)
{
// store the current position to set it again later, if the camera should not be reset
currentPosition = this->GetSelectedPosition("");
// store the current time step to set it again later, if the camera should not be reset
const mitk::TimePointType currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint();
if (geometry->IsValidTimePoint(currentTimePoint))
{
imageTimeStep = geometry->TimePointToTimeStep(currentTimePoint);
}
}
// initialize active render window
renderingManager->InitializeView(
this->GetActiveRenderWindowWidget()->GetRenderWindow()->GetVtkRenderWindow(), geometry, resetCamera);
if (!resetCamera)
{
this->SetSelectedPosition(currentPosition, "");
renderingManager->GetTimeNavigationController()->GetStepper()->SetPos(imageTimeStep);
}
}
void QmitkMxNMultiWidget::SetInteractionReferenceGeometry(const mitk::TimeGeometry* referenceGeometry)
{
// Set the interaction reference referenceGeometry for all render windows.
auto allRenderWindows = this->GetRenderWindows();
for (auto& renderWindow : allRenderWindows)
{
auto* baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow->GetVtkRenderWindow());
baseRenderer->SetInteractionReferenceGeometry(referenceGeometry);
}
}
bool QmitkMxNMultiWidget::HasCoupledRenderWindows() const
{
return false;
}
void QmitkMxNMultiWidget::SetSelectedPosition(const mitk::Point3D& newPosition, const QString& widgetName)
{
RenderWindowWidgetPointer renderWindowWidget;
if (widgetName.isNull() || widgetName.isEmpty())
{
renderWindowWidget = GetActiveRenderWindowWidget();
}
else
{
renderWindowWidget = GetRenderWindowWidget(widgetName);
}
if (nullptr != renderWindowWidget)
{
renderWindowWidget->GetSliceNavigationController()->SelectSliceByPoint(newPosition);
return;
}
MITK_ERROR << "Position can not be set for an unknown render window widget.";
}
const mitk::Point3D QmitkMxNMultiWidget::GetSelectedPosition(const QString& widgetName) const
{
RenderWindowWidgetPointer renderWindowWidget;
if (widgetName.isNull() || widgetName.isEmpty())
{
renderWindowWidget = GetActiveRenderWindowWidget();
}
else
{
renderWindowWidget = GetRenderWindowWidget(widgetName);
}
if (nullptr != renderWindowWidget)
{
return renderWindowWidget->GetCrosshairPosition();
}
MITK_ERROR << "Crosshair position can not be retrieved.";
return mitk::Point3D(0.0);
}
void QmitkMxNMultiWidget::SetCrosshairVisibility(bool visible)
{
// get the specific render window that sent the signal
QmitkRenderWindow* renderWindow = qobject_cast<QmitkRenderWindow*>(sender());
if (nullptr == renderWindow)
{
return;
}
auto renderWindowWidget = this->GetRenderWindowWidget(renderWindow);
renderWindowWidget->SetCrosshairVisibility(visible);
}
bool QmitkMxNMultiWidget::GetCrosshairVisibility() const
{
// get the specific render window that sent the signal
QmitkRenderWindow* renderWindow = qobject_cast<QmitkRenderWindow*>(sender());
if (nullptr == renderWindow)
{
return false;
}
auto renderWindowWidget = this->GetRenderWindowWidget(renderWindow);
return renderWindowWidget->GetCrosshairVisibility();
}
void QmitkMxNMultiWidget::SetCrosshairGap(unsigned int gapSize)
{
auto renderWindowWidgets = this->GetRenderWindowWidgets();
for (const auto& renderWindowWidget : renderWindowWidgets)
{
renderWindowWidget.second->SetCrosshairGap(gapSize);
}
}
void QmitkMxNMultiWidget::ResetCrosshair()
{
auto dataStorage = GetDataStorage();
if (nullptr == dataStorage)
{
return;
}
// get the specific render window that sent the signal
QmitkRenderWindow* renderWindow = qobject_cast<QmitkRenderWindow*>(sender());
if (nullptr == renderWindow)
{
return;
}
mitk::RenderingManager::GetInstance()->InitializeViewByBoundingObjects(renderWindow->GetVtkRenderWindow(), dataStorage);
SetWidgetPlaneMode(mitk::InteractionSchemeSwitcher::MITKStandard);
}
void QmitkMxNMultiWidget::SetWidgetPlaneMode(int userMode)
{
MITK_DEBUG << "Changing crosshair mode to " << userMode;
switch (userMode)
{
case 0:
SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKStandard);
break;
case 1:
SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationUncoupled);
break;
case 2:
SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationCoupled);
break;
case 3:
SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKSwivel);
break;
}
}
void QmitkMxNMultiWidget::EnableCrosshair()
{
auto renderWindowWidgets = this->GetRenderWindowWidgets();
for (const auto& renderWindowWidget : renderWindowWidgets)
{
renderWindowWidget.second->EnableCrosshair();
}
}
void QmitkMxNMultiWidget::DisableCrosshair()
{
auto renderWindowWidgets = this->GetRenderWindowWidgets();
for (const auto& renderWindowWidget : renderWindowWidgets)
{
renderWindowWidget.second->DisableCrosshair();
}
}
//////////////////////////////////////////////////////////////////////////
// PUBLIC SLOTS
// MOUSE EVENTS
//////////////////////////////////////////////////////////////////////////
void QmitkMxNMultiWidget::wheelEvent(QWheelEvent* e)
{
emit WheelMoved(e);
}
void QmitkMxNMultiWidget::mousePressEvent(QMouseEvent*)
{
// nothing here, but necessary for mouse interactions (.xml-configuration files)
}
void QmitkMxNMultiWidget::moveEvent(QMoveEvent* e)
{
QWidget::moveEvent(e);
// it is necessary to readjust the position of the overlays as the MultiWidget has moved
// unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here
emit Moved();
}
//////////////////////////////////////////////////////////////////////////
// PRIVATE
//////////////////////////////////////////////////////////////////////////
void QmitkMxNMultiWidget::SetLayoutImpl()
{
int requiredRenderWindowWidgets = GetRowCount() * GetColumnCount();
int existingRenderWindowWidgets = GetRenderWindowWidgets().size();
int difference = requiredRenderWindowWidgets - existingRenderWindowWidgets;
while (0 < difference)
{
// more render window widgets needed
CreateRenderWindowWidget();
--difference;
}
while (0 > difference)
{
// less render window widgets needed
RemoveRenderWindowWidget();
++difference;
}
auto firstRenderWindowWidget = GetFirstRenderWindowWidget();
if (nullptr != firstRenderWindowWidget)
{
SetActiveRenderWindowWidget(firstRenderWindowWidget);
}
GetMultiWidgetLayoutManager()->SetLayoutDesign(QmitkMultiWidgetLayoutManager::LayoutDesign::DEFAULT);
}
QmitkAbstractMultiWidget::RenderWindowWidgetPointer QmitkMxNMultiWidget::CreateRenderWindowWidget()
{
// create the render window widget and connect signal / slot
QString renderWindowWidgetName = GetNameFromIndex(GetNumberOfRenderWindowWidgets());
RenderWindowWidgetPointer renderWindowWidget = std::make_shared<QmitkRenderWindowWidget>(this, renderWindowWidgetName, GetDataStorage());
renderWindowWidget->SetCornerAnnotationText(renderWindowWidgetName.toStdString());
AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget);
auto renderWindow = renderWindowWidget->GetRenderWindow();
QmitkRenderWindowUtilityWidget* utilityWidget = new QmitkRenderWindowUtilityWidget(this, renderWindow, GetDataStorage());
renderWindowWidget->AddUtilityWidget(utilityWidget);
connect(utilityWidget, &QmitkRenderWindowUtilityWidget::SynchronizationToggled,
this, &QmitkMxNMultiWidget::ToggleSynchronization);
connect(this, &QmitkMxNMultiWidget::UpdateUtilityWidgetViewPlanes,
utilityWidget, &QmitkRenderWindowUtilityWidget::UpdateViewPlaneSelection);
// needs to be done after 'QmitkRenderWindowUtilityWidget::ToggleSynchronization' has been connected
// initially synchronize the node selection widget
utilityWidget->ToggleSynchronization(true);
auto layoutManager = GetMultiWidgetLayoutManager();
connect(renderWindow, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign);
connect(renderWindow, &QmitkRenderWindow::ResetView, this, &QmitkMxNMultiWidget::ResetCrosshair);
connect(renderWindow, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkMxNMultiWidget::SetCrosshairVisibility);
connect(renderWindow, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkMxNMultiWidget::SetWidgetPlaneMode);
return renderWindowWidget;
}
QmitkAbstractMultiWidget::RenderWindowWidgetPointer QmitkMxNMultiWidget::GetWindowFromIndex(size_t index)
{
if (index >= GetRenderWindowWidgets().size())
{
return nullptr;
}
auto renderWindowName = this->GetNameFromIndex(index);
auto renderWindowWidgets = GetRenderWindowWidgets();
auto it = renderWindowWidgets.find(renderWindowName);
if (it != renderWindowWidgets.end())
{
return it->second;
}
else
{
MITK_ERROR << "Could not find render window " << renderWindowName.toStdString() << ", although it should be there.";
return nullptr;
}
}
void QmitkMxNMultiWidget::LoadLayout(const nlohmann::json* jsonData)
{
if ((*jsonData).is_null())
{
QMessageBox::warning(this, "Load layout", "Could not read window layout");
return;
}
unsigned int windowCounter = 0;
try
{
auto version = jsonData->at("version").get<std::string>();
if (version != "1.0")
{
QMessageBox::warning(this, "Load layout", "Unknown layout version, could not load");
return;
}
delete this->layout();
auto content = BuildLayoutFromJSON(jsonData, &windowCounter);
auto hBoxLayout = new QHBoxLayout(this);
this->setLayout(hBoxLayout);
hBoxLayout->addWidget(content);
emit UpdateUtilityWidgetViewPlanes();
}
catch (nlohmann::json::out_of_range& e)
{
MITK_ERROR << "Error in loading window layout from JSON: " << e.what();
return;
}
while (GetNumberOfRenderWindowWidgets() > windowCounter)
{
RemoveRenderWindowWidget();
}
EnableCrosshair();
emit LayoutChanged();
}
void QmitkMxNMultiWidget::SaveLayout(std::ostream* outStream)
{
if (outStream == nullptr)
{
return;
}
auto layout = this->layout();
if (layout == nullptr)
return;
// There should only ever be one item: a splitter
auto widget = layout->itemAt(0)->widget();
auto splitter = dynamic_cast<QSplitter*>(widget);
if (!splitter)
{
MITK_ERROR << "Tried to save unexpected layout format. Make sure the layout of this instance contains a single QSplitter.";
return;
}
auto layoutJSON = BuildJSONFromLayout(splitter);
layoutJSON["version"] = "1.0";
layoutJSON["name"] = "Custom Layout";
*outStream << std::setw(4) << layoutJSON << std::endl;
}
nlohmann::json QmitkMxNMultiWidget::BuildJSONFromLayout(const QSplitter* splitter)
{
nlohmann::json resultJSON;
resultJSON["isWindow"] = false;
resultJSON["vertical"] = (splitter->orientation() == Qt::Vertical) ? true : false;
auto sizes = splitter->sizes();
auto content = nlohmann::json::array();
auto countSplitter = splitter->count();
for (int i = 0; i < countSplitter; ++i)
{
auto widget = splitter->widget(i);
nlohmann::json widgetJSON;
if (auto widgetSplitter = dynamic_cast<QSplitter*>(widget); widgetSplitter)
{
widgetJSON = BuildJSONFromLayout(widgetSplitter);
}
else if (auto widgetWindow = dynamic_cast<QmitkRenderWindowWidget*>(widget); widgetWindow)
{
widgetJSON["isWindow"] = true;
widgetJSON["viewDirection"] = widgetWindow->GetSliceNavigationController()->GetViewDirectionAsString();
}
widgetJSON["size"] = sizes[i];
content.push_back(widgetJSON);
}
resultJSON["content"] = content;
return resultJSON;
}
QSplitter* QmitkMxNMultiWidget::BuildLayoutFromJSON(const nlohmann::json* jsonData, unsigned int* windowCounter, QSplitter* parentSplitter)
{
bool vertical = jsonData->at("vertical").get<bool>();
auto orientation = vertical ? Qt::Vertical : Qt::Horizontal;
auto split = new QSplitter(orientation, parentSplitter);
QList<int> sizes;
for (auto object : jsonData->at("content"))
{
bool isWindow = object["isWindow"].get<bool>();
int size = object["size"].get<int>();
sizes.append(size);
if (isWindow)
{
auto viewDirection = object["viewDirection"].get<std::string>();
mitk::AnatomicalPlane viewPlane = mitk::AnatomicalPlane::Sagittal;
if (viewDirection == "Axial")
{
viewPlane = mitk::AnatomicalPlane::Axial;
}
else if (viewDirection == "Coronal")
{
viewPlane = mitk::AnatomicalPlane::Coronal;
}
else if (viewDirection == "Original")
{
viewPlane = mitk::AnatomicalPlane::Original;
}
else if (viewDirection == "Sagittal")
{
viewPlane = mitk::AnatomicalPlane::Sagittal;
}
// repurpose existing render windows as far as they already exist
auto window = GetWindowFromIndex(*windowCounter);
if (window == nullptr)
{
window = CreateRenderWindowWidget();
}
window->GetSliceNavigationController()->SetDefaultViewDirection(viewPlane);
window->GetSliceNavigationController()->Update();
split->addWidget(window.get());
window->show();
(*windowCounter)++;
}
else
{
auto subSplitter = BuildLayoutFromJSON(&object, windowCounter, split);
split->addWidget(subSplitter);
}
}
split->setSizes(sizes);
return split;
}
void QmitkMxNMultiWidget::SetDataBasedLayout(const QmitkAbstractNodeSelectionWidget::NodeList& nodes)
{
auto vSplit = new QSplitter(Qt::Vertical);
unsigned int windowCounter = 0;
for (auto node : nodes)
{
auto hSplit = new QSplitter(Qt::Horizontal);
for (auto viewPlane : { mitk::AnatomicalPlane::Axial, mitk::AnatomicalPlane::Coronal, mitk::AnatomicalPlane::Sagittal })
{
// repurpose existing render windows as far as they already exist
auto window = GetWindowFromIndex(windowCounter++);
if (window == nullptr)
{
window = CreateRenderWindowWidget();
}
auto utilityWidget = window->GetUtilityWidget();
utilityWidget->ToggleSynchronization(false);
utilityWidget->SetDataSelection(QList({ node }));
window->GetSliceNavigationController()->SetDefaultViewDirection(viewPlane);
window->GetSliceNavigationController()->Update();
auto baseRenderer = mitk::BaseRenderer::GetInstance(window->GetRenderWindow()->GetVtkRenderWindow());
+ node->SetVisibility(true, baseRenderer);
mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), node->GetData()->GetTimeGeometry());
hSplit->addWidget(window.get());
window->show();
}
auto sizes = QList<int>({1, 1, 1});
hSplit->setSizes(sizes);
vSplit->addWidget(hSplit);
}
delete this->layout();
auto hBoxLayout = new QHBoxLayout(this);
this->setLayout(hBoxLayout);
hBoxLayout->addWidget(vSplit);
emit UpdateUtilityWidgetViewPlanes();
while (GetNumberOfRenderWindowWidgets() > windowCounter)
{
RemoveRenderWindowWidget();
}
EnableCrosshair();
emit LayoutChanged();
}
void QmitkMxNMultiWidget::SetInitialSelection()
{
auto dataStorage = this->GetDataStorage();
if (nullptr == dataStorage)
{
return;
}
mitk::NodePredicateAnd::Pointer noHelperObjects = mitk::NodePredicateAnd::New();
noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")));
noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object")));
auto allNodes = dataStorage->GetSubset(noHelperObjects);
QmitkSynchronizedNodeSelectionWidget::NodeList currentSelection;
for (auto& node : *allNodes)
{
currentSelection.append(node);
}
m_SynchronizedWidgetConnector->ChangeSelection(currentSelection);
}
void QmitkMxNMultiWidget::ToggleSynchronization(QmitkSynchronizedNodeSelectionWidget* synchronizedWidget)
{
bool synchronized = synchronizedWidget->IsSynchronized();
if (synchronized)
{
m_SynchronizedWidgetConnector->ConnectWidget(synchronizedWidget);
m_SynchronizedWidgetConnector->SynchronizeWidget(synchronizedWidget);
}
else
{
m_SynchronizedWidgetConnector->DisconnectWidget(synchronizedWidget);
}
}
diff --git a/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp b/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp
index 65fe3ad56b..bda5298a5a 100644
--- a/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp
+++ b/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp
@@ -1,530 +1,530 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkPropertiesTableModel.h"
//# Own includes
#include "QmitkCustomVariants.h"
#include "mitkColorProperty.h"
#include "mitkEnumerationProperty.h"
#include "mitkProperties.h"
#include "mitkRenderingManager.h"
#include "mitkStringProperty.h"
//# Toolkit includes
#include <QBrush>
#include <QColor>
#include <QStringList>
#include <itkCommand.h>
//# PUBLIC CTORS,DTOR
QmitkPropertiesTableModel::QmitkPropertiesTableModel(QObject *parent, mitk::PropertyList::Pointer _PropertyList)
: QAbstractTableModel(parent),
m_PropertyList(nullptr),
m_BlockEvents(false),
m_SortDescending(false),
m_FilterKeyWord("")
{
this->SetPropertyList(_PropertyList);
}
QmitkPropertiesTableModel::~QmitkPropertiesTableModel()
{
// remove all event listeners by setting the property list to 0
this->SetPropertyList(nullptr);
}
//# PUBLIC GETTER
mitk::PropertyList::Pointer QmitkPropertiesTableModel::GetPropertyList() const
{
return m_PropertyList.Lock();
}
Qt::ItemFlags QmitkPropertiesTableModel::flags(const QModelIndex &index) const
{
// no editing so far, return default (enabled, selectable)
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
if (index.column() == PROPERTY_VALUE_COLUMN)
{
// there are also read only property items -> do not allow editing them
if (index.data(Qt::EditRole).isValid())
flags |= Qt::ItemIsEditable;
if (index.data(Qt::CheckStateRole).isValid())
flags |= Qt::ItemIsUserCheckable;
}
return flags;
}
QVariant QmitkPropertiesTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal)
{
switch (section)
{
case PROPERTY_NAME_COLUMN:
return tr("Name");
case PROPERTY_VALUE_COLUMN:
return tr("Value");
default:
return QVariant();
}
}
return QVariant();
}
QVariant QmitkPropertiesTableModel::data(const QModelIndex &index, int role) const
{
// empty data by default
QVariant data;
if (!index.isValid() || m_SelectedProperties.empty() || index.row() > (int)(m_SelectedProperties.size() - 1))
return data;
// the properties name
if (index.column() == PROPERTY_NAME_COLUMN)
{
if (role == Qt::DisplayRole)
data = QString::fromStdString(m_SelectedProperties[index.row()].first);
}
// the real properties value
else if (index.column() == PROPERTY_VALUE_COLUMN)
{
mitk::BaseProperty *baseProp = m_SelectedProperties[index.row()].second;
if (const mitk::ColorProperty *colorProp = dynamic_cast<const mitk::ColorProperty *>(baseProp))
{
mitk::Color col = colorProp->GetColor();
QColor qcol((int)(col.GetRed() * 255), (int)(col.GetGreen() * 255), (int)(col.GetBlue() * 255));
if (role == Qt::DisplayRole)
data.setValue(qcol);
else if (role == Qt::EditRole)
data.setValue(qcol);
}
else if (mitk::BoolProperty *boolProp = dynamic_cast<mitk::BoolProperty *>(baseProp))
{
if (role == Qt::CheckStateRole)
data = boolProp->GetValue() ? Qt::Checked : Qt::Unchecked;
}
else if (mitk::StringProperty *stringProp = dynamic_cast<mitk::StringProperty *>(baseProp))
{
if (role == Qt::DisplayRole)
data.setValue<QString>(QString::fromStdString(stringProp->GetValue()));
else if (role == Qt::EditRole)
data.setValue<QString>(QString::fromStdString(stringProp->GetValue()));
}
else if (mitk::IntProperty *intProp = dynamic_cast<mitk::IntProperty *>(baseProp))
{
if (role == Qt::DisplayRole)
data.setValue<int>(intProp->GetValue());
else if (role == Qt::EditRole)
data.setValue<int>(intProp->GetValue());
}
else if (mitk::FloatProperty *floatProp = dynamic_cast<mitk::FloatProperty *>(baseProp))
{
if (role == Qt::DisplayRole)
data.setValue<float>(floatProp->GetValue());
else if (role == Qt::EditRole)
data.setValue<float>(floatProp->GetValue());
}
else if (mitk::EnumerationProperty *enumerationProp = dynamic_cast<mitk::EnumerationProperty *>(baseProp))
{
if (role == Qt::DisplayRole)
data.setValue<QString>(QString::fromStdString(baseProp->GetValueAsString()));
else if (role == Qt::EditRole)
{
QStringList values;
for (auto it = enumerationProp->Begin(); it != enumerationProp->End(); it++)
{
values << QString::fromStdString(it->second);
}
data.setValue(values);
}
}
else
{
if (role == Qt::DisplayRole)
data.setValue(QString::fromStdString(m_SelectedProperties[index.row()].second->GetValueAsString()));
}
}
return data;
}
int QmitkPropertiesTableModel::rowCount(const QModelIndex & /*parent*/) const
{
// return the number of properties in the properties list.
return m_SelectedProperties.size();
}
int QmitkPropertiesTableModel::columnCount(const QModelIndex & /*parent*/) const
{
return 2;
}
//# PUBLIC SETTER
void QmitkPropertiesTableModel::SetPropertyList(mitk::PropertyList *_PropertyList)
{
// if propertylist really changed
if (m_PropertyList != _PropertyList)
{
auto propertyList = m_PropertyList.Lock();
// Remove delete listener if there was a propertylist before
if (propertyList.IsNotNull())
propertyList->RemoveObserver(m_PropertyListDeleteObserverTag);
// set new list
m_PropertyList = _PropertyList;
propertyList = m_PropertyList.Lock();
if (propertyList.IsNotNull())
{
auto command = itk::SimpleMemberCommand<QmitkPropertiesTableModel>::New();
command->SetCallbackFunction(this, &QmitkPropertiesTableModel::PropertyListDelete);
m_PropertyListDeleteObserverTag = propertyList->AddObserver(itk::DeleteEvent(), command);
}
this->Reset();
}
}
void QmitkPropertiesTableModel::PropertyListDelete()
{
if (!m_BlockEvents)
{
m_BlockEvents = true;
this->Reset();
m_BlockEvents = false;
}
}
void QmitkPropertiesTableModel::PropertyModified(const itk::Object *caller, const itk::EventObject & /*event*/)
{
if (!m_BlockEvents)
{
m_BlockEvents = true;
int row = this->FindProperty(dynamic_cast<const mitk::BaseProperty *>(caller));
QModelIndex indexOfChangedProperty = index(row, 1);
emit dataChanged(indexOfChangedProperty, indexOfChangedProperty);
m_BlockEvents = false;
}
}
void QmitkPropertiesTableModel::PropertyDelete(const itk::Object *caller, const itk::EventObject & /*event*/)
{
if (!m_BlockEvents)
{
m_BlockEvents = true;
int row = this->FindProperty(dynamic_cast<const mitk::BaseProperty *>(caller));
if (row >= 0)
this->Reset();
m_BlockEvents = false;
}
}
bool QmitkPropertiesTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
// TODO: check 'role' condition
if (index.isValid() && !m_SelectedProperties.empty() && index.row() < (int)(m_SelectedProperties.size()) &&
(role == Qt::EditRole || role == Qt::CheckStateRole))
{
// block all events now!
m_BlockEvents = true;
auto propertyList = m_PropertyList.Lock();
// the properties name
if (index.column() == PROPERTY_VALUE_COLUMN)
{
mitk::BaseProperty *baseProp = m_SelectedProperties[index.row()].second;
if (mitk::ColorProperty *colorProp = dynamic_cast<mitk::ColorProperty *>(baseProp))
{
QColor qcolor = value.value<QColor>();
if (!qcolor.isValid())
return false;
mitk::Color col = colorProp->GetColor();
col.SetRed(qcolor.red() / 255.0);
col.SetGreen(qcolor.green() / 255.0);
col.SetBlue(qcolor.blue() / 255.0);
colorProp->SetColor(col);
propertyList->InvokeEvent(itk::ModifiedEvent());
propertyList->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
else if (mitk::BoolProperty *boolProp = dynamic_cast<mitk::BoolProperty *>(baseProp))
{
boolProp->SetValue(value.toInt() == Qt::Checked ? true : false);
propertyList->InvokeEvent(itk::ModifiedEvent());
propertyList->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
else if (mitk::StringProperty *stringProp = dynamic_cast<mitk::StringProperty *>(baseProp))
{
stringProp->SetValue((value.value<QString>()).toStdString());
propertyList->InvokeEvent(itk::ModifiedEvent());
propertyList->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
else if (mitk::IntProperty *intProp = dynamic_cast<mitk::IntProperty *>(baseProp))
{
int intValue = value.value<int>();
if (intValue != intProp->GetValue())
{
intProp->SetValue(intValue);
propertyList->InvokeEvent(itk::ModifiedEvent());
propertyList->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
else if (mitk::FloatProperty *floatProp = dynamic_cast<mitk::FloatProperty *>(baseProp))
{
float floatValue = value.value<float>();
if (floatValue != floatProp->GetValue())
{
floatProp->SetValue(floatValue);
propertyList->InvokeEvent(itk::ModifiedEvent());
propertyList->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
else if (mitk::EnumerationProperty *enumerationProp = dynamic_cast<mitk::EnumerationProperty *>(baseProp))
{
std::string activatedItem = value.value<QString>().toStdString();
if (activatedItem != enumerationProp->GetValueAsString())
{
if (enumerationProp->IsValidEnumerationValue(activatedItem))
{
enumerationProp->SetValue(activatedItem);
propertyList->InvokeEvent(itk::ModifiedEvent());
propertyList->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
}
}
// property was changed by us, now we can accept property changes triggered by someone else
m_BlockEvents = false;
emit dataChanged(index, index);
return true;
}
return false;
}
void QmitkPropertiesTableModel::sort(int column, Qt::SortOrder order /*= Qt::AscendingOrder */)
{
bool sortDescending = (order == Qt::DescendingOrder) ? true : false;
// do not sort twice !!! (dont know why, but qt calls this func twice. STUPID!)
if (sortDescending != m_SortDescending)
{
m_SortDescending = sortDescending;
PropertyDataSetCompareFunction::CompareCriteria _CompareCriteria = PropertyDataSetCompareFunction::CompareByName;
PropertyDataSetCompareFunction::CompareOperator _CompareOperator =
m_SortDescending ? PropertyDataSetCompareFunction::Greater : PropertyDataSetCompareFunction::Less;
if (column == PROPERTY_VALUE_COLUMN)
_CompareCriteria = PropertyDataSetCompareFunction::CompareByValue;
PropertyDataSetCompareFunction compareFunc(_CompareCriteria, _CompareOperator);
std::sort(m_SelectedProperties.begin(), m_SelectedProperties.end(), compareFunc);
QAbstractTableModel::beginResetModel();
QAbstractTableModel::endResetModel();
}
}
//# PROTECTED GETTER
int QmitkPropertiesTableModel::FindProperty(const mitk::BaseProperty *_Property) const
{
int row = -1;
if (_Property)
{
// search for property that changed and emit datachanged on the corresponding ModelIndex
std::vector<PropertyDataSet>::const_iterator propertyIterator;
for (propertyIterator = m_SelectedProperties.begin(); propertyIterator != m_SelectedProperties.end();
propertyIterator++)
{
if (propertyIterator->second == _Property)
break;
}
if (propertyIterator != m_SelectedProperties.end())
row = std::distance(m_SelectedProperties.begin(), propertyIterator);
}
return row;
}
//# PROTECTED SETTER
void QmitkPropertiesTableModel::AddSelectedProperty(PropertyDataSet &_PropertyDataSet)
{
// subscribe for modified event
itk::MemberCommand<QmitkPropertiesTableModel>::Pointer _PropertyDataSetModifiedCommand =
itk::MemberCommand<QmitkPropertiesTableModel>::New();
_PropertyDataSetModifiedCommand->SetCallbackFunction(this, &QmitkPropertiesTableModel::PropertyModified);
m_PropertyModifiedObserverTags.push_back(
_PropertyDataSet.second->AddObserver(itk::ModifiedEvent(), _PropertyDataSetModifiedCommand));
// subscribe for delete event
itk::MemberCommand<QmitkPropertiesTableModel>::Pointer _PropertyDataSetDeleteCommand =
itk::MemberCommand<QmitkPropertiesTableModel>::New();
_PropertyDataSetDeleteCommand->SetCallbackFunction(this, &QmitkPropertiesTableModel::PropertyDelete);
m_PropertyDeleteObserverTags.push_back(
_PropertyDataSet.second->AddObserver(itk::DeleteEvent(), _PropertyDataSetDeleteCommand));
// add to the selection
m_SelectedProperties.push_back(_PropertyDataSet);
}
void QmitkPropertiesTableModel::RemoveSelectedProperty(unsigned int _Index)
{
PropertyDataSet &_PropertyDataSet = m_SelectedProperties.at(_Index);
// remove modified event listener
_PropertyDataSet.second->RemoveObserver(m_PropertyModifiedObserverTags[_Index]);
m_PropertyModifiedObserverTags.erase(m_PropertyModifiedObserverTags.begin() + _Index);
// remove delete event listener
_PropertyDataSet.second->RemoveObserver(m_PropertyDeleteObserverTags[_Index]);
m_PropertyDeleteObserverTags.erase(m_PropertyDeleteObserverTags.begin() + _Index);
// remove from selection
m_SelectedProperties.erase(m_SelectedProperties.begin() + _Index);
}
void QmitkPropertiesTableModel::Reset()
{
// remove all selected properties
while (!m_SelectedProperties.empty())
{
this->RemoveSelectedProperty(m_SelectedProperties.size() - 1);
}
std::vector<PropertyDataSet> allPredicates;
auto propertyList = m_PropertyList.Lock();
if (propertyList.IsNotNull())
{
// first of all: collect all properties from the list
for (auto it = propertyList->GetMap()->begin(); it != propertyList->GetMap()->end(); it++)
{
allPredicates.push_back(*it); //% TODO
}
}
// make a subselection if a keyword is specified
if (!m_FilterKeyWord.empty())
{
std::vector<PropertyDataSet> subSelection;
for (auto it = allPredicates.begin(); it != allPredicates.end(); it++)
{
// add this to the selection if it is matched by the keyword
if ((*it).first.find(m_FilterKeyWord) != std::string::npos)
subSelection.push_back((*it));
}
allPredicates.clear();
allPredicates = subSelection;
}
PropertyDataSet tmpPropertyDataSet;
// add all selected now to the Model
for (auto it = allPredicates.begin(); it != allPredicates.end(); it++)
{
tmpPropertyDataSet = *it;
this->AddSelectedProperty(tmpPropertyDataSet);
}
// sort the list as indicated by m_SortDescending
this->sort(m_SortDescending);
- // model was resetted
+ // model was reset
QAbstractTableModel::beginResetModel();
QAbstractTableModel::endResetModel();
}
void QmitkPropertiesTableModel::SetFilterPropertiesKeyWord(std::string _FilterKeyWord)
{
m_FilterKeyWord = _FilterKeyWord;
this->Reset();
}
QmitkPropertiesTableModel::PropertyDataSetCompareFunction::PropertyDataSetCompareFunction(
CompareCriteria _CompareCriteria, CompareOperator _CompareOperator)
: m_CompareCriteria(_CompareCriteria), m_CompareOperator(_CompareOperator)
{
}
bool QmitkPropertiesTableModel::PropertyDataSetCompareFunction::operator()(const PropertyDataSet &_Left,
const PropertyDataSet &_Right) const
{
switch (m_CompareCriteria)
{
case CompareByValue:
if (m_CompareOperator == Less)
return (_Left.second->GetValueAsString() < _Right.second->GetValueAsString());
else
return (_Left.second->GetValueAsString() > _Right.second->GetValueAsString());
break;
// CompareByName:
default:
if (m_CompareOperator == Less)
return (_Left.first < _Right.first);
else
return (_Left.first > _Right.first);
break;
}
}
QmitkPropertiesTableModel::PropertyListElementFilterFunction::PropertyListElementFilterFunction(
const std::string &_FilterKeyWord)
: m_FilterKeyWord(_FilterKeyWord)
{
}
bool QmitkPropertiesTableModel::PropertyListElementFilterFunction::operator()(const PropertyDataSet &_Elem) const
{
if (m_FilterKeyWord.empty())
return true;
return (_Elem.first.find(m_FilterKeyWord) == 0);
}
diff --git a/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.cpp b/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.cpp
index f1d8bbbc7a..85761b394e 100644
--- a/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.cpp
+++ b/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.cpp
@@ -1,708 +1,708 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 qt widgets module
#include <QmitkSynchronizedNodeSelectionWidget.h>
#include <QmitkCustomVariants.h>
#include <QmitkEnums.h>
#include <QmitkNodeSelectionDialog.h>
// mitk core module
#include <mitkImage.h>
#include <mitkNodePredicateNot.h>
#include <mitkNodePredicateProperty.h>
#include <mitkRenderWindowLayerUtilities.h>
QmitkSynchronizedNodeSelectionWidget::QmitkSynchronizedNodeSelectionWidget(QWidget* parent)
: QmitkAbstractNodeSelectionWidget(parent)
{
m_Controls.setupUi(this);
m_StorageModel = std::make_unique<QmitkRenderWindowDataNodeTableModel>(this);
m_Controls.tableView->setModel(m_StorageModel.get());
m_Controls.tableView->horizontalHeader()->setVisible(false);
m_Controls.tableView->verticalHeader()->setVisible(false);
m_Controls.tableView->setSelectionMode(QAbstractItemView::SingleSelection);
m_Controls.tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
m_Controls.tableView->setContextMenuPolicy(Qt::CustomContextMenu);
m_Controls.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
m_Controls.tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
this->SetUpConnections();
this->Initialize();
}
QmitkSynchronizedNodeSelectionWidget::~QmitkSynchronizedNodeSelectionWidget()
{
bool isSynchronized = this->IsSynchronized();
if (isSynchronized)
{
emit DeregisterSynchronization();
return;
}
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
auto dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNull())
{
return;
}
// If the model is not synchronized,
// we know that renderer-specific properties exist for all nodes.
// These properties need to be removed from the nodes.
auto allNodes = dataStorage->GetAll();
for (auto& node : *allNodes)
{
// Delete the relevant renderer-specific properties for the node using the current base renderer.
mitk::RenderWindowLayerUtilities::DeleteRenderWindowProperties(node, baseRenderer);
}
}
void QmitkSynchronizedNodeSelectionWidget::SetBaseRenderer(mitk::BaseRenderer* baseRenderer)
{
if (m_BaseRenderer == baseRenderer)
{
// no need to do something
return;
}
if (nullptr == baseRenderer)
{
return;
}
auto oldBaseRenderer = m_BaseRenderer.Lock();
m_BaseRenderer = baseRenderer;
auto dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNull())
{
return;
}
bool isSynchronized = this->IsSynchronized();
if (isSynchronized)
{
// If the model is synchronized,
// all nodes use global / default properties.
// No renderer-specific property lists should exist
// so there is no need to transfer any property values.
}
else
{
// If the model is not synchronized,
// we know that renderer-specific properties exist for all nodes.
// These properties need to be removed from the nodes and
// we need to transfer their values to new renderer-specific properties.
auto allNodes = dataStorage->GetAll();
for (auto& node : *allNodes)
{
// Set the relevant renderer-specific properties for the node using the new base renderer.
// By transferring the values from the old property list,
// the same property-state is kept when switching to another base renderer.
mitk::RenderWindowLayerUtilities::TransferRenderWindowProperties(node, baseRenderer, oldBaseRenderer);
// Delete the relevant renderer-specific properties for the node using the old base renderer.
mitk::RenderWindowLayerUtilities::DeleteRenderWindowProperties(node, oldBaseRenderer);
}
}
this->Initialize();
}
void QmitkSynchronizedNodeSelectionWidget::SetSelectAll(bool selectAll)
{
if (selectAll == m_Controls.selectionModeCheckBox->isChecked())
{
// no need to do something
return;
}
m_Controls.selectionModeCheckBox->setChecked(selectAll);
}
bool QmitkSynchronizedNodeSelectionWidget::GetSelectAll() const
{
return m_Controls.selectionModeCheckBox->isChecked();
}
void QmitkSynchronizedNodeSelectionWidget::SetSynchronized(bool synchronize)
{
if (synchronize == this->IsSynchronized())
{
// no need to do something
return;
}
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
auto dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNull())
{
return;
}
if (synchronize)
{
// set the base renderer of the model to nullptr, such that global properties are used
m_StorageModel->SetCurrentRenderer(nullptr);
// If the model is synchronized,
// we know that the model was not synchronized before.
// That means that all nodes use renderer-specific properties,
// but now all nodes need global properties.
// Thus we need to remove the renderer-specific properties of all nodes of the
// datastorage.
auto allNodes = dataStorage->GetAll();
for (auto& node : *allNodes)
{
// For helper / hidden nodes:
// If the node predicate does not match, do not remove the renderer-specific property
// This is relevant for the crosshair data nodes, which are only visible inside their
// corresponding render window.
if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node))
{
// Delete the relevant renderer-specific properties for the node using the current base renderer.
mitk::RenderWindowLayerUtilities::DeleteRenderWindowProperties(node, baseRenderer);
}
}
}
else
{
// set the base renderer of the model to current base renderer, such that renderer-specific properties are used
m_StorageModel->SetCurrentRenderer(baseRenderer);
// If the model is not synchronized anymore,
// we know that the model was synchronized before.
// That means that all nodes use global / default properties,
// but now all nodes need renderer-specific properties.
// Thus we need to modify the renderer-specific properties of all nodes of the
// datastorage:
// - hide those nodes, which are not part of the newly selected nodes.
// - keep the property values of those nodes, which are part of the new selection AND
// have been selected before
auto currentNodeSelection = this->GetCurrentInternalSelection();
auto allNodes = dataStorage->GetAll();
for (auto& node : *allNodes)
{
// check if the node is part of the current selection
auto finding = std::find(std::begin(currentNodeSelection), std::end(currentNodeSelection), node);
if (finding != std::end(currentNodeSelection)) // node found / part of the current selection
{
- // Set the relevant renderer-specific properties for the node using the curent base renderer.
+ // Set the relevant renderer-specific properties for the node using the current base renderer.
// By transferring the values from the global / default property list,
// the same property-state is kept when switching to non-synchronized mode.
mitk::RenderWindowLayerUtilities::TransferRenderWindowProperties(node, baseRenderer, nullptr);
}
else
{
// If the node is not part of the selection, unset the relevant renderer-specific properties.
// This will unset the "visible" and "layer" property for the renderer-specific property list and
// hide the node for this renderer.
// ATTENTION: This is required, since the synchronized property needs to be overwritten
// to make sure that the visibility is correctly set for the specific base renderer.
this->DeselectNode(node);
}
}
}
// Since the synchronization might lead to a different node order depending on the layer properties, the render window
// needs to be updated.
// Explicitly request an update since a renderer-specific property change does not mark the node as modified.
// see https://phabricator.mitk.org/T22322
mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow());
}
bool QmitkSynchronizedNodeSelectionWidget::IsSynchronized() const
{
return m_StorageModel->GetCurrentRenderer().IsNull();
}
void QmitkSynchronizedNodeSelectionWidget::OnSelectionModeChanged(bool selectAll)
{
emit SelectionModeChanged(selectAll);
if (selectAll)
{
this->SelectAll();
}
}
void QmitkSynchronizedNodeSelectionWidget::OnEditSelection()
{
QmitkNodeSelectionDialog* dialog = new QmitkNodeSelectionDialog(this);
dialog->SetDataStorage(m_DataStorage.Lock());
dialog->SetNodePredicate(m_NodePredicate);
dialog->SetCurrentSelection(m_StorageModel->GetCurrentSelection());
dialog->SetSelectionMode(QAbstractItemView::MultiSelection);
m_Controls.changeSelectionButton->setChecked(true);
if (dialog->exec())
{
m_Controls.selectionModeCheckBox->setChecked(false);
emit SelectionModeChanged(false);
auto selectedNodes = dialog->GetSelectedNodes();
this->HandleChangeOfInternalSelection(selectedNodes);
}
m_Controls.changeSelectionButton->setChecked(false);
delete dialog;
}
void QmitkSynchronizedNodeSelectionWidget::OnTableClicked(const QModelIndex& index)
{
if (!index.isValid() || m_StorageModel.get() != index.model())
{
return;
}
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
QVariant dataNodeVariant = index.data(QmitkDataNodeRole);
auto dataNode = dataNodeVariant.value<mitk::DataNode::Pointer>();
if (index.column() == 1) // node visibility column
{
bool visibiliy = index.data(Qt::EditRole).toBool();
m_StorageModel->setData(index, QVariant(!visibiliy), Qt::EditRole);
return;
}
if (index.column() == 2) // reinit node column
{
this->ReinitNode(dataNode);
return;
}
if (index.column() == 3) // remove node column
{
this->RemoveFromInternalSelection(dataNode);
return;
}
}
void QmitkSynchronizedNodeSelectionWidget::SetUpConnections()
{
connect(m_Controls.selectionModeCheckBox, &QCheckBox::clicked,
this, &QmitkSynchronizedNodeSelectionWidget::OnSelectionModeChanged);
connect(m_Controls.changeSelectionButton, &QPushButton::clicked,
this, &QmitkSynchronizedNodeSelectionWidget::OnEditSelection);
connect(m_Controls.tableView, &QTableView::clicked,
this, &QmitkSynchronizedNodeSelectionWidget::OnTableClicked);
}
void QmitkSynchronizedNodeSelectionWidget::SetSelection(const NodeList& newSelection)
{
this->HandleChangeOfInternalSelection(newSelection);
m_Controls.selectionModeCheckBox->setChecked(false);
}
void QmitkSynchronizedNodeSelectionWidget::Initialize()
{
auto baseRenderer = m_BaseRenderer.Lock();
auto dataStorage = m_DataStorage.Lock();
m_StorageModel->SetDataStorage(dataStorage);
m_StorageModel->SetCurrentRenderer(baseRenderer);
if (baseRenderer.IsNull() || dataStorage.IsNull())
{
m_Controls.selectionModeCheckBox->setEnabled(false);
m_Controls.changeSelectionButton->setEnabled(false);
// reset the model if no data storage is defined
m_StorageModel->removeRows(0, m_StorageModel->rowCount());
return;
}
// Use the new data storage / node predicate to correctly set the list of
// currently selected data nodes for the model.
// If a new data storage or node predicate has been defined,
// we switch to the "selectAll" mode and synchronize the selection for simplicity.
// enable UI
m_Controls.selectionModeCheckBox->setEnabled(true);
m_Controls.changeSelectionButton->setEnabled(true);
m_Controls.selectionModeCheckBox->setChecked(true);
// set the base renderer of the model to nullptr, such that global properties are used (synchronized mode)
m_StorageModel->SetCurrentRenderer(nullptr);
}
void QmitkSynchronizedNodeSelectionWidget::UpdateInfo()
{
}
void QmitkSynchronizedNodeSelectionWidget::OnDataStorageChanged()
{
this->Initialize();
}
void QmitkSynchronizedNodeSelectionWidget::OnNodePredicateChanged()
{
this->Initialize();
}
void QmitkSynchronizedNodeSelectionWidget::ReviseSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
bool isSynchronized = this->IsSynchronized();
if (isSynchronized)
{
this->ReviseSynchronizedSelectionChanged(oldInternalSelection, newInternalSelection);
}
else
{
this->ReviseDesynchronizedSelectionChanged(oldInternalSelection, newInternalSelection);
}
// Since a new selection might have a different rendering tree the render windows
// need to be updated.
// Explicitly request an update since a renderer-specific property change does not mark the node as modified.
// see https://phabricator.mitk.org/T22322
mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow());
}
void QmitkSynchronizedNodeSelectionWidget::OnInternalSelectionChanged()
{
m_StorageModel->SetCurrentSelection(this->GetCurrentInternalSelection());
}
bool QmitkSynchronizedNodeSelectionWidget::AllowEmissionOfSelection(const NodeList& /*emissionCandidates*/) const
{
return this->IsSynchronized();
}
void QmitkSynchronizedNodeSelectionWidget::OnNodeAddedToStorage(const mitk::DataNode* node)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
// For helper / hidden nodes
if (m_NodePredicate.IsNotNull() && !m_NodePredicate->CheckNode(node))
{
// If the node predicate does not match, do not add the node to the current selection.
// Leave the visibility as it is.
return;
}
// The selection mode determines if we want to show all nodes from the data storage
// or use a local selected list of nodes.
// We need to hide each new incoming data node, if we use a local selection,
// since we do not want to show / select newly added nodes immediately.
// We need to add the incoming node to our selection, if the selection mode check box
// is checked.
// We want to add the incoming node to our selection, if the node is a child node
// of an already selected node.
// Nodes added to the selection will be made visible.
if (m_Controls.selectionModeCheckBox->isChecked() || this->IsParentNodeSelected(node))
{
auto currentSelection = this->GetCurrentInternalSelection();
// Check if the nodes is already part of the internal selection.
// That can happen if another render window already added the new node and sent out the new, updated
// selection to be synchronized.
auto finding = std::find(std::begin(currentSelection), std::end(currentSelection), node);
if (finding != std::end(currentSelection)) // node found
{
// node already part of the selection
return;
}
currentSelection.append(const_cast<mitk::DataNode*>(node));
// This function will call 'QmitkSynchronizedNodeSelectionWidget::ReviseSelectionChanged'
// which will take care of the visibility-property for newly added node.
this->HandleChangeOfInternalSelection(currentSelection);
}
else
{
// If the widget is in "local-selection" state (selectionModeCheckBox unchecked),
// the new incoming node needs to be hid.
// Here it depends on the synchronization-state which properties need
// to be modified.
if (this->IsSynchronized())
{
// If the node will not be part of the new selection, hide the node.
const_cast<mitk::DataNode*>(node)->SetVisibility(false);
}
else
{
// If the widget is not synchronized, all nodes use renderer-specific properties.
// Thus we need to modify the renderer-specific properties of the node:
// - hide the node, which is not part of the selection
this->DeselectNode(const_cast<mitk::DataNode*>(node));
}
}
}
void QmitkSynchronizedNodeSelectionWidget::OnNodeModified(const itk::Object* caller, const itk::EventObject& event)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
if (!itk::ModifiedEvent().CheckEvent(&event))
{
return;
}
auto node = dynamic_cast<const mitk::DataNode*>(caller);
if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node))
{
auto currentSelection = this->GetCurrentInternalSelection();
// check if the node to be modified is part of the current selection
auto finding = std::find(std::begin(currentSelection), std::end(currentSelection), node);
if (finding == std::end(currentSelection)) // node not found
{
// node not part of the selection
return;
}
// We know that the node is relevant, but we don't know if the node modification was relevant
// for the rendering. We just request an update here.
// Explicitly request an update since a renderer-specific property change does not mark the node as modified.
// see https://phabricator.mitk.org/T22322
mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow());
m_StorageModel->UpdateModelData();
}
}
void QmitkSynchronizedNodeSelectionWidget::ReviseSynchronizedSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection)
{
// If the model is synchronized, all nodes use global / default properties.
// Thus we need to modify the global properties of the selection:
// - a) show those nodes, which are part of the new selection AND have not been
// selected before
// - b) keep the property values of those nodes, which are part of the new selection AND
// have been selected before
// - c) hide those nodes, which are part of the old selection AND
// have not been newly selected
for (auto& node : newInternalSelection)
{
// check if the node is part of the old selection
auto finding = std::find(std::begin(oldInternalSelection), std::end(oldInternalSelection), node);
if (finding == std::end(oldInternalSelection)) // node not found
{
// If the node is part of the new selection and was not already part of the old selection,
// set the relevant renderer-specific properties.
// This will set the "visible" property for the global / default property list
// and show the node for this renderer.
node->SetVisibility(true); // item a)
}
// else: item b): node that was already selected before does not need to be modified
}
for (auto& node : oldInternalSelection)
{
// check if the node is part of the new selection
auto finding = std::find(std::begin(newInternalSelection), std::end(newInternalSelection), node);
if (finding == std::end(newInternalSelection)) // node not found
{
// If the node is not part of the new selection, hide the node.
node->SetVisibility(false); // item c)
}
// else: item b): node that was already selected before does not need to be modified
}
}
void QmitkSynchronizedNodeSelectionWidget::ReviseDesynchronizedSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
// If the model is not synchronized, all nodes need renderer-specific properties.
// Thus we need to modify the renderer-specific properties of the selection:
// - a) set the renderer-specific properties of those nodes, which are part of the new selection AND
// have not been selected before (see 'SelectNode')
// - b) show those nodes, which are part of the new selection AND have not been
// selected before
// - c) keep the property values of those nodes, which are part of the new selection AND
// have been selected before
// - d) hide those nodes, which are part of the old selection AND
// have not been newly selected
// - e) set the renderer-specific properties of those nodes, which are part of the old selection AND
// have not been newly selected, to denote which nodes are selected
for (auto& node : newInternalSelection)
{
// check if the node is part of the old selection
auto finding = std::find(std::begin(oldInternalSelection), std::end(oldInternalSelection), node);
if (finding == std::end(oldInternalSelection)) // node not found
{
// If the node is part of the new selection and was not already part of the old selection,
// set the relevant renderer-specific properties.
// This will set the "visible" and "layer" property for the renderer-specific property list
// such that the global / default property list values are overwritten
mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(node, baseRenderer); // item a)
// Explicitly set the visibility to true for selected nodes to show them in the render window.
node->SetVisibility(true, baseRenderer); // item b)
}
// else: item c): node that was already selected before does not need to be modified
}
for (auto& node : oldInternalSelection)
{
// check if the node is part of the new selection
auto finding = std::find(std::begin(newInternalSelection), std::end(newInternalSelection), node);
if (finding == std::end(newInternalSelection)) // node not found
{
// If the node is not part of the new selection, unset the relevant renderer-specific properties.
// This will unset the "visible" and "layer" property for the renderer-specific property list and
// hide the node for this renderer.
// ATTENTION: This is required, since the synchronized global property needs to be overwritten
// to make sure that the visibility is correctly set for the specific base renderer.
this->DeselectNode(node); // item d) and e)
}
// else: item c): node that was already selected before does not need to be modified
}
}
void QmitkSynchronizedNodeSelectionWidget::ReinitNode(const mitk::DataNode* dataNode)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
auto selectedImage = dynamic_cast<mitk::Image*>(dataNode->GetData());
if (nullptr == selectedImage)
{
return;
}
auto boundingBoxPredicate = mitk::NodePredicateNot::New(
mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false), baseRenderer));
if (!boundingBoxPredicate->CheckNode(dataNode))
{
return;
}
mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), selectedImage->GetTimeGeometry());
}
void QmitkSynchronizedNodeSelectionWidget::RemoveFromInternalSelection(mitk::DataNode* dataNode)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
if (this->IsSynchronized())
{
// If the model is synchronized, all nodes use global / default properties.
// Thus we need to modify the global property of the node.
// Explicitly set the visibility to false for unselected nodes to hide them in the render window.
dataNode->SetVisibility(false);
}
m_Controls.selectionModeCheckBox->setChecked(false);
emit SelectionModeChanged(false);
this->RemoveNodeFromSelection(dataNode);
}
bool QmitkSynchronizedNodeSelectionWidget::IsParentNodeSelected(const mitk::DataNode* dataNode) const
{
auto dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNull())
{
return false;
}
auto currentSelection = this->GetCurrentInternalSelection();
auto parentNodes = dataStorage->GetSources(dataNode, m_NodePredicate, false);
for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it)
{
const mitk::DataNode* parentNode = it->Value();
auto finding = std::find(std::begin(currentSelection), std::end(currentSelection), parentNode);
if (finding != std::end(currentSelection)) // parent node found
{
// at least one parent node is part of the selection
return true;
}
}
return false;
}
void QmitkSynchronizedNodeSelectionWidget::DeselectNode(mitk::DataNode* dataNode)
{
auto baseRenderer = m_BaseRenderer.Lock();
if (baseRenderer.IsNull())
{
return;
}
if (nullptr == dataNode)
{
return;
}
if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(dataNode))
{
// If the node should not be part of the selection, set the relevant renderer-specific properties.
// This will set the "visible" and "layer" property for the renderer-specific property list,
// such that the global / default property list values are overwritten.
mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(dataNode, baseRenderer);
// Explicitly set the visibility to false for the node to hide them in the render window.
dataNode->SetVisibility(false, baseRenderer);
}
}
void QmitkSynchronizedNodeSelectionWidget::SelectAll()
{
auto dataStorage = m_DataStorage.Lock();
if (dataStorage.IsNull())
{
return;
}
auto allNodes = m_NodePredicate ? dataStorage->GetSubset(m_NodePredicate) : dataStorage->GetAll();
NodeList currentSelection;
for (auto& node : *allNodes)
{
currentSelection.append(node);
}
this->HandleChangeOfInternalSelection(currentSelection);
}
diff --git a/Modules/QtWidgets/test/QmitkAbstractNodeSelectionWidgetTest.cpp b/Modules/QtWidgets/test/QmitkAbstractNodeSelectionWidgetTest.cpp
index bf9590c68f..bc57e65258 100644
--- a/Modules/QtWidgets/test/QmitkAbstractNodeSelectionWidgetTest.cpp
+++ b/Modules/QtWidgets/test/QmitkAbstractNodeSelectionWidgetTest.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.
============================================================================*/
#include "QmitkAbstractNodeSelectionWidget.h"
#include <QApplication>
#include <mitkNodePredicateFunction.h>
#include <mitkStandaloneDataStorage.h>
#include "QmitkModelViewSelectionConnector.h"
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkRenderingTestHelper.h>
extern std::vector<std::string> globalCmdLineArgs;
class TestWidget : public QmitkAbstractNodeSelectionWidget
{
public:
explicit TestWidget(QWidget* parent = nullptr) : QmitkAbstractNodeSelectionWidget(parent), m_UpdateInfo(0), m_OnNodePredicateChanged(0), m_OnDataStorageChanged(0),
m_OnInternalSelectionChanged(0), m_OnNodeAddedToStorage(0), m_OnNodeRemovedFromStorage(0), m_ReviseSelectionChanged(0), m_AllowEmissionOfSelection(0), m_Allow(true), m_NewSelectionEmited(0)
{
connect(this, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &TestWidget::NewSelectionEmited);
};
int m_UpdateInfo;
void UpdateInfo() override
{
m_UpdateInfo++;
};
int m_OnNodePredicateChanged;
void OnNodePredicateChanged() override
{
m_OnNodePredicateChanged++;
};
int m_OnDataStorageChanged;
void OnDataStorageChanged() override
{
m_OnDataStorageChanged++;
};
int m_OnInternalSelectionChanged;
void OnInternalSelectionChanged() override
{
m_OnInternalSelectionChanged++;
};
int m_OnNodeAddedToStorage;
void OnNodeAddedToStorage(const mitk::DataNode* /*node*/) override
{
m_OnNodeAddedToStorage++;
};
int m_OnNodeRemovedFromStorage;
void OnNodeRemovedFromStorage(const mitk::DataNode* /*node*/) override
{
m_OnNodeRemovedFromStorage++;
};
int m_ReviseSelectionChanged;
void ReviseSelectionChanged(const NodeList& /*oldInternalSelection*/, NodeList& /*newInternalSelection*/) override
{
m_ReviseSelectionChanged++;
};
mutable int m_AllowEmissionOfSelection;
bool m_Allow;
bool AllowEmissionOfSelection(const NodeList& emissionCandidates) const override
{
m_AllowEmissionOfSelection++;
if (m_Allow) return QmitkAbstractNodeSelectionWidget::AllowEmissionOfSelection(emissionCandidates);
return false;
};
int m_NewSelectionEmited;
QmitkAbstractNodeSelectionWidget::NodeList m_LastNewEmision;
void NewSelectionEmited(NodeList selection)
{
m_NewSelectionEmited++;
m_LastNewEmision = selection;
};
};
class QmitkAbstractNodeSelectionWidgetTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(QmitkAbstractNodeSelectionWidgetTestSuite);
MITK_TEST(SetDataStorageTest);
MITK_TEST(DataStorageEventTest);
MITK_TEST(NodePredicateTest);
MITK_TEST(SelectOnlyVisibleNodesTest);
MITK_TEST(OtherSetterAndGetterTest);
MITK_TEST(AllowEmissionOfSelectionTest);
MITK_TEST(OnNodeModifiedTest);
MITK_TEST(SignalRecursionTest);
CPPUNIT_TEST_SUITE_END();
mitk::DataStorage::Pointer m_DataStorage;
mitk::DataNode::Pointer m_Node1;
mitk::DataNode::Pointer m_Node1_2;
mitk::DataNode::Pointer m_Node2;
mitk::DataNode::Pointer m_Node3;
QApplication* m_TestApp;
public:
void setUp() override
{
m_DataStorage = mitk::StandaloneDataStorage::New();
m_Node1 = mitk::DataNode::New();
m_Node1->SetName("node1_1");
m_Node2 = mitk::DataNode::New();
m_Node2->SetName("node2");
m_Node3 = mitk::DataNode::New();
m_Node3->SetName("node3");
m_Node1_2 = mitk::DataNode::New();
m_Node1_2->SetName("node1_2");
m_DataStorage->Add(m_Node1);
m_DataStorage->Add(m_Node2);
m_DataStorage->Add(m_Node3);
m_DataStorage->Add(m_Node1_2);
mitk::RenderingTestHelper::ArgcHelperClass cmdLineArgs(globalCmdLineArgs);
auto argc = cmdLineArgs.GetArgc();
auto argv = cmdLineArgs.GetArgv();
m_TestApp = new QApplication(argc, argv);
}
mitk::NodePredicateBase::Pointer GeneratTestPredicate(const std::string& name)
{
auto check = [name](const mitk::DataNode * node)
{
return node->GetName().find(name,0) == 0;
};
auto predicate = mitk::NodePredicateFunction::New(check);
return predicate.GetPointer();
}
void tearDown() override
{
delete m_TestApp;
}
void SetDataStorageTest()
{
TestWidget widget;
widget.SetDataStorage(nullptr);
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Set same data storage but triggered change", 0, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited);
widget.SetDataStorage(m_DataStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited);
widget.SetCurrentSelection({ m_Node1 });
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.GetSelectedNodes()));
widget.SetDataStorage(m_DataStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Set same data storage but triggered change", 1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.GetSelectedNodes()));
widget.SetDataStorage(nullptr);
CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Set same data storage but triggered change", 2, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes()));
}
void DataStorageEventTest()
{
TestWidget widget;
auto newNode = mitk::DataNode::New();
widget.SetDataStorage(m_DataStorage);
m_DataStorage->Add(newNode);
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited);
widget.SetCurrentSelection({ newNode });
m_DataStorage->Remove(m_Node1);
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ newNode }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ newNode }, widget.GetSelectedNodes()));
m_DataStorage->Remove(newNode);
CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes()));
widget.SetCurrentSelection({ m_Node2 });
m_DataStorage = nullptr;
CPPUNIT_ASSERT_EQUAL(4, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(4, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(4, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(4, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(4, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes()));
}
void NodePredicateTest()
{
TestWidget widget;
CPPUNIT_ASSERT(nullptr == widget.GetNodePredicate());
auto testPred = GeneratTestPredicate("node2");
widget.SetNodePredicate(testPred);
CPPUNIT_ASSERT(testPred == widget.GetNodePredicate());
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited);
widget.SetDataStorage(m_DataStorage);
widget.SetCurrentSelection({ m_Node3 });
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes()));
widget.SetCurrentSelection({ m_Node2 });
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node2 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node2 }, widget.GetSelectedNodes()));
//change predicate while nodes are selected
widget.SetNodePredicate(GeneratTestPredicate("node1"));
CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes()));
//change selection to mixed one (valid nodes and invalid ones)
widget.SetCurrentSelection({ m_Node1, m_Node2, m_Node1_2 });
CPPUNIT_ASSERT_EQUAL(3, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(3, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(3, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(3, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(3, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node1_2 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node1_2 }, widget.GetSelectedNodes()));
}
void SelectOnlyVisibleNodesTest()
{
TestWidget widget;
CPPUNIT_ASSERT_EQUAL(true, widget.GetSelectOnlyVisibleNodes());
CPPUNIT_ASSERT(nullptr == widget.GetNodePredicate());
auto testPred = GeneratTestPredicate("node2");
widget.SetNodePredicate(testPred);
widget.SetDataStorage(m_DataStorage);
widget.SetCurrentSelection({ m_Node3 });
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes()));
widget.SetSelectOnlyVisibleNodes(false);
CPPUNIT_ASSERT_EQUAL(false, widget.GetSelectOnlyVisibleNodes());
CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.GetSelectedNodes()));
//change selection to mixed one (valid nodes and invalid ones)
widget.SetCurrentSelection({ m_Node1, m_Node2, m_Node1_2 });
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node2, m_Node1_2 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node2, m_Node1_2 }, widget.GetSelectedNodes()));
//change predicate while nodes are selected
widget.SetNodePredicate(GeneratTestPredicate("node1"));
CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(3, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node2, m_Node1_2 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node2, m_Node1_2 }, widget.GetSelectedNodes()));
widget.SetSelectOnlyVisibleNodes(true);
CPPUNIT_ASSERT_EQUAL(true, widget.GetSelectOnlyVisibleNodes());
CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(4, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(3, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node1_2 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node1_2 }, widget.GetSelectedNodes()));
}
void OtherSetterAndGetterTest()
{
TestWidget widget;
CPPUNIT_ASSERT("Error. Select data." == widget.GetInvalidInfo());
CPPUNIT_ASSERT("Empty. Make a selection." == widget.GetEmptyInfo());
CPPUNIT_ASSERT("Select a data node" == widget.GetPopUpTitel());
CPPUNIT_ASSERT("" == widget.GetPopUpHint());
CPPUNIT_ASSERT(false == widget.GetSelectionIsOptional());
widget.SetInvalidInfo("SetInvalidInfo");
widget.SetEmptyInfo("SetEmptyInfo");
widget.SetPopUpTitel("SetPopUpTitel");
widget.SetPopUpHint("SetPopUpHint");
widget.SetSelectionIsOptional(true);
CPPUNIT_ASSERT("SetInvalidInfo" == widget.GetInvalidInfo());
CPPUNIT_ASSERT("SetEmptyInfo" == widget.GetEmptyInfo());
CPPUNIT_ASSERT("SetPopUpTitel" == widget.GetPopUpTitel());
CPPUNIT_ASSERT("SetPopUpHint" == widget.GetPopUpHint());
CPPUNIT_ASSERT(true == widget.GetSelectionIsOptional());
}
void AllowEmissionOfSelectionTest()
{
TestWidget widget;
widget.SetDataStorage(m_DataStorage);
widget.SetCurrentSelection({ m_Node3 });
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.GetSelectedNodes()));
widget.m_Allow = false;
widget.SetCurrentSelection({ m_Node1 });
CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.GetSelectedNodes()));
}
void OnNodeModifiedTest()
{
TestWidget widget;
widget.SetDataStorage(m_DataStorage);
widget.SetCurrentSelection({ m_Node3 });
- //Check OnNodeModified behavour without predicate
+ //Check OnNodeModified behaviour without predicate
m_Node3->SetName("NewName");
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.GetSelectedNodes()));
//simulate behaviour if allowance is changed to true
widget.m_Allow = false;
m_Node3->SetName("NewName_temp");
widget.m_Allow = true;
m_Node3->SetName("NewName");
CPPUNIT_ASSERT_EQUAL(3, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(6, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.GetSelectedNodes()));
//change irrelevant node
m_Node2->SetName("IrrelevantNode");
CPPUNIT_ASSERT_EQUAL(3, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(6, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node3 }, widget.GetSelectedNodes()));
//test with predicate set
widget.SetNodePredicate(GeneratTestPredicate("node1"));
m_Node3->SetName("NowAlsoIrrelevantNode");
CPPUNIT_ASSERT_EQUAL(4, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(7, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ }, widget.GetSelectedNodes()));
widget.SetCurrentSelection({ m_Node1, m_Node1_2 });
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1, m_Node1_2 }, widget.m_LastNewEmision));
m_Node1->SetName("NodeSortedOutByPredicate");
CPPUNIT_ASSERT_EQUAL(6, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(4, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(4, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(9, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(4, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1_2 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1_2 }, widget.GetSelectedNodes()));
}
void SignalRecursionTest()
{
TestWidget widget;
widget.SetDataStorage(m_DataStorage);
TestWidget widget2;
widget2.SetDataStorage(m_DataStorage);
QmitkAbstractNodeSelectionWidget::connect(&widget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, &widget2, &QmitkAbstractNodeSelectionWidget::SetCurrentSelection);
QmitkAbstractNodeSelectionWidget::connect(&widget2, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, &widget, &QmitkAbstractNodeSelectionWidget::SetCurrentSelection);
widget.SetCurrentSelection({ m_Node1 });
CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.GetSelectedNodes()));
CPPUNIT_ASSERT_EQUAL(1, widget2.m_UpdateInfo);
CPPUNIT_ASSERT_EQUAL(0, widget2.m_OnNodePredicateChanged);
CPPUNIT_ASSERT_EQUAL(1, widget2.m_OnDataStorageChanged);
CPPUNIT_ASSERT_EQUAL(1, widget2.m_OnInternalSelectionChanged);
CPPUNIT_ASSERT_EQUAL(0, widget2.m_OnNodeAddedToStorage);
CPPUNIT_ASSERT_EQUAL(0, widget2.m_OnNodeRemovedFromStorage);
CPPUNIT_ASSERT_EQUAL(1, widget2.m_ReviseSelectionChanged);
CPPUNIT_ASSERT_EQUAL(1, widget2.m_AllowEmissionOfSelection);
CPPUNIT_ASSERT_EQUAL(1, widget2.m_NewSelectionEmited);
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget2.m_LastNewEmision));
CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget2.GetSelectedNodes()));
}
};
MITK_TEST_SUITE_REGISTRATION(QmitkAbstractNodeSelectionWidget)
diff --git a/Modules/QtWidgets/test/QmitkThreadedLogTest.cpp b/Modules/QtWidgets/test/QmitkThreadedLogTest.cpp
index 87430b2647..976be30968 100644
--- a/Modules/QtWidgets/test/QmitkThreadedLogTest.cpp
+++ b/Modules/QtWidgets/test/QmitkThreadedLogTest.cpp
@@ -1,108 +1,108 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <itksys/SystemTools.hxx>
#include <mitkLogBackend.h>
#include <mitkStandardFileLocations.h>
#include <string>
#include <QtConcurrentRun>
void LogMessages(unsigned int threadID, unsigned int numberOfTimes)
{
unsigned int times = 0;
while (times < numberOfTimes)
{
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;
times += 5;
}
}
/**
\brief Test logging from Qt threads
*/
static void TestThreadSaveLog(bool toFile)
{
bool testSucceded = true;
try
{
if (toFile)
{
std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + "/qtestthreadlog.log";
itksys::SystemTools::RemoveFile(filename.c_str()); // remove old file, we do not want to append to large files
mitk::LogBackend::SetLogFile(filename);
}
unsigned int numberOfThreads = 10;
unsigned int threadRuntimeInMilliseconds = 4000;
QVector<QFuture<void>> threads;
// Spawn some threads
for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx)
{
threads.push_back(QtConcurrent::run(LogMessages, threadIdx, 100));
std::cout << "Created " << threadIdx << ". thread." << std::endl;
}
// wait for some time (milliseconds)
itksys::SystemTools::Delay(threadRuntimeInMilliseconds);
// Wait for all to finish
for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx)
{
threads[threadIdx].waitForFinished();
std::cout << threadIdx << ". thread has finished" << 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
+ // if no error occurred until now, everything is ok
MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging in different threads.");
}
int QmitkThreadedLogTest(int /* argc */, char * /*argv*/ [])
{
// always start with this!
MITK_TEST_BEGIN("QmitkThreadedLogTest")
- MITK_TEST_OUTPUT(<< "TESTING ALL LOGGING OUTPUTS, ERROR MESSAGES ARE ALSO TESTED AND NOT MEANING AN ERROR OCCURED!")
+ MITK_TEST_OUTPUT(<< "TESTING ALL LOGGING OUTPUTS, ERROR MESSAGES ARE ALSO TESTED AND NOT MEANING AN ERROR OCCURRED!")
TestThreadSaveLog(false); // false = to console
TestThreadSaveLog(true); // true = to file
MITK_TEST_OUTPUT(<< "Number of threads in QThreadPool: " << QThreadPool::globalInstance()->maxThreadCount())
// always end with this!
MITK_TEST_END()
}
diff --git a/Modules/QtWidgetsExt/include/QmitkFileChooser.h b/Modules/QtWidgetsExt/include/QmitkFileChooser.h
index 17e8ef219f..aa1b6c5105 100644
--- a/Modules/QtWidgetsExt/include/QmitkFileChooser.h
+++ b/Modules/QtWidgetsExt/include/QmitkFileChooser.h
@@ -1,114 +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.
============================================================================*/
#ifndef QmitkFileChooser_h
#define QmitkFileChooser_h
#include "MitkQtWidgetsExtExports.h"
#include <QWidget>
class QPushButton;
class QLineEdit;
///
/// \brief Convenience Widget showing a line edit with the path of
/// a file or directory and a button which invokes a file choose dialog
///
/// Various methods are given to influence the behaviour or presentation
///
class MITKQTWIDGETSEXT_EXPORT QmitkFileChooser : public QWidget
{
Q_OBJECT
public:
///
/// standard ctor, init values to defaults (see methods for values)
///
explicit QmitkFileChooser(QWidget *parent = nullptr, Qt::WindowFlags f = {});
///
/// determines whether the button "Select file" is shown on the left
/// underneath the line edit, default=false (=is shown in a vertical layout)
///
void SetHorizotalLayout(bool horizontalLayout);
///
/// determines whether the selection must be a directory
/// default=false
///
void SetSelectDir(bool selectDir);
///
/// determines whether the file/directory mustexist
/// default=true
///
void SetFileMustExist(bool fileMustExist);
///
/// sets the file input, default=""
///
void SetFile(const std::string &file);
///
/// sets a file pattern to be selected, default=""
///
void SetFilePattern(const std::string &filepattern);
///
/// sets whether the user can edit the input, default=false
///
void SetReadOnly(bool ReadOnly);
///
/// returns whether the selected file/directory exists
///
bool IsValidFile() const;
///
/// returns the currently set file (an absolute path)
///
virtual std::string GetFile() const;
signals:
///
- /// emitted when the input changed programatically or by the user
+ /// emitted when the input changed programmatically or by the user
///
void NewFileSelected(const std::string &);
protected slots:
///
/// show dialog here
///
virtual void OnSelectFileClicked(bool /*checked=false*/);
///
/// check for valid here
///
virtual void OnFileEditingFinished();
protected:
///
/// \see SetSelectDir()
///
bool m_SelectDir;
///
/// \see SetFileMustExist()
///
bool m_FileMustExist;
///
/// \see SetFilePattern()
///
QString m_FilePattern;
///
/// the select file button
///
QPushButton *m_SelectFile;
///
/// the line edit to show the current file
///
QLineEdit *m_File;
};
#endif
diff --git a/Modules/QtWidgetsExt/include/QmitkNumberPropertyEditor.h b/Modules/QtWidgetsExt/include/QmitkNumberPropertyEditor.h
index 3d2acc85ea..633910c115 100644
--- a/Modules/QtWidgetsExt/include/QmitkNumberPropertyEditor.h
+++ b/Modules/QtWidgetsExt/include/QmitkNumberPropertyEditor.h
@@ -1,83 +1,83 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitkNumberPropertyEditor_h
#define QmitkNumberPropertyEditor_h
#include "MitkQtWidgetsExtExports.h"
#include <QSpinBox>
#include <mitkProperties.h>
#include <mitkPropertyObserver.h>
/// @ingroup Widgets
class MITKQTWIDGETSEXT_EXPORT QmitkNumberPropertyEditor : public QSpinBox, public mitk::PropertyEditor
{
Q_OBJECT
Q_PROPERTY(short decimalPlaces READ getDecimalPlaces WRITE setDecimalPlaces)
Q_PROPERTY(bool showPercent READ getShowPercent WRITE setShowPercent)
Q_PROPERTY(int minValue READ minValue WRITE setMinValue)
Q_PROPERTY(int maxValue READ maxValue WRITE setMaxValue)
public:
QmitkNumberPropertyEditor(mitk::IntProperty *, QWidget *parent);
QmitkNumberPropertyEditor(mitk::FloatProperty *, QWidget *parent);
QmitkNumberPropertyEditor(mitk::DoubleProperty *, QWidget *parent);
~QmitkNumberPropertyEditor() override;
short getDecimalPlaces() const;
void setDecimalPlaces(short);
bool getShowPercent() const;
void setShowPercent(bool);
int minValue() const;
void setMinValue(int);
int maxValue() const;
void setMaxValue(int);
double doubleValue() const;
void setDoubleValue(double);
protected:
void initialize();
QString textFromValue(int) const override;
int valueFromText(const QString &) const override;
void PropertyChanged() override;
void PropertyRemoved() override;
void DisplayNumber();
union {
mitk::GenericProperty<int> *m_IntProperty;
mitk::GenericProperty<float> *m_FloatProperty;
mitk::GenericProperty<double> *m_DoubleProperty;
};
const int m_DataType;
short m_DecimalPlaces; // how many decimal places are shown
- double m_FactorPropertyToSpinbox; // internal conversion factor. neccessary because spinbox ranges work only with ints
- double m_FactorSpinboxToDisplay; // internal conversion factor. neccessary because spinbox ranges work only with ints
+ double m_FactorPropertyToSpinbox; // internal conversion factor. necessary because spinbox ranges work only with ints
+ double m_FactorSpinboxToDisplay; // internal conversion factor. necessary because spinbox ranges work only with ints
bool m_ShowPercents; // whether values are given in percent (0.5 -> 50%)
protected slots:
void onValueChanged(int);
private:
void adjustFactors(short, bool);
bool m_SelfChangeLock;
};
#endif
diff --git a/Modules/QtWidgetsExt/include/QmitkPointListModel.h b/Modules/QtWidgetsExt/include/QmitkPointListModel.h
index d3826c637f..05cbc0692a 100644
--- a/Modules/QtWidgetsExt/include/QmitkPointListModel.h
+++ b/Modules/QtWidgetsExt/include/QmitkPointListModel.h
@@ -1,125 +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 QmitkPointListModel_h
#define QmitkPointListModel_h
#include "MitkQtWidgetsExtExports.h"
#include <QAbstractListModel>
#include "mitkDataNode.h"
#include "mitkPointSet.h"
class MITKQTWIDGETSEXT_EXPORT QmitkPointListModel : public QAbstractListModel
{
Q_OBJECT
public:
QmitkPointListModel(mitk::DataNode * = nullptr, int t = 0, QObject *parent = nullptr);
~QmitkPointListModel() override;
Qt::ItemFlags flags(const QModelIndex &) const override;
/// interface of QAbstractListModel
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
/// interface of QAbstractListModel
QVariant data(const QModelIndex &index, int role) const override;
/// interface of QAbstractListModel
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
/// which point set to work on
void SetPointSetNode(mitk::DataNode *pointSetNode);
/// which point set to work on
mitk::PointSet *GetPointSet() const;
// which point set to work on
mitk::DataNode *GetPointSetNode() const;
/// which time step to display/model
void SetTimeStep(int t);
/// which time step to display/model
int GetTimeStep() const;
/// itk observer for point set "modified" events
void OnPointSetChanged(const itk::EventObject &e);
/// itk observer for point set "delete" events
void OnPointSetDeleted(const itk::EventObject &e);
/**
* \brief get point and point ID that correspond to a given QModelIndex
*
* The mitk::PointSet uses a map to store points in an ID<-->Point relation.
- * The IDs are not neccesarily continuously numbered, therefore, we can not
+ * The IDs are not necessarily continuously numbered, therefore, we can not
* directly use the QModelIndex as point ID. This method returns the point and
* the corresponding point id for a given QModelIndex. The point and the point ID
* are returned in the outgoing parameters p and id. If a valid point and ID were
* found, the method returns true, otherwise it returns false
* \param[in] index the index for which a point is requested.
The row() part of the index is used to find a corresponding point
* \param[out] p If a valid point is found, it will be stored in the p parameter
* \param[out] id If a valid point is found, the corresponding ID will be stored in id
* \return Returns true, if a valid point was found, false otherwise
*/
bool GetPointForModelIndex(const QModelIndex &index,
mitk::PointSet::PointType &p,
mitk::PointSet::PointIdentifier &id) const;
/**Documentation
* \brief returns a QModelIndex for a given point ID
*
* The mitk::PointSet uses a map to store points in an ID<-->Point relation.
- * The IDs are not neccesarily continuously numbered, therefore, we can not
+ * The IDs are not necessarily continuously numbered, therefore, we can not
* directly use the point ID as a QModelIndex. This method returns a QModelIndex
* for a given point ID in the outgoing parameter index.
* \param[in] id The point ID for which the QModelIndex will be created
* \param[out] index if a point with the ID id was found, index will contain a corresponding QModelIndex
* for that point
* \return returns true, if a valid QModelIndex was created, false otherwise
*/
bool GetModelIndexForPointID(mitk::PointSet::PointIdentifier id, QModelIndex &index) const;
public slots:
void MoveSelectedPointUp();
void MoveSelectedPointDown();
void RemoveSelectedPoint();
signals:
/// emitted, when views should update their selection status
/// (because mouse interactions in render windows can change
/// the selection status of points)
void SignalUpdateSelection();
protected:
/// internally observe different point set
void ObserveNewPointSet(mitk::DataNode *pointSetNode);
// initially checks if there is a PointSet as data in the DataNode.
// returns PointSet if so and nullptr if other data is set to node
mitk::PointSet *CheckForPointSetInNode(mitk::DataNode *node) const;
protected:
mitk::DataNode *m_PointSetNode;
unsigned int m_PointSetModifiedObserverTag;
unsigned int m_PointSetDeletedObserverTag;
int m_TimeStep;
};
#endif
diff --git a/Modules/QtWidgetsExt/include/QmitkPointListView.h b/Modules/QtWidgetsExt/include/QmitkPointListView.h
index f8eed5ff5b..667ea6745d 100644
--- a/Modules/QtWidgetsExt/include/QmitkPointListView.h
+++ b/Modules/QtWidgetsExt/include/QmitkPointListView.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 QmitkPointListView_h
#define QmitkPointListView_h
#include "MitkQtWidgetsExtExports.h"
#include <QmitkPointListModel.h>
#include <QLabel>
#include <QListView>
#include <mitkSliceNavigationController.h>
class QmitkAbstractMultiWidget;
/*!
* \brief GUI widget for handling mitk::PointSet
*
* Displays all the points in a mitk::PointSet graphically.
* Reacts automatically to changes in the PointSet's selection status.
* Updates PointSet's selection status when this list's selection changes.
*
* If a QmitkAbstractMultiWidget is assigned via SetMultiWidget(), the
* crosshair of the QmitkAbstractMultiWidget is moved to the currently selected
* point.
*
*/
class MITKQTWIDGETSEXT_EXPORT QmitkPointListView : public QListView
{
Q_OBJECT
public:
QmitkPointListView(QWidget *parent = nullptr);
~QmitkPointListView() override;
/// assign a point set for observation
void SetPointSetNode(mitk::DataNode *pointSetNode);
/// which point set to work on
const mitk::PointSet *GetPointSet() const;
/**
* \brief If Multiwidget is set, the crosshair is automatically centering to the selected point
* As an alternative, if you dont have a multiwidget, you can call SetSnc1, SetSnc2, SetSnc3 to set the
* SliceNavigationControllers directly to enable the focussing feature.
*/
void SetMultiWidget(QmitkAbstractMultiWidget* multiWidget);
/**
* \brief Return the QmitkAbstractMultiWidget that is used for updating the render window crosshair.
*/
QmitkAbstractMultiWidget* GetMultiWidget() const;
/**
* @brief Add a mitk::SliceNavigationController instance.
* @param snc The mitk::SliceNavigationController instance.
*
* This method adds \c snc to the set of slice navigation controllers which are
* used to navigate to the selected point.
*/
void AddSliceNavigationController(mitk::SliceNavigationController *snc);
/**
* @brief Remove a mitk::SliceNavigationController instance.
* @param snc The mitk::SliceNavigationController instance.
*
* This method removes \c snc from the set of slice navigation controllers which are
* used to navigate to the selected point.
*/
void RemoveSliceNavigationController(mitk::SliceNavigationController *snc);
signals:
- void SignalPointSelectionChanged(); ///< this signal is emmitted, if the selection of a point in the pointset is changed
+ void SignalPointSelectionChanged(); ///< this signal is emitted, if the selection of a point in the pointset is changed
void SignalTimeStepChanged(int);
protected slots:
/// Filtering double click event for editing point coordinates via a dialog
void OnPointDoubleClicked(const QModelIndex &index);
/// called when the point set data structure changes
void OnPointSetSelectionChanged();
/// called when the selection of the view widget changes
void OnListViewSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
/// open ContextMenu
void ctxMenu(const QPoint &pos);
/// Turn TimeStep Fading On/Off
void SetFading(bool onOff);
/// Delete all points in the list
void ClearPointList();
/// delete all points in the list in the current timestep
void ClearPointListTS();
protected:
void keyPressEvent(QKeyEvent *e) override; ///< react to F2, F3 and DEL keys
void wheelEvent(QWheelEvent *event) override; ///< change timestep of the current pointset by mouse wheel
std::set<mitk::SliceNavigationController *> m_Sncs;
QmitkPointListModel *m_PointListModel;
bool m_SelfCall;
bool m_showFading;
/// used to position the planes on a selected point
QmitkAbstractMultiWidget* m_MultiWidget;
};
#endif
diff --git a/Modules/QtWidgetsExt/include/QmitkTransferFunctionWidget.h b/Modules/QtWidgetsExt/include/QmitkTransferFunctionWidget.h
index 0634b6750f..b3473d2056 100755
--- a/Modules/QtWidgetsExt/include/QmitkTransferFunctionWidget.h
+++ b/Modules/QtWidgetsExt/include/QmitkTransferFunctionWidget.h
@@ -1,80 +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.
============================================================================*/
#ifndef QmitkTransferFunctionWidget_h
#define QmitkTransferFunctionWidget_h
#include "MitkQtWidgetsExtExports.h"
#include "ui_QmitkTransferFunctionWidget.h"
#include <mitkCommon.h>
#include <QWidget>
#include <mitkDataNode.h>
#include <mitkTransferFunctionProperty.h>
#include <QPushButton>
#include <QSlider>
#include <QmitkTransferFunctionWidget.h>
namespace mitk
{
class BaseRenderer;
}
class MITKQTWIDGETSEXT_EXPORT QmitkTransferFunctionWidget : public QWidget, public Ui::QmitkTransferFunctionWidget
{
Q_OBJECT
public:
QmitkTransferFunctionWidget(QWidget *parent = nullptr, Qt::WindowFlags f = {});
~QmitkTransferFunctionWidget() override;
void SetDataNode(mitk::DataNode *node, mitk::TimeStepType timestep = 0, const mitk::BaseRenderer *renderer = nullptr);
void SetScalarLabel(const QString &scalarLabel);
void ShowScalarOpacityFunction(bool show);
void ShowColorFunction(bool show);
void ShowGradientOpacityFunction(bool show);
void SetScalarOpacityFunctionEnabled(bool enable);
void SetColorFunctionEnabled(bool enable);
void SetGradientOpacityFunctionEnabled(bool enable);
public slots:
void SetXValueScalar(const QString text);
void SetYValueScalar(const QString text);
void SetXValueGradient(const QString text);
void SetYValueGradient(const QString text);
void SetXValueColor(const QString text);
void OnUpdateCanvas();
void UpdateRanges();
void UpdateStepSize();
void OnResetSlider();
void OnSpanChanged(double lower, double upper);
protected:
mitk::TransferFunctionProperty::Pointer tfpToChange;
- double m_RangeSliderMin;
- double m_RangeSliderMax;
-
mitk::SimpleHistogramCache histogramCache;
};
#endif
diff --git a/Modules/QtWidgetsExt/src/QmitkColorPropertyEditor.cpp b/Modules/QtWidgetsExt/src/QmitkColorPropertyEditor.cpp
index dd4d4e1d08..da1a3b5d1c 100644
--- a/Modules/QtWidgetsExt/src/QmitkColorPropertyEditor.cpp
+++ b/Modules/QtWidgetsExt/src/QmitkColorPropertyEditor.cpp
@@ -1,271 +1,271 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkColorPropertyEditor.h"
#include <QApplication>
#include <QCloseEvent>
#include <QLayout>
#include <QMouseEvent>
#include <QPainter>
#include <mitkRenderingManager.h>
//----- QmitkPopupColorChooser ---------------------------------------------------------
QmitkPopupColorChooser::QmitkPopupColorChooser(QWidget *parent, unsigned int steps, unsigned int size)
: QFrame(parent, Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::Tool | Qt::X11BypassWindowManagerHint),
my_parent(parent)
{
setSteps(steps);
setLineWidth(2);
setMouseTracking(true);
setFrameStyle(QFrame::Panel | QFrame::Raised);
setLineWidth(1);
ensurePolished();
resize(size, size);
hide();
}
QmitkPopupColorChooser::~QmitkPopupColorChooser()
{
}
void QmitkPopupColorChooser::setSteps(int steps)
{
m_Steps = steps;
m_Steps2 = m_Steps / 2;
m_HStep = 360 / m_Steps;
m_SStep = 512 / m_Steps;
m_VStep = 512 / m_Steps;
}
void QmitkPopupColorChooser::keyReleaseEvent(QKeyEvent *)
{
emit colorSelected(m_OriginalColor);
close();
}
void QmitkPopupColorChooser::mouseMoveEvent(QMouseEvent *e)
{
double x(e->pos().x());
double y(e->pos().y());
x /= width();
if (x >= 0.0)
{
x = (int)(x * (float)(m_Steps - 1)) / (float)(m_Steps - 1);
if (x > 1.0)
x = 1.0;
if (x < 0.0)
x = 0.0;
}
y /= height();
if (y >= 1.0)
y = 0.9;
if (y < 0.0)
y = 0.0;
y = (int)(y * (float)m_Steps) / (float)m_Steps;
m_H = static_cast<int>(y * 359.0);
if (x >= 0.5)
{
m_S = static_cast<int>((1.0 - x) * 511.0);
if (m_S > 255)
m_S = 255;
m_V = 255;
}
else
{
m_S = 255;
if (x < 0.0)
m_V = 0;
else
{
m_V = static_cast<int>(x * 511.0 + 511.0 / (float)(m_Steps - 1));
if (m_V > 255)
m_V = 255;
}
}
QColor color;
color.setHsv(m_H, m_S, m_V);
emit colorSelected(color);
}
void QmitkPopupColorChooser::mouseReleaseEvent(QMouseEvent *)
{
close();
}
void QmitkPopupColorChooser::closeEvent(QCloseEvent *e)
{
e->accept();
releaseKeyboard();
releaseMouse();
if (!m_popupParent)
return;
- // remember that we (as a popup) might recieve the mouse release
+ // remember that we (as a popup) might receive the mouse release
// event instead of the popupParent. This is due to the fact that
// the popupParent popped us up in its mousePressEvent handler. To
// avoid the button remaining in pressed state we simply send a
// faked mouse button release event to it.
// Maleike: parent should not pop us on MouseRelease!
QMouseEvent me(QEvent::MouseButtonRelease, QPoint(0, 0), QPoint(0, 0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
QApplication::sendEvent(m_popupParent, &me);
}
void QmitkPopupColorChooser::popup(QWidget *parent, const QPoint &point, const mitk::Color *color)
{
m_popupParent = parent;
if (m_popupParent)
{
QPoint newPos;
if (color)
{
QColor qcolor((int)((*color)[0] * 255.0), (int)((*color)[1] * 255.0), (int)((*color)[2] * 255.0));
int h, s, v;
qcolor.getHsv(&h, &s, &v);
if (h == -1) // set by Qt if color is achromatic ( but this widget does not display grays )
h = 10; // red
int x, y;
float cellwidth = (float)width() / (float)(m_Steps);
if (s > v) // restrict to the colors we can display
{ // left side, ramp from v = 255/m_Steps to v = 255
s = 255;
x = (int)((((float)v / 255.0) * ((float)m_Steps2) - 1.0) * cellwidth + cellwidth / 2);
}
else
{
v = 255;
x = (int)(((1.0 - ((float)s / 255.0)) * ((float)m_Steps2)) * cellwidth + cellwidth / 2
+
width() / 2);
}
y = (int)((float)h / 360.0 * (float)m_Steps * cellwidth);
m_OriginalColor.setHsv(h, s, v);
// move to color
newPos.setX(point.x() - x);
newPos.setY(point.y() - y);
}
else
{
// center widget
m_OriginalColor.setHsv(-1, 0, 0);
newPos.setX(point.x() - width() / 2);
newPos.setY(point.y() - height() / 2);
}
move(m_popupParent->mapToGlobal(newPos));
}
show();
raise();
grabMouse();
grabKeyboard();
}
void QmitkPopupColorChooser::paintEvent(QPaintEvent *)
{
QPainter painter(this);
drawGradient(&painter);
}
void QmitkPopupColorChooser::drawGradient(QPainter *p)
{
p->setWindow(0, 0, m_Steps - 1, m_Steps); // defines coordinate system
p->setPen(Qt::NoPen);
QColor c;
for (unsigned int h = 0; h < m_Steps; ++h)
{
for (unsigned int v = 1; v < m_Steps2; ++v)
{
c.setHsv(h * m_HStep, 255, v * m_VStep); // rainbow effect
p->setBrush(c); // solid fill with color c
p->drawRect(v - 1, h, m_Steps2, m_Steps); // draw the rectangle
}
for (unsigned int s = 0; s < m_Steps2; ++s)
{
c.setHsv(h * m_HStep, 255 - s * m_SStep, 255); // rainbow effect
p->setBrush(c); // solid fill with color c
p->drawRect(m_Steps2 + s - 1, h, m_Steps2, m_Steps); // draw the rectangle
}
}
}
//----- QmitkColorPropertyEditor --------------------------------------------------
// initialization of static pointer to color picker widget
QmitkPopupColorChooser *QmitkColorPropertyEditor::colorChooser = nullptr;
int QmitkColorPropertyEditor::colorChooserRefCount = 0;
QmitkColorPropertyEditor::QmitkColorPropertyEditor(const mitk::ColorProperty *property, QWidget *parent)
: QmitkColorPropertyView(property, parent)
{
if (colorChooserRefCount == 0)
{
colorChooser = new QmitkPopupColorChooser(nullptr, 50);
}
++colorChooserRefCount;
}
QmitkColorPropertyEditor::~QmitkColorPropertyEditor()
{
--colorChooserRefCount;
if (!colorChooserRefCount)
{
delete colorChooser;
colorChooser = nullptr;
}
}
void QmitkColorPropertyEditor::mousePressEvent(QMouseEvent *e)
{
connect(colorChooser, SIGNAL(colorSelected(QColor)), this, SLOT(onColorSelected(QColor)));
if (m_ColorProperty)
{
colorChooser->popup(this, e->pos(), &(m_ColorProperty->GetColor()));
}
}
void QmitkColorPropertyEditor::mouseReleaseEvent(QMouseEvent *)
{
disconnect(colorChooser, SIGNAL(colorSelected(QColor)), this, SLOT(onColorSelected(QColor)));
}
void QmitkColorPropertyEditor::onColorSelected(QColor c)
{
if (m_ColorProperty)
{
int r, g, b;
c.getRgb(&r, &g, &b);
const_cast<mitk::ColorProperty *>(m_ColorProperty)->SetColor(r / 255.0, g / 255.0, b / 255.0);
m_ColorProperty->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
diff --git a/Modules/QtWidgetsExt/src/QmitkColorPropertyView.cpp b/Modules/QtWidgetsExt/src/QmitkColorPropertyView.cpp
index 62407ef96a..b294b50b33 100644
--- a/Modules/QtWidgetsExt/src/QmitkColorPropertyView.cpp
+++ b/Modules/QtWidgetsExt/src/QmitkColorPropertyView.cpp
@@ -1,51 +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.
============================================================================*/
#include "QmitkColorPropertyView.h"
#include <QPixmap>
#define ROUND_P(x) ((int)((x) + 0.5))
QmitkColorPropertyView::QmitkColorPropertyView(const mitk::ColorProperty *property, QWidget *parent)
: QLabel(parent), PropertyView(property), m_ColorProperty(property)
{
- setText(" "); // two spaces for some minimun height
+ setText(" "); // two spaces for some minimum height
setMinimumSize(15, 15);
PropertyChanged();
m_WidgetPalette = QWidget::palette();
QWidget::setPalette(m_WidgetPalette);
QWidget::setAutoFillBackground(true);
}
QmitkColorPropertyView::~QmitkColorPropertyView()
{
}
void QmitkColorPropertyView::PropertyChanged()
{
if (m_Property)
DisplayColor();
}
void QmitkColorPropertyView::PropertyRemoved()
{
m_Property = nullptr;
m_ColorProperty = nullptr;
}
void QmitkColorPropertyView::DisplayColor()
{
const mitk::Color &tmp_col(m_ColorProperty->GetColor());
QColor color(ROUND_P(tmp_col[0] * 255.0), ROUND_P(tmp_col[1] * 255.0), ROUND_P(tmp_col[2] * 255.0));
m_WidgetPalette.setColor(QPalette::Window, color);
}
diff --git a/Modules/QtWidgetsExt/src/QmitkNumberPropertySlider.cpp b/Modules/QtWidgetsExt/src/QmitkNumberPropertySlider.cpp
index 11d5bfe122..07ef5678d1 100644
--- a/Modules/QtWidgetsExt/src/QmitkNumberPropertySlider.cpp
+++ b/Modules/QtWidgetsExt/src/QmitkNumberPropertySlider.cpp
@@ -1,341 +1,341 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <QmitkNumberPropertySlider.h>
#include <mitkProperties.h>
#include <mitkPropertyObserver.h>
#include <mitkRenderingManager.h>
#define DT_SHORT 1
#define DT_INT 2
#define DT_FLOAT 3
#define DT_DOUBLE 4
#define ROUND(x) (((x) > 0) ? int((x) + 0.5) : int((x)-0.5))
#define ROUND_SHORT(x) (((x) > 0) ? short((x) + 0.5) : short((x)-0.5))
class QmitkNumberPropertySlider::Impl
{
public:
Impl(QmitkNumberPropertySlider *q);
void DisplayNumber();
void adjustFactors(short, bool);
class Editor : public mitk::PropertyEditor
{
public:
Editor(mitk::IntProperty *, Impl *impl);
Editor(mitk::FloatProperty *, Impl *impl);
Editor(mitk::DoubleProperty *, Impl *impl);
void PropertyChanged() override;
void PropertyRemoved() override;
void BeginModifyProperty() { mitk::PropertyEditor::BeginModifyProperty(); }
void EndModifyProperty() { mitk::PropertyEditor::EndModifyProperty(); }
union {
mitk::GenericProperty<int> *m_IntProperty;
mitk::GenericProperty<float> *m_FloatProperty;
mitk::GenericProperty<double> *m_DoubleProperty;
};
const int m_DataType;
private:
Impl *m_Impl;
};
std::unique_ptr<Editor> m_PropEditor;
short m_DecimalPlaces; // how many decimal places are shown
- double m_FactorPropertyToSlider; // internal conversion factor. neccessary because slider ranges work only with ints
- double m_FactorSliderToDisplay; // internal conversion factor. neccessary because slider ranges work only with ints
+ double m_FactorPropertyToSlider; // internal conversion factor. necessary because slider ranges work only with ints
+ double m_FactorSliderToDisplay; // internal conversion factor. necessary because slider ranges work only with ints
bool m_ShowPercents; // whether values are given in percent (0.5 -> 50%)
bool m_SelfChangeLock;
private:
void initialize();
QmitkNumberPropertySlider *q;
};
QmitkNumberPropertySlider::Impl::Editor::Editor(mitk::IntProperty *property, Impl *impl)
: mitk::PropertyEditor(property), m_IntProperty(property), m_DataType(DT_INT), m_Impl(impl)
{
}
QmitkNumberPropertySlider::Impl::Editor::Editor(mitk::FloatProperty *property, Impl *impl)
: mitk::PropertyEditor(property), m_FloatProperty(property), m_DataType(DT_FLOAT), m_Impl(impl)
{
}
QmitkNumberPropertySlider::Impl::Editor::Editor(mitk::DoubleProperty *property, Impl *impl)
: mitk::PropertyEditor(property), m_DoubleProperty(property), m_DataType(DT_DOUBLE), m_Impl(impl)
{
}
QmitkNumberPropertySlider::~QmitkNumberPropertySlider()
{
}
void QmitkNumberPropertySlider::SetProperty(mitk::IntProperty *property)
{
if (property == nullptr)
{
d->m_PropEditor.reset();
this->setEnabled(false);
}
else
{
d->m_PropEditor.reset(new Impl::Editor(property, d.get()));
d->m_PropEditor->PropertyChanged();
this->setEnabled(true);
}
}
void QmitkNumberPropertySlider::SetProperty(mitk::FloatProperty *property)
{
if (property == nullptr)
{
d->m_PropEditor.reset();
this->setEnabled(false);
}
else
{
d->m_PropEditor.reset(new Impl::Editor(property, d.get()));
d->m_PropEditor->PropertyChanged();
this->setEnabled(true);
}
}
void QmitkNumberPropertySlider::SetProperty(mitk::DoubleProperty *property)
{
if (property == nullptr)
{
d->m_PropEditor.reset();
this->setEnabled(false);
}
else
{
d->m_PropEditor.reset(new Impl::Editor(property, d.get()));
d->m_PropEditor->PropertyChanged();
this->setEnabled(true);
}
}
QmitkNumberPropertySlider::Impl::Impl(QmitkNumberPropertySlider *q)
: m_DecimalPlaces(0),
m_FactorPropertyToSlider(1.0),
m_FactorSliderToDisplay(1.0),
m_ShowPercents(false),
m_SelfChangeLock(false),
q(q)
{
}
QmitkNumberPropertySlider::QmitkNumberPropertySlider(QWidget *parent) : QSlider(parent), d(new Impl(this))
{
connect(this, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int)));
this->setEnabled(false);
}
void QmitkNumberPropertySlider::Impl::adjustFactors(short newDecimalPlaces, bool newShowPercents)
{
int oldMax = q->maxValue();
int oldMin = q->minValue();
m_DecimalPlaces = newDecimalPlaces;
m_ShowPercents = newShowPercents;
m_FactorPropertyToSlider = pow(10.0, m_DecimalPlaces);
m_FactorSliderToDisplay = 1.0 / m_FactorPropertyToSlider;
if (m_ShowPercents)
m_FactorPropertyToSlider *= 100.0;
q->setMinimum(oldMin);
q->setMaximum(oldMax);
}
short QmitkNumberPropertySlider::getDecimalPlaces() const
{
return d->m_DecimalPlaces;
}
void QmitkNumberPropertySlider::setDecimalPlaces(short places)
{
if (d->m_PropEditor.get() == nullptr)
return;
switch (d->m_PropEditor->m_DataType)
{
case DT_FLOAT:
case DT_DOUBLE:
{
d->adjustFactors(places, d->m_ShowPercents);
d->DisplayNumber();
break;
}
default:
break;
}
}
bool QmitkNumberPropertySlider::getShowPercent() const
{
return d->m_ShowPercents;
}
void QmitkNumberPropertySlider::setShowPercent(bool showPercent)
{
if (showPercent == d->m_ShowPercents)
return; // nothing to change
if (d->m_PropEditor.get() == nullptr)
return;
switch (d->m_PropEditor->m_DataType)
{
case DT_FLOAT:
case DT_DOUBLE:
{
d->adjustFactors(d->m_DecimalPlaces, showPercent);
break;
}
default:
{
break;
}
}
d->DisplayNumber();
}
int QmitkNumberPropertySlider::minValue() const
{
return ROUND(QSlider::minimum() / d->m_FactorPropertyToSlider);
}
void QmitkNumberPropertySlider::setMinValue(int value)
{
QSlider::setMinimum(ROUND(value * d->m_FactorPropertyToSlider));
}
int QmitkNumberPropertySlider::maxValue() const
{
return ROUND(QSlider::maximum() / d->m_FactorPropertyToSlider);
}
void QmitkNumberPropertySlider::setMaxValue(int value)
{
QSlider::setMaximum(ROUND(value * d->m_FactorPropertyToSlider));
}
double QmitkNumberPropertySlider::doubleValue() const
{
return static_cast<double>((QSlider::value()) / d->m_FactorPropertyToSlider);
}
void QmitkNumberPropertySlider::setDoubleValue(double value)
{
QSlider::setValue(ROUND(value * d->m_FactorPropertyToSlider));
}
void QmitkNumberPropertySlider::onValueChanged(int value)
{
if (d->m_PropEditor.get() == nullptr)
return;
if (d->m_SelfChangeLock)
return; // valueChanged is even emitted, when this widget initiates a change to its value
// this may be useful some times, but in this use, it would be wrong:
// (A) is an editor with 3 decimal places
// (B) is an editor with 2 decimal places
// User changes A's displayed value to 4.002
// A's onValueChanged gets called, sets the associated Property to 4.002
// B's onPropertyChanged gets called, sets its display to 4.00
// B's onValueChanged gets called and sets the associated Property to 4.00
// A's onPropertyChanged gets called, sets its display to 4.000
d->m_PropEditor->BeginModifyProperty();
double newValue(value / d->m_FactorPropertyToSlider);
switch (d->m_PropEditor->m_DataType)
{
case DT_INT:
{
d->m_PropEditor->m_IntProperty->SetValue(ROUND(newValue));
break;
}
case DT_FLOAT:
{
d->m_PropEditor->m_FloatProperty->SetValue(newValue);
break;
}
case DT_DOUBLE:
{
d->m_PropEditor->m_DoubleProperty->SetValue(newValue);
break;
}
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
d->m_PropEditor->EndModifyProperty();
}
void QmitkNumberPropertySlider::Impl::Editor::PropertyChanged()
{
m_Impl->DisplayNumber();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkNumberPropertySlider::Impl::Editor::PropertyRemoved()
{
this->m_Property = nullptr;
}
void QmitkNumberPropertySlider::Impl::DisplayNumber()
{
if (m_PropEditor.get() == nullptr)
return;
m_SelfChangeLock = true;
switch (m_PropEditor->m_DataType)
{
case DT_INT:
{
int i = m_PropEditor->m_IntProperty->GetValue();
q->setValue(i);
break;
}
case DT_FLOAT:
{
float f = m_PropEditor->m_FloatProperty->GetValue();
q->setDoubleValue(f);
break;
}
case DT_DOUBLE:
{
double d = m_PropEditor->m_DoubleProperty->GetValue();
q->setDoubleValue(d);
break;
}
default:
break;
}
m_SelfChangeLock = false;
}
diff --git a/Modules/QtWidgetsExt/src/QmitkPointListWidget.cpp b/Modules/QtWidgetsExt/src/QmitkPointListWidget.cpp
index 04c09b9610..a179d3bce7 100644
--- a/Modules/QtWidgetsExt/src/QmitkPointListWidget.cpp
+++ b/Modules/QtWidgetsExt/src/QmitkPointListWidget.cpp
@@ -1,481 +1,481 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkPointListWidget.h"
#include <QDir>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QMessageBox>
#include <mitkIOUtil.h>
#include <QmitkAbstractMultiWidget.h>
#include <QmitkEditPointDialog.h>
#include <QmitkStyleManager.h>
#include <mitkPointSetDataInteractor.h>
QmitkPointListWidget::QmitkPointListWidget(QWidget *parent, int orientation)
: QWidget(parent),
m_PointListView(nullptr),
m_PointSetNode(nullptr),
m_Orientation(0),
m_MovePointUpBtn(nullptr),
m_MovePointDownBtn(nullptr),
m_RemovePointBtn(nullptr),
m_SavePointsBtn(nullptr),
m_LoadPointsBtn(nullptr),
m_ToggleAddPoint(nullptr),
m_AddPoint(nullptr),
m_TimeStepDisplay(nullptr),
m_DataInteractor(nullptr),
m_TimeStep(0),
m_EditAllowed(true),
m_NodeObserverTag(0)
{
m_PointListView = new QmitkPointListView();
if (orientation != 0)
m_Orientation = orientation;
SetupUi();
SetupConnections();
ObserveNewNode(nullptr);
}
QmitkPointListWidget::~QmitkPointListWidget()
{
m_DataInteractor = nullptr;
if (m_PointSetNode && m_NodeObserverTag)
{
m_PointSetNode->RemoveObserver(m_NodeObserverTag);
m_NodeObserverTag = 0;
}
delete m_PointListView;
}
void QmitkPointListWidget::SetupConnections()
{
connect(this->m_LoadPointsBtn, SIGNAL(clicked()), this, SLOT(OnBtnLoadPoints()));
connect(this->m_SavePointsBtn, SIGNAL(clicked()), this, SLOT(OnBtnSavePoints()));
connect(this->m_MovePointUpBtn, SIGNAL(clicked()), this, SLOT(MoveSelectedPointUp()));
connect(this->m_MovePointDownBtn, SIGNAL(clicked()), this, SLOT(MoveSelectedPointDown()));
connect(this->m_RemovePointBtn, SIGNAL(clicked()), this, SLOT(RemoveSelectedPoint()));
connect(this->m_ToggleAddPoint, SIGNAL(toggled(bool)), this, SLOT(OnBtnAddPoint(bool)));
connect(this->m_AddPoint, SIGNAL(clicked()), this, SLOT(OnBtnAddPointManually()));
connect(this->m_PointListView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnListDoubleClick()));
connect(this->m_PointListView, SIGNAL(SignalPointSelectionChanged()), this, SLOT(OnPointSelectionChanged()));
connect(this->m_PointListView, SIGNAL(SignalTimeStepChanged(int)), this, SLOT(OnTimeStepChanged(int)));
}
void QmitkPointListWidget::OnTimeStepChanged(int timeStep)
{
m_TimeStepLabel->setText(QString("%1").arg(timeStep));
}
void QmitkPointListWidget::SetupUi()
{
// Setup the buttons
m_ToggleAddPoint = new QPushButton();
m_ToggleAddPoint->setMaximumSize(25, 25);
m_ToggleAddPoint->setCheckable(true);
m_ToggleAddPoint->setToolTip("Toggle point editing (use SHIFT + Left Mouse Button to add Points)");
m_ToggleAddPoint->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/plus.svg")));
m_AddPoint = new QPushButton();
m_AddPoint->setMaximumSize(25, 25);
m_AddPoint->setToolTip("Manually add point");
m_AddPoint->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/plus-xyz.svg")));
m_RemovePointBtn = new QPushButton();
m_RemovePointBtn->setMaximumSize(25, 25);
m_RemovePointBtn->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/eraser.svg")));
m_RemovePointBtn->setToolTip("Erase one point from list (Hotkey: DEL)");
m_MovePointUpBtn = new QPushButton();
m_MovePointUpBtn->setMaximumSize(25, 25);
m_MovePointUpBtn->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/arrow-up.svg")));
m_MovePointUpBtn->setToolTip("Swap selected point upwards (Hotkey: F2)");
m_MovePointDownBtn = new QPushButton();
m_MovePointDownBtn->setMaximumSize(25, 25);
m_MovePointDownBtn->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/arrow-down.svg")));
m_MovePointDownBtn->setToolTip("Swap selected point downwards (Hotkey: F3)");
m_SavePointsBtn = new QPushButton();
m_SavePointsBtn->setMaximumSize(25, 25);
m_SavePointsBtn->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/save.svg")));
m_SavePointsBtn->setToolTip("Save points to file");
m_LoadPointsBtn = new QPushButton();
m_LoadPointsBtn->setMaximumSize(25, 25);
m_LoadPointsBtn->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/QtWidgetsExt/folder-open.svg")));
m_LoadPointsBtn->setToolTip("Load list of points from file (REPLACES current content)");
int i;
QBoxLayout *lay1;
QBoxLayout *lay2;
QBoxLayout *lay3;
switch (m_Orientation)
{
case 0:
lay1 = new QVBoxLayout(this);
lay2 = new QHBoxLayout();
i = 0;
break;
case 1:
lay1 = new QHBoxLayout(this);
lay2 = new QVBoxLayout();
i = -1;
break;
case 2:
lay1 = new QHBoxLayout(this);
lay2 = new QVBoxLayout();
i = 0;
break;
default:
lay1 = new QVBoxLayout(this);
lay2 = new QHBoxLayout();
i = -1;
break;
}
// setup Layouts
this->setLayout(lay1);
lay2->stretch(true);
lay2->addWidget(m_ToggleAddPoint);
lay2->addWidget(m_AddPoint);
lay2->addWidget(m_RemovePointBtn);
lay2->addWidget(m_MovePointUpBtn);
lay2->addWidget(m_MovePointDownBtn);
lay2->addWidget(m_SavePointsBtn);
lay2->addWidget(m_LoadPointsBtn);
// setup Labels
m_TimeStepDisplay = new QLabel;
m_TimeStepLabel = new QLabel;
lay3 = new QHBoxLayout;
m_TimeStepDisplay->setMaximumSize(200, 15);
lay3->stretch(true);
lay3->setAlignment(Qt::AlignLeft);
lay3->addWidget(m_TimeStepDisplay);
lay3->addWidget(m_TimeStepLabel);
m_TimeStepDisplay->setText("Time Step: ");
m_TimeStepLabel->setMaximumSize(10, 15);
this->OnTimeStepChanged(0);
//Add Layouts
lay1->insertWidget(i, m_PointListView);
this->setLayout(lay1);
lay1->addLayout(lay2);
lay1->addLayout(lay3);
}
void QmitkPointListWidget::SetPointSet(mitk::PointSet *newPs)
{
if (newPs == nullptr)
return;
this->m_PointSetNode->SetData(newPs);
dynamic_cast<QmitkPointListModel *>(this->m_PointListView->model())->SetPointSetNode(m_PointSetNode);
ObserveNewNode(m_PointSetNode);
}
void QmitkPointListWidget::SetPointSetNode(mitk::DataNode *newNode)
{
if (m_DataInteractor.IsNotNull())
m_DataInteractor->SetDataNode(newNode);
ObserveNewNode(newNode);
dynamic_cast<QmitkPointListModel *>(this->m_PointListView->model())->SetPointSetNode(newNode);
}
void QmitkPointListWidget::OnBtnSavePoints()
{
if ((dynamic_cast<mitk::PointSet *>(m_PointSetNode->GetData())) == nullptr)
return; // don't write empty point sets. If application logic requires something else then do something else.
if ((dynamic_cast<mitk::PointSet *>(m_PointSetNode->GetData()))->GetSize() == 0)
return;
// take the previously defined name of node as proposal for filename
std::string nodeName = m_PointSetNode->GetName();
nodeName = "/" + nodeName + ".mps";
QString fileNameProposal = QString();
fileNameProposal.append(nodeName.c_str());
QString aFilename = QFileDialog::getSaveFileName(
nullptr, "Save point set", QDir::currentPath() + fileNameProposal, "MITK Pointset (*.mps)");
if (aFilename.isEmpty())
return;
try
{
mitk::IOUtil::Save(m_PointSetNode->GetData(), aFilename.toStdString());
}
catch (...)
{
QMessageBox::warning(this,
"Save point set",
QString("File writer reported problems writing %1\n\n"
"PLEASE CHECK output file!")
.arg(aFilename));
}
}
void QmitkPointListWidget::OnBtnLoadPoints()
{
// get the name of the file to load
QString filename = QFileDialog::getOpenFileName(nullptr, "Open MITK Pointset", "", "MITK Point Sets (*.mps)");
if (filename.isEmpty())
return;
// attempt to load file
try
{
mitk::PointSet::Pointer pointSet = mitk::IOUtil::Load<mitk::PointSet>(filename.toStdString());
if (pointSet.IsNull())
{
QMessageBox::warning(this, "Load point set", QString("File reader could not read %1").arg(filename));
return;
}
// loading successful
-
this->SetPointSet(pointSet);
}
catch (...)
{
QMessageBox::warning(this, "Load point set", QString("File reader collapsed while reading %1").arg(filename));
}
- emit PointListChanged();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+ emit PointListChanged();
}
mitk::PointSet *QmitkPointListWidget::GetPointSet()
{
return dynamic_cast<mitk::PointSet *>(m_PointSetNode->GetData());
}
mitk::DataNode *QmitkPointListWidget::GetPointSetNode()
{
return m_PointSetNode;
}
void QmitkPointListWidget::SetMultiWidget(QmitkAbstractMultiWidget* multiWidget)
{
m_PointListView->SetMultiWidget(multiWidget);
}
void QmitkPointListWidget::RemoveSelectedPoint()
{
if (!m_PointSetNode)
return;
mitk::PointSet *pointSet = dynamic_cast<mitk::PointSet *>(m_PointSetNode->GetData());
if (!pointSet)
return;
if (pointSet->GetSize() == 0)
return;
QmitkPointListModel *pointListModel = dynamic_cast<QmitkPointListModel *>(m_PointListView->model());
pointListModel->RemoveSelectedPoint();
emit PointListChanged();
}
void QmitkPointListWidget::MoveSelectedPointDown()
{
if (!m_PointSetNode)
return;
mitk::PointSet *pointSet = dynamic_cast<mitk::PointSet *>(m_PointSetNode->GetData());
if (!pointSet)
return;
if (pointSet->GetSize() == 0)
return;
QmitkPointListModel *pointListModel = dynamic_cast<QmitkPointListModel *>(m_PointListView->model());
pointListModel->MoveSelectedPointDown();
emit PointListChanged();
}
void QmitkPointListWidget::MoveSelectedPointUp()
{
if (!m_PointSetNode)
return;
mitk::PointSet *pointSet = dynamic_cast<mitk::PointSet *>(m_PointSetNode->GetData());
if (!pointSet)
return;
if (pointSet->GetSize() == 0)
return;
QmitkPointListModel *pointListModel = dynamic_cast<QmitkPointListModel *>(m_PointListView->model());
pointListModel->MoveSelectedPointUp();
emit PointListChanged();
}
void QmitkPointListWidget::OnBtnAddPoint(bool checked)
{
if (m_PointSetNode.IsNotNull())
{
if (checked)
{
m_DataInteractor = m_PointSetNode->GetDataInteractor();
// If no data Interactor is present create a new one
if (m_DataInteractor.IsNull())
{
// Create PointSetData Interactor
m_DataInteractor = mitk::PointSetDataInteractor::New();
// Load the according state machine for regular point set interaction
m_DataInteractor->LoadStateMachine("PointSet.xml");
// Set the configuration file that defines the triggers for the transitions
m_DataInteractor->SetEventConfig("PointSetConfig.xml");
// set the DataNode (which already is added to the DataStorage
m_DataInteractor->SetDataNode(m_PointSetNode);
}
}
else
{
m_PointSetNode->SetDataInteractor(nullptr);
m_DataInteractor = nullptr;
}
- emit EditPointSets(checked);
+ emit PointListChanged();
}
}
void QmitkPointListWidget::OnBtnAddPointManually()
{
mitk::PointSet *pointSet = this->GetPointSet();
QmitkEditPointDialog editPointDialog(this);
if (this->GetPointSet()->IsEmpty())
{
editPointDialog.SetPoint(pointSet, 0, m_TimeStep);
}
else
{
mitk::PointSet::PointsIterator maxIt = pointSet->GetMaxId();
mitk::PointSet::PointIdentifier maxId = maxIt->Index();
editPointDialog.SetPoint(pointSet, maxId + 1, m_TimeStep);
}
editPointDialog.exec();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
+ emit PointListChanged();
}
void QmitkPointListWidget::OnListDoubleClick()
{
}
void QmitkPointListWidget::OnPointSelectionChanged()
{
emit this->PointSelectionChanged();
}
void QmitkPointListWidget::DeactivateInteractor(bool)
{
}
void QmitkPointListWidget::EnableEditButton(bool enabled)
{
m_EditAllowed = enabled;
if (enabled == false)
m_ToggleAddPoint->setEnabled(false);
else
m_ToggleAddPoint->setEnabled(true);
OnBtnAddPoint(enabled);
}
void QmitkPointListWidget::ObserveNewNode(mitk::DataNode *node)
{
if (m_DataInteractor.IsNotNull())
m_DataInteractor->SetDataNode(node);
// remove old observer
if (m_PointSetNode)
{
if (m_DataInteractor)
{
m_DataInteractor = nullptr;
m_ToggleAddPoint->setChecked(false);
}
m_PointSetNode->RemoveObserver(m_NodeObserverTag);
m_NodeObserverTag = 0;
}
m_PointSetNode = node;
// add new observer if necessary
if (m_PointSetNode)
{
itk::ReceptorMemberCommand<QmitkPointListWidget>::Pointer command =
itk::ReceptorMemberCommand<QmitkPointListWidget>::New();
command->SetCallbackFunction(this, &QmitkPointListWidget::OnNodeDeleted);
m_NodeObserverTag = m_PointSetNode->AddObserver(itk::DeleteEvent(), command);
}
else
{
m_NodeObserverTag = 0;
}
if (m_EditAllowed == true)
m_ToggleAddPoint->setEnabled(m_PointSetNode);
else
m_ToggleAddPoint->setEnabled(false);
m_RemovePointBtn->setEnabled(m_PointSetNode);
m_LoadPointsBtn->setEnabled(m_PointSetNode);
m_SavePointsBtn->setEnabled(m_PointSetNode);
m_AddPoint->setEnabled(m_PointSetNode);
}
void QmitkPointListWidget::OnNodeDeleted(const itk::EventObject &)
{
if (m_PointSetNode.IsNotNull() && !m_NodeObserverTag)
m_PointSetNode->RemoveObserver(m_NodeObserverTag);
m_NodeObserverTag = 0;
m_PointSetNode = nullptr;
m_PointListView->SetPointSetNode(nullptr);
m_ToggleAddPoint->setEnabled(false);
m_RemovePointBtn->setEnabled(false);
m_LoadPointsBtn->setEnabled(false);
m_SavePointsBtn->setEnabled(false);
m_AddPoint->setEnabled(false);
}
void QmitkPointListWidget::AddSliceNavigationController(mitk::SliceNavigationController *snc)
{
m_PointListView->AddSliceNavigationController(snc);
}
void QmitkPointListWidget::RemoveSliceNavigationController(mitk::SliceNavigationController *snc)
{
m_PointListView->RemoveSliceNavigationController(snc);
}
void QmitkPointListWidget::UnselectEditButton()
{
m_ToggleAddPoint->setChecked(false);
}
diff --git a/Modules/QtWidgetsExt/src/QmitkTransferFunctionCanvas.cpp b/Modules/QtWidgetsExt/src/QmitkTransferFunctionCanvas.cpp
index 7e68f1c758..9efbd2cc24 100755
--- a/Modules/QtWidgetsExt/src/QmitkTransferFunctionCanvas.cpp
+++ b/Modules/QtWidgetsExt/src/QmitkTransferFunctionCanvas.cpp
@@ -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.
============================================================================*/
#include "QmitkTransferFunctionCanvas.h"
#include <itkObject.h>
#include <QColorDialog>
#include <QMouseEvent>
#include <QPainter>
QmitkTransferFunctionCanvas::QmitkTransferFunctionCanvas(QWidget *parent, Qt::WindowFlags f)
: QWidget(parent, f),
m_GrabbedHandle(-1),
m_Lower(0.0),
m_Upper(1.0),
m_Min(0.0),
m_Max(1.0),
m_Histogram(nullptr),
m_ImmediateUpdate(false),
m_Range(0.0f),
m_LineEditAvailable(false),
m_XEdit(nullptr),
m_YEdit(nullptr)
{
setEnabled(false);
setFocusPolicy(Qt::ClickFocus);
}
void QmitkTransferFunctionCanvas::paintEvent(QPaintEvent *ev)
{
QWidget::paintEvent(ev);
}
std::pair<int, int> QmitkTransferFunctionCanvas::FunctionToCanvas(std::pair<double, double> functionPoint)
{
return std::make_pair(
(int)((functionPoint.first - m_Lower) / (m_Upper - m_Lower) * contentsRect().width()) + contentsRect().x(),
(int)(contentsRect().height() * (1 - functionPoint.second)) + contentsRect().y());
}
std::pair<double, double> QmitkTransferFunctionCanvas::CanvasToFunction(std::pair<int, int> canvasPoint)
{
return std::make_pair(
(canvasPoint.first - contentsRect().x()) * (m_Upper - m_Lower) / contentsRect().width() + m_Lower,
1.0 - (double)(canvasPoint.second - contentsRect().y()) / contentsRect().height());
}
void QmitkTransferFunctionCanvas::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
{
int nearHandle = GetNearHandle(mouseEvent->pos().x(), mouseEvent->pos().y());
if (nearHandle != -1)
{
this->DoubleClickOnHandle(nearHandle);
}
}
/** returns index of a near handle or -1 if none is near
*/
int QmitkTransferFunctionCanvas::GetNearHandle(int, int, unsigned int)
{
return -1;
}
void QmitkTransferFunctionCanvas::mousePressEvent(QMouseEvent *mouseEvent)
{
if (m_LineEditAvailable)
{
m_XEdit->clear();
if (m_YEdit)
m_YEdit->clear();
}
const auto pos = mouseEvent->position().toPoint();
m_GrabbedHandle = GetNearHandle(pos.x(), pos.y());
if ((mouseEvent->button() & Qt::LeftButton) && m_GrabbedHandle == -1)
{
auto [x, value] = this->CanvasToFunction(std::make_pair(pos.x(), pos.y()));
this->AddFunctionPoint(x, value);
m_GrabbedHandle = GetNearHandle(pos.x(), pos.y());
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
else if ((mouseEvent->button() & Qt::RightButton) && m_GrabbedHandle != -1 && this->GetFunctionSize() > 1)
{
this->RemoveFunctionPoint(this->GetFunctionX(m_GrabbedHandle));
m_GrabbedHandle = -1;
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
update();
}
void QmitkTransferFunctionCanvas::mouseMoveEvent(QMouseEvent *mouseEvent)
{
if (m_GrabbedHandle != -1)
{
const auto pos = mouseEvent->position().toPoint();
std::pair<double, double> newPos = this->CanvasToFunction(std::make_pair(pos.x(), pos.y()));
// X Clamping
{
// Check with predecessor
if (m_GrabbedHandle > 0)
if (newPos.first <= this->GetFunctionX(m_GrabbedHandle - 1))
newPos.first = this->GetFunctionX(m_GrabbedHandle);
- // Check with sucessor
+ // Check with successor
if (m_GrabbedHandle < this->GetFunctionSize() - 1)
if (newPos.first >= this->GetFunctionX(m_GrabbedHandle + 1))
newPos.first = this->GetFunctionX(m_GrabbedHandle);
// Clamping to histogramm
if (newPos.first < m_Min)
newPos.first = m_Min;
else if (newPos.first > m_Max)
newPos.first = m_Max;
}
// Y Clamping
{
if (newPos.second < 0.0)
newPos.second = 0.0;
else if (newPos.second > 1.0)
newPos.second = 1.0;
}
// Move selected point
this->MoveFunctionPoint(m_GrabbedHandle, newPos);
update();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkTransferFunctionCanvas::mouseReleaseEvent(QMouseEvent *)
{
update();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkTransferFunctionCanvas::PaintHistogram(QPainter &p)
{
if (m_Histogram)
{
p.save();
p.setPen(Qt::gray);
int displayWidth = contentsRect().width();
int displayHeight = contentsRect().height();
double windowLeft = m_Lower;
double windowRight = m_Upper;
double step = (windowRight - windowLeft) / double(displayWidth);
double pos = windowLeft;
for (int x = 0; x < displayWidth; x++)
{
double left = pos;
double right = pos + step;
float height = m_Histogram->GetRelativeBin(left, right);
if (height >= 0)
p.drawLine(x, displayHeight * (1 - height), x, displayHeight);
pos += step;
}
p.restore();
}
}
void QmitkTransferFunctionCanvas::keyPressEvent(QKeyEvent *e)
{
if (m_GrabbedHandle == -1)
return;
switch (e->key())
{
case Qt::Key_Delete:
if (this->GetFunctionSize() > 1)
{
this->RemoveFunctionPoint(GetFunctionX(m_GrabbedHandle));
m_GrabbedHandle = -1;
}
break;
case Qt::Key_Left:
this->MoveFunctionPoint(
m_GrabbedHandle,
ValidateCoord(std::make_pair(GetFunctionX(m_GrabbedHandle) - 1, GetFunctionY(m_GrabbedHandle))));
break;
case Qt::Key_Right:
this->MoveFunctionPoint(
m_GrabbedHandle,
ValidateCoord(std::make_pair(GetFunctionX(m_GrabbedHandle) + 1, GetFunctionY(m_GrabbedHandle))));
break;
case Qt::Key_Up:
this->MoveFunctionPoint(
m_GrabbedHandle,
ValidateCoord(std::make_pair(GetFunctionX(m_GrabbedHandle), GetFunctionY(m_GrabbedHandle) + 0.001)));
break;
case Qt::Key_Down:
this->MoveFunctionPoint(
m_GrabbedHandle,
ValidateCoord(std::make_pair(GetFunctionX(m_GrabbedHandle), GetFunctionY(m_GrabbedHandle) - 0.001)));
break;
}
update();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
-// Update immediatly while changing the transfer function
+// Update immediately while changing the transfer function
void QmitkTransferFunctionCanvas::SetImmediateUpdate(bool state)
{
m_ImmediateUpdate = state;
}
diff --git a/Modules/QtWidgetsExt/src/QmitkTransferFunctionWidget.cpp b/Modules/QtWidgetsExt/src/QmitkTransferFunctionWidget.cpp
index 773cea31c9..3bf76cd35a 100755
--- a/Modules/QtWidgetsExt/src/QmitkTransferFunctionWidget.cpp
+++ b/Modules/QtWidgetsExt/src/QmitkTransferFunctionWidget.cpp
@@ -1,271 +1,271 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkTransferFunctionWidget.h"
#include "mitkImageTimeSelector.h"
#include <mitkTransferFunctionProperty.h>
QmitkTransferFunctionWidget::QmitkTransferFunctionWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f)
{
this->setupUi(this);
// signals and slots connections
connect(m_XEditScalarOpacity, SIGNAL(textEdited(const QString &)), this, SLOT(SetXValueScalar(const QString &)));
connect(m_YEditScalarOpacity, SIGNAL(textEdited(const QString &)), this, SLOT(SetYValueScalar(const QString &)));
connect(m_XEditGradientOpacity, SIGNAL(textEdited(const QString &)), this, SLOT(SetXValueGradient(const QString &)));
connect(m_YEditGradientOpacity, SIGNAL(textEdited(const QString &)), this, SLOT(SetYValueGradient(const QString &)));
connect(m_XEditColor, SIGNAL(textEdited(const QString &)), this, SLOT(SetXValueColor(const QString &)));
m_RangeSlider->setMinimum(-2048);
m_RangeSlider->setMaximum(2048);
UpdateStepSize();
connect(m_RangeSlider, SIGNAL(valuesChanged(double, double)), this, SLOT(OnSpanChanged(double, double)));
// reset button
connect(m_RangeSliderReset, SIGNAL(pressed()), this, SLOT(OnResetSlider()));
m_ScalarOpacityFunctionCanvas->SetQLineEdits(m_XEditScalarOpacity, m_YEditScalarOpacity);
m_GradientOpacityCanvas->SetQLineEdits(m_XEditGradientOpacity, m_YEditGradientOpacity);
m_ColorTransferFunctionCanvas->SetQLineEdits(m_XEditColor, nullptr);
m_ScalarOpacityFunctionCanvas->SetTitle("Grayvalue -> Opacity");
m_GradientOpacityCanvas->SetTitle("Grayvalue/Gradient -> Opacity");
m_ColorTransferFunctionCanvas->SetTitle("Grayvalue -> Color");
}
QmitkTransferFunctionWidget::~QmitkTransferFunctionWidget()
{
}
void QmitkTransferFunctionWidget::SetScalarLabel(const QString &scalarLabel)
{
m_textLabelX->setText(scalarLabel);
m_textLabelX_2->setText(scalarLabel);
m_textLabelX_3->setText(scalarLabel);
m_ScalarOpacityFunctionCanvas->SetTitle(scalarLabel + " -> Opacity");
m_GradientOpacityCanvas->SetTitle(scalarLabel + "/Gradient -> Opacity");
m_ColorTransferFunctionCanvas->SetTitle(scalarLabel + " -> Color");
}
void QmitkTransferFunctionWidget::ShowScalarOpacityFunction(bool show)
{
m_ScalarOpacityWidget->setVisible(show);
}
void QmitkTransferFunctionWidget::ShowColorFunction(bool show)
{
m_ColorWidget->setVisible(show);
}
void QmitkTransferFunctionWidget::ShowGradientOpacityFunction(bool show)
{
m_GradientOpacityWidget->setVisible(show);
}
void QmitkTransferFunctionWidget::SetScalarOpacityFunctionEnabled(bool enable)
{
m_ScalarOpacityWidget->setEnabled(enable);
}
void QmitkTransferFunctionWidget::SetColorFunctionEnabled(bool enable)
{
m_ColorWidget->setEnabled(enable);
}
void QmitkTransferFunctionWidget::SetGradientOpacityFunctionEnabled(bool enable)
{
m_GradientOpacityWidget->setEnabled(enable);
}
void QmitkTransferFunctionWidget::SetDataNode(mitk::DataNode *node, mitk::TimeStepType timestep, const mitk::BaseRenderer *renderer)
{
if (node)
{
tfpToChange = dynamic_cast<mitk::TransferFunctionProperty *>(node->GetProperty("TransferFunction", renderer));
if (!tfpToChange)
{
if (!dynamic_cast<mitk::Image *>(node->GetData()))
{
MITK_WARN << "QmitkTransferFunctionWidget::SetDataNode called with non-image node";
goto turnOff;
}
node->SetProperty("TransferFunction", tfpToChange = mitk::TransferFunctionProperty::New());
}
mitk::TransferFunction::Pointer tf = tfpToChange->GetValue();
if (mitk::Image *data = dynamic_cast<mitk::Image *>(node->GetData()))
{
mitk::SimpleHistogram *h = nullptr;
if (data->GetTimeSteps() > 1)
{
if (!data->GetTimeGeometry()->IsValidTimeStep(timestep))
{
return;
}
mitk::ImageTimeSelector::Pointer timeselector = mitk::ImageTimeSelector::New();
timeselector->SetInput(data);
timeselector->SetTimeNr(timestep);
timeselector->UpdateLargestPossibleRegion();
auto inputImage = timeselector->GetOutput();
h = histogramCache[inputImage];
}
else
{
h = histogramCache[data];
}
- m_RangeSliderMin = h->GetMin();
- m_RangeSliderMax = h->GetMax();
+ auto rangeSliderMin = h->GetMin();
+ auto rangeSliderMax = h->GetMax();
UpdateStepSize();
m_RangeSlider->blockSignals(true);
- m_RangeSlider->setMinimum(m_RangeSliderMin);
- m_RangeSlider->setMaximum(m_RangeSliderMax);
- m_RangeSlider->setMinimumValue(m_RangeSliderMin);
- m_RangeSlider->setMaximumValue(m_RangeSliderMax);
+ m_RangeSlider->setMinimum(rangeSliderMin);
+ m_RangeSlider->setMaximum(rangeSliderMax);
+ m_RangeSlider->setMinimumValue(rangeSliderMin);
+ m_RangeSlider->setMaximumValue(rangeSliderMax);
m_RangeSlider->blockSignals(false);
m_ScalarOpacityFunctionCanvas->SetHistogram(h);
m_GradientOpacityCanvas->SetHistogram(h);
m_ColorTransferFunctionCanvas->SetHistogram(h);
}
OnUpdateCanvas();
return;
}
turnOff:
m_ScalarOpacityFunctionCanvas->setEnabled(false);
m_ScalarOpacityFunctionCanvas->SetHistogram(nullptr);
m_GradientOpacityCanvas->setEnabled(false);
m_GradientOpacityCanvas->SetHistogram(nullptr);
m_ColorTransferFunctionCanvas->setEnabled(false);
m_ColorTransferFunctionCanvas->SetHistogram(nullptr);
tfpToChange = nullptr;
}
void QmitkTransferFunctionWidget::OnUpdateCanvas()
{
if (tfpToChange.IsNull())
return;
mitk::TransferFunction::Pointer tf = tfpToChange->GetValue();
if (tf.IsNull())
return;
m_ScalarOpacityFunctionCanvas->SetPiecewiseFunction(tf->GetScalarOpacityFunction());
m_GradientOpacityCanvas->SetPiecewiseFunction(tf->GetGradientOpacityFunction());
m_ColorTransferFunctionCanvas->SetColorTransferFunction(tf->GetColorTransferFunction());
UpdateRanges();
m_ScalarOpacityFunctionCanvas->update();
m_GradientOpacityCanvas->update();
m_ColorTransferFunctionCanvas->update();
}
void QmitkTransferFunctionWidget::SetXValueScalar(const QString text)
{
if (!text.endsWith("."))
{
m_ScalarOpacityFunctionCanvas->SetX(text.toFloat());
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkTransferFunctionWidget::SetYValueScalar(const QString text)
{
if (!text.endsWith("."))
{
m_ScalarOpacityFunctionCanvas->SetY(text.toFloat());
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkTransferFunctionWidget::SetXValueGradient(const QString text)
{
if (!text.endsWith("."))
{
m_GradientOpacityCanvas->SetX(text.toFloat());
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkTransferFunctionWidget::SetYValueGradient(const QString text)
{
if (!text.endsWith("."))
{
m_GradientOpacityCanvas->SetY(text.toFloat());
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkTransferFunctionWidget::SetXValueColor(const QString text)
{
if (!text.endsWith("."))
{
m_ColorTransferFunctionCanvas->SetX(text.toFloat());
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkTransferFunctionWidget::UpdateRanges()
{
double lower = m_RangeSlider->minimumValue();
double upper = m_RangeSlider->maximumValue();
m_ScalarOpacityFunctionCanvas->SetMin(lower);
m_ScalarOpacityFunctionCanvas->SetMax(upper);
m_GradientOpacityCanvas->SetMin(lower);
m_GradientOpacityCanvas->SetMax(upper);
m_ColorTransferFunctionCanvas->SetMin(lower);
m_ColorTransferFunctionCanvas->SetMax(upper);
}
void QmitkTransferFunctionWidget::UpdateStepSize()
{
- double step = (m_RangeSliderMax - m_RangeSliderMin) / 1000.;
+ double step = (m_RangeSlider->maximum() - m_RangeSlider->minimum()) / 1000.;
m_RangeSlider->setSingleStep(step);
}
void QmitkTransferFunctionWidget::OnSpanChanged(double, double)
{
UpdateRanges();
m_GradientOpacityCanvas->update();
m_ColorTransferFunctionCanvas->update();
m_ScalarOpacityFunctionCanvas->update();
}
void QmitkTransferFunctionWidget::OnResetSlider()
{
m_RangeSlider->blockSignals(true);
- m_RangeSlider->setMaximumValue(m_RangeSliderMax);
- m_RangeSlider->setMinimumValue(m_RangeSliderMin);
+ m_RangeSlider->setMaximumValue(m_RangeSlider->maximum());
+ m_RangeSlider->setMinimumValue(m_RangeSlider->minimum());
m_RangeSlider->blockSignals(false);
UpdateRanges();
m_GradientOpacityCanvas->update();
m_ColorTransferFunctionCanvas->update();
m_ScalarOpacityFunctionCanvas->update();
}
diff --git a/Modules/QtWidgetsExt/src/QmitkVideoBackground.cpp b/Modules/QtWidgetsExt/src/QmitkVideoBackground.cpp
index cade3a0417..ab92c8bf42 100644
--- a/Modules/QtWidgetsExt/src/QmitkVideoBackground.cpp
+++ b/Modules/QtWidgetsExt/src/QmitkVideoBackground.cpp
@@ -1,288 +1,288 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkVideoBackground.h"
// MITK includes
#include "mitkRenderingManager.h"
#include "mitkVtkLayerController.h"
// QT includes
#include <QTimer>
// itk includes
#include <itkCommand.h>
// VTK includes
#include <vtkCallbackCommand.h>
#include <vtkCamera.h>
#include <vtkCommand.h>
#include <vtkCommand.h>
#include <vtkImageActor.h>
#include <vtkImageImport.h>
#include <vtkMapper.h>
#include <vtkObjectFactory.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkSystemIncludes.h>
QmitkVideoBackground::QmitkVideoBackground(QObject *parent)
: QObject(parent), m_QTimer(new QTimer(this)), m_VideoSource(nullptr), m_VideoSourceObserverTag(0)
{
this->ResetVideoBackground();
}
QmitkVideoBackground::QmitkVideoBackground(mitk::VideoSource *v, int)
: QObject(nullptr), m_QTimer(new QTimer(this)), m_VideoSource(nullptr), m_VideoSourceObserverTag(0)
{
this->SetVideoSource(v);
this->ResetVideoBackground();
}
void QmitkVideoBackground::ResetVideoBackground()
{
m_QTimer->setInterval(25);
connect(m_QTimer, SIGNAL(timeout()), SLOT(UpdateVideo()));
m_renderWindowVectorInfo.clear();
}
QmitkVideoBackground::~QmitkVideoBackground()
{
this->Disable();
}
void QmitkVideoBackground::AddRenderWindow(vtkRenderWindow *renderWindow)
{
if (!renderWindow || !m_VideoSource)
{
MITK_WARN << "No Renderwindow or VideoSource set!";
return;
}
this->RemoveRenderWindow(renderWindow);
vtkRenderer *videoRenderer = vtkRenderer::New();
vtkImageActor *videoActor = vtkImageActor::New();
vtkImageImport *videoImport = vtkImageImport::New();
videoImport->SetDataScalarTypeToUnsignedChar();
videoImport->SetNumberOfScalarComponents(3);
if (m_VideoSource->GetImageWidth() == 0)
m_VideoSource->FetchFrame();
videoImport->SetWholeExtent(0, m_VideoSource->GetImageWidth() - 1, 0, m_VideoSource->GetImageHeight() - 1, 0, 1 - 1);
videoImport->SetDataExtentToWholeExtent();
VideoBackgroundVectorInfo v;
v.renWin = renderWindow;
v.videoRenderer = videoRenderer;
v.videoActor = videoActor;
v.videoImport = videoImport;
// callback for the deletion of the renderwindow
vtkSmartPointer<vtkCallbackCommand> deleteCallback = vtkSmartPointer<vtkCallbackCommand>::New();
deleteCallback->SetCallback(QmitkVideoBackground::OnRenderWindowDelete);
deleteCallback->SetClientData(this);
v.renderWindowObserverTag = renderWindow->AddObserver(vtkCommand::DeleteEvent, deleteCallback);
m_renderWindowVectorInfo.push_back(v);
// completes the initialization
this->Modified();
}
void QmitkVideoBackground::RemoveRenderWindow(vtkRenderWindow *renderWindow)
{
this->RemoveRenderWindow(renderWindow, true);
}
void QmitkVideoBackground::RemoveRenderWindow(vtkRenderWindow *renderWindow, bool removeObserver)
{
// search for renderwindow and remove it
for (auto it = m_renderWindowVectorInfo.begin(); it != m_renderWindowVectorInfo.end(); it++)
{
if ((*it).renWin == renderWindow)
{
mitk::VtkLayerController *layerController = mitk::VtkLayerController::GetInstance((*it).renWin);
- // unregister video backround renderer from renderwindow
+ // unregister video background renderer from renderwindow
if (layerController)
layerController->RemoveRenderer((*it).videoRenderer);
(*it).videoRenderer->Delete();
(*it).videoActor->Delete();
(*it).videoImport->Delete();
// remove listener
if (removeObserver)
renderWindow->RemoveObserver((*it).renderWindowObserverTag);
m_renderWindowVectorInfo.erase(it);
break;
}
}
}
bool QmitkVideoBackground::IsRenderWindowIncluded(vtkRenderWindow *renderWindow)
{
for (auto it = m_renderWindowVectorInfo.begin(); it != m_renderWindowVectorInfo.end(); it++)
{
if ((*it).renWin == renderWindow)
return true;
}
return false;
}
void QmitkVideoBackground::Pause()
{
m_QTimer->stop();
}
void QmitkVideoBackground::Resume()
{
m_QTimer->start();
}
/**
* Enables drawing of the color Video background.
* If you want to disable it, call the Disable() function.
*/
void QmitkVideoBackground::Enable()
{
UpdateVideo();
Modified();
m_QTimer->start();
}
/**
* Disables drawing of the color Video background.
* If you want to enable it, call the Enable() function.
*/
void QmitkVideoBackground::Disable()
{
if (this->IsEnabled())
{
mitk::VtkLayerController *layerController = nullptr;
for (auto it = m_renderWindowVectorInfo.begin(); it != m_renderWindowVectorInfo.end(); it++)
{
layerController = mitk::VtkLayerController::GetInstance((*it).renWin);
if (layerController)
layerController->RemoveRenderer((*it).videoRenderer);
}
m_QTimer->stop();
}
}
bool QmitkVideoBackground::IsEnabled()
{
return m_QTimer->isActive();
}
void QmitkVideoBackground::UpdateVideo()
{
if (m_renderWindowVectorInfo.size() > 0)
{
unsigned char *src = nullptr;
try
{
src = m_VideoSource->GetVideoTexture();
}
catch (const std::logic_error &error)
{
MITK_DEBUG << error.what();
emit EndOfVideoSourceReached(m_VideoSource);
return;
}
if (src)
{
for (auto it = m_renderWindowVectorInfo.begin(); it != m_renderWindowVectorInfo.end(); it++)
{
(*it).videoImport->SetImportVoidPointer(src);
(*it).videoImport->Modified();
(*it).videoImport->Update();
mitk::RenderingManager::GetInstance()->RequestUpdate((*it).renWin);
}
emit NewFrameAvailable(m_VideoSource);
}
else
MITK_WARN << "No video texture available";
}
}
void QmitkVideoBackground::Modified()
{
- // ensures registration of video backrounds in each renderwindow
+ // ensures registration of video backgrounds in each renderwindow
for (auto it = m_renderWindowVectorInfo.begin(); it != m_renderWindowVectorInfo.end(); it++)
{
(*it).videoImport->Update();
(*it).videoActor->SetInputData((*it).videoImport->GetOutput());
(*it).videoRenderer->AddActor2D((*it).videoActor);
(*it).videoRenderer->ResetCamera();
(*it).videoRenderer->InteractiveOff();
(*it).videoRenderer->GetActiveCamera()->ParallelProjectionOn();
(*it).videoRenderer->GetActiveCamera()->SetParallelScale(m_VideoSource->GetImageHeight() / 2);
mitk::VtkLayerController *layerController = mitk::VtkLayerController::GetInstance((*it).renWin);
if (layerController && !layerController->IsRendererInserted((*it).videoRenderer))
layerController->InsertBackgroundRenderer((*it).videoRenderer, true);
}
}
void QmitkVideoBackground::SetVideoSource(mitk::VideoSource *videoSource)
{
if (m_VideoSource == videoSource)
return;
if (m_VideoSource)
m_VideoSource->RemoveObserver(m_VideoSourceObserverTag);
m_VideoSource = videoSource;
if (m_VideoSource)
{
itk::MemberCommand<QmitkVideoBackground>::Pointer _ModifiedCommand =
itk::MemberCommand<QmitkVideoBackground>::New();
_ModifiedCommand->SetCallbackFunction(this, &QmitkVideoBackground::OnVideoSourceDelete);
m_VideoSourceObserverTag = m_VideoSource->AddObserver(itk::DeleteEvent(), _ModifiedCommand);
}
}
void QmitkVideoBackground::SetTimerDelay(int ms)
{
m_QTimer->setInterval(ms);
}
mitk::VideoSource *QmitkVideoBackground::GetVideoSource()
{
return m_VideoSource;
}
int QmitkVideoBackground::GetTimerDelay()
{
return m_QTimer->interval();
}
void QmitkVideoBackground::OnVideoSourceDelete(const itk::Object *, const itk::EventObject &)
{
this->Disable(); // will only disable if enabled
m_VideoSource = nullptr;
}
void QmitkVideoBackground::OnRenderWindowDelete(vtkObject *object, unsigned long, void *clientdata, void *)
{
QmitkVideoBackground *instance = static_cast<QmitkVideoBackground *>(clientdata);
instance->RemoveRenderWindow(static_cast<vtkRenderWindow *>(object), false);
}
diff --git a/Modules/REST/documentation/REST.dox b/Modules/REST/documentation/REST.dox
index 93cfd5a122..9f49f002a1 100644
--- a/Modules/REST/documentation/REST.dox
+++ b/Modules/REST/documentation/REST.dox
@@ -1,194 +1,194 @@
/**
\page RESTModule REST Module
\tableofcontents
\section REST_brief Description
The MITK REST Module is able to manage REST requests. The main class is the RESTManager.
It is a MicroServices which can be accessed via
\code{.cpp}
auto *context = us::GetModuleContext();
auto managerRef = context->GetServiceReference<IRESTManager>();
if (managerRef)
{
auto managerService = context->GetService(managerRef);
if (managerService)
{
//call the function you need from the service
}
}
\endcode
\subsection REST_Technical Technical background
- The module uses the <a href="https://github.com/Microsoft/cpprestsdk">Microsoft C++ REST SDK</a> for REST mechanisms as well as JSON convertion and asynchronic programming.
+ The module uses the <a href="https://github.com/Microsoft/cpprestsdk">Microsoft C++ REST SDK</a> for REST mechanisms as well as JSON conversion and asynchronic programming.
\section Use_REST How to use the REST Module
You can use the REST module from two different perspectives in MITK:
<ol>
<li> The Server view (receive requests from clients)
<li> The Client view (send requests to servers)
</ol>
The following sections will give you an introduction on how to use which of those roles:
\subsection Server_Use Use from a Server perspective
To act as a server, you need to implement the <code>IRESTObserver</code>, which has a <code>Notify()</code> method that has to be implemented.
In this <code>Notify()</code> method you specify how you want to react to incoming requests and with which data you want to respond to the requests.
You can then start listening for requests from clients as shown below:
\code{.cpp}
auto *context = us::GetModuleContext();
auto managerRef = context->GetServiceReference<IRESTManager>();
if (managerRef)
{
auto managerService = context->GetService(managerRef);
if (managerService)
{
managerService->ReceiveRequests(uri /*specify your uri which you want to receive requests for*/, this);
}
}
\endcode
If a client sends a request, the Notify method is called and a response is sent. By now, only GET-requests from clients are supported.
If you want to stop listening for requests you can do this by calling
\code{.cpp}
auto *context = us::GetModuleContext();
auto managerRef = context->GetServiceReference<IRESTManager>();
if (managerRef)
{
auto managerService = context->GetService(managerRef);
if (managerService)
{
managerService->HandleDeleteObserver(this, uri);
}
}
\endcode
You don't have to specify a uri in the HandleDeleteObserver method, if you only call <code>managerService->HandleDeleteObserver(this);</code>, all uris you receive requests for are deleted and you aren't listening to any requests anymore.
\subsection Client_Use Use from a Client perspective
The following example shows how to send requests from a client perspective:
\code{.cpp}
//Get the microservice
auto *context = us::ModuleRegistry::GetModule(1)->GetModuleContext();
auto managerRef = context->GetServiceReference<mitk::IRESTManager>();
if (managerRef)
{
auto managerService = context->GetService(managerRef);
if (managerService)
{
//Call the send request method which starts the actual request
managerService
->SendRequest(U("https://jsonplaceholder.typicode.com/posts/1"))
.then([=](pplx::task<web::json::value> resultTask)/*It is important to use task-based continuation*/ {
try
{
//Get the result of the request
//This will throw an exception if the ascendent task threw an exception (e.g. invalid URI)
web::json::value result = resultTask.get();
//Do something with the result (e.g. convert it to a QString to update an UI element)
utility::string_t stringT = result.to_string();
std::string stringStd(stringT.begin(), stringT.end());
QString stringQ = QString::fromStdString(stringStd);
//Note: if you want to update your UI, do this by using signals and slots.
//The UI can't be updated from a Thread different to the Qt main thread
emit UpdateLabel(stringQ);
}
catch (const mitk::Exception &exception)
{
//Exceptions from ascendent tasks are catched here
MITK_ERROR << exception.what();
return;
}
});
}
}
\endcode
The steps you need to make are the following:
<ol>
<li> Get the microservice. You can get the microservice via the module context. If you want to use the microservice within a plug-in, you need to get the module context from the <code>us::ModuleRegistry</code>.
<li> Call the <code>SendRequest</code> method. This will start the request itself and is performed asynchronously. As soon as the response is sent by the server, the <code>.then(...)</code> block is executed.
<li> Choose parameters for <code>.then(...)</code> block. For exception handling, it is important to choose <code>pplx::task<web::json::value> </code>. This is a task-based continuation.
For more information, visit https://docs.microsoft.com/en-us/cpp/parallel/concrt/exception-handling-in-the-concurrency-runtime?view=vs-2017.
<li> Get the result of the request. You can get the JSON-value of the result by callint <code>.get()</code>. At this point, an exception is thrown if something in the previous tasks threw an exception.
<li> Do something with the result.
\note If you want to modify GUI elements within the <code>.then(...)</code> block, you need to do this by using signals and slots because GUI elements can only be modified by th Qt Main Thread.
For more information, visit https://doc.qt.io/qt-6/thread-basics.html#gui-thread-and-worker-thread
<li> Exception handling. Here you can define the behaviour if an exception is thrown, exceptions from ascendent tasks are also catched here.
</ol>
Code, which is followed by this codeblock shown above will be performed asynchronously while waiting for the result.
Besides Get-Requests, you can also perform Put or Post requests by specifying a <code>RequestType</code> in the <code>SendRequest</code> method.
The following example shows, how you can perform multiple tasks, encapsulated to one joined task. The steps are based on the example for one request and only the specific steps for encapsulation are described.
\code{.cpp}
//Get the microservice
//Get microservice
auto *context = us::ModuleRegistry::GetModule(1)->GetModuleContext();
auto managerRef = context->GetServiceReference<mitk::IRESTManager>();
if (managerRef)
{
auto managerService = context->GetService(managerRef);
if (managerService)
{
//Create multiple tasks e.g. as shown below
std::vector<pplx::task<void>> tasks;
for (int i = 0; i < 20; i++)
{
pplx::task<void> singleTask = managerService->SendRequest(L"https://jsonplaceholder.typicode.com/posts/1")
.then([=](pplx::task<web::json::value> resultTask) {
//Do something when a single task is done
try
{
resultTask.get();
emit UpdateProgressBar();
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
});
tasks.emplace_back(singleTask);
}
//Create a joinTask which includes all tasks you've created
auto joinTask = pplx::when_all(begin(tasks), end(tasks));
//Run asynchonously
joinTask.then([=](pplx::task<void> resultTask) {
//Do something when all tasks are finished
try
{
resultTask.get();
emit UpdateLabel("All tasks finished");
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
});
}
\endcode
The steps you need to make are the following:
<ol>
<li> Get the microservice. See example above.
<li> Create multiple tasks. In this example, 20 identical tasks are created and are saved into a vector. In general, it is possible to place any tasks in that vector.
<li> Do something when a single task is done. Here, an action is performed if a single tasks is finished. In this example, a progress bar is loaded by a specific number of percent.
<li> Create a joinTask. Here, all small tasks are encapsulated in one big task.
<li> Run joinTask asynchonously. The <code>then(...)</code> of the joinTask is performed when all single tasks are finished.
<li> Do something when all tasks are finished. The handling of the end of a joinTask is equivalent to the end of a single tasks.
</ol>
*/
diff --git a/Modules/REST/include/mitkIRESTManager.h b/Modules/REST/include/mitkIRESTManager.h
index a40a9432fb..609779776e 100644
--- a/Modules/REST/include/mitkIRESTManager.h
+++ b/Modules/REST/include/mitkIRESTManager.h
@@ -1,133 +1,133 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkIRESTManager_h
#define mitkIRESTManager_h
#include <mitkServiceInterface.h>
#include <MitkRESTExports.h>
#include <mitkRESTUtil.h>
#include <cpprest/json.h>
#include <cpprest/uri.h>
#include <cpprest/http_client.h>
namespace mitk
{
class IRESTObserver;
class RESTServer;
/**
* @class IRESTManager
* @brief This is a microservice interface for managing REST requests.
*/
class MITKREST_EXPORT IRESTManager
{
public:
virtual ~IRESTManager();
/**
* @brief request type for client requests by calling SendRequest
*/
enum class RequestType
{
Get,
Post,
Put
};
/**
* @brief Executes a HTTP request in the mitkRESTClient class
*
- * @throw mitk::Exception if RequestType is not suported
+ * @throw mitk::Exception if RequestType is not supported
* @param uri defines the URI the request is send to
* @param type the RequestType of the HTTP request (optional)
* @param headers the headers for the request (optional)
* @return task to wait for
*/
virtual pplx::task<web::json::value> SendRequest(
const web::uri &uri,
const RequestType &type = RequestType::Get,
const std::map<utility::string_t, utility::string_t> headers = {}) = 0;
/**
* @brief Executes a HTTP request in the mitkRESTClient class
*
* @param uri defines the URI the request is send to
* @param type the RequestType of the HTTP request (optional)
* @param body the body for the request (optional)
* @param headers the headers for the request (optional)
* @param filePath the file path to store the request to (optional)
* @return task to wait for
*/
virtual pplx::task<web::json::value> SendJSONRequest(
const web::uri &uri,
const RequestType &type = RequestType::Get,
const web::json::value *body = nullptr,
const std::map<utility::string_t, utility::string_t> headers = {},
const utility::string_t &filePath = {}
) = 0;
/**
* @brief Executes a HTTP request in the mitkRESTClient class
*
* @param uri defines the URI the request is send to
* @param type the RequestType of the HTTP request (optional)
* @param body the body for the request (optional)
* @param headers the headers for the request (optional)
* @return task to wait for
*/
virtual pplx::task<web::json::value> SendBinaryRequest(const web::uri &uri,
const RequestType &type = RequestType::Get,
const std::vector<unsigned char> *body = {},
const std::map<utility::string_t, utility::string_t> headers = {}) = 0;
/**
* @brief starts listening for requests if there isn't another observer listening and the port is free
*
* @param uri defines the URI for which incoming requests should be send to the observer
* @param observer the observer which handles the incoming requests
*/
virtual void ReceiveRequest(const web::uri &uri, IRESTObserver *observer) = 0;
/**
* @brief Handles incoming requests by notifying the observer which should receive it
*
* @param uri defines the URI of the request
* @param body the body of the request
* @param method the http method of the request
* @param headers the http headers of the request
* @return the response
*/
virtual web::http::http_response Handle(const web::uri &uri,
const web::json::value &body,
const web::http::method &method,
const mitk::RESTUtil::ParamMap &headers) = 0;
/**
* @brief Handles the deletion of an observer for all or a specific uri
*
* @param observer the observer which shouldn't receive requests anymore
* @param uri the uri for which the observer doesn't handle requests anymore (optional)
*/
virtual void HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri = {}) = 0;
virtual const std::map<int, RESTServer *>& GetServerMap() = 0;
virtual const std::map<std::pair<int, utility::string_t>, IRESTObserver *>& GetObservers() = 0;
};
}
MITK_DECLARE_SERVICE_INTERFACE(mitk::IRESTManager, "org.mitk.IRESTManager")
#endif
diff --git a/Modules/REST/test/CMakeLists.txt b/Modules/REST/test/CMakeLists.txt
index e42e23ad3f..2e7b24b2f7 100644
--- a/Modules/REST/test/CMakeLists.txt
+++ b/Modules/REST/test/CMakeLists.txt
@@ -1,2 +1,2 @@
mitk_create_module_tests()
-set_tests_properties(mitkRESTClientTest mitkRESTServerTest PROPERTIES RUN_SERIAL TRUE)
+set_tests_properties(mitkRESTClientTest mitkRESTServerTest mitkRESTServerHttpLibTest PROPERTIES RUN_SERIAL TRUE)
diff --git a/Modules/REST/test/files.cmake b/Modules/REST/test/files.cmake
index 7299b4bcce..57628aaee3 100644
--- a/Modules/REST/test/files.cmake
+++ b/Modules/REST/test/files.cmake
@@ -1,4 +1,5 @@
set(MODULE_TESTS
+ mitkRESTServerHttpLibTest.cpp
mitkRESTClientTest.cpp
mitkRESTServerTest.cpp
)
\ No newline at end of file
diff --git a/Modules/REST/test/mitkRESTClientTest.cpp b/Modules/REST/test/mitkRESTClientTest.cpp
index cad9e820a8..7acdbc44b3 100644
--- a/Modules/REST/test/mitkRESTClientTest.cpp
+++ b/Modules/REST/test/mitkRESTClientTest.cpp
@@ -1,264 +1,264 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkIRESTManager.h>
#include <mitkIRESTObserver.h>
#include <mitkRESTClient.h>
#include <mitkRESTUtil.h>
#include <usGetModuleContext.h>
#include <usModuleContext.h>
#include <usServiceReference.h>
#include <vtkDebugLeaks.h>
#include <atomic>
class mitkRESTClientTestSuite : public mitk::TestFixture, mitk::IRESTObserver
{
CPPUNIT_TEST_SUITE(mitkRESTClientTestSuite);
// MITK_TEST(GetRequestValidURI_ReturnsExpectedJSON); GET requests do not support content yet?
MITK_TEST(MultipleGetRequestValidURI_AllTasksFinish);
// MITK_TEST(PutRequestValidURI_ReturnsExpectedJSON); Does not work reliably on dart clients
// MITK_TEST(PostRequestValidURI_ReturnsExpectedJSON); -- " --
MITK_TEST(GetRequestInvalidURI_ThrowsException);
MITK_TEST(PutRequestInvalidURI_ThrowsException);
MITK_TEST(PostRequestInvalidURI_ThrowsException);
CPPUNIT_TEST_SUITE_END();
public:
mitk::IRESTManager *m_Service;
web::json::value m_Data;
web::http::http_response Notify(const web::uri &,
const web::json::value &,
const web::http::method &,
const mitk::RESTUtil::ParamMap &) override
{
auto response = web::http::http_response();
response.set_body(m_Data);
response.set_status_code(web::http::status_codes::OK);
return response;
}
/**
- * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used
+ * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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_Data = web::json::value();
m_Data[U("userId")] = web::json::value(1);
m_Data[U("id")] = web::json::value(1);
m_Data[U("title")] = web::json::value(U("this is a title"));
m_Data[U("body")] = web::json::value(U("this is a body"));
us::ServiceReference<mitk::IRESTManager> serviceRef =
us::GetModuleContext()->GetServiceReference<mitk::IRESTManager>();
if (serviceRef)
{
m_Service = us::GetModuleContext()->GetService(serviceRef);
}
if (!m_Service)
{
CPPUNIT_FAIL("Getting Service in setUp() failed");
}
m_Service->ReceiveRequest(U("http://localhost:8080/clienttest"), this);
}
void tearDown() override { m_Service->HandleDeleteObserver(this); }
void GetRequestValidURI_ReturnsExpectedJSON()
{
web::json::value result;
m_Service->SendRequest(U("http://localhost:8080/clienttest"))
.then([&](pplx::task<web::json::value> resultTask) {
try
{
result = resultTask.get();
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
})
.wait();
CPPUNIT_ASSERT_MESSAGE("Result is the expected JSON value", result == m_Data);
}
void MultipleGetRequestValidURI_AllTasksFinish()
{
std::atomic<int> count {0};
// Create multiple tasks e.g. as shown below
std::vector<pplx::task<void>> tasks;
for (int i = 0; i < 20; ++i)
{
pplx::task<void> singleTask = m_Service->SendRequest(U("http://localhost:8080/clienttest"))
.then([&](pplx::task<web::json::value> resultTask) {
// Do something when a single task is done
try
{
resultTask.get();
count += 1;
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
});
tasks.emplace_back(singleTask);
}
// Create a joinTask which includes all tasks you've created
auto joinTask = pplx::when_all(begin(tasks), end(tasks));
// Run asynchonously
joinTask
.then([&](pplx::task<void> resultTask) {
// Do something when all tasks are finished
try
{
resultTask.get();
count += 1;
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
})
.wait();
CPPUNIT_ASSERT_MESSAGE("Multiple Requests", 21 == count);
}
void PutRequestValidURI_ReturnsExpectedJSON()
{
// optional: link might get invalid or content is changed
web::json::value result;
m_Service
->SendJSONRequest(
U("https://jsonplaceholder.typicode.com/posts/1"), mitk::IRESTManager::RequestType::Put)
.then([&](pplx::task<web::json::value> resultTask) {
try
{
result = resultTask.get();
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
})
.wait();
CPPUNIT_ASSERT_MESSAGE(
"Result is the expected JSON value, check if the link is still valid since this is an optional test",
result == m_Data);
}
void PostRequestValidURI_ReturnsExpectedJSON()
{
// optional: link might get invalid or content is changed
web::json::value result;
web::json::value data;
data[U("userId")] = m_Data[U("userId")];
data[U("title")] = m_Data[U("title")];
data[U("body")] = m_Data[U("body")];
m_Service
->SendJSONRequest(U("https://jsonplaceholder.typicode.com/posts"), mitk::IRESTManager::RequestType::Post, &data)
.then([&](pplx::task<web::json::value> resultTask) {
try
{
result = resultTask.get();
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
})
.wait();
data[U("id")] = web::json::value(101);
CPPUNIT_ASSERT_MESSAGE(
"Result is the expected JSON value, check if the link is still valid since this is an optional test",
result == data);
}
void PostRequestHeaders_Success()
{
mitk::RESTUtil::ParamMap headers;
headers.insert(mitk::RESTUtil::ParamMap::value_type(
U("Content-Type"), U("multipart/related; type=\"application/dicom\"; boundary=boundary")));
m_Service->SendRequest(U("http://localhost:8080/clienttest")).then([&](pplx::task<web::json::value> resultTask) {
// Do something when a single task is done
try
{
resultTask.get();
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
});
}
void GetException()
{
// Method which makes a get request to an invalid uri
web::json::value result;
m_Service->SendRequest(U("http://localhost:1234/invalid"))
.then([&](pplx::task<web::json::value> resultTask) { result = resultTask.get(); })
.wait();
}
void GetRequestInvalidURI_ThrowsException() { CPPUNIT_ASSERT_THROW(GetException(), mitk::Exception); }
void PutException()
{
// Method which makes a put request to an invalid uri
web::json::value result;
m_Service->SendJSONRequest(U("http://localhost:1234/invalid"), mitk::IRESTManager::RequestType::Put, &m_Data)
.then([&](pplx::task<web::json::value> resultTask) { result = resultTask.get(); })
.wait();
}
void PutRequestInvalidURI_ThrowsException() { CPPUNIT_ASSERT_THROW(PutException(), mitk::Exception); }
void PostException()
{
// Method which makes a post request to an invalid uri
web::json::value result;
m_Service->SendJSONRequest(U("http://localhost:1234/invalid"), mitk::IRESTManager::RequestType::Post, &m_Data)
.then([&](pplx::task<web::json::value> resultTask) { result = resultTask.get(); })
.wait();
}
void PostRequestInvalidURI_ThrowsException() { CPPUNIT_ASSERT_THROW(PostException(), mitk::Exception); }
};
MITK_TEST_SUITE_REGISTRATION(mitkRESTClient)
diff --git a/Modules/REST/test/mitkRESTServerHttpLibTest.cpp b/Modules/REST/test/mitkRESTServerHttpLibTest.cpp
new file mode 100644
index 0000000000..8d0f63f3e2
--- /dev/null
+++ b/Modules/REST/test/mitkRESTServerHttpLibTest.cpp
@@ -0,0 +1,106 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+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 <future>
+#include <httplib.h>
+#include <map>
+#include <mitkTestFixture.h>
+#include <mitkTestingMacros.h>
+#include <nlohmann/json.hpp>
+#include <thread>
+#include <chrono>
+
+using namespace std::chrono_literals;
+
+class mitkRESTServerHttpLibTestSuite : public mitk::TestFixture
+{
+ CPPUNIT_TEST_SUITE(mitkRESTServerHttpLibTestSuite);
+ MITK_TEST(OpenListener_Succeed);
+ MITK_TEST(OpenListenerGetRequestSamePath_ReturnExpectedJSON);
+ CPPUNIT_TEST_SUITE_END();
+ std::vector<std::future<void>> m_ft;
+
+public:
+ void OpenListener_Succeed()
+ {
+ httplib::Server svr;
+ svr.Get("/get",
+ [](const httplib::Request &, httplib::Response &res)
+ { res.set_content("Hello World from MITK!", "text/plain"); });
+ std::future<void> ft = std::async(std::launch::async, [&]() { svr.listen("localhost", 8080); });
+ while (!svr.is_running())
+ {
+ std::this_thread::sleep_for(100ms);
+ }
+ CPPUNIT_ASSERT_MESSAGE("Server is running", svr.is_running());
+ svr.stop();
+ }
+
+ void OpenListenerGetRequestSamePath_ReturnExpectedJSON()
+ {
+ httplib::Server svr;
+ std::map<size_t, std::string> msgdb;
+ msgdb[0] = "hello_MITK";
+
+ svr.Get(R"(/msg/(\d+))",
+ [&](const httplib::Request &req, httplib::Response &res)
+ {
+ auto n = req.matches[1];
+ nlohmann::json jRes;
+ try
+ {
+ jRes["id"] = std::stoi(n);
+ jRes["msg"] = msgdb[std::stoi(n)];
+
+ res.set_content(jRes.dump(), "application/json");
+ }
+ catch (const std::exception &)
+ {
+ res.set_content("Cannot find the requested message.", "text/plain");
+ }
+ });
+ std::future<void> ft = std::async(std::launch::async, [&]() { svr.listen("localhost", 8080); });
+ while (!svr.is_running())
+ {
+ std::this_thread::sleep_for(100ms);
+ }
+ httplib::Client cli("http://localhost:8080");
+ try
+ {
+ if (auto response = cli.Get("/msg/0"))
+ {
+ if (response->status == 200)
+ {
+ auto js = nlohmann::json::parse(response->body);
+ std::string msg = js["msg"];
+ CPPUNIT_ASSERT_MESSAGE("Result is the expected JSON value", msg == msgdb[0]);
+ }
+ else
+ {
+ CPPUNIT_ASSERT_MESSAGE("A connection cannot be established", false);
+ }
+ }
+ else
+ {
+ CPPUNIT_ASSERT_MESSAGE("A connection cannot be established", false);
+ }
+ }
+ catch (const std::exception &e)
+ {
+ svr.stop();
+ CPPUNIT_ASSERT_MESSAGE(e.what(), false);
+ }
+ svr.stop();
+ }
+};
+
+MITK_TEST_SUITE_REGISTRATION(mitkRESTServerHttpLib)
diff --git a/Modules/REST/test/mitkRESTServerTest.cpp b/Modules/REST/test/mitkRESTServerTest.cpp
index 1292d98b5f..22f1fe65cf 100644
--- a/Modules/REST/test/mitkRESTServerTest.cpp
+++ b/Modules/REST/test/mitkRESTServerTest.cpp
@@ -1,252 +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.
============================================================================*/
#ifdef _WIN32
#include <Windows.h>
#endif
#include <mitkTestFixture.h>
#include <mitkTestingMacros.h>
#include <mitkIRESTManager.h>
#include <mitkIRESTObserver.h>
#include <mitkRESTServer.h>
#include <usGetModuleContext.h>
#include <usModuleContext.h>
#include <usServiceReference.h>
#include <vtkDebugLeaks.h>
class mitkRESTServerTestSuite : public mitk::TestFixture, mitk::IRESTObserver
{
CPPUNIT_TEST_SUITE(mitkRESTServerTestSuite);
MITK_TEST(OpenListener_Succeed);
MITK_TEST(TwoListenerSameHostSamePort_OnlyOneOpened);
MITK_TEST(CloseListener_Succeed);
MITK_TEST(OpenMultipleListenerCloseOne_Succeed);
MITK_TEST(OpenMultipleListenerCloseAll_Succeed);
// MITK_TEST(OpenListenerGetRequestSamePath_ReturnExpectedJSON); GET requests do not support content yet?
MITK_TEST(CloseListener_NoRequestPossible);
MITK_TEST(OpenListenerGetRequestDifferentPath_ReturnNotFound);
MITK_TEST(OpenListenerCloseAndReopen_Succeed);
MITK_TEST(HandleHeader_Succeed);
CPPUNIT_TEST_SUITE_END();
public:
mitk::IRESTManager *m_Service;
web::json::value m_Data;
web::http::http_response Notify(const web::uri &,
const web::json::value &,
const web::http::method &,
const mitk::RESTUtil::ParamMap &headers) override
{
auto response = web::http::http_response();
response.set_body(m_Data);
mitk::RESTUtil::ParamMap::const_iterator contentTypePos = headers.find(U("Content-Type"));
if (contentTypePos != headers.end() && contentTypePos->second == U("awesome/type"))
{
m_Data[U("result")] = web::json::value(U("awesome/type"));
}
return response;
}
/**
- * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used
+ * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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_Data = web::json::value();
m_Data[U("userId")] = web::json::value(1);
m_Data[U("id")] = web::json::value(1);
m_Data[U("title")] = web::json::value(U("this is a title"));
m_Data[U("body")] = web::json::value(U("this is a body"));
auto serviceRef = us::GetModuleContext()->GetServiceReference<mitk::IRESTManager>();
if (serviceRef)
m_Service = us::GetModuleContext()->GetService(serviceRef);
if (!m_Service)
CPPUNIT_FAIL("Getting Service in setUp() failed");
}
void tearDown() override { m_Service->HandleDeleteObserver(this); }
void OpenListener_Succeed()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size());
}
void TwoListenerSameHostSamePort_OnlyOneOpened()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
m_Service->ReceiveRequest(U("http://localhost:8080/serverexample"), this);
CPPUNIT_ASSERT_MESSAGE("Open two listener with a different path,same host, same port, observer map size is two",
2 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open two listener with a different path,same host, same port, server map size is one",
1 == m_Service->GetServerMap().size());
}
void CloseListener_Succeed()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size());
m_Service->HandleDeleteObserver(this);
CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size());
}
void OpenMultipleListenerCloseOne_Succeed()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
m_Service->ReceiveRequest(U("http://localhost:8090/serverexample"), this);
CPPUNIT_ASSERT_MESSAGE("Open two listener, observer map size is two", 2 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open two listener, server map size is two", 2 == m_Service->GetServerMap().size());
m_Service->HandleDeleteObserver(this, U("http://localhost:8080/servertest"));
CPPUNIT_ASSERT_MESSAGE("Closed one of two listeners, observer map is size is one",
1 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Closed one of two listener, server map size is one", 1 == m_Service->GetServerMap().size());
}
void OpenMultipleListenerCloseAll_Succeed()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
m_Service->ReceiveRequest(U("http://localhost:8090/serverexample"), this);
CPPUNIT_ASSERT_MESSAGE("Open two listener, observer map size is two", 2 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open two listener, server map size is two", 2 == m_Service->GetServerMap().size());
m_Service->HandleDeleteObserver(this);
CPPUNIT_ASSERT_MESSAGE("Closed all listeners, observer map is empty", 0 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Closed all listeners, server map is empty", 0 == m_Service->GetServerMap().size());
}
void OpenListenerGetRequestSamePath_ReturnExpectedJSON()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
web::json::value result;
auto body = web::json::value();
m_Service->SendRequest(U("http://localhost:8080/servertest"))
.then([&](pplx::task<web::json::value> resultTask) {
try
{
result = resultTask.get();
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
})
.wait();
CPPUNIT_ASSERT_MESSAGE("Opened listener and send request to same uri, returned expected JSON", result == m_Data);
}
void RequestToClosedListener()
{
web::json::value result;
m_Service->SendRequest(U("http://localhost:8080/servertest"))
.then([&](pplx::task<web::json::value> resultTask) { result = resultTask.get(); })
.wait();
}
void CloseListener_NoRequestPossible()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size());
m_Service->HandleDeleteObserver(this);
CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size());
CPPUNIT_ASSERT_THROW(RequestToClosedListener(), mitk::Exception);
}
void RequestToDifferentPathNotFound()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
web::json::value result;
m_Service->SendRequest(U("http://localhost:8080/serverexample"))
.then([&](pplx::task<web::json::value> resultTask) { result = resultTask.get(); })
.wait();
}
void OpenListenerGetRequestDifferentPath_ReturnNotFound()
{
CPPUNIT_ASSERT_THROW(RequestToDifferentPathNotFound(), mitk::Exception);
}
void OpenListenerCloseAndReopen_Succeed()
{
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size());
m_Service->HandleDeleteObserver(this);
CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size());
m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this);
CPPUNIT_ASSERT_MESSAGE("Reopened listener, observer map size is one", 1 == m_Service->GetObservers().size());
CPPUNIT_ASSERT_MESSAGE("Reopened listener, server map size is one", 1 == m_Service->GetServerMap().size());
}
void HandleHeader_Succeed()
{
mitk::RESTUtil::ParamMap headers;
headers.insert(mitk::RESTUtil::ParamMap::value_type(U("Content-Type"), U("awesome/type")));
m_Service->SendRequest(U("http://localhost:8080/clienttest")).then([&](pplx::task<web::json::value> resultTask) {
// Do something when a single task is done
try
{
auto result = resultTask.get();
- CPPUNIT_ASSERT_MESSAGE("Sent Header is not successfull transfered to server",
+ CPPUNIT_ASSERT_MESSAGE("Sent Header is not successful transferred to server",
result[U("result")].as_string() == U("awesome/type"));
}
catch (const mitk::Exception &exception)
{
MITK_ERROR << exception.what();
return;
}
});
}
};
MITK_TEST_SUITE_REGISTRATION(mitkRESTServer)
diff --git a/Modules/RESTService/include/mitkRESTManager.h b/Modules/RESTService/include/mitkRESTManager.h
index 5ea1b5e457..2e62c12ea7 100644
--- a/Modules/RESTService/include/mitkRESTManager.h
+++ b/Modules/RESTService/include/mitkRESTManager.h
@@ -1,153 +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 mitkRESTManager_h
#define mitkRESTManager_h
#include <MitkRESTServiceExports.h>
#include <mitkIRESTManager.h>
#include <mitkRESTUtil.h>
namespace mitk
{
/**
* @class RESTManager
* @brief this is a microservice for managing REST-requests, used for non-qt applications.
*
* RESTManagerQt in the CppRestSdkQt module inherits from this class and is the equivalent microservice
* used for Qt applications.
*/
class MITKRESTSERVICE_EXPORT RESTManager : public IRESTManager
{
public:
RESTManager();
~RESTManager() override;
/**
* @brief Executes a HTTP request in the mitkRESTClient class
*
- * @throw mitk::Exception if RequestType is not suported
+ * @throw mitk::Exception if RequestType is not supported
* @param uri defines the URI the request is send to
* @param type the RequestType of the HTTP request (optional)
* @param headers the headers for the request (optional)
* @return task to wait for
*/
pplx::task<web::json::value> SendRequest(
const web::uri &uri,
const RequestType &type = RequestType::Get,
const std::map<utility::string_t, utility::string_t> headers = {}) override;
/**
* @brief Executes a HTTP request in the mitkRESTClient class
*
- * @throw mitk::Exception if RequestType is not suported
+ * @throw mitk::Exception if RequestType is not supported
* @param uri defines the URI the request is send to
* @param type the RequestType of the HTTP request (optional)
* @param body the body for the request (optional)
* @param headers the headers for the request (optional)
* @param filePath the file path to store the request to (optional)
* @return task to wait for
*/
pplx::task<web::json::value> SendJSONRequest(const web::uri &uri,
const RequestType &type = RequestType::Get,
const web::json::value *body = nullptr,
const std::map<utility::string_t, utility::string_t> headers = {},
const utility::string_t &filePath = {}) override;
/**
* @brief Executes a HTTP request in the mitkRESTClient class
*
- * @throw mitk::Exception if RequestType is not suported
+ * @throw mitk::Exception if RequestType is not supported
* @param uri defines the URI the request is send to
* @param type the RequestType of the HTTP request (optional)
* @param body the body for the request (optional)
* @param headers the headers for the request (optional)
* @return task to wait for
*/
pplx::task<web::json::value> SendBinaryRequest(
const web::uri &uri,
const RequestType &type = RequestType::Get,
const std::vector<unsigned char> *body = {},
const std::map<utility::string_t, utility::string_t> headers = {}) override;
/**
* @brief starts listening for requests if there isn't another observer listening and the port is free
*
* @param uri defines the URI for which incoming requests should be send to the observer
* @param observer the observer which handles the incoming requests
*/
void ReceiveRequest(const web::uri &uri, IRESTObserver *observer) override;
/**
* @brief Handles incoming requests by notifying the observer which should receive it
*
* @param uri defines the URI of the request
* @param body the body of the request
* @param method the http method of the request
* @param headers the http headers of the request
* @return the response
*/
web::http::http_response Handle(const web::uri &uri,
const web::json::value &body,
const web::http::method &method,
const mitk::RESTUtil::ParamMap &headers) override;
/**
* @brief Handles the deletion of an observer for all or a specific uri
*
* @param observer the observer which shouldn't receive requests anymore
* @param uri the uri for which the observer doesn't handle requests anymore (optional)
*/
void HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri = {}) override;
/**
* @brief internal use only
*/
const std::map<int, RESTServer *> &GetServerMap() override;
std::map<std::pair<int, utility::string_t>, IRESTObserver *> &GetObservers() override;
private:
/**
* @brief adds an observer if a port is free, called by ReceiveRequest method
*
* @param uri the uri which builds the key for the observer map
* @param observer the observer which is added
*/
void AddObserver(const web::uri &uri, IRESTObserver *observer);
/**
* @brief handles server management if there is already a server under a port, called by ReceiveRequest method
*
* @param uri the uri which which is requested to be added
* @param observer the observer which proceeds the request
*/
void RequestForATakenPort(const web::uri &uri, IRESTObserver *observer);
/**
* @brief deletes an observer, called by HandleDeleteObserver method
*
* @param it the iterator comparing the observers in HandleDeleteObserver method
* @return bool if there is another observer under the port
*/
bool DeleteObserver(std::map<std::pair<int, utility::string_t>, IRESTObserver *>::iterator &it);
void SetServerMap(const int port, RESTServer *server);
void DeleteFromServerMap(const int port);
void SetObservers(const std::pair<int, utility::string_t> key, IRESTObserver *observer);
std::map<int, RESTServer *> m_ServerMap; // Map with port server pairs
std::map<std::pair<int, utility::string_t>, IRESTObserver *> m_Observers; // Map with all observers
};
} // namespace mitk
#endif
diff --git a/Modules/RESTService/src/mitkRESTManager.cpp b/Modules/RESTService/src/mitkRESTManager.cpp
index 1f59480cd6..d619170369 100644
--- a/Modules/RESTService/src/mitkRESTManager.cpp
+++ b/Modules/RESTService/src/mitkRESTManager.cpp
@@ -1,258 +1,258 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkIRESTObserver.h>
#include <mitkRESTClient.h>
#include <mitkRESTManager.h>
#include <mitkRESTServer.h>
#include <mitkExceptionMacro.h>
#include <mitkLog.h>
mitk::RESTManager::RESTManager() {}
mitk::RESTManager::~RESTManager() {}
pplx::task<web::json::value> mitk::RESTManager::SendRequest(
const web::uri &uri, const RequestType &type, const std::map<utility::string_t, utility::string_t> headers)
{
pplx::task<web::json::value> answer;
auto client = new RESTClient;
switch (type)
{
case RequestType::Get:
answer = client->Get(uri, headers);
break;
default:
mitkThrow() << "Request Type not supported";
}
return answer;
}
pplx::task<web::json::value> mitk::RESTManager::SendBinaryRequest(
const web::uri &uri,
const RequestType &type,
const std::vector<unsigned char> *content,
const std::map<utility::string_t, utility::string_t> headers)
{
pplx::task<web::json::value> answer;
auto client = new RESTClient;
switch (type)
{
case RequestType::Post:
if (nullptr == content)
MITK_WARN << "Content for post is empty, this will create an empty resource";
answer = client->Post(uri, content, headers);
break;
default:
mitkThrow() << "Request Type not supported for binary data";
}
return answer;
}
pplx::task<web::json::value> mitk::RESTManager::SendJSONRequest(
const web::uri &uri,
const RequestType &type,
const web::json::value *content,
const std::map<utility::string_t, utility::string_t> headers,
const utility::string_t &filePath)
{
pplx::task<web::json::value> answer;
auto client = new RESTClient;
switch (type)
{
case RequestType::Get:
answer = !filePath.empty() ? client->Get(uri, filePath, headers) : client->Get(uri, headers);
break;
case RequestType::Post:
if (nullptr == content)
MITK_WARN << "Content for post is empty, this will create an empty resource";
answer = client->Post(uri, content, headers);
break;
case RequestType::Put:
if (nullptr == content)
- MITK_WARN << "Content for put is empty, this will empty the ressource";
+ MITK_WARN << "Content for put is empty, this will empty the resource";
answer = client->Put(uri, content);
break;
default:
mitkThrow() << "Request Type not supported";
}
return answer;
}
void mitk::RESTManager::ReceiveRequest(const web::uri &uri, mitk::IRESTObserver *observer)
{
// New instance of RESTServer in m_ServerMap, key is port of the request
auto port = uri.port();
// Checking if port is free to add a new Server
if (0 == m_ServerMap.count(port))
{
this->AddObserver(uri, observer);
// creating server instance
auto server = new RESTServer(uri.authority());
// add reference to server instance to map
m_ServerMap[port] = server;
// start Server
server->OpenListener();
}
// If there is already a server under this port
else
{
this->RequestForATakenPort(uri, observer);
}
}
web::http::http_response mitk::RESTManager::Handle(const web::uri &uri,
const web::json::value &body,
const web::http::method &method,
const mitk::RESTUtil::ParamMap &headers)
{
// Checking if there is an observer for the port and path
auto key = std::make_pair(uri.port(), uri.path());
if (0 != m_Observers.count(key))
{
return m_Observers[key]->Notify(uri, body, method, headers);
}
// No observer under this port, return null which results in status code 404 (s. RESTServer)
else
{
MITK_WARN << "No Observer can handle the data";
web::http::http_response response(web::http::status_codes::BadGateway);
response.set_body(U("No one can handle the request under the given port."));
return response;
}
}
void mitk::RESTManager::HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri)
{
for (auto it = m_Observers.begin(); it != m_Observers.end();)
{
mitk::IRESTObserver *obsMap = it->second;
- // Check wether observer is at this place in map
+ // Check whether observer is at this place in map
if (observer == obsMap)
{
- // Check wether it is the right uri to be deleted
+ // Check whether it is the right uri to be deleted
if (uri.is_empty() || uri.path() == it->first.second)
{
int port = it->first.first;
bool noObserverForPort = this->DeleteObserver(it);
if (noObserverForPort)
{
// there isn't an observer at this port, delete m_ServerMap entry for this port
// close listener
m_ServerMap[port]->CloseListener();
delete m_ServerMap[port];
// delete server from map
m_ServerMap.erase(port);
}
}
else
{
++it;
}
}
else
{
++it;
}
}
}
const std::map<int, mitk::RESTServer *> &mitk::RESTManager::GetServerMap()
{
return m_ServerMap;
}
std::map<std::pair<int, utility::string_t>, mitk::IRESTObserver *> &mitk::RESTManager::GetObservers()
{
return m_Observers;
}
void mitk::RESTManager::AddObserver(const web::uri &uri, IRESTObserver *observer)
{
// new observer has to be added
std::pair<int, utility::string_t> key(uri.port(), uri.path());
m_Observers[key] = observer;
}
void mitk::RESTManager::RequestForATakenPort(const web::uri &uri, IRESTObserver *observer)
{
// Same host, means new observer but not a new server instance
if (uri.authority() == m_ServerMap[uri.port()]->GetUri())
{
// new observer has to be added
std::pair<int, utility::string_t> key(uri.port(), uri.path());
// only add a new observer if there isn't already an observer for this uri
if (0 == m_Observers.count(key))
{
this->AddObserver(uri, observer);
}
else
{
- MITK_ERROR << "Threre is already a observer handeling this data";
+ MITK_ERROR << "There is already an observer handling this data";
}
}
// Error, since another server can't be added under this port
else
{
MITK_ERROR << "There is already another server listening under this port";
}
}
bool mitk::RESTManager::DeleteObserver(std::map<std::pair<int, utility::string_t>, IRESTObserver *>::iterator &it)
{
int port = it->first.first;
it = m_Observers.erase(it);
for (auto observer : m_Observers)
{
if (port == observer.first.first)
{
// there still exists an observer for this port
return false;
}
}
return true;
}
void mitk::RESTManager::SetServerMap(const int port, RESTServer *server)
{
m_ServerMap[port] = server;
}
void mitk::RESTManager::DeleteFromServerMap(const int port)
{
m_ServerMap.erase(port);
}
void mitk::RESTManager::SetObservers(const std::pair<int, utility::string_t> key, IRESTObserver *observer)
{
m_Observers[key] = observer;
}
diff --git a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp
index ade9c8016d..d28994ae53 100644
--- a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp
+++ b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp
@@ -1,213 +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 "mitkROIIO.h"
#include <mitkProportionalTimeGeometry.h>
#include <mitkROI.h>
#include <mitkROIIOMimeTypes.h>
-#include <filesystem>
+#include <mitkFileSystem.h>
#include <fstream>
namespace
{
int CheckFileFormat(const nlohmann::json& json)
{
if ("MITK ROI" != json["FileFormat"].get<std::string>())
mitkThrow() << "Unknown file format (expected \"MITK ROI\")!";
auto version = json["Version"].get<int>();
- if (1 != version)
- mitkThrow() << "Unknown file format version (expected version 1)!";
+ if (version < 1 || version > 2)
+ mitkThrow() << "Unknown file format version (expected version 1 or 2)!";
return version;
}
mitk::Vector3D GetSize(const mitk::BaseGeometry* geometry)
{
auto bounds = geometry->GetBounds();
mitk::Vector3D result;
result[0] = bounds[1];
result[1] = bounds[3];
result[2] = bounds[5];
return result;
}
void SetSize(mitk::BaseGeometry* geometry, const mitk::Vector3D& size)
{
mitk::BaseGeometry::BoundsArrayType bounds({ 0.0, size[0], 0.0, size[1], 0.0, size[2] });
geometry->SetBounds(bounds);
}
- mitk::TimeGeometry::Pointer ReadGeometry(const nlohmann::json& jGeometry)
+ mitk::TimeGeometry::Pointer ReadGeometry(const nlohmann::json& jGeometry, int version)
{
auto geometry = mitk::Geometry3D::New();
geometry->ImageGeometryOn();
if (!jGeometry.is_object())
mitkThrow() << "Geometry is expected to be a JSON object.";
+ bool hasTransform = false;
+
+ if (jGeometry.contains("Transform"))
+ {
+ if (version < 2)
+ mitkThrow() << "Transforms are not supported in MITK ROI file format version 1.";
+
+ auto transform = mitk::AffineTransform3D::New();
+ mitk::FromJSON(jGeometry["Transform"], transform);
+ geometry->SetIndexToWorldTransform(transform);
+
+ hasTransform = true;
+ }
+
if (jGeometry.contains("Origin"))
+ {
+ if (hasTransform)
+ mitkThrow() << "Origin is already defined by Transform.";
+
geometry->SetOrigin(jGeometry["Origin"].get<mitk::Point3D>());
+ }
if (jGeometry.contains("Spacing"))
+ {
+ if (hasTransform)
+ mitkThrow() << "Spacing is already defined by Transform.";
+
geometry->SetSpacing(jGeometry["Spacing"].get<mitk::Vector3D>());
+ }
if (jGeometry.contains("Size"))
SetSize(geometry, jGeometry["Size"].get<mitk::Vector3D>());
auto timeSteps = jGeometry.contains("TimeSteps")
? jGeometry["TimeSteps"].get<mitk::TimeStepType>()
: 1;
auto result = mitk::ProportionalTimeGeometry::New();
result->Initialize(geometry, timeSteps);
return result;
}
nlohmann::json WriteGeometry(const mitk::TimeGeometry* timeGeometry)
{
auto geometry = timeGeometry->GetGeometryForTimeStep(0);
- nlohmann::json result = {
- { "Origin", geometry->GetOrigin() },
- { "Spacing", geometry->GetSpacing() },
- { "Size", GetSize(geometry) }
- };
+ nlohmann::json result;
+
+ mitk::ToJSON(result["Transform"], geometry->GetIndexToWorldTransform());
+ result["Size"] = GetSize(geometry);
auto timeSteps = timeGeometry->CountTimeSteps();
if (timeSteps > 1)
result["TimeSteps"] = timeSteps;
return result;
}
}
mitk::ROIIO::ROIIO()
: AbstractFileIO(ROI::GetStaticNameOfClass(), MitkROIIOMimeTypes::ROI_MIMETYPE(), "MITK ROI")
{
this->RegisterService();
}
std::vector<mitk::BaseData::Pointer> mitk::ROIIO::DoRead()
{
auto *stream = this->GetInputStream();
std::ifstream fileStream;
if (nullptr == stream)
{
auto filename = this->GetInputLocation();
- if (filename.empty() || !std::filesystem::exists(filename))
+ if (filename.empty() || !fs::exists(filename))
mitkThrow() << "Invalid or nonexistent filename: \"" << filename << "\"!";
fileStream.open(filename);
if (!fileStream.is_open())
mitkThrow() << "Could not open file \"" << filename << "\" for reading!";
stream = &fileStream;
}
auto result = ROI::New();
try
{
auto j = nlohmann::json::parse(*stream);
- CheckFileFormat(j);
+ auto version = CheckFileFormat(j);
- auto geometry = ReadGeometry(j["Geometry"]);
+ auto geometry = ReadGeometry(j["Geometry"], version);
result->SetTimeGeometry(geometry);
if (j.contains("Name"))
result->SetProperty("name", mitk::StringProperty::New(j["Name"].get<std::string>()));
if (j.contains("Caption"))
result->SetProperty("caption", mitk::StringProperty::New(j["Caption"].get<std::string>()));
for (const auto& roi : j["ROIs"])
result->AddElement(roi.get<ROI::Element>());
}
catch (const nlohmann::json::exception &e)
{
mitkThrow() << e.what();
}
return { result };
}
void mitk::ROIIO::Write()
{
auto input = dynamic_cast<const ROI*>(this->GetInput());
if (input == nullptr)
mitkThrow() << "Invalid input for writing!";
if (input->GetNumberOfElements() == 0)
mitkThrow() << "No ROIs found!";
auto* stream = this->GetOutputStream();
std::ofstream fileStream;
if (stream == nullptr)
{
auto filename = this->GetOutputLocation();
if (filename.empty())
mitkThrow() << "Neither an output stream nor an output filename was specified!";
fileStream.open(filename);
if (!fileStream.is_open())
mitkThrow() << "Could not open file \"" << filename << "\" for writing!";
stream = &fileStream;
}
if (!stream->good())
mitkThrow() << "Stream for writing is not good!";
nlohmann::ordered_json j = {
{ "FileFormat", "MITK ROI" },
- { "Version", 1 }
+ { "Version", 2 }
};
if (auto name = input->GetProperty("name"); name.IsNotNull())
j["Name"] = name->GetValueAsString();
j["Geometry"] = WriteGeometry(input->GetTimeGeometry());
auto caption = input->GetConstProperty("caption");
if (caption.IsNotNull())
j["Caption"] = caption->GetValueAsString();
nlohmann::json rois;
for (const auto& roi : *input)
rois.push_back(roi.second);
j["ROIs"] = rois;
*stream << std::setw(2) << j << std::endl;
}
mitk::ROIIO* mitk::ROIIO::IOClone() const
{
return new ROIIO(*this);
}
diff --git a/Modules/ROI/autoload/IO/src/mitkROIIOMimeTypes.cpp b/Modules/ROI/autoload/IO/src/mitkROIIOMimeTypes.cpp
index 91b8032fe5..dc4b3430ad 100644
--- a/Modules/ROI/autoload/IO/src/mitkROIIOMimeTypes.cpp
+++ b/Modules/ROI/autoload/IO/src/mitkROIIOMimeTypes.cpp
@@ -1,75 +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 <mitkROIIOMimeTypes.h>
#include <mitkIOMimeTypes.h>
-#include <filesystem>
+#include <mitkFileSystem.h>
#include <fstream>
#include <nlohmann/json.hpp>
mitk::MitkROIIOMimeTypes::MitkROIMimeType::MitkROIMimeType()
: CustomMimeType(ROI_MIMETYPE_NAME())
{
this->AddExtension("json");
this->SetCategory("MITK ROI");
this->SetComment("MITK ROI");
}
bool mitk::MitkROIIOMimeTypes::MitkROIMimeType::AppliesTo(const std::string& path) const
{
bool result = CustomMimeType::AppliesTo(path);
- if (!std::filesystem::exists(path)) // T18572
+ if (!fs::exists(path)) // T18572
return result;
std::ifstream file(path);
if (!file.is_open())
return false;
auto json = nlohmann::json::parse(file, nullptr, false);
if (json.is_discarded() || !json.is_object())
return false;
if ("MITK ROI" != json.value("FileFormat", ""))
return false;
- if (1 != json.value<int>("Version", 0))
+ auto version = json.value<int>("Version", 0);
+
+ if (version < 1 || version > 2)
return false;
return true;
}
mitk::MitkROIIOMimeTypes::MitkROIMimeType* mitk::MitkROIIOMimeTypes::MitkROIMimeType::Clone() const
{
return new MitkROIMimeType(*this);
}
mitk::MitkROIIOMimeTypes::MitkROIMimeType mitk::MitkROIIOMimeTypes::ROI_MIMETYPE()
{
return MitkROIMimeType();
}
std::string mitk::MitkROIIOMimeTypes::ROI_MIMETYPE_NAME()
{
return IOMimeTypes::DEFAULT_BASE_NAME() + ".roi";
}
std::vector<mitk::CustomMimeType*> mitk::MitkROIIOMimeTypes::Get()
{
std::vector<CustomMimeType*> mimeTypes;
mimeTypes.push_back(ROI_MIMETYPE().Clone());
return mimeTypes;
}
diff --git a/Modules/ROI/src/mitkROI.cpp b/Modules/ROI/src/mitkROI.cpp
index 6f5e614b1a..e0d73fab09 100644
--- a/Modules/ROI/src/mitkROI.cpp
+++ b/Modules/ROI/src/mitkROI.cpp
@@ -1,399 +1,401 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkROI.h>
void mitk::to_json(nlohmann::json& j, const ROI::Element& roi)
{
j["ID"] = roi.GetID();
if (roi.HasTimeSteps())
{
auto timeSteps = roi.GetTimeSteps();
for (const auto t : timeSteps)
{
nlohmann::json jTimeStep = {
{ "t", t },
{ "Min", roi.GetMin(t) },
{ "Max", roi.GetMax(t) }
};
if (auto* properties = roi.GetProperties(t); properties != nullptr && !properties->IsEmpty())
properties->ToJSON(jTimeStep["Properties"]);
j["TimeSteps"].push_back(jTimeStep);
}
}
else
{
j["Min"] = roi.GetMin();
j["Max"] = roi.GetMax();
}
if (auto* properties = roi.GetDefaultProperties(); properties != nullptr && !properties->IsEmpty())
properties->ToJSON(j["Properties"]);
}
void mitk::from_json(const nlohmann::json& j, ROI::Element& roi)
{
auto id = j["ID"].get<unsigned int>();
roi.SetID(id);
if (j.contains("TimeSteps"))
{
for (const auto& jTimeStep : j["TimeSteps"])
{
auto t = jTimeStep["t"].get<TimeStepType>();
roi.SetMin(jTimeStep["Min"].get<Point3D>(), t);
roi.SetMax(jTimeStep["Max"].get<Point3D>(), t);
if (jTimeStep.contains("Properties"))
{
auto properties = mitk::PropertyList::New();
properties->FromJSON(jTimeStep["Properties"]);
roi.SetProperties(properties, t);
}
}
}
else
{
roi.SetMin(j["Min"].get<Point3D>());
roi.SetMax(j["Max"].get<Point3D>());
}
if (j.contains("Properties"))
{
auto properties = mitk::PropertyList::New();
properties->FromJSON(j["Properties"]);
roi.SetDefaultProperties(properties);
}
}
mitk::ROI::Element::Element()
: Element(0)
{
}
mitk::ROI::Element::Element(unsigned int id)
: m_ID(id),
m_DefaultProperties(PropertyList::New())
{
}
unsigned int mitk::ROI::Element::GetID() const
{
return m_ID;
}
void mitk::ROI::Element::SetID(unsigned int id)
{
m_ID = id;
}
bool mitk::ROI::Element::HasTimeStep(TimeStepType t) const
{
return m_Min.count(t) != 0 && m_Max.count(t) != 0;
}
bool mitk::ROI::Element::HasTimeSteps() const
{
// Check for multiple time steps.
if (m_Min.size() > 1 && m_Max.size() > 1)
return true;
// Check for single time step that is not 0.
if (m_Min.size() >= 1 && m_Max.size() >= 1)
return m_Min.count(0) == 0 && m_Max.count(0) == 0;
// Single time step 0.
return false;
}
std::vector<mitk::TimeStepType> mitk::ROI::Element::GetTimeSteps() const
{
std::vector<TimeStepType> result;
result.reserve(m_Min.size());
for (const auto& [t, min] : m_Min)
{
+ (void)min; // Prevent unused variable error in older compilers
+
if (m_Max.count(t) != 0)
result.push_back(t);
}
return result;
}
mitk::Point3D mitk::ROI::Element::GetMin(TimeStepType t) const
{
return m_Min.at(t);
}
void mitk::ROI::Element::SetMin(const Point3D& min, TimeStepType t)
{
m_Min[t] = min;
}
mitk::Point3D mitk::ROI::Element::GetMax(TimeStepType t) const
{
return m_Max.at(t);
}
void mitk::ROI::Element::SetMax(const Point3D& max, TimeStepType t)
{
m_Max[t] = max;
}
mitk::PropertyList* mitk::ROI::Element::GetDefaultProperties() const
{
return m_DefaultProperties;
}
void mitk::ROI::Element::SetDefaultProperties(PropertyList* properties)
{
m_DefaultProperties = properties;
}
mitk::PropertyList* mitk::ROI::Element::GetProperties(TimeStepType t) const
{
if (m_Properties.count(t) != 0)
return m_Properties.at(t);
return nullptr;
}
void mitk::ROI::Element::SetProperties(PropertyList* properties, TimeStepType t)
{
m_Properties[t] = properties;
}
mitk::BaseProperty::ConstPointer mitk::ROI::Element::GetConstProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext) const
{
return !contextName.empty()
? this->GetConstProperty(propertyKey, std::stoul(contextName), fallBackOnDefaultContext)
: m_DefaultProperties->GetConstProperty(propertyKey);
}
mitk::BaseProperty::ConstPointer mitk::ROI::Element::GetConstProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext) const
{
auto it = m_Properties.find(t);
if (it != m_Properties.end() && it->second.IsNotNull())
{
auto property = it->second->GetConstProperty(propertyKey);
if (property.IsNotNull())
return property;
}
if (!fallBackOnDefaultContext)
return nullptr;
return m_DefaultProperties->GetConstProperty(propertyKey);
}
std::vector<std::string> mitk::ROI::Element::GetPropertyKeys(const std::string& contextName, bool includeDefaultContext) const
{
return !contextName.empty()
? this->GetPropertyKeys(std::stoul(contextName), includeDefaultContext)
: m_DefaultProperties->GetPropertyKeys();
}
std::vector<std::string> mitk::ROI::Element::GetPropertyKeys(TimeStepType t, bool includeDefaultContext) const
{
auto it = m_Properties.find(t);
std::vector<std::string> result;
if (it != m_Properties.end() && it->second.IsNotNull())
result = it->second->GetPropertyKeys();
if (includeDefaultContext)
{
auto keys = m_DefaultProperties->GetPropertyKeys();
auto end = result.cend();
std::remove_copy_if(keys.cbegin(), keys.cend(), std::back_inserter(result), [&, result, end](const std::string& key) {
return end != std::find(result.cbegin(), end, key);
});
}
return result;
}
std::vector<std::string> mitk::ROI::Element::GetPropertyContextNames() const
{
std::vector<std::string> result;
result.reserve(m_Properties.size());
std::transform(m_Properties.cbegin(), m_Properties.cend(), std::back_inserter(result), [](const PropertyListsType::value_type& property) {
return std::to_string(property.first);
});
return result;
}
mitk::BaseProperty* mitk::ROI::Element::GetNonConstProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext)
{
return !contextName.empty()
? this->GetNonConstProperty(propertyKey, std::stoul(contextName), fallBackOnDefaultContext)
: m_DefaultProperties->GetNonConstProperty(propertyKey);
}
mitk::BaseProperty* mitk::ROI::Element::GetNonConstProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext)
{
auto it = m_Properties.find(t);
if (it != m_Properties.end() && it->second.IsNotNull())
{
auto* property = it->second->GetNonConstProperty(propertyKey);
if (property != nullptr)
return property;
}
if (!fallBackOnDefaultContext)
return nullptr;
return m_DefaultProperties->GetNonConstProperty(propertyKey);
}
void mitk::ROI::Element::SetProperty(const std::string& propertyKey, BaseProperty* property, const std::string& contextName, bool fallBackOnDefaultContext)
{
if (!contextName.empty())
{
this->SetProperty(propertyKey, property, std::stoul(contextName), fallBackOnDefaultContext);
return;
}
m_DefaultProperties->SetProperty(propertyKey, property);
}
void mitk::ROI::Element::SetProperty(const std::string& propertyKey, BaseProperty* property, TimeStepType t, bool fallBackOnDefaultContext)
{
auto it = m_Properties.find(t);
if (it != m_Properties.end() && it->second.IsNotNull())
{
it->second->SetProperty(propertyKey, property);
return;
}
if (!fallBackOnDefaultContext)
mitkThrow() << "Time step " << t << " does not exist!";
m_DefaultProperties->SetProperty(propertyKey, property);
}
void mitk::ROI::Element::RemoveProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext)
{
if (!contextName.empty())
{
this->RemoveProperty(propertyKey, std::stoul(contextName), fallBackOnDefaultContext);
return;
}
m_DefaultProperties->RemoveProperty(propertyKey);
}
void mitk::ROI::Element::RemoveProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext)
{
auto it = m_Properties.find(t);
if (it != m_Properties.end() && it->second.IsNotNull())
{
it->second->RemoveProperty(propertyKey);
return;
}
if (!fallBackOnDefaultContext)
mitkThrow() << "Time step " << t << " does not exist!";
m_DefaultProperties->RemoveProperty(propertyKey);
}
mitk::ROI::ROI()
{
}
mitk::ROI::ROI(const Self& other)
: BaseData(other)
{
}
mitk::ROI::~ROI()
{
}
size_t mitk::ROI::GetNumberOfElements() const
{
return m_Elements.size();
}
void mitk::ROI::AddElement(const Element& element)
{
const auto id = element.GetID();
if (m_Elements.count(id) != 0)
mitkThrow() << "ROI already contains an element with ID " << std::to_string(id) << '.';
m_Elements[id] = element;
}
const mitk::ROI::Element& mitk::ROI::GetElement(unsigned int id) const
{
return m_Elements.at(id);
}
mitk::ROI::Element& mitk::ROI::GetElement(unsigned int id)
{
return m_Elements.at(id);
}
mitk::ROI::ConstIterator mitk::ROI::begin() const
{
return m_Elements.begin();
}
mitk::ROI::ConstIterator mitk::ROI::end() const
{
return m_Elements.end();
}
mitk::ROI::Iterator mitk::ROI::begin()
{
return m_Elements.begin();
}
mitk::ROI::Iterator mitk::ROI::end()
{
return m_Elements.end();
}
void mitk::ROI::SetRequestedRegionToLargestPossibleRegion()
{
}
bool mitk::ROI::RequestedRegionIsOutsideOfTheBufferedRegion()
{
return false;
}
bool mitk::ROI::VerifyRequestedRegion()
{
return true;
}
void mitk::ROI::SetRequestedRegion(const itk::DataObject*)
{
}
diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp
index 1435d4c6d7..052150c5c0 100644
--- a/Modules/ROI/src/mitkROIMapper2D.cpp
+++ b/Modules/ROI/src/mitkROIMapper2D.cpp
@@ -1,187 +1,188 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkROIMapper2D.h>
#include "mitkROIMapperHelper.h"
#include <vtkCubeSource.h>
#include <vtkPlane.h>
#include <vtkPolyDataMapper.h>
#include <vtkPolyDataPlaneCutter.h>
#include <vtkPropAssembly.h>
+#include <vtkTransformPolyDataFilter.h>
#include <boost/algorithm/string.hpp>
#include <regex>
namespace
{
mitk::Point3D GetBottomLeftPoint(vtkPoints* points, mitk::BaseRenderer* renderer)
{
mitk::Point3D point = points->GetPoint(0);
mitk::Point2D bottomLeftDisplayPoint;
renderer->WorldToDisplay(point, bottomLeftDisplayPoint);
auto numPoints = points->GetNumberOfPoints();
mitk::Point2D displayPoint;
for (decltype(numPoints) i = 1; i < numPoints; ++i)
{
point.FillPoint(points->GetPoint(i));
renderer->WorldToDisplay(point, displayPoint);
bottomLeftDisplayPoint[0] = std::min(bottomLeftDisplayPoint[0], displayPoint[0]);
bottomLeftDisplayPoint[1] = std::min(bottomLeftDisplayPoint[1], displayPoint[1]);
}
renderer->DisplayToWorld(bottomLeftDisplayPoint, point);
return point;
}
}
mitk::ROIMapper2D::LocalStorage::LocalStorage()
{
}
mitk::ROIMapper2D::LocalStorage::~LocalStorage()
{
}
const mitk::PlaneGeometry* mitk::ROIMapper2D::LocalStorage::GetLastPlaneGeometry() const
{
return m_LastPlaneGeometry;
}
void mitk::ROIMapper2D::LocalStorage::SetLastPlaneGeometry(const PlaneGeometry* planeGeometry)
{
m_LastPlaneGeometry = planeGeometry;
}
void mitk::ROIMapper2D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override)
{
Superclass::SetDefaultProperties(node, renderer, override);
ROIMapperHelper::SetDefaultProperties(node, renderer, override);
}
mitk::ROIMapper2D::ROIMapper2D()
{
}
mitk::ROIMapper2D::~ROIMapper2D()
{
}
void mitk::ROIMapper2D::GenerateDataForRenderer(BaseRenderer* renderer)
{
const auto timePoint = renderer->GetWorldTimeGeometry()->TimeStepToTimePoint(renderer->GetTimeStep());
const auto* planeGeometry = renderer->GetCurrentWorldPlaneGeometry();
auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer);
const auto* dataNode = this->GetDataNode();
if (localStorage->GetLastPlaneGeometry() != nullptr && localStorage->GetLastPlaneGeometry()->IsOnPlane(planeGeometry) &&
localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime() &&
localStorage->GetLastTimePoint() == timePoint)
{
return;
}
localStorage->SetLastPlaneGeometry(planeGeometry->Clone());
localStorage->SetLastTimePoint(timePoint);
auto data = static_cast<const ROI*>(this->GetData());
if (!data->GetTimeGeometry()->IsValidTimePoint(timePoint))
return;
const auto t = data->GetTimeGeometry()->TimePointToTimeStep(timePoint);
auto propAssembly = vtkSmartPointer<vtkPropAssembly>::New();
if (dataNode->IsVisible(renderer))
{
const auto* geometry = data->GetGeometry(t);
- const auto halfSpacing = geometry->GetSpacing() * 0.5f;
auto plane = vtkSmartPointer<vtkPlane>::New();
plane->SetOrigin(planeGeometry->GetOrigin().data());
plane->SetNormal(planeGeometry->GetNormal().data());
for (const auto& [id, roi] : *data)
{
+ (void)id; // Prevent unused variable error in older compilers
+
if (!roi.HasTimeStep(t))
continue;
- Point3D min;
- geometry->IndexToWorld(roi.GetMin(t), min);
- min -= halfSpacing;
-
- Point3D max;
- geometry->IndexToWorld(roi.GetMax(t), max);
- max += halfSpacing;
+ Point3D min = roi.GetMin(t);
+ Point3D max = roi.GetMax(t);
auto cube = vtkSmartPointer<vtkCubeSource>::New();
cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]);
+ auto transform = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
+ transform->SetTransform(geometry->GetVtkTransform());
+ transform->SetInputConnection(cube->GetOutputPort());
+
auto cutter = vtkSmartPointer<vtkPolyDataPlaneCutter>::New();
- cutter->SetInputConnection(cube->GetOutputPort());
+ cutter->SetInputConnection(transform->GetOutputPort());
cutter->SetPlane(plane);
cutter->Update();
auto* slicePolyData = cutter->GetOutput();
if (slicePolyData->GetNumberOfLines() == 0)
continue;
auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(cutter->GetOutputPort());
auto actor = vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
this->ApplyColorAndOpacityProperties(renderer, actor);
ROIMapperHelper::ApplyIndividualProperties(roi, t, actor);
propAssembly->AddPart(actor);
if (std::string caption; dataNode->GetStringProperty("caption", caption, renderer))
{
caption = ROIMapperHelper::ParseCaption(caption, roi, t);
if (!caption.empty())
{
auto bottomLeftPoint = GetBottomLeftPoint(slicePolyData->GetPoints(), renderer);
auto captionActor = ROIMapperHelper::CreateCaptionActor(caption, bottomLeftPoint, actor->GetProperty(), dataNode, renderer);
propAssembly->AddPart(captionActor);
}
}
}
}
localStorage->SetPropAssembly(propAssembly);
localStorage->UpdateGenerateDataTime();
}
void mitk::ROIMapper2D::ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor)
{
auto* property = actor->GetProperty();
float opacity = 1.0f;
this->GetDataNode()->GetOpacity(opacity, renderer);
property->SetOpacity(opacity);
}
vtkProp* mitk::ROIMapper2D::GetVtkProp(BaseRenderer* renderer)
{
return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly();
}
diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp
index e8120de0e3..6ab6c0c73f 100644
--- a/Modules/ROI/src/mitkROIMapper3D.cpp
+++ b/Modules/ROI/src/mitkROIMapper3D.cpp
@@ -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.
============================================================================*/
#include <mitkROIMapper3D.h>
#include <mitkROI.h>
#include "mitkROIMapperHelper.h"
#include <vtkActor.h>
#include <vtkCubeSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkSmartPointer.h>
+#include <vtkTransformPolyDataFilter.h>
mitk::ROIMapper3D::LocalStorage::LocalStorage()
{
}
mitk::ROIMapper3D::LocalStorage::~LocalStorage()
{
}
void mitk::ROIMapper3D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override)
{
Superclass::SetDefaultProperties(node, renderer, override);
ROIMapperHelper::SetDefaultProperties(node, renderer, override);
}
mitk::ROIMapper3D::ROIMapper3D()
{
}
mitk::ROIMapper3D::~ROIMapper3D()
{
}
void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer)
{
const auto timePoint = renderer->GetWorldTimeGeometry()->TimeStepToTimePoint(renderer->GetTimeStep());
auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer);
const auto* dataNode = this->GetDataNode();
if (localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime() &&
localStorage->GetLastTimePoint() == timePoint)
{
return;
}
localStorage->SetLastTimePoint(timePoint);
auto data = static_cast<const ROI*>(this->GetData());
if (!data->GetTimeGeometry()->IsValidTimePoint(timePoint))
return;
const auto t = data->GetTimeGeometry()->TimePointToTimeStep(timePoint);
auto propAssembly = vtkSmartPointer<vtkPropAssembly>::New();
if (dataNode->IsVisible(renderer))
{
const auto* geometry = data->GetGeometry();
- const auto halfSpacing = geometry->GetSpacing() * 0.5f;
for (const auto& [id, roi] : *data)
{
+ (void)id; // Prevent unused variable error in older compilers
+
if (!roi.HasTimeStep(t))
continue;
- Point3D min;
- geometry->IndexToWorld(roi.GetMin(t), min);
- min -= halfSpacing;
-
- Point3D max;
- geometry->IndexToWorld(roi.GetMax(t), max);
- max += halfSpacing;
+ Point3D min = roi.GetMin(t);
+ Point3D max = roi.GetMax(t);
auto cube = vtkSmartPointer<vtkCubeSource>::New();
cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]);
- cube->Update();
+
+ auto transform = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
+ transform->SetTransform(geometry->GetVtkTransform());
+ transform->SetInputConnection(cube->GetOutputPort());
auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
- mapper->SetInputConnection(cube->GetOutputPort());
+ mapper->SetInputConnection(transform->GetOutputPort());
auto actor = vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
this->ApplyColorAndOpacityProperties(renderer, actor);
ROIMapperHelper::ApplyIndividualProperties(roi, t, actor);
propAssembly->AddPart(actor);
}
}
localStorage->SetPropAssembly(propAssembly);
localStorage->UpdateGenerateDataTime();
}
void mitk::ROIMapper3D::ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor)
{
auto* property = actor->GetProperty();
float opacity = 1.0f;
this->GetDataNode()->GetOpacity(opacity, renderer);
property->SetOpacity(opacity);
}
vtkProp* mitk::ROIMapper3D::GetVtkProp(BaseRenderer* renderer)
{
return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly();
}
diff --git a/Modules/RT/include/mitkDoseImageVtkMapper2D.h b/Modules/RT/include/mitkDoseImageVtkMapper2D.h
index e659d17103..d87eb8812a 100644
--- a/Modules/RT/include/mitkDoseImageVtkMapper2D.h
+++ b/Modules/RT/include/mitkDoseImageVtkMapper2D.h
@@ -1,303 +1,303 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkDoseImageVtkMapper2D_h
#define mitkDoseImageVtkMapper2D_h
//MITK
#include <mitkCommon.h>
#include <MitkRTExports.h>
//MITK Rendering
#include "mitkBaseRenderer.h"
#include "mitkVtkMapper.h"
#include "mitkExtractSliceFilter.h"
//VTK
#include <vtkSmartPointer.h>
#include <vtkPropAssembly.h>
#include <vtkCellArray.h>
class vtkActor;
class vtkPolyDataMapper;
class vtkPlaneSource;
class vtkImageData;
class vtkLookupTable;
class vtkImageExtractComponents;
class vtkImageReslice;
class vtkImageChangeInformation;
class vtkPoints;
class vtkMitkThickSlicesFilter;
class vtkPolyData;
class vtkMitkApplyLevelWindowToRGBFilter;
class vtkMitkLevelWindowFilter;
namespace mitk {
/** \brief Mapper to resample and display 2D slices of a 3D image.
*
* First, the image is resliced by means of vtkImageReslice. The volume image
* serves as input to the mapper in addition to spatial placement of the slice and a few other
* properties such as thick slices. This code was already present in the old version
* (mitkImageMapperGL2D).
*
* Next, the obtained slice (m_ReslicedImage) is put into a vtkMitkLevelWindowFilter
* and the scalar levelwindow, opacity levelwindow and optional clipping to
* local image bounds are applied
*
* Next, the output of the vtkMitkLevelWindowFilter is used to create a texture
* (m_Texture) and a plane onto which the texture is rendered (m_Plane). For
* mapping purposes, a vtkPolyDataMapper (m_Mapper) is utilized. Orthographic
* projection is applied to create the effect of a 2D image. The mapper and the
* texture are assigned to the actor (m_Actor) which is passed to the VTK rendering
* pipeline via the method GetVtkProp().
*
* In order to transform the textured plane to the correct position in space, the
* same transformation as used for reslicing is applied to both the camera and the
* vtkActor. All important steps are explained in more detail below. The resulting
* 2D image (by reslicing the underlying 3D input image appropriately) can either
* be directly rendered in a 2D view or just be calculated to be used later by another
* rendering entity, e.g. in texture mapping in a 3D view.
*
* Properties that can be set for images and influence the imageMapper2D are:
*
* - \b "opacity": (FloatProperty) Opacity of the image
* - \b "color": (ColorProperty) Color of the image
* - \b "LookupTable": (mitkLookupTableProperty) If this property is set,
* the default lookuptable will be ignored and the "LookupTable" value
* will be used instead.
* - \b "Image Rendering.Mode": This property decides which mode is used to render images. (E.g. if a lookup table or a transferfunction is applied). Detailed documentation about the modes can be found here: \link mitk::RenderingModeProperty \endlink
* - \b "Image Rendering.Transfer Function": (mitkTransferFunctionProperty) If this
* property is set, a color transferfunction will be used to color the image.
* - \b "binary": (BoolProperty) is the image a binary image or not
* - \b "outline binary": (BoolProperty) show outline of the image or not
* - \b "texture interpolation": (BoolProperty) texture interpolation of the image
* - \b "reslice interpolation": (VtkResliceInterpolationProperty) reslice interpolation of the image
* - \b "in plane resample extent by geometry": (BoolProperty) Do it or not
* - \b "bounding box": (BoolProperty) Is the Bounding Box of the image shown or not
* - \b "layer": (IntProperty) Layer of the image
* - \b "volume annotation color": (ColorProperty) color of the volume annotation, TODO has to be reimplemented
* - \b "volume annotation unit": (StringProperty) annotation unit as string (does not implicit convert the unit!)
unit is ml or cm3, TODO has to be reimplemented
* The default properties are:
* - \b "opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite )
* - \b "color", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite )
* - \b "binary", mitk::BoolProperty::New( true ), renderer, overwrite )
* - \b "outline binary", mitk::BoolProperty::New( false ), renderer, overwrite )
* - \b "texture interpolation", mitk::BoolProperty::New( false ) )
* - \b "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() )
* - \b "in plane resample extent by geometry", mitk::BoolProperty::New( false ) )
* - \b "bounding box", mitk::BoolProperty::New( false ) )
* - \b "layer", mitk::IntProperty::New(10), renderer, overwrite)
* - \b "Image Rendering.Transfer Function": Default color transfer function for CTs
* - \b "LookupTable": Rainbow color.
* If the modality-property is set for an image, the mapper uses modality-specific default properties,
* e.g. color maps, if they are defined.
* \ingroup Mapper
*/
class MITKRT_EXPORT DoseImageVtkMapper2D : public VtkMapper
{
public:
/** Standard class typedefs. */
mitkClassMacro( DoseImageVtkMapper2D,VtkMapper );
/** Method for creation through the object factory. */
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/** \brief Get the Image to map */
const mitk::Image *GetInput(void);
/** \brief Checks whether this mapper needs to update itself and generate
* data. */
void Update(mitk::BaseRenderer * renderer) override;
//### methods of MITK-VTK rendering pipeline
vtkProp* GetVtkProp(mitk::BaseRenderer* renderer) override;
//### end of methods of MITK-VTK rendering pipeline
/** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */
/**
* To render axial, coronal, and sagittal, the mapper is called three times.
* For performance reasons, the corresponding data for each view is saved in the
* internal helper class LocalStorage. This allows rendering n views with just
* 1 mitkMapper using n vtkMapper.
* */
class MITKRT_EXPORT LocalStorage : public mitk::Mapper::BaseLocalStorage
{
public:
/** \brief Actor of a 2D render window. */
vtkSmartPointer<vtkActor> m_Actor;
vtkSmartPointer<vtkPropAssembly> m_Actors;
/** \brief Mapper of a 2D render window. */
vtkSmartPointer<vtkPolyDataMapper> m_Mapper;
vtkSmartPointer<vtkImageExtractComponents> m_VectorComponentExtractor;
/** \brief Current slice of a 2D render window.*/
vtkSmartPointer<vtkImageData> m_ReslicedImage;
/** \brief Empty vtkPolyData that is set when rendering geometry does not
* intersect the image geometry.
* \warning This member variable is set to nullptr,
* if no image geometry is inside the plane geometry
* of the respective render window. Any user of this
* slice has to check whether it is set to nullptr!
*/
vtkSmartPointer<vtkPolyData> m_EmptyPolyData;
/** \brief Plane on which the slice is rendered as texture. */
vtkSmartPointer<vtkPlaneSource> m_Plane;
/** \brief The texture which is used to render the current slice. */
vtkSmartPointer<vtkTexture> m_Texture;
/** \brief The lookuptables for colors and level window */
vtkSmartPointer<vtkLookupTable> m_DefaultLookupTable;
vtkSmartPointer<vtkLookupTable> m_BinaryLookupTable;
vtkSmartPointer<vtkLookupTable> m_ColorLookupTable;
/** \brief The actual reslicer (one per renderer) */
mitk::ExtractSliceFilter::Pointer m_Reslicer;
/** \brief Filter for thick slices */
vtkSmartPointer<vtkMitkThickSlicesFilter> m_TSFilter;
- /** \brief PolyData object containg all lines/points needed for outlining the contour.
+ /** \brief PolyData object containing all lines/points needed for outlining the contour.
This container is used to save a computed contour for the next rendering execution.
For instance, if you zoom or pann, there is no need to recompute the contour. */
vtkSmartPointer<vtkPolyData> m_OutlinePolyData;
/** \brief Timestamp of last update of stored data. */
itk::TimeStamp m_LastUpdateTime;
/** \brief mmPerPixel relation between pixel and mm. (World spacing).*/
mitk::ScalarType* m_mmPerPixel;
/** \brief This filter is used to apply the level window to Grayvalue and RBG(A) images. */
vtkSmartPointer<vtkMitkLevelWindowFilter> m_LevelWindowFilter;
/** \brief Default constructor of the local storage. */
LocalStorage();
/** \brief Default deconstructor of the local storage. */
~LocalStorage() override;
};
/** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */
mitk::LocalStorageHandler<LocalStorage> m_LSH;
/** \brief Get the LocalStorage corresponding to the current renderer. */
LocalStorage* GetLocalStorage(mitk::BaseRenderer* renderer);
/** \brief Set the default properties for general image rendering. */
static void SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer = nullptr, bool overwrite = false);
/** \brief This method switches between different rendering modes (e.g. use a lookup table or a transfer function).
* Detailed documentation about the modes can be found here: \link mitk::RenderingModeProperty \endlink
*/
void ApplyRenderingMode(mitk::BaseRenderer *renderer);
protected:
/** \brief Transforms the actor to the actual position in 3D.
* \param renderer The current renderer corresponding to the render window.
*/
void TransformActor(mitk::BaseRenderer* renderer);
/** \brief Generates a plane according to the size of the resliced image in milimeters.
*
* In VTK a vtkPlaneSource is defined through three points. The origin and two
* points defining the axes of the plane (see VTK documentation). The origin is
* set to (xMin; yMin; Z), where xMin and yMin are the minimal bounds of the
* resliced image in space. Z is relevant for blending and the layer property.
* The center of the plane (C) is also the center of the view plane (cf. the image above).
*
* \note For the standard MITK view with three 2D render windows showing three
* different slices, three such planes are generated. All these planes are generated
* in the XY-plane (even if they depict a YZ-slice of the volume).
*
*/
void GeneratePlane(mitk::BaseRenderer* renderer, double planeBounds[6]);
/** \brief Generates a vtkPolyData object containing the outline of a given binary slice.
\param renderer: Pointer to the renderer containing the needed information
\note This code is based on code from the iil library.
*/
vtkSmartPointer<vtkPolyData> CreateOutlinePolyData(mitk::BaseRenderer* renderer);
/** Default constructor */
DoseImageVtkMapper2D();
/** Default deconstructor */
~DoseImageVtkMapper2D() override;
/** \brief Does the actual resampling, without rendering the image yet.
* All the data is generated inside this method. The vtkProp (or Actor)
* is filled with content (i.e. the resliced image).
*
* After generation, a 4x4 transformation matrix(t) of the current slice is obtained
* from the vtkResliceImage object via GetReslicesAxis(). This matrix is
* applied to each textured plane (actor->SetUserTransform(t)) to transform everything
* to the actual 3D position (cf. the following image).
*
* \image html cameraPositioning3D.png
*
*/
void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override;
/** \brief This method uses the vtkCamera clipping range and the layer property
- * to calcualte the depth of the object (e.g. image or contour). The depth is used
+ * to calculate the depth of the object (e.g. image or contour). The depth is used
* to keep the correct order for the final VTK rendering.*/
float CalculateLayerDepth(mitk::BaseRenderer* renderer);
/** \brief This method applies (or modifies) the lookuptable for all types of images.
* \warning To use the lookup table, the property 'Lookup Table' must be set and a 'Image Rendering.Mode'
* which uses the lookup table must be set.
*/
void ApplyLookuptable(mitk::BaseRenderer* renderer);
/** \brief This method applies a color transfer function.
- * Internally, a vtkColorTransferFunction is used. This is usefull for coloring continous
+ * Internally, a vtkColorTransferFunction is used. This is useful for coloring continuous
* images (e.g. float)
* \warning To use the color transfer function, the property 'Image Rendering.Transfer Function' must be set and a 'Image Rendering.Mode' which uses the color transfer function must be set.
*/
void ApplyColorTransferFunction(mitk::BaseRenderer* renderer);
/**
* @brief ApplyLevelWindow Apply the level window for the given renderer.
* \warning To use the level window, the property 'LevelWindow' must be set and a 'Image Rendering.Mode' which uses the level window must be set.
* @param renderer Level window for which renderer?
*/
void ApplyLevelWindow(mitk::BaseRenderer* renderer);
/** \brief Set the color of the image/polydata */
void ApplyColor( mitk::BaseRenderer* renderer );
/** \brief Set the opacity of the actor. */
void ApplyOpacity( mitk::BaseRenderer* renderer );
/**
* \brief Calculates whether the given rendering geometry intersects the
* given SlicedGeometry3D.
*
* This method checks if the given PlaneGeometry intersects the given
* SlicedGeometry3D. It calculates the distance of the PlaneGeometry to all
* 8 cornerpoints of the SlicedGeometry3D. If all distances have the same
* sign (all positive or all negative) there is no intersection.
* If the distances have different sign, there is an intersection.
**/
bool RenderingGeometryIntersectsImage( const PlaneGeometry* renderingGeometry, SlicedGeometry3D* imageGeometry );
private:
void CreateLevelOutline(mitk::BaseRenderer* renderer, const mitk::IsoDoseLevel* level, float pref, vtkSmartPointer<vtkPoints> points, vtkSmartPointer<vtkCellArray> lines, vtkSmartPointer<vtkUnsignedCharArray> colors);
};
} // namespace mitk
#endif
diff --git a/Modules/RT/include/mitkRTConstants.h b/Modules/RT/include/mitkRTConstants.h
index 5efef8090c..bba72c91a8 100644
--- a/Modules/RT/include/mitkRTConstants.h
+++ b/Modules/RT/include/mitkRTConstants.h
@@ -1,107 +1,107 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkRTConstants_h
#define mitkRTConstants_h
#include <string>
#include "MitkRTExports.h"
namespace mitk
{
struct MITKRT_EXPORT RTConstants
{
/**
* Name of the property that indicates if a data/node is a dose.
*/
static const std::string DOSE_PROPERTY_NAME;
/**
* Name of the property that encodes the prescribed dose associated with the data node
* If a RTPLAN file exists the value can be extracted from the tag (300A,0026) - Target Prescription Dose in the plan file.
*/
static const std::string PRESCRIBED_DOSE_PROPERTY_NAME;
/**
- * Name of the property that encodes the reference dose that should be used for relative dose vizualization/evaluation purpose.
- * It is often the prescribed dose but may differ e.g. when to dose distributions sould be compared using the same reference.
+ * Name of the property that encodes the reference dose that should be used for relative dose visualization/evaluation purpose.
+ * It is often the prescribed dose but may differ e.g. when to dose distributions should be compared using the same reference.
*/
static const std::string REFERENCE_DOSE_PROPERTY_NAME;
/**
* Name of the property that encodes the reference structure set.
*/
static const std::string REFERENCE_STRUCTURE_SET_PROPERTY_NAME;
/**
* Name of the property that encodes the optional string property holding the information from the tag (3004,0004) - Dose Type.
* This contains useful information for medical doctors
*/
static const std::string DOSE_TYPE_PROPERTY_NAME;
/**
* Name of the property that encodes the optional string property holding the description information from the tag (300A,0016) - Dose Reference Description.
*/
static const std::string REFERENCE_DESCRIPTION_DOSE_PROPERTY_NAME;
/**
* Name of the property that encodes the optional string property holding the information from the tag (3004,000A) - Dose Summation Type.
* This contains useful information for medical doctors
*/
static const std::string DOSE_SUMMATION_TYPE_PROPERTY_NAME;
/**
* Name of the property that encodes the number of fractions.
* It is for example in DICOM stored in tag (300A,0078) - Number of Fractions Prescribed (from the RTPLAN file if this file exists).
* This value could be used to further scale the dose according to dose summation type.
* For example a given plan consists of 8 fractions. Scaling the fraction dose by 8 gives the complete planned dose.
*/
static const std::string DOSE_FRACTION_COUNT_PROPERTY_NAME;
/**
* Name of the property that encodes the number of beams.
* It is for example in DICOM stored in tag (300A,0080) - Number of Beams (from the RTPLAN file if this file exists).
*/
static const std::string DOSE_FRACTION_NUMBER_OF_BEAMS_PROPERTY_NAME;
/**
* Name of the property that encodes the radiation type of beams.
* It is for example in DICOM stored in tag (300A,00C6) - Radiation Type (from the RTPLAN file if this file exists).
*/
static const std::string DOSE_RADIATION_TYPE_PROPERTY_NAME;
/**
* Name of the property that encodes if the iso line rendering should be activated for the node.
*/
static const std::string DOSE_SHOW_ISOLINES_PROPERTY_NAME;
/**
* Name of the property that encodes if the color wash rendering should be activated for the node.
*/
static const std::string DOSE_SHOW_COLORWASH_PROPERTY_NAME;
/**
* Name of the property that encodes if the set of iso levels should be used to visualize the dose distribution.
*/
static const std::string DOSE_ISO_LEVELS_PROPERTY_NAME;
/**
* Name of the property that encodes user defined iso values that mark special dose values in the distribution.
*/
static const std::string DOSE_FREE_ISO_VALUES_PROPERTY_NAME;
};
}
#endif
diff --git a/Modules/RT/src/mitkDICOMRTMimeTypes.cpp b/Modules/RT/src/mitkDICOMRTMimeTypes.cpp
index d72e25afa4..55f25884ea 100644
--- a/Modules/RT/src/mitkDICOMRTMimeTypes.cpp
+++ b/Modules/RT/src/mitkDICOMRTMimeTypes.cpp
@@ -1,244 +1,244 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkDICOMRTMimeTypes.h>
#include <mitkIOMimeTypes.h>
#include <mitkDICOMDCMTKTagScanner.h>
#include <mitkDICOMTagPath.h>
#include <mitkDICOMFileReaderSelector.h>
#include <mitkDICOMFileReader.h>
#include <itksys/SystemTools.hxx>
namespace mitk
{
std::array<std::unique_ptr<CustomMimeType>, 3> DICOMRTMimeTypes::Get()
{
return {
std::make_unique<RTDoseMimeType>(),
std::make_unique<RTPlanMimeType>(),
std::make_unique<RTStructMimeType>()
};
}
// Mime Types
DICOMRTMimeTypes::RTDoseMimeType::RTDoseMimeType()
: CustomMimeType(DICOMRT_DOSE_MIMETYPE_NAME())
{
std::string category = "DICOMRT";
this->SetCategory(category);
this->SetComment("RTDose");
this->AddExtension("dcm");
}
bool DICOMRTMimeTypes::RTDoseMimeType::AppliesTo(const std::string &path) const
{
bool canRead( CustomMimeType::AppliesTo(path) );
if (!canRead)
{
return false;
}
// fix for bug 18572
// Currently this function is called for writing as well as reading, in that case
- // the image information can of course not be parsed or further identifyed.
- //so as a work arround we just return the current canRead if the file does not exist.
+ // the image information can of course not be parsed or further identified.
+ //so as a work around we just return the current canRead if the file does not exist.
if (!itksys::SystemTools::FileExists(path.c_str()))
{
return canRead;
}
// end fix for bug 18572
auto modality = GetModality(path);
if (modality == "RTDOSE")
{
return canReadByDicomFileReader(path);
}
return false;
}
std::string DICOMRTMimeTypes::GetModality(const std::string & path)
{
const auto modalityTagPath = DICOMTagPath(0x0008, 0x0060);
mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New();
scanner->SetInputFiles({ path });
scanner->AddTagPaths({ modalityTagPath });
scanner->Scan();
mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList();
std::string modality = "";
if (frames.empty())
return modality;
auto findings = frames.front()->GetTagValueAsString(modalityTagPath);
modality = findings.front().value;
return modality;
}
bool DICOMRTMimeTypes::canReadByDicomFileReader(const std::string & filename)
{
mitk::DICOMFileReaderSelector::Pointer selector = mitk::DICOMFileReaderSelector::New();
selector->LoadBuiltIn3DConfigs();
selector->SetInputFiles({ filename });
mitk::DICOMFileReader::Pointer reader = selector->GetFirstReaderWithMinimumNumberOfOutputImages();
return reader.IsNotNull();
}
DICOMRTMimeTypes::RTDoseMimeType* DICOMRTMimeTypes::RTDoseMimeType::Clone() const
{
return new RTDoseMimeType(*this);
}
DICOMRTMimeTypes::RTStructMimeType::RTStructMimeType()
: CustomMimeType(DICOMRT_STRUCT_MIMETYPE_NAME())
{
std::string category = "DICOMRT";
this->SetCategory(category);
this->SetComment("RTStruct");
this->AddExtension("dcm");
}
bool DICOMRTMimeTypes::RTStructMimeType::AppliesTo(const std::string &path) const
{
bool canRead(CustomMimeType::AppliesTo(path));
if (!canRead) {
return false;
}
// fix for bug 18572
// Currently this function is called for writing as well as reading, in that case
- // the image information can of course not be parsed or further identifyed.
- //so as a work arround we just return the current canRead if the file does not exist.
+ // the image information can of course not be parsed or further identified.
+ //so as a work around we just return the current canRead if the file does not exist.
if (!itksys::SystemTools::FileExists(path.c_str()))
{
return canRead;
}
// end fix for bug 18572
auto modality = GetModality(path);
if (modality == "RTSTRUCT") {
return true;
}
return false;
}
DICOMRTMimeTypes::RTStructMimeType* DICOMRTMimeTypes::RTStructMimeType::Clone() const
{
return new RTStructMimeType(*this);
}
DICOMRTMimeTypes::RTPlanMimeType::RTPlanMimeType()
: CustomMimeType(DICOMRT_PLAN_MIMETYPE_NAME())
{
std::string category = "DICOMRT";
this->SetCategory(category);
this->SetComment("RTPLAN");
this->AddExtension("dcm");
}
bool DICOMRTMimeTypes::RTPlanMimeType::AppliesTo(const std::string &path) const
{
bool canRead(CustomMimeType::AppliesTo(path));
if (!canRead) {
return false;
}
// fix for bug 18572
// Currently this function is called for writing as well as reading, in that case
- // the image information can of course not be parsed or further identifyed.
- //so as a work arround we just return the current canRead if the file does not exist.
+ // the image information can of course not be parsed or further identified.
+ //so as a work around we just return the current canRead if the file does not exist.
if (!itksys::SystemTools::FileExists(path.c_str()))
{
return canRead;
}
// end fix for bug 18572
auto modality = GetModality(path);
if (modality == "RTPLAN") {
return true;
}
return false;
}
DICOMRTMimeTypes::RTPlanMimeType* DICOMRTMimeTypes::RTPlanMimeType::Clone() const
{
return new RTPlanMimeType(*this);
}
DICOMRTMimeTypes::RTDoseMimeType DICOMRTMimeTypes::DICOMRT_DOSE_MIMETYPE()
{
return RTDoseMimeType();
}
DICOMRTMimeTypes::RTStructMimeType DICOMRTMimeTypes::DICOMRT_STRUCT_MIMETYPE()
{
return RTStructMimeType();
}
DICOMRTMimeTypes::RTPlanMimeType DICOMRTMimeTypes::DICOMRT_PLAN_MIMETYPE()
{
return RTPlanMimeType();
}
// Names
std::string DICOMRTMimeTypes::DICOMRT_DOSE_MIMETYPE_NAME()
{
return IOMimeTypes::DEFAULT_BASE_NAME() + ".dicomrt.dose";
}
std::string DICOMRTMimeTypes::DICOMRT_STRUCT_MIMETYPE_NAME()
{
return IOMimeTypes::DEFAULT_BASE_NAME() + ".dicomrt.struct";
}
std::string DICOMRTMimeTypes::DICOMRT_PLAN_MIMETYPE_NAME()
{
return IOMimeTypes::DEFAULT_BASE_NAME() + ".dicomrt.plan";
}
// Descriptions
std::string DICOMRTMimeTypes::DICOMRT_DOSE_MIMETYPE_DESCRIPTION()
{
return "RTDOSE reader";
}
std::string DICOMRTMimeTypes::DICOMRT_STRUCT_MIMETYPE_DESCRIPTION()
{
return "RTSTRUCT reader";
}
std::string DICOMRTMimeTypes::DICOMRT_PLAN_MIMETYPE_DESCRIPTION()
{
return "RTPLAN reader";
}
}
diff --git a/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp b/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp
index e3698ba136..083611bf1a 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 sagittal) 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
+ // set the vtk output property to true, makes sure that no unneeded mitk image conversion
// 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
+ // this is needed when thick mode was enable before. 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
+ // Bounds information for reslicing (only required 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.
+ // in every iteration 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::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 sagittal
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 sagittal)
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/RTUI/Helper/mitkRTUIConstants.h b/Modules/RTUI/Helper/mitkRTUIConstants.h
index e0d280938b..b0b9b78aed 100644
--- a/Modules/RTUI/Helper/mitkRTUIConstants.h
+++ b/Modules/RTUI/Helper/mitkRTUIConstants.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 mitkRTUIConstants_h
#define mitkRTUIConstants_h
#include <string>
#include <mitkDoseValueType.h>
#include "MitkRTUIExports.h"
namespace mitk
{
struct MITKRTUI_EXPORT RTUIConstants
{
/** ID/Path of main preference node for RT UI. */
static const std::string ROOT_PREFERENCE_NODE_ID;
- /** Bool that indicates how the prescribed dose should be defined, if unkown. True: UNKNOWN_PRESCRIBED_DOSE_HANDLING_VALUE should be used as
- default dose value in Gy; False: it should be used as fraction of the max dose to determin the prescribed dose.*/
+ /** Bool that indicates how the prescribed dose should be defined, if unknown. True: UNKNOWN_PRESCRIBED_DOSE_HANDLING_VALUE should be used as
+ default dose value in Gy; False: it should be used as fraction of the max dose to determine the prescribed dose.*/
static const std::string UNKNOWN_PRESCRIBED_DOSE_HANDLING_AS_DEFAULT_ID;
- /** Value that is used to determin unknown prescribed doses.*/
+ /** Value that is used to determine unknown prescribed doses.*/
static const std::string UNKNOWN_PRESCRIBED_DOSE_HANDLING_VALUE_ID;
/** ID/Path of main preference node where all iso dose level presets are stored (e.g. ROOT_ISO_PRESETS_PREFERENCE_NODE_ID+"/[Preset1]"). */
static const std::string ROOT_ISO_PRESETS_PREFERENCE_NODE_ID;
/** ID/Path of main preference for dose visualization preferences. */
static const std::string ROOT_DOSE_VIS_PREFERENCE_NODE_ID;
/** ID for the reference dose stored as preference. */
static const std::string REFERENCE_DOSE_ID;
/** ID for the preference flag that indicates if the reference dose is synced for all nodes*/
static const std::string GLOBAL_REFERENCE_DOSE_SYNC_ID;
- /** ID for the flag if dose should be displayed as absoulte dose. */
+ /** ID for the flag if dose should be displayed as absolute dose. */
static const std::string DOSE_DISPLAY_ABSOLUTE_ID;
- /** ID for the global visiblity switch for iso line visualization. */
+ /** ID for the global visibility switch for iso line visualization. */
static const std::string GLOBAL_VISIBILITY_ISOLINES_ID;
- /** ID for the global visiblity switch for color wash visualization. */
+ /** ID for the global visibility switch for color wash visualization. */
static const std::string GLOBAL_VISIBILITY_COLORWASH_ID;
/** ID for the selected iso preset that should be used (value of ROOT_ISO_PRESETS_PREFERENCE_NODE_ID + value of this key can
be used to construct the passed to the selected preset. */
static const std::string SELECTED_ISO_PRESET_ID;
/** ID for the relative dose value of an iso dose level. */
static const std::string ISO_LEVEL_DOSE_VALUE_ID;
/** ID for the color (red component) of an iso dose level. */
static const std::string ISO_LEVEL_COLOR_RED_ID;
/** ID for the color (green component) of an iso dose level. */
static const std::string ISO_LEVEL_COLOR_GREEN_ID;
/** ID for the color (blue component) of an iso dose level. */
static const std::string ISO_LEVEL_COLOR_BLUE_ID;
- /** ID for the visiblity switch for iso line visualization. */
+ /** ID for the visibility switch for iso line visualization. */
static const std::string ISO_LEVEL_VISIBILITY_ISOLINES_ID;
- /** ID for the visiblity switch for color wash visualization. */
+ /** ID for the visibility switch for color wash visualization. */
static const std::string ISO_LEVEL_VISIBILITY_COLORWASH_ID;
/** Default value used as reference_dose_if not defined by application or data node*/
static const DoseValueAbs DEFAULT_REFERENCE_DOSE_VALUE;
};
struct MITKRTUI_EXPORT RTCTKEventConstants
{
/** ID/Path of main preference node for RT UI. */
static const std::string TOPIC_REFERENCE_DOSE;
static const std::string TOPIC_REFERENCE_DOSE_CHANGED;
static const std::string TOPIC_ISO_DOSE_LEVEL_PRESETS;
static const std::string TOPIC_ISO_DOSE_LEVEL_PRESETS_CHANGED;
static const std::string TOPIC_GLOBAL_VISIBILITY_CHANGED;
};
}
#endif
diff --git a/Modules/RTUI/Qmitk/QmitkIsoDoseLevelSetModel.h b/Modules/RTUI/Qmitk/QmitkIsoDoseLevelSetModel.h
index afca7d0327..959cdeff2e 100644
--- a/Modules/RTUI/Qmitk/QmitkIsoDoseLevelSetModel.h
+++ b/Modules/RTUI/Qmitk/QmitkIsoDoseLevelSetModel.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 QmitkIsoDoseLevelSetModel_h
#define QmitkIsoDoseLevelSetModel_h
#include <QAbstractTableModel>
#include "mitkIsoDoseLevelCollections.h"
#include "MitkRTUIExports.h"
/*!
\class QmitkIsoDoseLevelSetModel
Model that handles a iso dose level set and allows viewing and editing of its contents.
Please see special delegates (QmitkDoseColorDelegate, QmitkDoseValueDelegate, QmitkDoseVisualStyleDelegate) to
handle visualization and editing in views that work on this model.
\warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation.
*/
class MITKRTUI_EXPORT QmitkIsoDoseLevelSetModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit QmitkIsoDoseLevelSetModel(QObject *parent = nullptr);
~QmitkIsoDoseLevelSetModel() override {};
/** Sets the data handled by the model and resets the modified flag*/
void setIsoDoseLevelSet(mitk::IsoDoseLevelSet *pSet);
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;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool getShowAbsoluteDose() const;
mitk::DoseValueAbs getReferenceDose() const;
bool getVisibilityEditOnly() const;
void switchVisibilityIsoLines(bool activate);
void switchVisibilityColorWash(bool activate);
void invertVisibilityIsoLines();
void invertVisibilityColorWash();
void swapVisibility();
void addLevel();
void deleteLevel(const QModelIndex &index);
/**Indicates if the content of the model was modified since the data was set via setIsoDoseLevelSet()*/
bool isModified();
public Q_SLOTS:
/**
* \brief Slot that can be used to set the prescribed dose.
*/
void setReferenceDose(double newReferenceDose);
/**
* \brief Slot that can be used to adjust whether the dose should be displayed in absolute or relative units.
*/
void setShowAbsoluteDose(bool showAbsoluteDose);
/**
- * \brief Slat that can be used to adjust wether the model allows to edit only visibilities (no dose value or color)
+ * \brief Slat that can be used to adjust whether the model allows to edit only visibilities (no dose value or color)
*/
void setVisibilityEditOnly(bool onlyVisibility);
private:
mitk::IsoDoseLevelSet::Pointer m_DoseSet;
bool m_showAbsoluteDose;
bool m_visibilityEditOnly;
mitk::DoseValueAbs m_referenceDose;
/** Indicates if the data of the model was modified, since the model was set. */
bool m_modified;
};
#endif
diff --git a/Modules/SceneSerialization/src/mitkGeometryDataSerializer.h b/Modules/SceneSerialization/src/mitkGeometryDataSerializer.h
index a42d01afd6..d7574701ea 100644
--- a/Modules/SceneSerialization/src/mitkGeometryDataSerializer.h
+++ b/Modules/SceneSerialization/src/mitkGeometryDataSerializer.h
@@ -1,39 +1,39 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkGeometryDataSerializer_h
#define mitkGeometryDataSerializer_h
#include "mitkBaseDataSerializer.h"
namespace mitk
{
/**
\brief Serializes mitk::GeometryData for mitk::SceneIO.
\warning depends on mitk::GeometryDataWriterService which is first implemented only for the Geometry3D class!
- See current status of that class if you want to use other geomety types.
+ See current status of that class if you want to use other geometry types.
*/
class GeometryDataSerializer : public BaseDataSerializer
{
public:
mitkClassMacro(GeometryDataSerializer, BaseDataSerializer);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self) std::string Serialize() override;
protected:
GeometryDataSerializer();
~GeometryDataSerializer() override;
};
} // namespace
#endif
diff --git a/Modules/SceneSerialization/src/mitkSceneIO.cpp b/Modules/SceneSerialization/src/mitkSceneIO.cpp
index 6072a7bc3f..53517d2563 100644
--- a/Modules/SceneSerialization/src/mitkSceneIO.cpp
+++ b/Modules/SceneSerialization/src/mitkSceneIO.cpp
@@ -1,578 +1,578 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <Poco/Delegate.h>
#include <Poco/Path.h>
#include <Poco/TemporaryFile.h>
#include <Poco/Zip/Compress.h>
#include <Poco/Zip/Decompress.h>
#include "mitkBaseDataSerializer.h"
#include "mitkPropertyListSerializer.h"
#include "mitkSceneIO.h"
#include "mitkSceneReader.h"
#include "mitkBaseRenderer.h"
#include "mitkProgressBar.h"
#include "mitkRenderingManager.h"
#include "mitkStandaloneDataStorage.h"
#include <mitkLocaleSwitch.h>
#include <mitkStandardFileLocations.h>
#include <mitkUIDGenerator.h>
#include <itkObjectFactoryBase.h>
#include <fstream>
#include <mitkIOUtil.h>
#include <sstream>
#include "itksys/SystemTools.hxx"
#include <tinyxml2.h>
mitk::SceneIO::SceneIO() : m_WorkingDirectory(""), m_UnzipErrors(0)
{
}
mitk::SceneIO::~SceneIO()
{
}
std::string mitk::SceneIO::CreateEmptyTempDirectory()
{
mitk::UIDGenerator uidGen;
// std::string returnValue = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() +
// Poco::Path::separator() + "SceneIOTemp" + uidGen.GetUID();
std::string returnValue = Poco::Path::temp() + "SceneIOTemp" + uidGen.GetUID();
std::string uniquename = returnValue + Poco::Path::separator();
Poco::File tempdir(uniquename);
try
{
bool existsNot = tempdir.createDirectory();
if (!existsNot)
{
- MITK_ERROR << "Warning: Directory already exitsts: " << uniquename << " (choosing another)";
+ MITK_ERROR << "Warning: Directory already exists: " << uniquename << " (choosing another)";
returnValue = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + Poco::Path::separator() +
"SceneIOTempDirectory" + uidGen.GetUID();
uniquename = returnValue + Poco::Path::separator();
Poco::File tempdir2(uniquename);
if (!tempdir2.createDirectory())
{
- MITK_ERROR << "Warning: Second directory also already exitsts: " << uniquename;
+ MITK_ERROR << "Warning: Second directory also already exists: " << uniquename;
}
}
}
catch (std::exception &e)
{
MITK_ERROR << "Could not create temporary directory " << uniquename << ":" << e.what();
return "";
}
return returnValue;
}
mitk::DataStorage::Pointer mitk::SceneIO::LoadScene(const std::string &filename,
DataStorage *pStorage,
bool clearStorageFirst)
{
mitk::LocaleSwitch localeSwitch("C");
// prepare data storage
DataStorage::Pointer storage = pStorage;
if (storage.IsNull())
{
storage = StandaloneDataStorage::New().GetPointer();
}
// test input filename
if (filename.empty())
{
MITK_ERROR << "No filename given. Not possible to load scene.";
return storage;
}
// test if filename can be read
std::ifstream file(filename.c_str(), std::ios::binary);
if (!file.good())
{
MITK_ERROR << "Cannot open '" << filename << "' for reading";
return storage;
}
// get new temporary directory
m_WorkingDirectory = CreateEmptyTempDirectory();
if (m_WorkingDirectory.empty())
{
MITK_ERROR << "Could not create temporary directory. Cannot open scene files.";
return storage;
}
// unzip all filenames contents to temp dir
m_UnzipErrors = 0;
Poco::Zip::Decompress unzipper(file, Poco::Path(m_WorkingDirectory));
unzipper.EError += Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string>>(
this, &SceneIO::OnUnzipError);
unzipper.EOk += Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const Poco::Path>>(
this, &SceneIO::OnUnzipOk);
unzipper.decompressAllFiles();
unzipper.EError -= Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string>>(
this, &SceneIO::OnUnzipError);
unzipper.EOk -= Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const Poco::Path>>(
this, &SceneIO::OnUnzipOk);
if (m_UnzipErrors)
{
MITK_ERROR << "There were " << m_UnzipErrors << " errors unzipping '" << filename
<< "'. Will attempt to read whatever could be unzipped.";
}
// transcode locale-dependent string
m_WorkingDirectory = Poco::Path::transcode (m_WorkingDirectory);
auto indexFile = m_WorkingDirectory + mitk::IOUtil::GetDirectorySeparator() + "index.xml";
storage = LoadSceneUnzipped(indexFile, storage, clearStorageFirst);
// delete temp directory
try
{
Poco::File deleteDir(m_WorkingDirectory);
deleteDir.remove(true); // recursive
}
catch (...)
{
MITK_ERROR << "Could not delete temporary directory " << m_WorkingDirectory;
}
- // return new data storage, even if empty or uncomplete (return as much as possible but notify calling method)
+ // return new data storage, even if empty or incomplete (return as much as possible but notify calling method)
return storage;
}
mitk::DataStorage::Pointer mitk::SceneIO::LoadSceneUnzipped(const std::string &indexfilename,
DataStorage *pStorage,
bool clearStorageFirst)
{
mitk::LocaleSwitch localeSwitch("C");
// prepare data storage
DataStorage::Pointer storage = pStorage;
if (storage.IsNull())
{
storage = StandaloneDataStorage::New().GetPointer();
}
if (clearStorageFirst)
{
try
{
storage->Remove(storage->GetAll());
}
catch (...)
{
MITK_ERROR << "DataStorage cannot be cleared properly.";
}
}
// test input filename
if (indexfilename.empty())
{
MITK_ERROR << "No filename given. Not possible to load scene.";
return storage;
}
// transcode locale-dependent string
std::string tempfilename;
std::string workingDir;
itksys::SystemTools::SplitProgramPath(indexfilename, workingDir, tempfilename);
// test if index.xml exists
// parse index.xml with TinyXML
tinyxml2::XMLDocument document;
if (tinyxml2::XML_SUCCESS != document.LoadFile(indexfilename.c_str()))
{
MITK_ERROR << "Could not open/read/parse " << workingDir << mitk::IOUtil::GetDirectorySeparator()
<< "index.xml\nTinyXML reports: " << document.ErrorStr() << std::endl;
return storage;
}
SceneReader::Pointer reader = SceneReader::New();
if (!reader->LoadScene(document, workingDir, storage))
{
MITK_ERROR << "There were errors while loading scene file " << indexfilename << ". Your data may be corrupted";
}
- // return new data storage, even if empty or uncomplete (return as much as possible but notify calling method)
+ // return new data storage, even if empty or incomplete (return as much as possible but notify calling method)
return storage;
}
bool mitk::SceneIO::SaveScene(DataStorage::SetOfObjects::ConstPointer sceneNodes,
const DataStorage *storage,
const std::string &filename)
{
if (!sceneNodes)
{
MITK_ERROR << "No set of nodes given. Not possible to save scene.";
return false;
}
if (!storage)
{
MITK_ERROR << "No data storage given. Not possible to save scene."; // \TODO: Technically, it would be possible to
// save the nodes without their relation
return false;
}
if (filename.empty())
{
MITK_ERROR << "No filename given. Not possible to save scene.";
return false;
}
mitk::LocaleSwitch localeSwitch("C");
try
{
m_FailedNodes = DataStorage::SetOfObjects::New();
m_FailedProperties = PropertyList::New();
// start XML DOM
tinyxml2::XMLDocument document;
document.InsertEndChild(document.NewDeclaration());
auto *version = document.NewElement("Version");
version->SetAttribute("Writer", __FILE__);
version->SetAttribute("Revision", "$Revision: 17055 $");
version->SetAttribute("FileVersion", 1);
document.InsertEndChild(version);
// DataStorage::SetOfObjects::ConstPointer sceneNodes = storage->GetSubset( predicate );
if (sceneNodes.IsNull())
{
MITK_WARN << "Saving empty scene to " << filename;
}
else
{
if (sceneNodes->size() == 0)
{
MITK_WARN << "Saving empty scene to " << filename;
}
MITK_INFO << "Storing scene with " << sceneNodes->size() << " objects to " << filename;
m_WorkingDirectory = CreateEmptyTempDirectory();
if (m_WorkingDirectory.empty())
{
MITK_ERROR << "Could not create temporary directory. Cannot create scene files.";
return false;
}
ProgressBar::GetInstance()->AddStepsToDo(sceneNodes->size());
// find out about dependencies
typedef std::map<DataNode *, std::string> UIDMapType;
typedef std::map<DataNode *, std::list<std::string>> SourcesMapType;
UIDMapType nodeUIDs; // for dependencies: ID of each node
SourcesMapType sourceUIDs; // for dependencies: IDs of a node's parent nodes
UIDGenerator nodeUIDGen("OBJECT_");
for (auto iter = sceneNodes->begin(); iter != sceneNodes->end(); ++iter)
{
DataNode *node = iter->GetPointer();
if (!node)
continue; // unlikely event that we get a nullptr pointer as an object for saving. just ignore
// generate UIDs for all source objects
DataStorage::SetOfObjects::ConstPointer sourceObjects = storage->GetSources(node);
for (auto sourceIter = sourceObjects->begin();
sourceIter != sourceObjects->end();
++sourceIter)
{
if (std::find(sceneNodes->begin(), sceneNodes->end(), *sourceIter) == sceneNodes->end())
continue; // source is not saved, so don't generate a UID for this source
// create a uid for the parent object
if (nodeUIDs[*sourceIter].empty())
{
nodeUIDs[*sourceIter] = nodeUIDGen.GetUID();
}
// store this dependency for writing
sourceUIDs[node].push_back(nodeUIDs[*sourceIter]);
}
if (nodeUIDs[node].empty())
{
nodeUIDs[node] = nodeUIDGen.GetUID();
}
}
// write out objects, dependencies and properties
for (auto iter = sceneNodes->begin(); iter != sceneNodes->end(); ++iter)
{
DataNode *node = iter->GetPointer();
if (node)
{
auto *nodeElement = document.NewElement("node");
std::string filenameHint(node->GetName());
filenameHint = itksys::SystemTools::MakeCindentifier(
filenameHint.c_str()); // escape filename <-- only allow [A-Za-z0-9_], replace everything else with _
// store dependencies
auto searchUIDIter = nodeUIDs.find(node);
if (searchUIDIter != nodeUIDs.end())
{
// store this node's ID
nodeElement->SetAttribute("UID", searchUIDIter->second.c_str());
}
auto searchSourcesIter = sourceUIDs.find(node);
if (searchSourcesIter != sourceUIDs.end())
{
// store all source IDs
for (auto sourceUIDIter = searchSourcesIter->second.begin();
sourceUIDIter != searchSourcesIter->second.end();
++sourceUIDIter)
{
auto *uidElement = document.NewElement("source");
uidElement->SetAttribute("UID", sourceUIDIter->c_str());
nodeElement->InsertEndChild(uidElement);
}
}
// store basedata
if (BaseData *data = node->GetData())
{
// std::string filenameHint( node->GetName() );
bool error(false);
auto *dataElement = SaveBaseData(document, data, filenameHint, error); // returns a reference to a file
if (error)
{
m_FailedNodes->push_back(node);
}
// store basedata properties
PropertyList *propertyList = data->GetPropertyList();
if (propertyList && !propertyList->IsEmpty())
{
auto *baseDataPropertiesElement =
SavePropertyList(document, propertyList, filenameHint + "-data"); // returns a reference to a file
dataElement->InsertEndChild(baseDataPropertiesElement);
}
nodeElement->InsertEndChild(dataElement);
}
// store all renderwindow specific propertylists
mitk::DataNode::PropertyListKeyNames propertyListKeys = node->GetPropertyListNames();
for (const auto &renderWindowName : propertyListKeys)
{
PropertyList *propertyList = node->GetPropertyList(renderWindowName);
if (propertyList && !propertyList->IsEmpty())
{
auto *renderWindowPropertiesElement =
SavePropertyList(document, propertyList, filenameHint + "-" + renderWindowName); // returns a reference to a file
renderWindowPropertiesElement->SetAttribute("renderwindow", renderWindowName.c_str());
nodeElement->InsertEndChild(renderWindowPropertiesElement);
}
}
// don't forget the renderwindow independent list
PropertyList *propertyList = node->GetPropertyList();
if (propertyList && !propertyList->IsEmpty())
{
auto *propertiesElement =
SavePropertyList(document, propertyList, filenameHint + "-node"); // returns a reference to a file
nodeElement->InsertEndChild(propertiesElement);
}
document.InsertEndChild(nodeElement);
}
else
{
MITK_WARN << "Ignoring nullptr node during scene serialization.";
}
ProgressBar::GetInstance()->Progress();
} // end for all nodes
} // end if sceneNodes
std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory );
auto xmlFilename = defaultLocale_WorkingDirectory + Poco::Path::separator() + "index.xml";
if (tinyxml2::XML_SUCCESS != document.SaveFile(xmlFilename.c_str()))
{
MITK_ERROR << "Could not write scene to " << defaultLocale_WorkingDirectory << Poco::Path::separator() << "index.xml"
<< "\nTinyXML reports '" << document.ErrorStr() << "'";
return false;
}
else
{
try
{
Poco::File deleteFile(filename.c_str());
if (deleteFile.exists())
{
deleteFile.remove();
}
// create zip at filename
std::ofstream file(filename.c_str(), std::ios::binary | std::ios::out);
if (!file.good())
{
MITK_ERROR << "Could not open a zip file for writing: '" << filename << "'";
return false;
}
else
{
Poco::Zip::Compress zipper(file, true);
Poco::Path tmpdir(m_WorkingDirectory);
zipper.addRecursive(tmpdir);
zipper.close();
}
try
{
Poco::File deleteDir(m_WorkingDirectory);
deleteDir.remove(true); // recursive
}
catch (...)
{
MITK_ERROR << "Could not delete temporary directory " << m_WorkingDirectory;
return false; // ok?
}
}
catch (std::exception &e)
{
MITK_ERROR << "Could not create ZIP file from " << m_WorkingDirectory << "\nReason: " << e.what();
return false;
}
return true;
}
}
catch (std::exception &e)
{
MITK_ERROR << "Caught exception during saving temporary files to disk. Error description: '" << e.what() << "'";
return false;
}
}
tinyxml2::XMLElement *mitk::SceneIO::SaveBaseData(tinyxml2::XMLDocument &doc, BaseData *data, const std::string &filenamehint, bool &error)
{
assert(data);
error = true;
// find correct serializer
// the serializer must
// - create a file containing all information to recreate the BaseData object --> needs to know where to put this
// file (and a filename?)
// - TODO what to do about writers that creates one file per timestep?
auto *element = doc.NewElement("data");
element->SetAttribute("type", data->GetNameOfClass());
// construct name of serializer class
std::string serializername(data->GetNameOfClass());
serializername += "Serializer";
std::list<itk::LightObject::Pointer> thingsThatCanSerializeThis =
itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str());
if (thingsThatCanSerializeThis.size() < 1)
{
MITK_ERROR << "No serializer found for " << data->GetNameOfClass() << ". Skipping object";
}
for (auto iter = thingsThatCanSerializeThis.begin();
iter != thingsThatCanSerializeThis.end();
++iter)
{
if (auto *serializer = dynamic_cast<BaseDataSerializer *>(iter->GetPointer()))
{
serializer->SetData(data);
serializer->SetFilenameHint(filenamehint);
std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory );
serializer->SetWorkingDirectory(defaultLocale_WorkingDirectory);
try
{
std::string writtenfilename = serializer->Serialize();
element->SetAttribute("file", writtenfilename.c_str());
if (!writtenfilename.empty())
error = false;
}
catch (std::exception &e)
{
MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what();
}
break;
}
}
element->SetAttribute("UID", data->GetUID().c_str());
return element;
}
tinyxml2::XMLElement *mitk::SceneIO::SavePropertyList(tinyxml2::XMLDocument &doc, PropertyList *propertyList, const std::string &filenamehint)
{
assert(propertyList);
// - TODO what to do about shared properties (same object in two lists or behind several keys)?
auto *element = doc.NewElement("properties");
// construct name of serializer class
PropertyListSerializer::Pointer serializer = PropertyListSerializer::New();
serializer->SetPropertyList(propertyList);
serializer->SetFilenameHint(filenamehint);
std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory );
serializer->SetWorkingDirectory(defaultLocale_WorkingDirectory);
try
{
std::string writtenfilename = serializer->Serialize();
element->SetAttribute("file", writtenfilename.c_str());
PropertyList::Pointer failedProperties = serializer->GetFailedProperties();
if (failedProperties.IsNotNull())
{
// move failed properties to global list
m_FailedProperties->ConcatenatePropertyList(failedProperties, true);
}
}
catch (std::exception &e)
{
MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what();
}
return element;
}
const mitk::SceneIO::FailedBaseDataListType *mitk::SceneIO::GetFailedNodes()
{
return m_FailedNodes.GetPointer();
}
const mitk::PropertyList *mitk::SceneIO::GetFailedProperties()
{
return m_FailedProperties;
}
void mitk::SceneIO::OnUnzipError(const void * /*pSender*/,
std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> &info)
{
++m_UnzipErrors;
MITK_ERROR << "Error while unzipping: " << info.second;
}
void mitk::SceneIO::OnUnzipOk(const void * /*pSender*/,
std::pair<const Poco::Zip::ZipLocalFileHeader, const Poco::Path> & /*info*/)
{
// MITK_INFO << "Unzipped ok: " << info.second.toString();
}
diff --git a/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp b/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp
index 9337bdf315..271bb45d9b 100644
--- a/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp
+++ b/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp
@@ -1,468 +1,468 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkSceneReaderV1.h"
#include "Poco/Path.h"
#include "mitkBaseRenderer.h"
#include "mitkIOUtil.h"
#include "mitkProgressBar.h"
#include "mitkPropertyListDeserializer.h"
#include "mitkSerializerMacros.h"
#include <mitkUIDManipulator.h>
#include <mitkRenderingModeProperty.h>
#include <tinyxml2.h>
-#include <filesystem>
+#include <mitkFileSystem.h>
#include <map>
MITK_REGISTER_SERIALIZER(SceneReaderV1)
namespace
{
typedef std::pair<mitk::DataNode::Pointer, std::list<std::string>> NodesAndParentsPair;
bool NodeSortByLayerIsLessThan(const NodesAndParentsPair &left, const NodesAndParentsPair &right)
{
if (left.first.IsNotNull() && right.first.IsNotNull())
{
int leftLayer;
int rightLayer;
if (left.first->GetIntProperty("layer", leftLayer) && right.first->GetIntProperty("layer", rightLayer))
{
return leftLayer < rightLayer;
}
else
{
// fall back to name sort
return left.first->GetName() < right.first->GetName();
}
}
// in all other cases, fall back to stupid pointer comparison
// this is not reasonable but at least answers the sorting
// question clearly
return left.first.GetPointer() < right.first.GetPointer();
}
// This is a workaround until we are able to save time-related information in an
// actual file format of surfaces.
void ApplyProportionalTimeGeometryProperties(mitk::BaseData* data)
{
auto* geometry = dynamic_cast<mitk::ProportionalTimeGeometry*>(data->GetTimeGeometry());
if (nullptr == geometry)
return;
auto properties = data->GetPropertyList();
float value = 0.0f;
if (properties->GetFloatProperty("ProportionalTimeGeometry.FirstTimePoint", value))
{
if (value == -std::numeric_limits<float>::infinity())
value = std::numeric_limits<float>::lowest();
geometry->SetFirstTimePoint(value);
}
if (properties->GetFloatProperty("ProportionalTimeGeometry.StepDuration", value))
geometry->SetStepDuration(value);
}
- mitk::PropertyList::Pointer DeserializeProperties(const tinyxml2::XMLElement *propertiesElement, const std::filesystem::path& basePath)
+ mitk::PropertyList::Pointer DeserializeProperties(const tinyxml2::XMLElement *propertiesElement, const fs::path& basePath)
{
if (propertiesElement == nullptr)
return nullptr;
- std::filesystem::path path(propertiesElement->Attribute("file"));
+ fs::path path(propertiesElement->Attribute("file"));
if (path.empty())
return nullptr;
if (!basePath.empty())
path = basePath / path;
auto deserializer = mitk::PropertyListDeserializer::New();
deserializer->SetFilename(path.string());
deserializer->Deserialize();
return deserializer->GetOutput();
}
}
bool mitk::SceneReaderV1::LoadScene(tinyxml2::XMLDocument &document, const std::string &workingDirectory, DataStorage *storage)
{
assert(storage);
bool error(false);
// TODO prepare to detect errors (such as cycles) from wrongly written or edited xml files
- // Get number of elements to initialze progress bar
+ // Get number of elements to initialize progress bar
// 1. if there is a <data type="..." file="..."> element,
// - construct a name for the appropriate serializer
// - try to instantiate this serializer via itk object factory
// - if serializer could be created, use it to read the file into a BaseData object
// - if successful, call the new node's SetData(..)
// create a node for the tag "data" and test if node was created
typedef std::vector<mitk::DataNode::Pointer> DataNodeVector;
DataNodeVector DataNodes;
unsigned int listSize = 0;
for (auto *element = document.FirstChildElement("node"); element != nullptr;
element = element->NextSiblingElement("node"))
{
++listSize;
}
ProgressBar::GetInstance()->AddStepsToDo(listSize * 2);
// Deserialize base data properties before reading the actual data to be
// able to provide them as read-only meta data to the data reader.
std::map<std::string, PropertyList::Pointer> baseDataPropertyLists;
for (auto *nodeElement = document.FirstChildElement("node"); nodeElement != nullptr;
nodeElement = nodeElement->NextSiblingElement("node"))
{
const auto *uid = nodeElement->Attribute("UID");
if (uid == nullptr)
continue;
auto *dataElement = nodeElement->FirstChildElement("data");
if (dataElement != nullptr)
{
auto properties = DeserializeProperties(dataElement->FirstChildElement("properties"), workingDirectory);
if (properties.IsNotNull())
baseDataPropertyLists[uid] = properties;
}
}
for (auto *element = document.FirstChildElement("node"); element != nullptr;
element = element->NextSiblingElement("node"))
{
mitk::PropertyList* properties = nullptr;
const auto *uid = element->Attribute("UID");
if (uid != nullptr)
{
auto iter = baseDataPropertyLists.find(uid);
if (iter != baseDataPropertyLists.end())
properties = iter->second;
}
const auto *dataElement = element->FirstChildElement("data");
auto dataNode = this->LoadBaseDataFromDataTag(dataElement, properties, workingDirectory, error);
if (dataNode.IsNull())
continue;
auto* baseData = dataNode->GetData();
if (baseData != nullptr && properties != nullptr)
{
baseData->SetPropertyList(properties);
ApplyProportionalTimeGeometryProperties(baseData);
}
DataNodes.push_back(dataNode);
ProgressBar::GetInstance()->Progress();
}
// iterate all nodes
// first level nodes should be <node> elements
auto nit = DataNodes.begin();
for (auto *element = document.FirstChildElement("node"); element != nullptr || nit != DataNodes.end();
element = element->NextSiblingElement("node"), ++nit)
{
mitk::DataNode::Pointer node = *nit;
// 1. check child nodes
const char *uida = element->Attribute("UID");
std::string uid("");
if (uida)
{
uid = uida;
m_NodeForID[uid] = node.GetPointer();
m_IDForNode[node.GetPointer()] = uid;
}
else
{
MITK_ERROR << "No UID found for current node. Node will have no parents.";
error = true;
}
// 2. if there are <properties> nodes,
// - instantiate the appropriate PropertyListDeSerializer
// - use them to construct PropertyList objects
// - add these properties to the node (if necessary, use renderwindow name)
bool success = DecorateNodeWithProperties(node, element, workingDirectory);
if (!success)
{
MITK_ERROR << "Could not load properties for node.";
error = true;
}
// remember node for later adding to DataStorage
m_OrderedNodePairs.push_back(std::make_pair(node, std::list<std::string>()));
// 3. if there are <source> elements, remember parent objects
for (auto *source = element->FirstChildElement("source"); source != nullptr;
source = source->NextSiblingElement("source"))
{
const char *sourceUID = source->Attribute("UID");
if (sourceUID)
{
m_OrderedNodePairs.back().second.push_back(std::string(sourceUID));
}
}
ProgressBar::GetInstance()->Progress();
} // end for all <node>
// sort our nodes by their "layer" property
// (to be inserted in that order)
m_OrderedNodePairs.sort(&NodeSortByLayerIsLessThan);
// remove all unknown parent UIDs
for (auto nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end();
++nodesIter)
{
for (auto parentsIter = nodesIter->second.begin();
parentsIter != nodesIter->second.end();)
{
if (m_NodeForID.find(*parentsIter) == m_NodeForID.end())
{
parentsIter = nodesIter->second.erase(parentsIter);
MITK_WARN << "Found a DataNode with unknown parents. Will add it to DataStorage without any parent objects.";
error = true;
}
else
{
++parentsIter;
}
}
}
// repeat the following loop ...
// ... for all created nodes
unsigned int lastMapSize(0);
while (lastMapSize !=
m_OrderedNodePairs
.size()) // this is to prevent infinite loops; each iteration must at least add one node to DataStorage
{
lastMapSize = m_OrderedNodePairs.size();
// iterate (layer) ordered nodes backwards
// we insert the highest layers first
for (auto nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end();
++nodesIter)
{
bool addThisNode(true);
// if any parent node is not yet in DataStorage, skip node for now and check later
for (auto parentsIter = nodesIter->second.begin();
parentsIter != nodesIter->second.end();
++parentsIter)
{
if (!storage->Exists(m_NodeForID[*parentsIter]))
{
addThisNode = false;
break;
}
}
if (addThisNode)
{
DataStorage::SetOfObjects::Pointer parents = DataStorage::SetOfObjects::New();
for (auto parentsIter = nodesIter->second.begin();
parentsIter != nodesIter->second.end();
++parentsIter)
{
parents->push_back(m_NodeForID[*parentsIter]);
}
// if all parents are found in datastorage (or are unknown), add node to DataStorage
storage->Add(nodesIter->first, parents);
// remove this node from m_OrderedNodePairs
m_OrderedNodePairs.erase(nodesIter);
// break this for loop because iterators are probably invalid
break;
}
}
}
// All nodes that are still in m_OrderedNodePairs at this point are not part of a proper directed graph structure.
// We'll add such nodes without any parent information.
for (auto nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end();
++nodesIter)
{
storage->Add(nodesIter->first);
MITK_WARN << "Encountered node that is not part of a directed graph structure. Will be added to DataStorage "
"without parents.";
error = true;
}
return !error;
}
mitk::DataNode::Pointer mitk::SceneReaderV1::LoadBaseDataFromDataTag(const tinyxml2::XMLElement *dataElement,
const PropertyList *properties,
const std::string &workingDirectory,
bool &error)
{
DataNode::Pointer node;
if (dataElement)
{
const char *filename = dataElement->Attribute("file");
if (filename && strlen(filename) != 0)
{
try
{
auto baseData = IOUtil::Load(workingDirectory + Poco::Path::separator() + filename, properties);
node = DataNode::New();
node->SetData(baseData);
}
catch (std::exception &e)
{
MITK_ERROR << "Error during attempt to read '" << filename << "'. Exception says: " << e.what();
error = true;
}
if (node.IsNull())
{
MITK_ERROR << "Error during attempt to read '" << filename << "'. Factory returned nullptr object.";
error = true;
}
}
else
{
MITK_ERROR << "File attribute of data tag is empty!";
error = true;
}
const char* dataUID = dataElement->Attribute("UID");
if (!error && dataUID != nullptr)
{
UIDManipulator manip(node->GetData());
manip.SetUID(dataUID);
}
}
// in case there was no <data> element we create a new empty node (for appending a propertylist later)
if (node.IsNull())
{
node = DataNode::New();
}
return node;
}
void mitk::SceneReaderV1::ClearNodePropertyListWithExceptions(DataNode &node, PropertyList &propertyList)
{
// Basically call propertyList.Clear(), but implement exceptions (see bug 19354)
BaseData *data = node.GetData();
PropertyList::Pointer propertiesToKeep = PropertyList::New();
if (dynamic_cast<Image *>(data))
{
/*
Older scene files (before changes of bug 17547) could contain
a RenderingMode property with value "LevelWindow_Color".
Since bug 17547 this value has been removed and replaced by
the default value LookupTable_LevelWindow_Color.
This new default value does only result in "black-to-white"
CT images (or others) if there is a corresponding lookup
table. Such a lookup table is provided as a default value
by the Image mapper. Since that value was never present in
older scene files, we do well in not removing the new
default value here. Otherwise the mapper would fall back
to another default which is all the colors of the rainbow :-(
*/
BaseProperty::Pointer lutProperty = propertyList.GetProperty("LookupTable");
propertiesToKeep->SetProperty("LookupTable", lutProperty);
/*
Older scene files (before changes of T14807) may contain
multi-component images without the "Image.Displayed Component"
property.
As the treatment as multi-component image and the corresponding
visualization options hinges on that property we should not delete
it, if it was added by the mapper.
This is a fix for the issue reported in T19919.
*/
BaseProperty::Pointer compProperty = propertyList.GetProperty("Image.Displayed Component");
if (compProperty.IsNotNull())
{
propertiesToKeep->SetProperty("Image.Displayed Component", compProperty);
}
}
propertyList.Clear();
propertyList.ConcatenatePropertyList(propertiesToKeep);
}
bool mitk::SceneReaderV1::DecorateNodeWithProperties(DataNode *node,
const tinyxml2::XMLElement *nodeElement,
const std::string &workingDirectory)
{
assert(node);
assert(nodeElement);
bool error(false);
for (auto *properties = nodeElement->FirstChildElement("properties"); properties != nullptr;
properties = properties->NextSiblingElement("properties"))
{
const char *propertiesfilea(properties->Attribute("file"));
std::string propertiesfile(propertiesfilea ? propertiesfilea : "");
const char *renderwindowa(properties->Attribute("renderwindow"));
std::string renderwindow(renderwindowa ? renderwindowa : "");
PropertyList::Pointer propertyList =
node->GetPropertyList(renderwindow); // DataNode implementation always returns a propertylist
ClearNodePropertyListWithExceptions(*node, *propertyList);
// use deserializer to construct new properties
PropertyListDeserializer::Pointer deserializer = PropertyListDeserializer::New();
deserializer->SetFilename(workingDirectory + Poco::Path::separator() + propertiesfile);
bool success = deserializer->Deserialize();
error |= !success;
PropertyList::Pointer readProperties = deserializer->GetOutput();
if (readProperties.IsNotNull())
{
propertyList->ConcatenatePropertyList(readProperties, true); // true = replace
}
else
{
MITK_ERROR << "Property list reader did not return a property list. This is an implementation error. Please tell "
"your developer.";
error = true;
}
}
return !error;
}
diff --git a/Modules/SceneSerialization/test/mitkDataStorageCompare.h b/Modules/SceneSerialization/test/mitkDataStorageCompare.h
index a2b9868cf5..6f374b61f8 100644
--- a/Modules/SceneSerialization/test/mitkDataStorageCompare.h
+++ b/Modules/SceneSerialization/test/mitkDataStorageCompare.h
@@ -1,245 +1,245 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkDataStorageCompare_h
#define mitkDataStorageCompare_h
#include "mitkDataStorage.h"
namespace mitk
{
/**
Helper class to compare two DataStorages.
This is purposely _not_ a mitk::Equal() function because
the main intention is to use this class for I/O testing,
and because the definition of equal is really not clear
for two DataStorages.
In the context of I/O tests, one could want to verify
(given two storages "reference" and "test")
* that all nodes from reference are in test (identification of nodes is difficult)
* all nodes from test are in reference
* all property lists are identical (types and values)
* all data is identical (types, values)
* all mappers are identical (types, not state)
* all interactors are identical (type)
* ... ?
This class offers a number of flags that describe one of
those aspects. These flags can be combined by the OR
operator ("|"). The Compare() method will only test those
aspects.
Once Compare() has been executed there are a couple of
methods to query the results or print a small report.
<b>Finding nodes</b>:
Identifying nodes to be compares poses a problem. This is
because we don't have node identifiers but only names.
Names are not unique and are not even required to exist
or to be non-empty.
This class does not solve this problem completely. It
identifies nodes by constructing an "identifier" that
includes the node name and the BaseData class name of
the node itself and all its direct and indirect parents.
This leaves certain cases where two nodes in the hierarchy
would get the same identifier. For the use in test cases
(\sa SceneIOTestScenarioProvider) this is sufficient, however,
since we can make sure that each node gets an individual name.
*/
class DataStorageCompare
{
public:
/**
* \brief Flag describing the aspects of comparing two DataStorages.
*
* Flag values can be combined by the "|" operator (bitwise or).
*/
typedef enum {
CMP_Nothing = 0, ///< No tests
CMP_Hierarchy = 1, ///< Compare hierarchy
CMP_Data = 2, ///< Compare BaseData
CMP_Properties = 4, ///< Compare PropertyLists
CMP_Mappers = 8, ///< Compare Mapper types
CMP_Interactors = 16, ///< Compare interactors
CMP_All = 0xfffffff, ///< Compare all known aspects
} Tests;
/**
* \brief Constructor taking reference and test DataStorage.
*
* \param reference Reference DataStorage object.
* \param test Test DataStorage object.
* \param flags Flags describing the desired test aspects (\sa Tests).
* \param eps precision for floating point comparisons.
*/
DataStorageCompare(const DataStorage *reference,
const DataStorage *test,
Tests flags = CMP_All,
double eps = mitk::eps);
/**
* \brief Shorthand for Compare(true).
*/
bool CompareVerbose();
/**
* \brief Execute the comparison.
*
* \param verbose if true, the comparison will output messages for all differences found and Report() will be called
* at the end.
* \return true if reference and test are judged equal regarding all flags provided in constructor.
*/
bool Compare(bool verbose = false);
/**
* \brief Prints a small summary of what tests have been executed and which ones failed or passed.
*
* Called automatically by Compare() when that method is called with the verbose flag.
*/
void Report();
private:
/**
* \brief Compares that the structure of nodes is identical.
*
* See class comment on identifying nodes for details.
*
* Tests both directions: test nodes included in reference
* storage and reference nodes included in test storage.
*/
bool CompareHierarchy(bool verbose);
/**
* \brief Compares pairs of DataNodes to each other.
*
* See class comment on identifying nodes for details.
*
* Identifies nodes pairs and calls AreNodesEqual() for
* pairs that are clearly identified. Warnings are issues
* for unclear cases.
*/
bool CompareDataNodes(bool verbose);
/**
* \brief Called for each pair of nodes, tests all aspects asked for in constructor.
*
* Receives a reference and a test DataNode and executes all comparisons
* that where required during construction of this DataStorageCompare
* via the Tests flags.
*
* Calls to specific methods for each type of comparison.
*
* \sa IsDataEqual()
* \sa ArePropertyListsEqual()
* \sa AreMappersEqual()
*/
bool AreNodesEqual(const mitk::DataNode *reference, const mitk::DataNode *test, bool verbose = false);
/**
* \brief Compares two BaseData instances.
*
* Makes use of the BaseDataEqual services to compare objects.
*/
bool IsDataEqual(const mitk::BaseData *reference, const mitk::BaseData *test, bool verbose = false);
/**
* \brief Compares all property lists of given nodes.
*
* Tests presence of all render specific and default lists,
* then compares them via the other method named ArePropertyListsEqual().
*/
bool ArePropertyListsEqual(const mitk::DataNode &reference, const mitk::DataNode &test, bool verbose = false);
/**
* \brief Compares the mapper types(!) of given nodes.
*
*/
bool AreMappersEqual(const mitk::DataNode &reference, const mitk::DataNode &test, bool verbose);
/**
* \brief Compares two instances of PropertyList.
*
* Tests if all properties are found in both lists.
* Tests equalness of properties via BaseProperty::operator==().
*/
bool ArePropertyListsEqual(const mitk::PropertyList &reference,
const mitk::PropertyList &test,
bool verbose = false);
/// Precision/Epsilon used for certain floating point comparisons.
double m_Eps;
/// Flags describing what to compare.
Tests m_TestAspects;
/// Reference data storage.
DataStorage::ConstPointer m_ReferenceDS;
/// Test data storage.
DataStorage::ConstPointer m_TestDS;
/**
- * \brief Structure associating "hierachy descriptors" to DataNodes.
+ * \brief Structure associating "hierarchy descriptors" to DataNodes.
*
* The string keys are "hierarchy descriptors" which are
* built by GenerateHierarchyDescriptor() for individual nodes.
*/
typedef std::multimap<std::string, DataNode::Pointer> HierarchyDescriptorMap;
/// Structure of the reference storage, filled by Compare().
HierarchyDescriptorMap m_RefNodesByHierarchy;
/// Structure of the test storage, filled by Compare().
HierarchyDescriptorMap m_TestNodesByHierarchy;
/**
- * \brief Calculate hierachy descriptors for each node, store them in the result map.
+ * \brief Calculate hierarchy descriptors for each node, store them in the result map.
*
*/
void DescribeHierarchyOfNodes(DataStorage::ConstPointer storage, HierarchyDescriptorMap &result);
/**
* \brief Generate descriptor for one single node.
*
*/
std::string GenerateNodeDescriptor(mitk::DataNode::Pointer node);
/**
* \brief Generate descriptor for the complete hierarchy of a node.
*
* \param node DataNode to describe.
* \param storage DataStorage to find out about the node's parents.
*/
std::string GenerateHierarchyDescriptor(DataNode::Pointer node, DataStorage::ConstPointer storage);
bool m_HierarchyPassed;
bool m_DataPassed;
bool m_PropertiesPassed;
bool m_MappersPassed;
bool m_InteractorsPassed;
int m_AspectsFailed;
}; // class
/// Needed to make the flag DataStorageCompare::Tests usable.
inline DataStorageCompare::Tests operator|(DataStorageCompare::Tests a, DataStorageCompare::Tests b)
{
return static_cast<DataStorageCompare::Tests>(static_cast<int>(a) | static_cast<int>(b));
}
} // namespace
#endif
diff --git a/Modules/SceneSerialization/test/mitkSceneIOTestScenarioProvider.h b/Modules/SceneSerialization/test/mitkSceneIOTestScenarioProvider.h
index 7db8477d82..017b6416c5 100644
--- a/Modules/SceneSerialization/test/mitkSceneIOTestScenarioProvider.h
+++ b/Modules/SceneSerialization/test/mitkSceneIOTestScenarioProvider.h
@@ -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.
============================================================================*/
#ifndef mitkSceneIOTestScenarioProvider_h
#define mitkSceneIOTestScenarioProvider_h
#include "mitkStandaloneDataStorage.h"
namespace mitk
{
/**
\brief Provides DataStorages that serve as test scenarios.
This class provides the structure Scenario to describe a
single test scenario for SceneIO and similar objects.
A single scenario consists of
- a DataStorage (in reality a method that constructs one)
- an identifier / name
- some flags that describe the scenario
\warning Probably we should split the scenario and
its description (== our expectations) at some
time. This will be necessary once that we have
two SceneIO mechanisms that differ in their
capabilities.
*/
class SceneIOTestScenarioProvider
{
public:
/// Typedef for type BuilderMethodPointer which is function pointer to member of SceneIOTestScenarioProvider
typedef DataStorage::Pointer (SceneIOTestScenarioProvider::*BuilderMethodPointer)() const;
/**
Structure to describe a single scenario.
Holds some descriptive members plus a pointer to a
method in SceneIOTestScenarioProvider that is able
to create a DataStorage. This DataStorage shall
represent some particularity to be tested in a
related test.
*/
struct Scenario
{
std::string key; ///< Description / ID.
bool serializable; ///< Do we expect that this can be stored in a .mitk file?
std::string referenceArchiveFilename; ///< Absolute filename with a reference .mitk file.
bool referenceArchiveLoadable; ///< Do we expect that the reference can be loaded without errors?
double comparisonPrecision; ///< Precision used for floating point comparisons after save/load cycle (eps).
/// Construct the DataStorage for this scenario.
DataStorage::Pointer BuildDataStorage() const;
/**
\param _key Description / ID.
\param _scenarioProvider object that contains the member function in _providerMethod
\param _providerMethod pointer to a member that creates a DataStorage for the scenario
\param _isSerializable Do we expect that this can be stored in a .mitk file?
\param _referenceArchiveFilename Absolute filename with a reference .mitk file.
\param _isReferenceLoadable Do we expect that the reference can be loaded without errors?
- \param _comparisonPrecision Precision used for floating point comparisions after save/load cycle (eps).
+ \param _comparisonPrecision Precision used for floating point comparisons after save/load cycle (eps).
*/
Scenario(const std::string &_key,
const SceneIOTestScenarioProvider *_scenarioProvider,
SceneIOTestScenarioProvider::BuilderMethodPointer _providerMethod,
bool _isSerializable,
const std::string &_referenceArchiveFilename,
bool _isReferenceLoadable,
double _comparisonPrecision);
private:
const SceneIOTestScenarioProvider *m_ScenarioProvider;
SceneIOTestScenarioProvider::BuilderMethodPointer m_ProviderMethod;
};
/// List of Scenarios.
typedef std::vector<Scenario> ScenarioList;
/// Returns _all_ registered scenarios.
ScenarioList GetAllScenarios() const;
private:
ScenarioList m_Scenarios;
/// Configures how many items count as many for some tests.
int m_HowMuchIsMany;
/**
Registers one scenario with its description and a method for DataStorage creations.
*/
void AddScenario(const std::string &key,
BuilderMethodPointer creator,
bool isSerializable,
const std::string &referenceArchiveFilename = std::string(),
bool isReferenceLoadable = false,
double comparisionPrecision = mitk::eps);
/**
An empty storage.
*/
DataStorage::Pointer EmptyStorage() const;
/**
One single node without anything.
*/
DataStorage::Pointer OneEmptyNode() const;
/**
One single node with a name.
*/
DataStorage::Pointer OneEmptyNamedNode() const;
/**
Flat list.
\verbatim
|-o
|-o
|-o
|-o
...
\endverbatim
*/
DataStorage::Pointer ManyTopLevelNodes() const;
/**
Degenerated tree.
A tree like this:
\verbatim
|-o
|-o
|-o
...
\endverbatim
*/
DataStorage::Pointer LineOfManyOnlyChildren() const;
/**
Nodes with multiple parents.
*/
DataStorage::Pointer ComplicatedFamilySituation() const;
/**
Basic core type Image.
*/
DataStorage::Pointer Image() const;
/**
Basic core type Surface.
*/
DataStorage::Pointer Surface() const;
/**
Basic core type PointSet.
*/
DataStorage::Pointer PointSet() const;
/**
GeometryData object (separate for specific precision).
*/
DataStorage::Pointer GeometryData() const;
/**
Properties for various render windows, containing no or long names or values.
*/
DataStorage::Pointer SpecialProperties() const;
public:
// Helper to simplify writing the registration
#define AddSaveAndRestoreScenario(name) AddScenario(#name, &mitk::SceneIOTestScenarioProvider::name, true);
// this one in the header so it is clearly visible when someone
// adds a test to the bottom of the list
SceneIOTestScenarioProvider() : m_HowMuchIsMany(50)
{
/// declare all your test cases here!
AddSaveAndRestoreScenario(EmptyStorage);
AddSaveAndRestoreScenario(OneEmptyNode);
AddSaveAndRestoreScenario(OneEmptyNamedNode);
AddSaveAndRestoreScenario(ManyTopLevelNodes);
AddSaveAndRestoreScenario(LineOfManyOnlyChildren);
AddSaveAndRestoreScenario(ComplicatedFamilySituation);
AddSaveAndRestoreScenario(Image);
AddSaveAndRestoreScenario(Surface);
AddSaveAndRestoreScenario(PointSet);
if (sizeof(size_t) != 4)
// this test is deactivated on 32 bit systems since it fails
// on the only 32 bit dartclient. To activate it there, one
// would have to debug the precision problem on a 32 bit
// machine and either adapt expectations or fix a bug.
AddSaveAndRestoreScenario(GeometryData);
AddSaveAndRestoreScenario(SpecialProperties);
// AddScenario("GeometryData", &mitk::SceneIOTestScenarioProvider::GeometryData, true, std::string(), false,
// mitk::eps);
}
}; // class
} // namespace
#endif
diff --git a/Modules/SceneSerializationBase/include/mitkVectorPropertySerializer.h b/Modules/SceneSerializationBase/include/mitkVectorPropertySerializer.h
index 0ac6ef7e12..736e20bd9d 100644
--- a/Modules/SceneSerializationBase/include/mitkVectorPropertySerializer.h
+++ b/Modules/SceneSerializationBase/include/mitkVectorPropertySerializer.h
@@ -1,149 +1,149 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkVectorPropertySerializer_h
#define mitkVectorPropertySerializer_h
#include "mitkBasePropertySerializer.h"
#include "mitkVectorProperty.h"
#include <mitkLexicalCast.h>
#include <tinyxml2.h>
namespace mitk
{
/**
\brief Serializes a VectorProperty
Serializes an instance of VectorProperty into a XML structure like
\verbatim
<Values>
<Value idx="0" value="17.3"/>
<Value idx="1" value="7.2"/>
<Value idx="2" value="-17.3"/>
</Values>
\endverbatim
This class is implemented as a template and makes use of std::stringstream
for necessary conversions of specific data types to and from string.
For numeric types, the class adds a precision token to stringstream that
should usually suffice.
*/
template <typename DATATYPE>
class MITKSCENESERIALIZATIONBASE_EXPORT VectorPropertySerializer : public BasePropertySerializer
{
public:
// Expand manually most of mitkClassMacro:
// mitkClassMacro(VectorProperty<DATATYPE>, mitk::BaseProperty);
- // This manual expansion is done to override explicitely
+ // This manual expansion is done to override explicitly
// the GetNameOfClass methods
typedef VectorProperty<DATATYPE> PropertyType;
typedef VectorPropertySerializer<DATATYPE> Self;
typedef BasePropertySerializer SuperClass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
std::vector<std::string> GetClassHierarchy() const override { return mitk::GetClassHierarchy<Self>(); }
// This function must return different
// strings in function of the template parameter!
// Serialization depends on this feature.
static const char *GetStaticNameOfClass()
{
// concatenate a prefix dependent on the template type and our own classname
static std::string nameOfClass =
std::string(VectorPropertyDataType<DATATYPE>::prefix()) + "VectorPropertySerializer";
return nameOfClass.c_str();
}
const char *GetNameOfClass() const override { return this->GetStaticNameOfClass(); }
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
//! Build an XML version of this property
tinyxml2::XMLElement* Serialize(tinyxml2::XMLDocument& doc) override
{
auto *listElement = doc.NewElement("Values");
if (const PropertyType *prop = dynamic_cast<const PropertyType *>(m_Property.GetPointer()))
{
typename PropertyType::VectorType elements = prop->GetValue();
unsigned int index(0);
for (auto listEntry : elements)
{
std::stringstream indexS;
indexS << index++;
auto *entryElement = doc.NewElement("Value");
entryElement->SetAttribute("idx", indexS.str().c_str());
entryElement->SetAttribute("value", boost::lexical_cast<std::string>(listEntry).c_str());
listElement->InsertEndChild(entryElement);
}
return listElement;
}
else
{
return nullptr;
}
}
//! Construct a property from an XML serialization
BaseProperty::Pointer Deserialize(const tinyxml2::XMLElement *listElement) override
{
typename PropertyType::VectorType datalist;
if (listElement)
{
std::string valueString;
DATATYPE value;
for (auto *valueElement = listElement->FirstChildElement("Value"); valueElement;
valueElement = valueElement->NextSiblingElement("Value"))
{
valueString = valueElement->Attribute("value");
if (valueString.empty())
{
MITK_ERROR << "Missing value attribute in <Values> list";
return nullptr;
}
try
{
value = boost::lexical_cast<DATATYPE>(valueString);
}
catch (boost::bad_lexical_cast &e)
{
MITK_ERROR << "Could not parse '" << valueString << "' as number: " << e.what();
return nullptr;
}
datalist.push_back(value);
}
typename PropertyType::Pointer property = PropertyType::New();
property->SetValue(datalist);
return property.GetPointer();
}
else
{
MITK_ERROR << "Missing <Values> tag.";
}
return nullptr;
}
};
typedef VectorPropertySerializer<double> DoubleVectorPropertySerializer;
typedef VectorPropertySerializer<int> IntVectorPropertySerializer;
} // namespace
#endif
diff --git a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx
index aeb755b305..6e4ef33a4e 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
+ this->m_OutputImage = imagePtr; // here we store the image, we have to write 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
+ // counting voxels over iterations; 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[k] = topIndex[k];
}
else
{
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/itkConnectedAdaptiveThresholdImageFilter.txx b/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx
index c6e07c296d..b0cfd8abc0 100644
--- a/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx
+++ b/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx
@@ -1,305 +1,305 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 _itkConnectedAdaptiveThresholdImageFilter_txx
#define _itkConnectedAdaptiveThresholdImageFilter_txx
#include "itkAdaptiveThresholdIterator.h"
#include "itkBinaryThresholdImageFunction.h"
#include "itkConnectedAdaptiveThresholdImageFilter.h"
#include "itkMinimumMaximumImageFilter.h"
#include "itkThresholdImageFilter.h"
namespace itk
{
/**
* Constructor
*/
template <class TInputImage, class TOutputImage>
ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::ConnectedAdaptiveThresholdImageFilter()
: m_OutoutImageMaskFineSegmentation(nullptr),
m_GrowingDirectionIsUpwards(true),
m_SeedpointValue(0),
m_DetectedLeakagePoint(0),
m_InitValue(0),
m_AdjLowerTh(0),
m_AdjUpperTh(0),
m_FineDetectionMode(false),
m_DiscardLastPreview(false),
m_SegmentationCancelled(false)
{
}
template <class TInputImage, class TOutputImage>
void ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::GenerateData()
{
typename ConnectedAdaptiveThresholdImageFilter::InputImageConstPointer inputImage = this->GetInput();
typename ConnectedAdaptiveThresholdImageFilter::OutputImagePointer outputImage = this->GetOutput();
typename Superclass::InputPixelObjectType::Pointer lowerThreshold = this->GetLowerInput();
typename Superclass::InputPixelObjectType::Pointer upperThreshold = this->GetUpperInput();
// kommt drauf, wie wir hier die Pipeline aufbauen
this->SetLower(lowerThreshold->Get());
this->SetUpper(upperThreshold->Get());
typedef BinaryThresholdImageFunction<InputImageType> FunctionType;
typedef AdaptiveThresholdIterator<OutputImageType, FunctionType> IteratorType;
int initValue = IteratorType::CalculateInitializeValue((int)(this->GetLower()), (int)(this->GetUpper()));
// Initialize the output according to the segmentation (fine or raw)
if (m_FineDetectionMode)
{
outputImage = this->m_OutoutImageMaskFineSegmentation;
}
typename ConnectedAdaptiveThresholdImageFilter::OutputImageRegionType region = outputImage->GetRequestedRegion();
outputImage->SetBufferedRegion(region);
outputImage->Allocate();
if (!m_FineDetectionMode)
- { // only initalize the output image if we are using the raw segmentation mode
+ { // only initialize the output image if we are using the raw segmentation mode
outputImage->FillBuffer((typename ConnectedAdaptiveThresholdImageFilter::OutputImagePixelType)initValue);
}
typename FunctionType::Pointer function = FunctionType::New();
function->SetInputImage(inputImage);
typename Superclass::SeedContainerType seeds;
seeds = this->GetSeeds();
// pass parameters needed for region growing to iterator
IteratorType it(outputImage, function, seeds);
it.SetFineDetectionMode(m_FineDetectionMode);
it.SetExpansionDirection(m_GrowingDirectionIsUpwards);
it.SetMinTH((int)(this->GetLower()));
it.SetMaxTH((int)(this->GetUpper()));
it.GoToBegin();
this->m_SeedpointValue = it.GetSeedPointValue();
if ((this->GetLower()) > this->m_SeedpointValue || this->m_SeedpointValue > (this->GetUpper()))
{
// set m_SegmentationCancelled to true, so if it doesn't reach the point where it is set back to false
- // we can asssume that there was an error
+ // we can assume that there was an error
this->m_SegmentationCancelled = true;
return;
}
// iterate through image until
while (!it.IsAtEnd())
{
// make iterator go one step further (calls method DoFloodStep())
++it;
}
this->m_DetectedLeakagePoint = it.GetLeakagePoint();
this->m_SegmentationCancelled = false;
}
template <class TInputImage, class TOutputImage>
TOutputImage *itk::ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::GetResultImage()
{
return m_OutoutImageMaskFineSegmentation;
}
template <class TInputImage, class TOutputImage>
typename ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::IndexType
itk::ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::CorrectSeedPointPosition(
unsigned int sizeOfVolume, int lowerTh, int upperTh)
{
typename ConnectedAdaptiveThresholdImageFilter::InputImageConstPointer inputImage = this->GetInput();
typedef typename TInputImage::IndexType IndexType;
IndexType itkIntelligentSeedIndex;
int seedPixelValue = inputImage->GetPixel(m_SeedPointIndex);
// set new seed index to the voxel with the darkest value and shortest distance to original seed
if (seedPixelValue > upperTh || seedPixelValue < lowerTh)
{
// MITK_INFO << "seed pixel value [BEFORE] = " << seedPixelValue;
// ToDo crop region
itk::Index<3> workindex;
for (int i = 0; i < 3; i++)
{
workindex[i] = m_SeedPointIndex[i] - sizeOfVolume / 2;
if (workindex[i] < 0)
workindex[i] = 0;
}
itk::Size<3> worksize;
for (int i = 0; i < 3; i++)
{
worksize[i] = sizeOfVolume;
}
itk::ImageRegion<3> workregion(workindex, worksize);
itk::ImageRegionIterator<TInputImage> regionIt(const_cast<TInputImage *>(inputImage.GetPointer()), workregion);
// int darkestGrayValue=seedPixelValue;
int currentGrayValue;
float distance = (float)(sizeOfVolume / 2);
float relativeDistance = 1; // between 0 and 1
mitk::Vector3D seedVector, currentVector;
mitk::FillVector3D(seedVector, m_SeedPointIndex[0], m_SeedPointIndex[1], m_SeedPointIndex[2]);
currentVector = seedVector;
float costValue = 0; // beware, Depending on seeking upper or lower value...
for (regionIt.GoToBegin(); !regionIt.IsAtEnd(); ++regionIt)
{
// get current gray value
currentGrayValue = regionIt.Value();
// get current seed index
m_SeedPointIndex = regionIt.GetIndex();
// fill current vector
mitk::FillVector3D(currentVector, m_SeedPointIndex[0], m_SeedPointIndex[1], m_SeedPointIndex[2]);
// calculate distance from original seed to new seed
mitk::Vector3D distVector = currentVector - seedVector;
distance = fabs(distVector.GetSquaredNorm());
relativeDistance = distance / (sizeOfVolume / 2);
// calculate "cost function"
float currentCostValue = (1 - relativeDistance) * currentGrayValue;
if (currentCostValue < costValue && currentGrayValue < upperTh)
{
itkIntelligentSeedIndex = regionIt.GetIndex();
costValue = currentCostValue;
// MITK_INFO <<"cost value="<< costValue;
// MITK_INFO <<"darkest and closest Voxel ="<< currentGrayValue;
// MITK_INFO <<"m_UPPER="<< upperTh;
}
}
// MITK_INFO<< "seed pixel value [AFTER] =" << inputImage->GetPixel(itkIntelligentSeedIndex) <<"\n";
}
else
{ // no correction of the seed point is needed, just pass the original seed
itkIntelligentSeedIndex = m_SeedPointIndex;
}
return itkIntelligentSeedIndex;
}
template <class TInputImage, class TOutputImage>
void itk::ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::CropMask(unsigned int croppingSize)
{
// initialize center point of the working region
itk::Index<3> workindex;
for (int i = 0; i < 3; i++)
{
workindex[i] = m_SeedPointIndex[i] - croppingSize / 2;
if (workindex[i] < 0)
workindex[i] = 0;
}
// initialize working volume
itk::Size<3> worksize;
for (int i = 0; i < 3; i++)
{
worksize[i] = croppingSize;
}
// set working region
itk::ImageRegion<3> workregion(workindex, worksize);
// check if the entire region is inside the image
if (!(m_OutoutImageMaskFineSegmentation->GetLargestPossibleRegion().IsInside(workregion)))
{
// if not then crop to the intersection of the image (gemeinsame Schnittmenge Bild und workingRegion)
if (!(workregion.Crop(m_OutoutImageMaskFineSegmentation->GetLargestPossibleRegion())))
{
MITK_ERROR << "Cropping working region failed!";
return;
}
}
// initialize region iterator
itk::ImageRegionIterator<TOutputImage> regionIt(m_OutoutImageMaskFineSegmentation, workregion);
for (regionIt.GoToBegin(); !regionIt.IsAtEnd(); ++regionIt)
{
// and set all voxel inside the working region to zero
regionIt.Set(0);
}
}
template <class TInputImage, class TOutputImage>
unsigned int itk::ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::AdjustIteratorMask()
{
typedef itk::ThresholdImageFilter<TOutputImage> ThresholdFilterType;
typedef itk::MinimumMaximumImageFilter<TOutputImage> MaxFilterType;
typename ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New();
typename MaxFilterType::Pointer maxFilter = MaxFilterType::New();
unsigned int maxValue;
if (!m_DiscardLastPreview)
{
// get the biggest value of the image
maxFilter->SetInput(m_OutoutImageMaskFineSegmentation);
maxFilter->UpdateLargestPossibleRegion();
maxValue = maxFilter->GetMaximum();
}
else
{ // use the last biggest value in the preview. This was set in SetParameterForFineSegmentation(...adjLowerTh...) []
maxValue = m_AdjLowerTh;
}
- // set all values <lower && >upper to zero (thresouldOutside uses < and > NOT <= and >=)
+ // set all values <lower && >upper to zero (thresholdOutside uses < and > NOT <= and >=)
thresholdFilter->SetInput(m_OutoutImageMaskFineSegmentation);
thresholdFilter->SetOutsideValue(0);
thresholdFilter->ThresholdOutside(m_AdjLowerTh, maxValue);
thresholdFilter->UpdateLargestPossibleRegion();
// set all values in between lower and upper (>=lower && <=upper) to the highest value in the image
thresholdFilter->SetInput(thresholdFilter->GetOutput());
thresholdFilter->SetOutsideValue(maxValue);
thresholdFilter->ThresholdOutside(0, m_AdjLowerTh - 1);
thresholdFilter->UpdateLargestPossibleRegion();
m_OutoutImageMaskFineSegmentation = thresholdFilter->GetOutput();
return maxValue;
}
template <class TInputImage, class TOutputImage>
void itk::ConnectedAdaptiveThresholdImageFilter<TInputImage, TOutputImage>::SetParameterForFineSegmentation(
TOutputImage *iteratorMaskForFineSegmentation,
unsigned int adjLowerTh,
unsigned int adjUpperTh,
itk::Index<3> seedPoint,
bool discardLeafSegmentation)
{
- // just to make sure we´re in the right mode and the mask exsits
+ // just to make sure we´re in the right mode and the mask exists
if (m_FineDetectionMode && iteratorMaskForFineSegmentation)
{
m_OutoutImageMaskFineSegmentation = iteratorMaskForFineSegmentation;
m_AdjLowerTh = adjLowerTh;
m_AdjUpperTh = adjUpperTh; // still needed?
m_SeedPointIndex = seedPoint;
m_DiscardLastPreview = discardLeafSegmentation;
}
else
{
if (!m_FineDetectionMode)
{
MITK_ERROR << "Fine-detection-segmentation mode not set!";
}
else
{
MITK_ERROR << "Iterator-mask-image not set!";
}
}
}
} // end namespace itk
#endif
diff --git a/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx
index f103d54896..852c3ad118 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 <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
+ // corresponding 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/mitkContourModelSetToImageFilter.h b/Modules/Segmentation/Algorithms/mitkContourModelSetToImageFilter.h
index a19f46b578..4257bc9f32 100644
--- a/Modules/Segmentation/Algorithms/mitkContourModelSetToImageFilter.h
+++ b/Modules/Segmentation/Algorithms/mitkContourModelSetToImageFilter.h
@@ -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.
============================================================================*/
#ifndef mitkContourModelSetToImageFilter_h
#define mitkContourModelSetToImageFilter_h
#include <MitkSegmentationExports.h>
#include <mitkImageSource.h>
namespace mitk
{
class ContourModelSet;
/**
* @brief Fills a given mitk::ContourModelSet into a given mitk::Image
* @ingroup Process
*/
class MITKSEGMENTATION_EXPORT ContourModelSetToImageFilter : public ImageSource
{
public:
mitkClassMacro(ContourModelSetToImageFilter, ImageSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
virtual void SetMakeOutputBinary(bool makeOutputBinary);
virtual void SetMakeOutputLabelPixelType(bool makeOutputLabelPixelType);
itkSetMacro(PaintingPixelValue, int);
itkSetMacro(TimeStep, unsigned int);
itkGetMacro(MakeOutputBinary, bool);
itkGetMacro(MakeOutputLabelPixelType, bool);
itkGetMacro(PaintingPixelValue, int);
itkBooleanMacro(MakeOutputBinary);
itkBooleanMacro(MakeOutputLabelPixelType);
/**
* 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
+ * Once all the subclasses of ProcessObject provide an appropriate
* MakeOutput(), then ProcessObject::MakeOutput() can be made pure
* virtual.
*/
itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override;
void GenerateInputRequestedRegion() override;
void GenerateOutputInformation() override;
void GenerateData() override;
const mitk::ContourModelSet *GetInput(void);
using itk::ProcessObject::SetInput;
virtual void SetInput(const mitk::ContourModelSet *input);
/**
* @brief Set the image which will be used to initialize the output of this filter.
* @param refImage the image used to initialize the output image
*/
void SetImage(const mitk::Image *refImage);
const mitk::Image *GetImage(void);
protected:
ContourModelSetToImageFilter();
~ContourModelSetToImageFilter() override;
/**
* @brief Initializes the volume of the output image with zeros
*/
void InitializeOutputEmpty();
bool m_MakeOutputBinary;
bool m_MakeOutputLabelPixelType;
int m_PaintingPixelValue;
unsigned int m_TimeStep;
const mitk::Image *m_ReferenceImage;
};
}
#endif
diff --git a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp
index 0aec6159c9..3d0ba2fd76 100644
--- a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp
+++ b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp
@@ -1,484 +1,484 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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)
+ // Convert to DefaultSegmentationDataType (because TobiasHeimannCorrectionAlgorithm relies 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::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;
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();
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/mitkImageLiveWireContourModelFilter.h b/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.h
index 5790ebb16b..e4bbc93c5c 100644
--- a/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.h
+++ b/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.h
@@ -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.
============================================================================*/
#ifndef mitkImageLiveWireContourModelFilter_h
#define mitkImageLiveWireContourModelFilter_h
#include "mitkCommon.h"
#include "mitkContourModel.h"
#include "mitkContourModelSource.h"
#include <MitkSegmentationExports.h>
#include <mitkImage.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <itkShortestPathCostFunctionLiveWire.h>
#include <itkShortestPathImageFilter.h>
namespace mitk
{
/**
\brief Calculates a LiveWire contour between two points in an image.
- For defining costs between two pixels specific features are extraced from the image and tranformed into a single cost
+ For defining costs between two pixels specific features are extracted from the image and transformed into a single cost
value.
\sa ShortestPathCostFunctionLiveWire
- The filter is able to create dynamic cost tranfer map and thus use on the fly training.
+ The filter is able to create dynamic cost transfer map and thus use on the fly training.
\note On the fly training will only be used for next update.
The computation uses the last calculated segment to map cost according to features in the area of the segment.
Caution: time support currently not available. Filter will always work on the first
timestep in its current implementation.
\ingroup ContourModelFilters
\ingroup Process
*/
class MITKSEGMENTATION_EXPORT ImageLiveWireContourModelFilter : public ContourModelSource
{
public:
mitkClassMacro(ImageLiveWireContourModelFilter, ContourModelSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
typedef ContourModel OutputType;
typedef OutputType::Pointer OutputTypePointer;
typedef mitk::Image InputType;
typedef itk::Image<float, 2> InternalImageType;
typedef itk::ShortestPathImageFilter<InternalImageType, InternalImageType> ShortestPathImageFilterType;
typedef itk::ShortestPathCostFunctionLiveWire<InternalImageType> CostFunctionType;
typedef std::vector<itk::Index<2>> ShortestPathType;
/** \brief start point in world coordinates*/
itkSetMacro(StartPoint, mitk::Point3D);
itkGetMacro(StartPoint, mitk::Point3D);
/** \brief end point in woorld coordinates*/
itkSetMacro(EndPoint, mitk::Point3D);
itkGetMacro(EndPoint, mitk::Point3D);
- /** \brief Create dynamic cost tranfer map - use on the fly training.
+ /** \brief Create dynamic cost transfer map - use on the fly training.
\note On the fly training will be used for next update only.
The computation uses the last calculated segment to map cost according to features in the area of the segment.
*/
itkSetMacro(UseDynamicCostMap, bool);
itkGetMacro(UseDynamicCostMap, bool);
/** \brief Clear all repulsive points used in the cost function
*/
void ClearRepulsivePoints();
/** \brief Set a vector with repulsive points to use in the cost function
*/
void SetRepulsivePoints(const ShortestPathType &points);
/** \brief Add a single repulsive point to the cost function
*/
void AddRepulsivePoint(const itk::Index<2> &idx);
/** \brief Remove a single repulsive point from the cost function
*/
void RemoveRepulsivePoint(const itk::Index<2> &idx);
virtual void SetInput(const InputType *input);
using Superclass::SetInput;
virtual void SetInput(unsigned int idx, const InputType *input);
const InputType *GetInput(void);
const InputType *GetInput(unsigned int idx);
virtual OutputType *GetOutput();
virtual void DumpMaskImage();
- /** \brief Create dynamic cost tranfer map - on the fly training*/
+ /** \brief Create dynamic cost transfer map - on the fly training*/
bool CreateDynamicCostMap(mitk::ContourModel *path = nullptr);
void SetUseCostFunction(bool doUseCostFunction) { m_ShortestPathFilter->SetUseCostFunction(doUseCostFunction); };
protected:
ImageLiveWireContourModelFilter();
~ImageLiveWireContourModelFilter() override;
void GenerateOutputInformation() override{};
void GenerateData() override;
void UpdateLiveWire();
/** \brief start point in worldcoordinates*/
mitk::Point3D m_StartPoint;
/** \brief end point in woorldcoordinates*/
mitk::Point3D m_EndPoint;
/** \brief Start point in index*/
mitk::Point3D m_StartPointInIndex;
/** \brief End point in index*/
mitk::Point3D m_EndPointInIndex;
/** \brief The cost function to compute costs between two pixels*/
CostFunctionType::Pointer m_CostFunction;
/** \brief Shortest path filter according to cost function m_CostFunction*/
ShortestPathImageFilterType::Pointer m_ShortestPathFilter;
- /** \brief Flag to use a dynmic cost map or not*/
+ /** \brief Flag to use a dynamic cost map or not*/
bool m_UseDynamicCostMap;
unsigned int m_TimeStep;
template <typename TPixel, unsigned int VImageDimension>
void ItkPreProcessImage(const itk::Image<TPixel, VImageDimension> *inputImage);
template <typename TPixel, unsigned int VImageDimension>
void CreateDynamicCostMapByITK(const itk::Image<TPixel, VImageDimension> *inputImage,
mitk::ContourModel *path = nullptr);
InternalImageType::Pointer m_InternalImage;
};
}
#endif
diff --git a/Modules/Segmentation/Algorithms/mitkManualSegmentationToSurfaceFilter.h b/Modules/Segmentation/Algorithms/mitkManualSegmentationToSurfaceFilter.h
index 39b3972a4b..acfdba0b55 100644
--- a/Modules/Segmentation/Algorithms/mitkManualSegmentationToSurfaceFilter.h
+++ b/Modules/Segmentation/Algorithms/mitkManualSegmentationToSurfaceFilter.h
@@ -1,164 +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 mitkManualSegmentationToSurfaceFilter_h
#define mitkManualSegmentationToSurfaceFilter_h
#include <MitkSegmentationExports.h>
#include <mitkImageToSurfaceFilter.h>
#include <vtkImageGaussianSmooth.h>
#include <vtkImageMedian3D.h>
#include <vtkImageResample.h>
#include <vtkImageThreshold.h>
namespace mitk
{
/**
* @brief Supplies a 3D surface from pre-processed segmentation.
*
* The resulting surface depends on a filter pipeline based on vtkMedian (1) and a Gaussian filter with
* vtkImageGaussianSmooth (2).
* All voxel can be changed to an isotropic representation of the
- * image (ATTANTION: the number of voxels in the will change). The
+ * image (ATTENTION: the number of voxels in the image will change). The
* resulting isotropic image has 1mm isotropic voxel by default. But
* can be varied freely.
*
* @ingroup ImageFilters
* @ingroup Process
*/
class MITKSEGMENTATION_EXPORT ManualSegmentationToSurfaceFilter : public ImageToSurfaceFilter
{
public:
mitkClassMacro(ManualSegmentationToSurfaceFilter, ImageToSurfaceFilter);
typedef double vtkDouble;
/**
* Will pre-process a segmentation voxelwise. The segmentation can use
* a hole fill relating a median filter and smooth by a Gaussian
* filter.
* The image can be interpolated to an isotropic image.
* By default every filter is disabled.
* This method calls CreateSurface from mitkImageToSurfaceFilter and
* does not need a manual call since we use Update().
*/
void GenerateData() override;
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* Supplies a method for setting median filter by a bool value.
*/
itkSetMacro(MedianFilter3D, bool);
/**
* Return state if median filter is enabled.
*/
itkGetConstMacro(MedianFilter3D, bool);
/**
* Enable the median filter (first filter in pipeline).
*/
itkBooleanMacro(MedianFilter3D);
/**
* Supplies a method to enable Interpolation.
*/
itkSetMacro(Interpolation, bool);
/**
* Returns activation state of interpolation filter.
*/
itkGetConstMacro(Interpolation, bool);
/**
* Enable the interpolation filter (second filter in pipeline) for
* isotropic voxel.
*/
itkBooleanMacro(Interpolation);
/**
* Supplies a method for Gaussian filter (third filter in pipeline).
*/
itkSetMacro(UseGaussianImageSmooth, bool);
/**
* Returns activation state of standard deviation filter.
*/
itkGetConstMacro(UseGaussianImageSmooth, bool);
/**
* Enables Gaussian image smooth. As well the threshold for the
* CreateSurface() method will raise the threshold to 49 and changes
* the image range set from 0 to 100. It is made for reasons in
* binary images to prevent conflicts with the used filter. There are
* better results for dividing fore- and background.
*/
itkBooleanMacro(UseGaussianImageSmooth);
/**
* Set standard deviation for Gaussian Filter.
* @param _arg by default 1.5
*/
itkSetMacro(GaussianStandardDeviation, double);
/**
* Returns the standard deviation of the Gaussian filter which will be
* used when filter is enabled.
*/
itkGetConstMacro(GaussianStandardDeviation, double);
/**
* Set the Kernel for Median3DFilter. By default kernel is set to 3x3x3.
* If you choose '1' nothing will be processed in this direction.
*/
void SetMedianKernelSize(int x, int y, int z);
/**
* Returns the kernel size in the first direction.
*/
itkGetConstMacro(MedianKernelSizeX, int);
/**
* Returns the kernel size in the second direction.
*/
itkGetConstMacro(MedianKernelSizeY, int);
/**
* Returns the kernel size in the third direction.
*/
itkGetConstMacro(MedianKernelSizeZ, int);
/**
* Set the values for Spacing in X, Y and Z-Dimension
*/
void SetInterpolation(vtkDouble x, vtkDouble y, vtkDouble z);
protected:
ManualSegmentationToSurfaceFilter();
~ManualSegmentationToSurfaceFilter() override;
bool m_MedianFilter3D;
int m_MedianKernelSizeX, m_MedianKernelSizeY, m_MedianKernelSizeZ;
bool m_UseGaussianImageSmooth; // Gaussian Filter
double m_GaussianStandardDeviation;
bool m_Interpolation;
vtkDouble m_InterpolationX;
vtkDouble m_InterpolationY;
vtkDouble m_InterpolationZ;
}; // namespace
}
#endif
diff --git a/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h b/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h
index 56f821ad56..db4c40ed51 100644
--- a/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h
+++ b/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h
@@ -1,84 +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.
============================================================================*/
#ifndef mitkVtkImageOverwrite_h
#define mitkVtkImageOverwrite_h
#include <MitkSegmentationExports.h>
#include <vtkImageReslice.h>
-/** \brief A vtk Filter based on vtkImageReslice with the aditional feature to write a slice into the given input
+/** \brief A vtk Filter based on vtkImageReslice with the additional feature to write a slice into the given input
volume.
All optimizations for e.g. the plane directions or interpolation are stripped away, the algorithm only interpolates
nearest
neighbor and uses the non optimized execute function of vtkImageReslice. Note that any interpolation doesn't make
sense
for round trip use extract->edit->overwrite, because it is nearly impossible to invert the interpolation.
There are two use cases for the Filter which are specified by the overwritemode property:
1)Extract slices from a 3D volume.
Overwritemode = false
In this mode the class can be used like vtkImageReslice. The usual way to do this is:
- Set an image volume as input
- Set the ResliceAxes via SetResliceAxesDirectionCosines and SetResliceAxesOrigin
- Set the the OutputSpacing, OutputOrigin and OutputExtent
- Call Update
2)Overwrite a 3D volume at a given slice.
Overwritemode = true
The handling in this mode is quite similar to the description above with the addition that the
InputSlice needs to be specified via SetInputSlice(vtkImageData*).
- Set the properties mentioned above (Note that SetInput specifies the volume to write to)
- Set the slice to that has to be overwritten in the volume ( SetInputSlice(vtkImageData*)
After calling Update() there is no need to retrieve the output as the input volume is modified.
\sa vtkImageReslice
- (Note that the execute and interpolation functions are no members and thus can not be overriden)
+ (Note that the execute and interpolation functions are no members and thus can not be overridden)
*/
class MITKSEGMENTATION_EXPORT mitkVtkImageOverwrite : public vtkImageReslice
{
public:
static mitkVtkImageOverwrite *New();
vtkTypeMacro(mitkVtkImageOverwrite, vtkImageReslice);
/** \brief Set the mode either to reslice (false) or to overwrite (true).
Default: false
*/
void SetOverwriteMode(bool b);
bool IsOverwriteMode() { return m_Overwrite_Mode; }
/** \brief Set the slice for overwrite mode.
Note:
It is recommend not to use this in reslice mode because otherwise the slice will be modified!
*/
void SetInputSlice(vtkImageData *slice);
protected:
mitkVtkImageOverwrite();
~mitkVtkImageOverwrite() override;
bool m_Overwrite_Mode;
/** Overridden from vtkImageReslice. \sa vtkImageReslice::ThreadedRequestData */
void ThreadedRequestData(vtkInformation *vtkNotUsed(request),
vtkInformationVector **vtkNotUsed(inputVector),
vtkInformationVector *vtkNotUsed(outputVector),
vtkImageData ***inData,
vtkImageData **outData,
int outExt[6],
int id) override;
};
#endif
diff --git a/Modules/Segmentation/CMakeLists.txt b/Modules/Segmentation/CMakeLists.txt
index 1708ede8d0..eea51f14ae 100644
--- a/Modules/Segmentation/CMakeLists.txt
+++ b/Modules/Segmentation/CMakeLists.txt
@@ -1,11 +1,11 @@
mitk_create_module(
INCLUDE_DIRS Algorithms Controllers DataManagement Interactions Rendering SegmentationUtilities/BooleanOperations SegmentationUtilities/MorphologicalOperations
DEPENDS MitkAlgorithmsExt MitkSurfaceInterpolation MitkGraphAlgorithms MitkContourModel MitkMultilabel MitkBoundingShape
PACKAGE_DEPENDS
- PUBLIC ITK|QuadEdgeMesh+RegionGrowing
+ PUBLIC ITK|QuadEdgeMesh+RegionGrowing httplib
PRIVATE ITK|LabelMap+MathematicalMorphology VTK|ImagingGeneral
TARGET_DEPENDS PRIVATE GrowCut
)
add_subdirectory(cmdapps)
add_subdirectory(Testing)
diff --git a/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp b/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp
index 3fe619bf30..5d4fddcabf 100644
--- a/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp
+++ b/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp
@@ -1,626 +1,626 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkSegmentationInterpolationController.h"
#include "mitkImageCast.h"
#include "mitkImageReadAccessor.h"
#include "mitkImageTimeSelector.h"
#include <mitkExtractSliceFilter.h>
#include <mitkImageAccessByItk.h>
//#include <mitkPlaneGeometry.h>
#include <itkCommand.h>
#include <itkImage.h>
#include <itkImageSliceConstIteratorWithIndex.h>
#include <thread>
namespace
{
// itk::Object provides a const version of AddObserver() (which uses const_cast internally)
// but not a const version of RemoveObserver().
void RemoveObserverFromConstObject(const itk::Object* constObject, unsigned long observerTag)
{
if (nullptr != constObject)
{
auto* object = const_cast<itk::Object*>(constObject);
object->RemoveObserver(observerTag);
}
}
}
mitk::SegmentationInterpolationController::InterpolatorMapType
mitk::SegmentationInterpolationController::s_InterpolatorForImage; // static member initialization
mitk::SegmentationInterpolationController *mitk::SegmentationInterpolationController::InterpolatorForImage(
const Image *image)
{
auto iter = s_InterpolatorForImage.find(image);
if (iter != s_InterpolatorForImage.end())
{
return iter->second;
}
else
{
return nullptr;
}
}
mitk::SegmentationInterpolationController::SegmentationInterpolationController()
: m_SegmentationModifiedObserverTag(std::make_pair(0UL, false)),
m_BlockModified(false),
m_2DInterpolationActivated(false),
m_EnableSliceImageCache(false)
{
}
void mitk::SegmentationInterpolationController::Activate2DInterpolation(bool status)
{
m_2DInterpolationActivated = status;
}
mitk::SegmentationInterpolationController *mitk::SegmentationInterpolationController::GetInstance()
{
static mitk::SegmentationInterpolationController::Pointer m_Instance;
if (m_Instance.IsNull())
{
m_Instance = SegmentationInterpolationController::New();
}
return m_Instance;
}
mitk::SegmentationInterpolationController::~SegmentationInterpolationController()
{
// remove this from the list of interpolators
for (auto iter = s_InterpolatorForImage.begin(); iter != s_InterpolatorForImage.end(); ++iter)
{
if (iter->second == this)
{
s_InterpolatorForImage.erase(iter);
break;
}
}
}
void mitk::SegmentationInterpolationController::OnImageModified(const itk::EventObject &)
{
if (!m_BlockModified && m_Segmentation.IsNotNull() && m_2DInterpolationActivated)
{
SetSegmentationVolume(m_Segmentation);
}
}
void mitk::SegmentationInterpolationController::BlockModified(bool block)
{
m_BlockModified = block;
}
void mitk::SegmentationInterpolationController::SetSegmentationVolume(const Image *segmentation)
{
// clear old information (remove all time steps
m_SegmentationCountInSlice.clear();
// delete this from the list of interpolators
auto iter = s_InterpolatorForImage.find(segmentation);
if (iter != s_InterpolatorForImage.end())
{
s_InterpolatorForImage.erase(iter);
}
if (m_SegmentationModifiedObserverTag.second)
{
RemoveObserverFromConstObject(m_Segmentation, m_SegmentationModifiedObserverTag.first);
m_SegmentationModifiedObserverTag.second = false;
}
if (nullptr == segmentation || !segmentation->IsInitialized())
{
m_Segmentation = nullptr;
this->InvokeEvent(itk::AbortEvent());
return;
}
if (segmentation->GetDimension() > 4 || segmentation->GetDimension() < 3)
{
itkExceptionMacro("SegmentationInterpolationController needs a 3D-segmentation or 3D+t.");
}
m_Segmentation = segmentation;
auto command = itk::ReceptorMemberCommand<SegmentationInterpolationController>::New();
command->SetCallbackFunction(this, &SegmentationInterpolationController::OnImageModified);
m_SegmentationModifiedObserverTag.first = segmentation->AddObserver(itk::ModifiedEvent(), command);
m_SegmentationModifiedObserverTag.second = true;
m_SegmentationCountInSlice.resize(m_Segmentation->GetTimeSteps());
for (unsigned int timeStep = 0; timeStep < m_Segmentation->GetTimeSteps(); ++timeStep)
{
m_SegmentationCountInSlice[timeStep].resize(3);
for (unsigned int dim = 0; dim < 3; ++dim)
{
m_SegmentationCountInSlice[timeStep][dim].clear();
m_SegmentationCountInSlice[timeStep][dim].resize(m_Segmentation->GetDimension(dim));
m_SegmentationCountInSlice[timeStep][dim].assign(m_Segmentation->GetDimension(dim), 0);
}
}
s_InterpolatorForImage.insert(std::make_pair(m_Segmentation, this));
// for all timesteps
// scan whole image
for (unsigned int timeStep = 0; timeStep < m_Segmentation->GetTimeSteps(); ++timeStep)
{
ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New();
timeSelector->SetInput(m_Segmentation);
timeSelector->SetTimeNr(timeStep);
timeSelector->UpdateLargestPossibleRegion();
Image::Pointer segmentation3D = timeSelector->GetOutput();
AccessFixedDimensionByItk_2(segmentation3D, ScanWholeVolume, 3, m_Segmentation, timeStep);
}
// PrintStatus();
SetReferenceVolume(m_ReferenceImage);
Modified();
}
void mitk::SegmentationInterpolationController::SetReferenceVolume(const Image *referenceImage)
{
m_ReferenceImage = referenceImage;
if (m_ReferenceImage.IsNull())
return; // no image set - ignore it then
assert(m_Segmentation.IsNotNull()); // should never happen
// ensure the reference image has the same dimensionality and extents as the segmentation image
if (m_ReferenceImage.IsNull() || m_Segmentation.IsNull() ||
m_ReferenceImage->GetDimension() != m_Segmentation->GetDimension() ||
m_ReferenceImage->GetPixelType().GetNumberOfComponents() != 1 ||
m_Segmentation->GetPixelType().GetNumberOfComponents() != 1)
{
MITK_WARN << "Segmentation image has different image characteristics than reference image." << std::endl;
m_ReferenceImage = nullptr;
return;
}
for (unsigned int dim = 0; dim < m_Segmentation->GetDimension(); ++dim)
if (m_ReferenceImage->GetDimension(dim) != m_Segmentation->GetDimension(dim))
{
MITK_WARN << "original patient image does not match segmentation (different extent in dimension " << dim
<< "), ignoring patient image" << std::endl;
m_ReferenceImage = nullptr;
return;
}
}
void mitk::SegmentationInterpolationController::SetChangedVolume(const Image *sliceDiff, unsigned int timeStep)
{
if (!sliceDiff)
return;
if (sliceDiff->GetDimension() != 3)
return;
AccessFixedDimensionByItk_1(sliceDiff, ScanChangedVolume, 3, timeStep);
// PrintStatus();
Modified();
}
void mitk::SegmentationInterpolationController::SetChangedSlice(const Image *sliceDiff,
unsigned int sliceDimension,
unsigned int sliceIndex,
unsigned int timeStep)
{
if (!sliceDiff)
return;
if (sliceDimension > 2)
return;
if (timeStep >= m_SegmentationCountInSlice.size())
return;
if (sliceIndex >= m_SegmentationCountInSlice[timeStep][sliceDimension].size())
return;
unsigned int dim0(0);
unsigned int dim1(1);
// determine the other two dimensions
switch (sliceDimension)
{
default:
case 2:
dim0 = 0;
dim1 = 1;
break;
case 1:
dim0 = 0;
dim1 = 2;
break;
case 0:
dim0 = 1;
dim1 = 2;
break;
}
mitk::ImageReadAccessor readAccess(sliceDiff);
auto *rawSlice = (unsigned char *)readAccess.GetData();
if (!rawSlice)
return;
AccessFixedDimensionByItk_1(
sliceDiff, ScanChangedSlice, 2, SetChangedSliceOptions(sliceDimension, sliceIndex, dim0, dim1, timeStep, rawSlice));
Modified();
}
template <typename DATATYPE>
void mitk::SegmentationInterpolationController::ScanChangedSlice(const itk::Image<DATATYPE, 2> *,
const SetChangedSliceOptions &options)
{
auto *pixelData((DATATYPE *)options.pixelData);
unsigned int timeStep(options.timeStep);
unsigned int sliceDimension(options.sliceDimension);
unsigned int sliceIndex(options.sliceIndex);
if (sliceDimension > 2)
return;
if (sliceIndex >= m_SegmentationCountInSlice[timeStep][sliceDimension].size())
return;
unsigned int dim0(options.dim0);
unsigned int dim1(options.dim1);
int numberOfPixels(0); // number of pixels in this slice that are not 0
unsigned int dim0max = m_SegmentationCountInSlice[timeStep][dim0].size();
unsigned int dim1max = m_SegmentationCountInSlice[timeStep][dim1].size();
// scan the slice from two directions
// and set the flags for the two dimensions of the slice
for (unsigned int v = 0; v < dim1max; ++v)
{
for (unsigned int u = 0; u < dim0max; ++u)
{
DATATYPE value = *(pixelData + u + v * dim0max);
assert((signed)m_SegmentationCountInSlice[timeStep][dim0][u] + (signed)value >=
0); // just for debugging. This must always be true, otherwise some counting is going wrong
assert((signed)m_SegmentationCountInSlice[timeStep][dim1][v] + (signed)value >= 0);
m_SegmentationCountInSlice[timeStep][dim0][u] =
static_cast<unsigned int>(m_SegmentationCountInSlice[timeStep][dim0][u] + value);
m_SegmentationCountInSlice[timeStep][dim1][v] =
static_cast<unsigned int>(m_SegmentationCountInSlice[timeStep][dim1][v] + value);
numberOfPixels += static_cast<int>(value);
}
}
// flag for the dimension of the slice itself
assert((signed)m_SegmentationCountInSlice[timeStep][sliceDimension][sliceIndex] + numberOfPixels >= 0);
m_SegmentationCountInSlice[timeStep][sliceDimension][sliceIndex] += numberOfPixels;
// MITK_INFO << "scan t=" << timeStep << " from (0,0) to (" << dim0max << "," << dim1max << ") (" << pixelData << "-"
// << pixelData+dim0max*dim1max-1 << ") in slice " << sliceIndex << " found " << numberOfPixels << " pixels" <<
// std::endl;
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::SegmentationInterpolationController::ScanChangedVolume(const itk::Image<TPixel, VImageDimension> *diffImage,
unsigned int timeStep)
{
typedef itk::ImageSliceConstIteratorWithIndex<itk::Image<TPixel, VImageDimension>> IteratorType;
IteratorType iter(diffImage, diffImage->GetLargestPossibleRegion());
iter.SetFirstDirection(0);
iter.SetSecondDirection(1);
int numberOfPixels(0); // number of pixels in this slice that are not 0
typename IteratorType::IndexType index;
unsigned int x = 0;
unsigned int y = 0;
unsigned int z = 0;
iter.GoToBegin();
while (!iter.IsAtEnd())
{
while (!iter.IsAtEndOfSlice())
{
while (!iter.IsAtEndOfLine())
{
index = iter.GetIndex();
x = index[0];
y = index[1];
z = index[2];
TPixel value = iter.Get();
assert((signed)m_SegmentationCountInSlice[timeStep][0][x] + (signed)value >=
0); // just for debugging. This must always be true, otherwise some counting is going wrong
assert((signed)m_SegmentationCountInSlice[timeStep][1][y] + (signed)value >= 0);
m_SegmentationCountInSlice[timeStep][0][x] =
static_cast<unsigned int>(m_SegmentationCountInSlice[timeStep][0][x] + value);
m_SegmentationCountInSlice[timeStep][1][y] =
static_cast<unsigned int>(m_SegmentationCountInSlice[timeStep][1][y] + value);
numberOfPixels += static_cast<int>(value);
++iter;
}
iter.NextLine();
}
assert((signed)m_SegmentationCountInSlice[timeStep][2][z] + numberOfPixels >= 0);
m_SegmentationCountInSlice[timeStep][2][z] += numberOfPixels;
numberOfPixels = 0;
iter.NextSlice();
}
}
template <typename DATATYPE>
void mitk::SegmentationInterpolationController::ScanWholeVolume(const itk::Image<DATATYPE, 3> *,
const Image *volume,
unsigned int timeStep)
{
if (!volume)
return;
if (timeStep >= m_SegmentationCountInSlice.size())
return;
ImageReadAccessor readAccess(volume, volume->GetVolumeData(timeStep));
for (unsigned int slice = 0; slice < volume->GetDimension(2); ++slice)
{
const auto *rawVolume =
static_cast<const DATATYPE *>(readAccess.GetData()); // we again promise not to change anything, we'll just count
const DATATYPE *rawSlice = rawVolume + (volume->GetDimension(0) * volume->GetDimension(1) * slice);
ScanChangedSlice<DATATYPE>(nullptr, SetChangedSliceOptions(2, slice, 0, 1, timeStep, rawSlice));
}
}
void mitk::SegmentationInterpolationController::PrintStatus()
{
- unsigned int timeStep(0); // if needed, put a loop over time steps around everyting, but beware, output will be long
+ unsigned int timeStep(0); // if needed, put a loop over time steps around everything, but beware, output will be long
MITK_INFO << "Interpolator status (timestep 0): dimensions " << m_SegmentationCountInSlice[timeStep][0].size() << " "
<< m_SegmentationCountInSlice[timeStep][1].size() << " " << m_SegmentationCountInSlice[timeStep][2].size()
<< std::endl;
MITK_INFO << "Slice 0: " << m_SegmentationCountInSlice[timeStep][2][0] << std::endl;
// row "x"
for (unsigned int index = 0; index < m_SegmentationCountInSlice[timeStep][0].size(); ++index)
{
if (m_SegmentationCountInSlice[timeStep][0][index] > 0)
MITK_INFO << "O";
else
MITK_INFO << ".";
}
MITK_INFO << std::endl;
// rows "y" and "z" (diagonal)
for (unsigned int index = 1; index < m_SegmentationCountInSlice[timeStep][1].size(); ++index)
{
if (m_SegmentationCountInSlice[timeStep][1][index] > 0)
MITK_INFO << "O";
else
MITK_INFO << ".";
if (m_SegmentationCountInSlice[timeStep][2].size() > index) // if we also have a z value here, then print it, too
{
for (unsigned int indent = 1; indent < index; ++indent)
MITK_INFO << " ";
if (m_SegmentationCountInSlice[timeStep][2][index] > 0)
MITK_INFO << m_SegmentationCountInSlice[timeStep][2][index]; //"O";
else
MITK_INFO << ".";
}
MITK_INFO << std::endl;
}
// z indices that are larger than the biggest y index
for (unsigned int index = m_SegmentationCountInSlice[timeStep][1].size();
index < m_SegmentationCountInSlice[timeStep][2].size();
++index)
{
for (unsigned int indent = 0; indent < index; ++indent)
MITK_INFO << " ";
if (m_SegmentationCountInSlice[timeStep][2][index] > 0)
MITK_INFO << m_SegmentationCountInSlice[timeStep][2][index]; //"O";
else
MITK_INFO << ".";
MITK_INFO << std::endl;
}
}
mitk::Image::Pointer mitk::SegmentationInterpolationController::Interpolate(unsigned int sliceDimension,
unsigned int sliceIndex,
const mitk::PlaneGeometry *currentPlane,
unsigned int timeStep,
ShapeBasedInterpolationAlgorithm::Pointer algorithm)
{
if (m_Segmentation.IsNull() || nullptr == currentPlane)
return nullptr;
if (timeStep >= m_SegmentationCountInSlice.size())
return nullptr;
if (sliceDimension > 2)
return nullptr;
if (0 == sliceIndex)
return nullptr; // First slice, nothing to interpolate
const unsigned int lastSliceIndex = m_SegmentationCountInSlice[timeStep][sliceDimension].size() - 1;
if (lastSliceIndex <= sliceIndex)
return nullptr; // Last slice, nothing to interpolate
if (m_SegmentationCountInSlice[timeStep][sliceDimension][sliceIndex] > 0)
return nullptr; // Slice contains segmentation, nothing to interopolate
unsigned int lowerBound = 0;
unsigned int upperBound = 0;
bool bounds = false;
for (lowerBound = sliceIndex - 1; ; --lowerBound)
{
if (m_SegmentationCountInSlice[timeStep][sliceDimension][lowerBound] > 0)
{
bounds = true;
break;
}
if (0 == lowerBound)
break;
}
if (!bounds)
return nullptr;
bounds = false;
for (upperBound = sliceIndex + 1; upperBound <= lastSliceIndex; ++upperBound)
{
if (m_SegmentationCountInSlice[timeStep][sliceDimension][upperBound] > 0)
{
bounds = true;
break;
}
}
if (!bounds)
return nullptr;
// We have found two neighboring slices with segmentations and made sure that the current slice does not contain anything
mitk::Image::Pointer lowerSlice;
mitk::Image::Pointer upperSlice;
mitk::Image::Pointer resultImage;
try
{
// Extract current slice
resultImage = this->ExtractSlice(currentPlane, sliceIndex, timeStep);
// Creating PlaneGeometry for lower slice
auto reslicePlane = currentPlane->Clone();
// Transforming the current origin so that it matches the lower slice
auto origin = currentPlane->GetOrigin();
m_Segmentation->GetSlicedGeometry(timeStep)->WorldToIndex(origin, origin);
origin[sliceDimension] = lowerBound;
m_Segmentation->GetSlicedGeometry(timeStep)->IndexToWorld(origin, origin);
reslicePlane->SetOrigin(origin);
// Extract lower slice
lowerSlice = this->ExtractSlice(reslicePlane, lowerBound, timeStep, true);
if (lowerSlice.IsNull())
return nullptr;
// Transforming the current origin so that it matches the upper slice
m_Segmentation->GetSlicedGeometry(timeStep)->WorldToIndex(origin, origin);
origin[sliceDimension] = upperBound;
m_Segmentation->GetSlicedGeometry(timeStep)->IndexToWorld(origin, origin);
reslicePlane->SetOrigin(origin);
// Extract the upper slice
upperSlice = this->ExtractSlice(reslicePlane, upperBound, timeStep, true);
if (upperSlice.IsNull())
return nullptr;
}
catch (const std::exception &e)
{
MITK_ERROR << "Error in 2D interpolation: " << e.what();
return nullptr;
}
// Interpolation algorithm inputs:
// - Two segmentations (guaranteed to be of the same data type)
// - Orientation of the segmentations (sliceDimension)
// - Position of the two slices (sliceIndices)
// - Reference image
//
// The interpolation algorithm can use e.g. itk::ImageSliceConstIteratorWithIndex to
// inspect the reference image at appropriate positions.
if (algorithm.IsNull())
algorithm = mitk::ShapeBasedInterpolationAlgorithm::New();
return algorithm->Interpolate(
lowerSlice.GetPointer(),
lowerBound,
upperSlice.GetPointer(),
upperBound,
sliceIndex,
sliceDimension,
resultImage,
timeStep,
m_ReferenceImage);
}
mitk::Image::Pointer mitk::SegmentationInterpolationController::ExtractSlice(const PlaneGeometry* planeGeometry, unsigned int sliceIndex, unsigned int timeStep, bool cache)
{
static const auto MAX_CACHE_SIZE = 2 * std::thread::hardware_concurrency();
const auto key = std::make_pair(sliceIndex, timeStep);
if (cache && m_EnableSliceImageCache)
{
std::lock_guard<std::mutex> lock(m_SliceImageCacheMutex);
if (0 != m_SliceImageCache.count(key))
return m_SliceImageCache[key];
if (MAX_CACHE_SIZE < m_SliceImageCache.size())
m_SliceImageCache.clear();
}
auto extractor = ExtractSliceFilter::New();
extractor->SetInput(m_Segmentation);
extractor->SetTimeStep(timeStep);
extractor->SetResliceTransformByGeometry(m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep(timeStep));
extractor->SetVtkOutputRequest(false);
extractor->SetWorldGeometry(planeGeometry);
extractor->Update();
if (cache && m_EnableSliceImageCache)
{
std::lock_guard<std::mutex> lock(m_SliceImageCacheMutex);
m_SliceImageCache[key] = extractor->GetOutput();
}
return extractor->GetOutput();
}
void mitk::SegmentationInterpolationController::EnableSliceImageCache()
{
m_EnableSliceImageCache = true;
}
void mitk::SegmentationInterpolationController::DisableSliceImageCache()
{
m_EnableSliceImageCache = false;
m_SliceImageCache.clear();
}
diff --git a/Modules/Segmentation/Controllers/mitkToolManager.h b/Modules/Segmentation/Controllers/mitkToolManager.h
index f6bbc61208..edb52d86b0 100644
--- a/Modules/Segmentation/Controllers/mitkToolManager.h
+++ b/Modules/Segmentation/Controllers/mitkToolManager.h
@@ -1,302 +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.
============================================================================*/
#ifndef mitkToolManager_h
#define mitkToolManager_h
#include "mitkDataNode.h"
#include "mitkDataStorage.h"
#include "mitkTool.h"
#include "mitkWeakPointer.h"
#include <MitkSegmentationExports.h>
#pragma GCC visibility push(default)
#include <itkEventObject.h>
#pragma GCC visibility pop
#include <vector>
namespace mitk
{
class Image;
class PlaneGeometry;
/**
\brief Manages and coordinates instances of mitk::Tool.
\sa QmitkToolSelectionBox
\sa Tool
\sa QmitkSegmentationView
\ingroup Interaction
\ingroup ToolManagerEtAl
- There is a separate page describing the general design of QmitkSegmentationView: \ref QmitkSegmentationTechnicalPage
-
This class creates and manages several instances of mitk::Tool.
\li ToolManager creates instances of mitk::Tool by asking the itk::ObjectFactory to list all known implementations
of mitk::Tool.
As a result, one has to implement both a subclass of mitk::Tool and a matching subclass of
itk::ObjectFactoryBase that is registered
- to the top-level itk::ObjectFactory. For an example, see mitkContourToolFactory.h. (this limitiation of
+ to the top-level itk::ObjectFactory. For an example, see mitkContourToolFactory.h. (this limitation of
one-class-one-factory is due
to the implementation of itk::ObjectFactory).
In MITK, the right place to register the factories to itk::ObjectFactory is the mitk::QMCoreObjectFactory or
mitk::SBCoreObjectFactory.
\li ToolManager knows a set of "reference" DataNodes and a set of "working" DataNodes. The first application are
segmentation tools, where the
reference is the original image and the working data the (kind of) binary segmentation. However, ToolManager is
implemented more generally, so that
there could be other tools that work, e.g., with surfaces.
\li There is a set of events that are sent by ToolManager. At the moment these are TODO update documentation:
- mitk::ToolReferenceDataChangedEvent whenever somebody calls SetReferenceData. Most of the time this actually
means that the data has changed, but
there might be cases where the same data is passed to SetReferenceData a second time, so don't rely on the
assumption that something actually changed.
- mitk::ToolSelectedEvent is sent when a (truly) different tool was activated. In reaction to this event you can
ask for the active Tool using
GetActiveTool or GetActiveToolID (where nullptr or -1 indicate that NO tool is active at the moment).
- Design descisions:
+ Design decisions:
\li Not a singleton, because there could be two functionalities using tools, each one with different
reference/working data.
$Author$
*/
class MITKSEGMENTATION_EXPORT ToolManager : public itk::Object
{
public:
typedef std::vector<Tool::Pointer> ToolVectorType;
typedef std::vector<Tool::ConstPointer> ToolVectorTypeConst;
typedef std::vector<DataNode *> DataVectorType; // has to be observed for delete events!
typedef std::map<DataNode *, unsigned long> NodeTagMapType;
Message<> NodePropertiesChanged;
Message<> NewNodesGenerated;
Message1<DataVectorType *> NewNodeObjectsGenerated;
Message<> ActiveToolChanged;
Message<> ReferenceDataChanged;
Message<> WorkingDataChanged;
Message<> RoiDataChanged;
Message<> SelectedTimePointChanged;
Message1<std::string> ToolErrorMessage;
Message1<std::string> GeneralToolMessage;
mitkClassMacroItkParent(ToolManager, itk::Object);
mitkNewMacro1Param(ToolManager, DataStorage *);
/**
\brief Gives you a list of all tools.
This is const on purpose.
*/
const ToolVectorTypeConst GetTools();
int GetToolID(const Tool *tool);
/**
\param id The tool of interest.
Counting starts with 0.
*/
Tool *GetToolById(int id);
/**
\param id The tool to activate. Provide -1 for disabling any tools.
Counting starts with 0.
- Registeres a listner for NodeRemoved event at DataStorage (see mitk::ToolManager::OnNodeRemoved).
+ Registers a listener for NodeRemoved event at DataStorage (see mitk::ToolManager::OnNodeRemoved).
*/
bool ActivateTool(int id);
template <class T>
int GetToolIdByToolType()
{
int id = 0;
for (auto iter = m_Tools.begin(); iter != m_Tools.end(); ++iter, ++id)
{
if (dynamic_cast<T *>(iter->GetPointer()))
{
return id;
}
}
return -1;
}
/**
\return -1 for "No tool is active"
*/
int GetActiveToolID();
/**
\return nullptr for "No tool is active"
*/
Tool *GetActiveTool();
/**
\brief Set a list of data/images as reference objects.
*/
void SetReferenceData(DataVectorType);
/**
\brief Set single data item/image as reference object.
*/
void SetReferenceData(DataNode *);
/**
\brief Set a list of data/images as working objects.
*/
void SetWorkingData(DataVectorType);
/**
\brief Set single data item/image as working object.
*/
void SetWorkingData(DataNode *);
/**
\brief Set a list of data/images as roi objects.
*/
void SetRoiData(DataVectorType);
/**
\brief Set a single data item/image as roi object.
*/
void SetRoiData(DataNode *);
/**
\brief Get the list of reference data.
*/
DataVectorType GetReferenceData();
/**
\brief Get the current reference data.
\warning If there is a list of items, this method will only return the first list item.
*/
DataNode *GetReferenceData(int);
/**
\brief Get the list of working data.
*/
DataVectorType GetWorkingData();
/**
\brief Get the current working data.
\warning If there is a list of items, this method will only return the first list item.
*/
DataNode *GetWorkingData(unsigned int);
/**
\brief Get the current roi data
*/
DataVectorType GetRoiData();
/**
\brief Get the roi data at position idx
*/
DataNode *GetRoiData(int idx);
DataStorage::Pointer GetDataStorage() const;
void SetDataStorage(DataStorage &storage);
/** Get the current selected time point of the RenderManager
*/
TimePointType GetCurrentTimePoint() const;
/**
\brief Tell that someone is using tools.
GUI elements should call this when they become active. This method increases an internal "client count".
*/
void RegisterClient();
/**
\brief Tell that someone is NOT using tools.
GUI elements should call this when they become active. This method increases an internal "client count".
*/
void UnregisterClient();
/** \brief Initialize all classes derived from mitk::Tool by itkObjectFactoy */
void InitializeTools();
void OnOneOfTheReferenceDataDeletedConst(const itk::Object *caller, const itk::EventObject &e);
void OnOneOfTheReferenceDataDeleted(itk::Object *caller, const itk::EventObject &e);
void OnOneOfTheWorkingDataDeletedConst(const itk::Object *caller, const itk::EventObject &e);
void OnOneOfTheWorkingDataDeleted(itk::Object *caller, const itk::EventObject &e);
void OnOneOfTheRoiDataDeletedConst(const itk::Object *caller, const itk::EventObject &e);
void OnOneOfTheRoiDataDeleted(itk::Object *caller, const itk::EventObject &e);
/**
\brief Connected to tool's messages
This method just resends error messages coming from any of the tools. This way clients (GUIs) only have to observe
one message.
*/
void OnToolErrorMessage(std::string s);
void OnGeneralToolMessage(std::string s);
protected:
/**
You may specify a list of tool "groups" that should be available for this ToolManager. Every Tool can report its
group
as a string. This constructor will try to find the tool's group inside the supplied string. If there is a match,
the tool is accepted. Effectively, you can provide a human readable list like "default, lymphnodevolumetry,
oldERISstuff".
*/
ToolManager(DataStorage *storage); // purposely hidden
~ToolManager() override;
ToolVectorType m_Tools;
Tool *m_ActiveTool;
int m_ActiveToolID;
us::ServiceRegistration<InteractionEventObserver> m_ActiveToolRegistration;
DataVectorType m_ReferenceData;
NodeTagMapType m_ReferenceDataObserverTags;
DataVectorType m_WorkingData;
NodeTagMapType m_WorkingDataObserverTags;
DataVectorType m_RoiData;
NodeTagMapType m_RoiDataObserverTags;
int m_RegisteredClients;
WeakPointer<DataStorage> m_DataStorage;
/// \brief Callback for NodeRemove events
void OnNodeRemoved(const mitk::DataNode *node);
/** Callback for time changed events*/
void OnTimeChangedConst(const itk::Object* caller, const itk::EventObject& e);
void OnTimeChanged(itk::Object* caller, const itk::EventObject& e);
void EnsureTimeObservation();
void StopTimeObservation();
private:
/** Time point of last detected change*/
TimePointType m_LastTimePoint = 0;
/** Tag of the observer that listens to time changes*/
unsigned long m_TimePointObserverTag = 0;
/** Pointer to the observed time stepper*/
WeakPointer<TimeNavigationController> m_CurrentTimeNavigationController;
};
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp
index 48cfaf8ff7..b07f24e53f 100644
--- a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp
+++ b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp
@@ -1,331 +1,331 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkContourModelLiveWireInteractor.h"
#include "mitkInteractionPositionEvent.h"
#include "mitkToolManager.h"
#include "mitkBaseRenderer.h"
#include "mitkRenderingManager.h"
#include <mitkInteractionConst.h>
#include "mitkIOUtil.h"
mitk::ContourModelLiveWireInteractor::ContourModelLiveWireInteractor() : ContourModelInteractor()
{
m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New();
m_LiveWireFilter->SetUseCostFunction(true);
m_NextActiveVertexDown.Fill(0);
m_NextActiveVertexUp.Fill(0);
}
mitk::ContourModelLiveWireInteractor::~ContourModelLiveWireInteractor()
{
}
void mitk::ContourModelLiveWireInteractor::ConnectActionsAndFunctions()
{
CONNECT_CONDITION("checkisOverPoint", OnCheckPointClick);
CONNECT_CONDITION("mouseMove", IsHovering);
CONNECT_FUNCTION("movePoint", OnMovePoint);
CONNECT_FUNCTION("deletePoint", OnDeletePoint);
CONNECT_FUNCTION("addPoint", OnAddPoint)
CONNECT_FUNCTION("finish", OnFinishEditing);
}
bool mitk::ContourModelLiveWireInteractor::OnCheckPointClick(const InteractionEvent *interactionEvent)
{
auto isVertexSelected = Superclass::OnCheckPointClick(interactionEvent);
if (isVertexSelected)
{
auto* contour = dynamic_cast<mitk::ContourModel*>(this->GetDataNode()->GetData());
const auto* positionEvent =
dynamic_cast<const mitk::InteractionPositionEvent*>(interactionEvent);
mitk::Point3D click = positionEvent->GetPositionInWorld();
const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData());
auto controlVertices = contour->GetControlVertices(timeStep);
const mitk::ContourModel::VertexType* nextPoint = contour->GetNextControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep);
const mitk::ContourModel::VertexType* previousPoint = contour->GetPreviousControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep);
this->SplitContourFromSelectedVertex(contour, nextPoint, previousPoint, timeStep);
m_NextActiveVertexUp = nextPoint->Coordinates;
m_NextActiveVertexDown = previousPoint->Coordinates;
// clear previous void positions
this->m_LiveWireFilter->ClearRepulsivePoints();
// all points in lower and upper part should be marked as repulsive points to not be changed
this->SetRepulsivePoints(previousPoint, m_ContourLeft, timeStep);
this->SetRepulsivePoints(nextPoint, m_ContourRight, timeStep);
// clear container with void points between neighboring control points
m_ContourBeingModified.clear();
}
return isVertexSelected;
}
void mitk::ContourModelLiveWireInteractor::SetWorkingImage(mitk::Image *_arg)
{
if (this->m_WorkingSlice != _arg)
{
this->m_WorkingSlice = _arg;
this->m_LiveWireFilter->SetInput(this->m_WorkingSlice);
}
}
void mitk::ContourModelLiveWireInteractor::OnAddPoint(StateMachineAction* sm, InteractionEvent* interactionEvent)
{
Superclass::OnAddPoint(sm, interactionEvent);
}
void mitk::ContourModelLiveWireInteractor::OnDeletePoint(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData());
auto *contour = dynamic_cast<mitk::ContourModel *>(this->GetDataNode()->GetData());
if (contour == nullptr)
{
MITK_ERROR << "Invalid Contour!";
return;
}
if (contour->GetSelectedVertex())
{
mitk::ContourModel::Pointer newContour = mitk::ContourModel::New();
newContour->Expand(contour->GetTimeSteps());
newContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone());
newContour->Concatenate(m_ContourLeft, timeStep);
// recompute contour between neighbored two active control points
this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown);
this->m_LiveWireFilter->SetEndPoint(this->m_NextActiveVertexUp);
this->m_LiveWireFilter->Update();
mitk::ContourModel *liveWireContour = this->m_LiveWireFilter->GetOutput();
assert(liveWireContour);
if (liveWireContour->IsEmpty(timeStep))
return;
liveWireContour->RemoveVertexAt(0, timeStep);
liveWireContour->RemoveVertexAt(liveWireContour->GetNumberOfVertices(timeStep) - 1, timeStep);
// insert new live wire computed points
newContour->Concatenate(liveWireContour, timeStep);
// insert right side of original contour
newContour->Concatenate(this->m_ContourRight, timeStep);
newContour->SetClosed(contour->IsClosed(timeStep), timeStep);
// instead of leaving a single point, delete all points
if (newContour->GetNumberOfVertices(timeStep) <= 2)
{
newContour->Clear(timeStep);
}
this->GetDataNode()->SetData(newContour);
mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow());
}
}
void mitk::ContourModelLiveWireInteractor::OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
if (!positionEvent)
return;
const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData());
mitk::Point3D currentPosition = positionEvent->GetPositionInWorld();
auto *contour = dynamic_cast<mitk::ContourModel *>(this->GetDataNode()->GetData());
if (contour == nullptr)
{
MITK_ERROR << "invalid contour";
return;
}
mitk::ContourModel::Pointer editingContour = mitk::ContourModel::New();
editingContour->Expand(contour->GetTimeSteps());
editingContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone());
// recompute left live wire, i.e. the contour between previous active vertex and selected vertex
this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown);
this->m_LiveWireFilter->SetEndPoint(currentPosition);
// remove void positions between previous active vertex and next active vertex.
if (!m_ContourBeingModified.empty())
{
std::vector<itk::Index<2>>::const_iterator iter = m_ContourBeingModified.begin();
for (; iter != m_ContourBeingModified.end(); iter++)
{
this->m_LiveWireFilter->RemoveRepulsivePoint((*iter));
}
}
// update to get the left livewire. Remember that the points in the rest of the contour are already
// set as void positions in the filter
this->m_LiveWireFilter->Update();
mitk::ContourModel::Pointer leftLiveWire = this->m_LiveWireFilter->GetOutput();
assert(leftLiveWire);
if (!leftLiveWire->IsEmpty(timeStep))
leftLiveWire->RemoveVertexAt(0, timeStep);
editingContour->Concatenate(leftLiveWire, timeStep);
// the new index of the selected vertex
unsigned int selectedVertexIndex =
this->m_ContourLeft->GetNumberOfVertices(timeStep) + leftLiveWire->GetNumberOfVertices(timeStep) - 1;
// at this point the container has to be empty
m_ContourBeingModified.clear();
// add points from left live wire contour
auto iter = leftLiveWire->IteratorBegin(timeStep);
for (; iter != leftLiveWire->IteratorEnd(timeStep); iter++)
{
itk::Index<2> idx;
this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx);
this->m_LiveWireFilter->AddRepulsivePoint(idx);
// add indices
m_ContourBeingModified.push_back(idx);
}
// recompute right live wire, i.e. the contour between selected vertex and next active vertex
this->m_LiveWireFilter->SetStartPoint(currentPosition);
this->m_LiveWireFilter->SetEndPoint(m_NextActiveVertexUp);
// update filter with all contour points set as void but the right live wire portion to be calculated now
this->m_LiveWireFilter->Update();
mitk::ContourModel::Pointer rightLiveWire = this->m_LiveWireFilter->GetOutput();
assert(rightLiveWire);
if (!leftLiveWire->IsEmpty(timeStep))
leftLiveWire->SetControlVertexAt(leftLiveWire->GetNumberOfVertices() - 1, timeStep);
if (!rightLiveWire->IsEmpty(timeStep))
rightLiveWire->RemoveVertexAt(0, timeStep);
editingContour->Concatenate(rightLiveWire, timeStep);
mitk::ContourModel::Pointer newContour = mitk::ContourModel::New();
newContour->Expand(contour->GetTimeSteps());
newContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone());
// concatenate left original contour
newContour->Concatenate(this->m_ContourLeft, timeStep);
newContour->Concatenate(editingContour, timeStep, true);
// set last inserted vertex as selected
newContour->SelectVertexAt(selectedVertexIndex, timeStep);
// set as control point
newContour->SetSelectedVertexAsControlPoint(true);
// concatenate right original contour
newContour->Concatenate(this->m_ContourRight, timeStep);
newContour->SetClosed(contour->IsClosed(timeStep), timeStep);
this->GetDataNode()->SetData(newContour);
mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());
}
void mitk::ContourModelLiveWireInteractor::SplitContourFromSelectedVertex(mitk::ContourModel *srcContour,
const mitk::ContourModel::VertexType *nextPoint,
const mitk::ContourModel::VertexType *previousPoint,
int timeStep)
{
m_ContourLeft = mitk::ContourModel::New();
m_ContourRight = mitk::ContourModel::New();
auto it = srcContour->IteratorBegin();
- // part between nextPoint and end of Countour
+ // part between nextPoint and end of Contour
bool upperPart = false;
- // part between start of countour and previousPoint
+ // part between start of contour and previousPoint
bool lowerPart = true;
// edge cases when point right before first control vertex is selected or first control vertex is selected
if (nextPoint == (*it) || srcContour->GetSelectedVertex() == (*it))
{
upperPart = true;
lowerPart = false;
m_ContourLeft->AddVertex(previousPoint->Coordinates, previousPoint->IsControlPoint, timeStep);
}
// if first control vertex is selected, move to next point before adding vertices to m_ContourRight
// otherwise, second line appears when moving the vertex
if (srcContour->GetSelectedVertex() == (*it))
{
while (*it != nextPoint)
{
it++;
}
}
for (; it != srcContour->IteratorEnd(timeStep); it++)
{
// everything in lower part should be added to m_CountoutLeft
if (lowerPart)
{
m_ContourLeft->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timeStep);
}
// start of "restricted area" where no vertex should be added to m_CountoutLeft or m_CountoutRight
if (*it == previousPoint)
{
lowerPart = false;
upperPart = false;
}
// start of upperPart
if (*it == nextPoint)
{
upperPart = true;
}
// everything in upper part should be added to m_CountoutRight
if (upperPart)
{
m_ContourRight->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timeStep);
}
}
}
void mitk::ContourModelLiveWireInteractor::SetRepulsivePoints(const mitk::ContourModel::VertexType *pointToExclude,
mitk::ContourModel *contour,
int timeStep)
{
auto it = contour->IteratorBegin();
for (; it != contour->IteratorEnd(timeStep); it++)
{
if (*it != pointToExclude)
{
itk::Index<2> idx;
this->m_WorkingSlice->GetGeometry()->WorldToIndex((*it)->Coordinates, idx);
this->m_LiveWireFilter->AddRepulsivePoint(idx);
}
}
}
void mitk::ContourModelLiveWireInteractor::OnFinishEditing(StateMachineAction *, InteractionEvent *)
{
}
diff --git a/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp b/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp
index 2ce18ddc49..e9ba8bb092 100644
--- a/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkEraseRegionTool.cpp
@@ -1,87 +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.
============================================================================*/
#include "mitkEraseRegionTool.h"
#include "mitkEraseRegionTool.xpm"
#include <mitkImagePixelReadAccessor.h>
#include <mitkImageGenerator.h>
#include <mitkImageAccessByItk.h>
// us
#include <usGetModuleContext.h>
#include <usModule.h>
#include <usModuleContext.h>
#include <usModuleResource.h>
namespace mitk
{
MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, EraseRegionTool, "Erase tool");
}
const char **mitk::EraseRegionTool::GetXPM() const
{
return mitkEraseRegionTool_xpm;
}
us::ModuleResource mitk::EraseRegionTool::GetIconResource() const
{
us::Module *module = us::GetModuleContext()->GetModule();
us::ModuleResource resource = module->GetResource("Erase.svg");
return resource;
}
us::ModuleResource mitk::EraseRegionTool::GetCursorIconResource() const
{
us::Module *module = us::GetModuleContext()->GetModule();
us::ModuleResource resource = module->GetResource("Erase_Cursor.svg");
return resource;
}
const char *mitk::EraseRegionTool::GetName() const
{
return "Erase";
}
-template <typename TPixel, unsigned int VImageDimension>
-void DoFillImage(itk::Image<TPixel, VImageDimension>* image)
-{
- image->FillBuffer(1);
-};
-
mitk::Image::Pointer mitk::EraseRegionTool::GenerateFillImage(const Image* workingSlice, Point3D seedPoint, mitk::Label::PixelType& seedLabelValue) const
{
itk::Index<2> seedIndex;
workingSlice->GetGeometry()->WorldToIndex(seedPoint, seedIndex);
using AccessorType = ImagePixelReadAccessor<Label::PixelType, 2>;
AccessorType accessor(workingSlice);
seedLabelValue = accessor.GetPixelByIndex(seedIndex);
Image::Pointer fillImage;
if ( seedLabelValue == LabelSetImage::UNLABELED_VALUE)
- { //clicked on background remove everything which is not locked.
- fillImage = workingSlice->Clone();
- AccessByItk(fillImage, DoFillImage);
+ {
+ return nullptr;
}
else
{
fillImage = Superclass::GenerateFillImage(workingSlice, seedPoint, seedLabelValue);
}
return fillImage;
}
void mitk::EraseRegionTool::PrepareFilling(const Image* /*workingSlice*/, Point3D /*seedPoint*/)
{
m_FillLabelValue = LabelSetImage::UNLABELED_VALUE;
};
diff --git a/Modules/Segmentation/Interactions/mitkEraseRegionTool.h b/Modules/Segmentation/Interactions/mitkEraseRegionTool.h
index 63c7daedce..74bb9e66a9 100644
--- a/Modules/Segmentation/Interactions/mitkEraseRegionTool.h
+++ b/Modules/Segmentation/Interactions/mitkEraseRegionTool.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 mitkEraseRegionTool_h
#define mitkEraseRegionTool_h
#include "mitkFillRegionBaseTool.h"
#include <MitkSegmentationExports.h>
namespace us
{
class ModuleResource;
}
namespace mitk
{
/**
\brief Erase the inside of a contour by
filling the inside of a contour with the background pixel value.
\sa SetRegionTool
\ingroup Interactions
Finds the outer contour of a shape in 2D (possibly including single patches) and sets all
the pixels inside to the background pixel value (erasing a segmentation).
- If clicked on the background, the outer contour might contain the whole image and thus
- fill the whole image with the background pixel value.
\warning Only to be instantiated by mitk::ToolManager.
*/
class MITKSEGMENTATION_EXPORT EraseRegionTool : public FillRegionBaseTool
{
public:
mitkClassMacro(EraseRegionTool, FillRegionBaseTool);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
const char **GetXPM() const override;
us::ModuleResource GetCursorIconResource() const override;
us::ModuleResource GetIconResource() const override;
const char *GetName() const override;
protected:
EraseRegionTool() = default; // purposely hidden
~EraseRegionTool() = default;
Image::Pointer GenerateFillImage(const Image* workingSlice, Point3D seedPoint, mitk::Label::PixelType& seedLabelValue) const override;
void PrepareFilling(const Image* workingSlice, Point3D seedPoint) override;
};
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h
index 519f5b361a..d189e21a85 100644
--- a/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h
+++ b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.h
@@ -1,141 +1,141 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkFeedbackContourTool_h
#define mitkFeedbackContourTool_h
#include "mitkCommon.h"
#include "mitkContourModelUtils.h"
#include "mitkContourUtils.h" //TODO remove legacy support
#include "mitkImage.h"
#include "mitkSegTool2D.h"
#include <MitkSegmentationExports.h>
#include "mitkDataNode.h"
#include "mitkImageCast.h"
namespace mitk
{
/**
\brief Base class for tools that use a contour for feedback
\sa Tool
\sa ContourModel
\ingroup Interaction
\ingroup ToolManagerEtAl
Implements helper methods, that might be of use to all kind of 2D segmentation tools that use a contour for user
feedback.
- Providing a feedback contour that might be added or removed from the visible scene (SetFeedbackContourVisible).
- Filling of a contour into a 2D slice
These helper methods are actually implemented in ContourUtils now. FeedbackContourTool only forwards such requests.
\warning Only to be instantiated by mitk::ToolManager.
$Author: nolden $
*/
class MITKSEGMENTATION_EXPORT FeedbackContourTool : public SegTool2D
{
public:
mitkClassMacro(FeedbackContourTool, SegTool2D);
protected:
FeedbackContourTool(); // purposely hidden
FeedbackContourTool(const char *); // purposely hidden
~FeedbackContourTool() override;
const ContourModel *GetFeedbackContour() const;
/** (Re)initialize the feesback contour by creating a new instance.
* It is assured that the new instance as the same time geometry than
* the working image.*/
void InitializeFeedbackContour(bool isClosed);
/** Clears the current time step of the feedback contour and resets its closed state.*/
void ClearsCurrentFeedbackContour(bool isClosed);
/** Updates the feedback contour of the currently selected time point. The update will be done
* by clearing all existings vertices at the current time point and copying the vertics of the
* source model at the specified source time step.*/
void UpdateCurrentFeedbackContour(const ContourModel* sourceModel, TimeStepType sourceTimeStep = 0);
/** Updates the feedback contour at the time step specified by feedbackTimeStep. The update will be done
* by clearing all existings vertices at feedbackTimeStep and copying the vertics of the
* source model at the specified source time step.*/
void UpdateFeedbackContour(const ContourModel* sourceModel, TimeStepType feedbackTimeStep, TimeStepType sourceTimeStep = 0);
/** Adds a vertex to the feedback contour for the current time point. */
void AddVertexToCurrentFeedbackContour(const Point3D& point);
/** Adds a vertex to the feedback contour for the passed time step. If time step is invalid, nothing will be added.*/
void AddVertexToFeedbackContour(const Point3D& point, TimeStepType feedbackTimeStep);
void SetFeedbackContourVisible(bool);
/// Provide values from 0.0 (black) to 1.0 (full color)
void SetFeedbackContourColor(float r, float g, float b);
void SetFeedbackContourColorDefault();
void Deactivated() override;
void Activated() override;
/**
\brief Projects a contour onto an image point by point. Converts from world to index coordinates.
\param slice
\param contourIn3D
*/
ContourModel::Pointer ProjectContourTo2DSlice(const Image *slice,
const ContourModel *contourIn3D);
/**
\brief Projects a slice index coordinates of a contour back into world coordinates.
\param sliceGeometry
\param contourIn2D
*/
ContourModel::Pointer BackProjectContourFrom2DSlice(const BaseGeometry *sliceGeometry,
const ContourModel *contourIn2D);
- /** Helper methods that checks all precondition and if they are fullfilled does the following:
+ /** Helper methods that checks all precondition and if they are fulfilled does the following:
* 1. Gets the contour of the time point specified by positionEvent.
* 2. Gets the affacted working slice of the time point specified by positionEvent.
- * 3. projects the contour onto the working slice and then fills it with the passed paintingPixelValue (adjusted by the current active lable value)
+ * 3. projects the contour onto the working slice and then fills it with the passed paintingPixelValue (adjusted by the current active label value)
* to the slice.
* 4. writes the slice back into the working image using SegTool2D::WriteBackSegmentationResult().*/
void WriteBackFeedbackContourAsSegmentationResult(const InteractionPositionEvent* positionEvent, int paintingPixelValue, bool setInvisibleAfterSuccess = true);
/**
\brief Fill a contour in a 2D slice with a specified pixel value.
*/
void FillContourInSlice(ContourModel *projectedContour, Image *sliceImage, int paintingPixelValue = 1);
/**
\brief Fill a contour in a 2D slice with a specified pixel value at a given time step.
*/
void FillContourInSlice(ContourModel *projectedContour,
unsigned int timeStep,
Image *sliceImage,
int paintingPixelValue = 1);
private:
ContourModel::Pointer m_FeedbackContour;
DataNode::Pointer m_FeedbackContourNode;
bool m_FeedbackContourVisible;
};
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.cpp b/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.cpp
index 2bf51465fc..bcb0d74f29 100644
--- a/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.cpp
@@ -1,146 +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 "mitkFillRegionBaseTool.h"
#include "mitkToolManager.h"
#include "mitkBaseRenderer.h"
#include "mitkDataStorage.h"
#include "mitkITKImageImport.h"
#include "mitkImageAccessByItk.h"
#include "mitkRenderingManager.h"
#include <itkConnectedThresholdImageFilter.h>
mitk::FillRegionBaseTool::FillRegionBaseTool() : SegTool2D("MouseReleaseOnly")
{
}
mitk::FillRegionBaseTool::~FillRegionBaseTool()
{
}
void mitk::FillRegionBaseTool::ConnectActionsAndFunctions()
{
CONNECT_FUNCTION("Release", OnClick);
}
template <typename TPixel, unsigned int VImageDimension>
void DoITKRegionGrowing(const itk::Image<TPixel, VImageDimension>* oldSegImage,
mitk::Image::Pointer& filledRegionImage, itk::Index<VImageDimension> seedIndex, mitk::Label::PixelType& seedLabel )
{
typedef itk::Image<TPixel, VImageDimension> InputImageType;
typedef itk::Image<mitk::Label::PixelType, VImageDimension> OutputImageType;
typedef itk::ConnectedThresholdImageFilter<InputImageType, OutputImageType> RegionGrowingFilterType;
seedLabel = oldSegImage->GetPixel(seedIndex);
typename OutputImageType::Pointer itkResultImage;
- filledRegionImage = nullptr;
try
{
typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New();
regionGrower->SetInput(oldSegImage);
regionGrower->SetReplaceValue(1);
regionGrower->AddSeed(seedIndex);
regionGrower->SetLower(seedLabel);
regionGrower->SetUpper(seedLabel);
regionGrower->Update();
itkResultImage = regionGrower->GetOutput();
}
catch (const itk::ExceptionObject&)
{
return; // can't work
}
catch (...)
{
return;
}
- mitk::CastToMitkImage(itkResultImage, filledRegionImage);
+ filledRegionImage->SetChannel(itkResultImage->GetBufferPointer());
}
void mitk::FillRegionBaseTool::OnClick(StateMachineAction*, InteractionEvent* interactionEvent)
{
auto positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>(interactionEvent);
if (nullptr == positionEvent)
return;
auto labelSetImage = dynamic_cast<const LabelSetImage*>(this->GetWorkingData());
if (nullptr == labelSetImage)
{
return;
}
if (!IsPositionEventInsideImageRegion(positionEvent, labelSetImage))
{
return;
}
m_LastEventSender = positionEvent->GetSender();
m_LastEventSlice = m_LastEventSender->GetSlice();
auto workingSlice = this->GetAffectedWorkingSlice(positionEvent);
auto click = positionEvent->GetPositionInWorld();
m_SeedLabelValue = 0;
auto fillImage = this->GenerateFillImage(workingSlice, click, m_SeedLabelValue);
if (fillImage.IsNull())
{
return; //nothing to fill;
}
if (labelSetImage->IsLabelLocked(m_SeedLabelValue) && m_SeedLabelValue!=labelSetImage->GetActiveLabel()->GetValue())
{
ErrorMessage.Send("Label of selected region is locked. Tool operation has no effect.");
return;
}
this->PrepareFilling(workingSlice, click);
//as fill region tools should always allow to manipulate active label
//(that is what the user expects/knows when using tools so far:
//the active label can always be changed even if locked)
//we realize that by cloning the relevant label set and changing the lock state
//this fillLabelSet is used for the transfer.
auto activeLabelClone = labelSetImage->GetActiveLabel()->Clone();
if (nullptr != activeLabelClone)
{
activeLabelClone->SetLocked(false);
}
TransferLabelContentAtTimeStep(fillImage, workingSlice, { activeLabelClone }, 0, LabelSetImage::UNLABELED_VALUE, LabelSetImage::UNLABELED_VALUE, false, { {1, m_FillLabelValue} }, m_MergeStyle);
this->WriteBackSegmentationResult(positionEvent, workingSlice);
mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());
}
mitk::Image::Pointer mitk::FillRegionBaseTool::GenerateFillImage(const Image* workingSlice, Point3D seedPoint, mitk::Label::PixelType& seedLabelValue) const
{
itk::Index<2> seedIndex;
workingSlice->GetGeometry()->WorldToIndex(seedPoint, seedIndex);
- Image::Pointer fillImage;
-
+ auto fillImage = Image::New();
+ fillImage->Initialize(workingSlice);
AccessFixedDimensionByItk_n(workingSlice, DoITKRegionGrowing, 2, (fillImage, seedIndex, seedLabelValue));
return fillImage;
}
diff --git a/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.h b/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.h
index e2d91d2c9e..5e68e1fdf4 100644
--- a/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.h
+++ b/Modules/Segmentation/Interactions/mitkFillRegionBaseTool.h
@@ -1,83 +1,83 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkFillRegionBaseTool_h
#define mitkFillRegionBaseTool_h
#include "mitkCommon.h"
#include "mitkContourModelUtils.h"
#include "mitkContourUtils.h" //TODO remove legacy support
#include "mitkImage.h"
#include "mitkSegTool2D.h"
#include <MitkSegmentationExports.h>
#include "mitkDataNode.h"
#include "mitkImageCast.h"
namespace mitk
{
/**
\brief Base class for tools that fill a connected region of a 2D slice
\sa Tool
\ingroup Interaction
\ingroup ToolManagerEtAl
\warning Only to be instantiated by mitk::ToolManager.
$Author: nolden $
*/
class MITKSEGMENTATION_EXPORT FillRegionBaseTool : public SegTool2D
{
public:
mitkClassMacro(FillRegionBaseTool, SegTool2D);
protected:
FillRegionBaseTool(); // purposely hidden
~FillRegionBaseTool() override;
void ConnectActionsAndFunctions() override;
/// \brief Add a control point and finish current segment.
virtual void OnClick(StateMachineAction*, InteractionEvent* interactionEvent);
/** Function that generates the mask image that indicates which pixels should be filled.
* Caller of this function assumes that all pixels that should be filled have the value 1.
* Pixels that should stay untouched should have the value 0.
* The default implementation marks the connected reagion around seedPoint, that has
* the same pixel value/label like the seedPoint.
- * You may reimplement this function to change the strategy to determin the fill region.
- * @param workingSlice part of the segmentation image that should be used to determin the fill image.
+ * You may reimplement this function to change the strategy to determine the fill region.
+ * @param workingSlice part of the segmentation image that should be used to determine the fill image.
* @param seedPoint The world coordinate position where the user has cliced.
* @param [out] seedLabelValue The function should return the label value that should be assumed
* as clicked on, given the seedPoint.
* @return Return the image maske that indicates which pixels should be filled. Returning
* a null pointer indicates that there is nothing to fill.
*/
virtual Image::Pointer GenerateFillImage(const Image* workingSlice, Point3D seedPoint, mitk::Label::PixelType& seedLabelValue) const;
/** Function that is called by OnClick before the filling is executed. If you want to do special
- * preperation (e.g. change m_FillLabelValue, you can overwrite this function. */
+ * preparation (e.g. change m_FillLabelValue, you can overwrite this function. */
virtual void PrepareFilling(const Image* workingSlice, Point3D seedPoint) = 0;
Label::PixelType m_FillLabelValue = 0;
Label::PixelType m_SeedLabelValue = 0;
MultiLabelSegmentation::MergeStyle m_MergeStyle = MultiLabelSegmentation::MergeStyle::Replace;
private:
};
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitkLassoTool.h b/Modules/Segmentation/Interactions/mitkLassoTool.h
index d86b1d3e92..72a5a0f6c2 100644
--- a/Modules/Segmentation/Interactions/mitkLassoTool.h
+++ b/Modules/Segmentation/Interactions/mitkLassoTool.h
@@ -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.
============================================================================*/
#ifndef mitkLassoTool_h
#define mitkLassoTool_h
#include "mitkEditableContourTool.h"
#include "mitkContourTool.h"
#include "mitkContourModelInteractor.h"
namespace mitk
{
/**
\brief A 2D segmentation tool to draw polygon structures.
The contour between the last point and the current mouse position is
computed by forming a straight line.
The tool always assumes that unconfirmed contours are always defined for the
current time point. So the time step in which the contours will be stored as
segmentations will be determined when the contours got confirmed. Then they
- will be transfered to the slices of the currently selected time step.
+ will be transferred to the slices of the currently selected time step.
\sa SegTool2D
\ingroup Interaction
\ingroup ToolManagerEtAl
\warning Only to be instantiated by mitk::ToolManager.
*/
class MITKSEGMENTATION_EXPORT LassoTool : public EditableContourTool
{
public:
mitkClassMacro(LassoTool, SegTool2D);
itkFactorylessNewMacro(Self);
us::ModuleResource GetCursorIconResource() const override;
us::ModuleResource GetIconResource() const override;
const char *GetName() const override;
const char **GetXPM() const override;
protected:
LassoTool();
~LassoTool() override;
void ConnectActionsAndFunctions() override;
void FinishTool() override;
private:
mitk::ContourModelInteractor::Pointer m_ContourInteractor;
};
}
#endif
diff --git a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h
index a598e8c3bb..859fb6e9e9 100644
--- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h
+++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h
@@ -1,90 +1,90 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkLiveWireTool2D_h
#define mitkLiveWireTool2D_h
#include <mitkEditableContourTool.h>
#include <mitkContourModelLiveWireInteractor.h>
namespace mitk
{
/**
\brief A 2D segmentation tool based on a LiveWire approach.
The contour between the last point and the current mouse position is
computed by searching the shortest path according to specific features of
the image. The contour thus tends to snap to the boundary of objects.
The tool always assumes that unconfirmed contours are always defined for the
current time point. So the time step in which the contours will be stored as
segmentations will be determined when the contours got confirmed. Then they
- will be transfered to the slices of the currently selected time step.
+ will be transferred to the slices of the currently selected time step.
Changing the time point/time step while tool is active will updated the working
slice the live wire filter. So the behavior of the active live wire contour is
always WYSIWYG (What you see is what you get).
\sa SegTool2D
\sa ImageLiveWireContourModelFilter
\ingroup Interaction
\ingroup ToolManagerEtAl
\warning Only to be instantiated by mitk::ToolManager.
*/
class MITKSEGMENTATION_EXPORT LiveWireTool2D : public EditableContourTool
{
public:
mitkClassMacro(LiveWireTool2D, EditableContourTool);
itkFactorylessNewMacro(Self);
us::ModuleResource GetCursorIconResource() const override;
us::ModuleResource GetIconResource() const override;
const char *GetName() const override;
const char **GetXPM() const override;
protected:
LiveWireTool2D();
~LiveWireTool2D() override;
void ConnectActionsAndFunctions() override;
void UpdateLiveWireContour();
void OnTimePointChanged() override;
mitk::Point3D PrepareInitContour(const mitk::Point3D& clickedPoint) override;
virtual void FinalizePreviewContour(const Point3D& clickedPoint) override;
virtual void InitializePreviewContour(const Point3D& clickedPoint) override;
virtual void UpdatePreviewContour(const Point3D& clickedPoint) override;
private:
/// \brief Don't use dynamic cost map for LiveWire calculation.
void OnMouseMoveNoDynamicCosts(StateMachineAction *, InteractionEvent *interactionEvent);
/// \brief Finish contour interaction.
void FinishTool() override;
template <typename TPixel, unsigned int VImageDimension>
void FindHighestGradientMagnitudeByITK(itk::Image<TPixel, VImageDimension> *inputImage,
itk::Index<3> &index,
itk::Index<3> &returnIndex);
mitk::ContourModelLiveWireInteractor::Pointer m_ContourInteractor;
mitk::ImageLiveWireContourModelFilter::Pointer m_LiveWireFilter;
bool m_CreateAndUseDynamicCosts;
};
}
#endif
diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.cpp b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.cpp
new file mode 100644
index 0000000000..20427becea
--- /dev/null
+++ b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.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.
+
+============================================================================*/
+
+// MITK
+#include "mitkMonaiLabel2DTool.h"
+#include <mitkIOUtil.h>
+#include <mitkImageReadAccessor.h>
+#include <mitkSegTool2D.h>
+
+// us
+#include <usGetModuleContext.h>
+#include <usModule.h>
+#include <usModuleContext.h>
+#include <usModuleResource.h>
+#include <usServiceReference.h>
+
+namespace mitk
+{
+ MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, MonaiLabel2DTool, "MonaiLabel2D");
+}
+
+void mitk::MonaiLabel2DTool::Activated()
+{
+ Superclass::Activated();
+ this->SetLabelTransferScope(LabelTransferScope::AllLabels);
+}
+
+const char **mitk::MonaiLabel2DTool::GetXPM() const
+{
+ return nullptr;
+}
+
+us::ModuleResource mitk::MonaiLabel2DTool::GetIconResource() const
+{
+ us::Module *module = us::GetModuleContext()->GetModule();
+ us::ModuleResource resource = module->GetResource("AI.svg");
+ return resource;
+}
+
+const char *mitk::MonaiLabel2DTool::GetName() const
+{
+ return "MONAI Label 2D";
+}
+
+void mitk::MonaiLabel2DTool::WriteImage(const Image *inputAtTimeStep, const std::string &inputImagePath) const
+{
+ auto extendedImage = Image::New();
+ std::array<unsigned int, 3> dim = {inputAtTimeStep->GetDimension(0), inputAtTimeStep->GetDimension(1), 1};
+ mitk::PixelType pt = inputAtTimeStep->GetPixelType();
+ extendedImage->Initialize(pt, 3, dim.data());
+
+ ImageReadAccessor readAccessor(inputAtTimeStep);
+ extendedImage->SetVolume(readAccessor.GetData());
+
+ IOUtil::Save(extendedImage.GetPointer(), inputImagePath);
+}
+
+void mitk::MonaiLabel2DTool::WriteBackResults(LabelSetImage *previewImage,
+ LabelSetImage *segResults,
+ TimeStepType timeStep) const
+ {
+ if (segResults->GetTimeGeometry()->CountTimeSteps() > 1)
+ {
+ mitkThrow() << "Invalid time geometry found while writing back segmentation. "
+ " Expected static segmentation output from model.";
+ }
+ mitk::SegTool2D::WriteSliceToVolume(previewImage, this->GetWorkingPlaneGeometry(), segResults, timeStep, false);
+ }
diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h
new file mode 100644
index 0000000000..51ca9e40ed
--- /dev/null
+++ b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h
@@ -0,0 +1,53 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+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 mitkMonaiLabel2DTool_h
+#define mitkMonaiLabel2DTool_h
+
+#include "mitkMonaiLabelTool.h"
+#include <MitkSegmentationExports.h>
+
+namespace us
+{
+ class ModuleResource;
+}
+
+namespace mitk
+{
+ /**
+ \brief MonaiLabel segmentation 2D tool.
+
+ \ingroup ToolManagerEtAl
+ \sa mitk::Tool
+ \sa QmitkInteractiveSegmentation
+
+ */
+ class MITKSEGMENTATION_EXPORT MonaiLabel2DTool : public MonaiLabelTool
+ {
+ public:
+ mitkClassMacro(MonaiLabel2DTool, MonaiLabelTool);
+ itkFactorylessNewMacro(Self);
+ itkCloneMacro(Self);
+
+ const char *GetName() const override;
+ const char **GetXPM() const override;
+ us::ModuleResource GetIconResource() const override;
+
+ void Activated() override;
+ void WriteImage(const Image *inputAtTimeStep, const std::string &inputImagePath) const override;
+ void WriteBackResults(LabelSetImage *previewImage, LabelSetImage *segResults, TimeStepType timeStep) const override;
+
+ protected:
+ MonaiLabel2DTool() = default;
+ ~MonaiLabel2DTool() = default;
+ };
+}
+#endif
diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp
new file mode 100644
index 0000000000..b83a3363fa
--- /dev/null
+++ b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp
@@ -0,0 +1,67 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+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 "mitkMonaiLabel3DTool.h"
+
+#include <mitkIOUtil.h>
+#include <usGetModuleContext.h>
+#include <usModule.h>
+#include <usModuleContext.h>
+#include <usModuleResource.h>
+#include <usServiceReference.h>
+#include <mitkImageReadAccessor.h>
+
+namespace mitk
+{
+ MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, MonaiLabel3DTool, "MonaiLabel3D");
+}
+
+void mitk::MonaiLabel3DTool::Activated()
+{
+ Superclass::Activated();
+ this->SetLabelTransferScope(LabelTransferScope::AllLabels);
+}
+
+const char **mitk::MonaiLabel3DTool::GetXPM() const
+{
+ return nullptr;
+}
+
+us::ModuleResource mitk::MonaiLabel3DTool::GetIconResource() const
+{
+ us::Module *module = us::GetModuleContext()->GetModule();
+ us::ModuleResource resource = module->GetResource("AI.svg");
+ return resource;
+}
+
+const char *mitk::MonaiLabel3DTool::GetName() const
+{
+ return "MONAI Label 3D";
+}
+
+void mitk::MonaiLabel3DTool::WriteImage(const Image *inputAtTimeStep, const std::string &inputImagePath) const
+{
+ IOUtil::Save(inputAtTimeStep, inputImagePath);
+}
+
+void mitk::MonaiLabel3DTool::WriteBackResults(LabelSetImage *previewImage,
+ LabelSetImage *segResults,
+ TimeStepType timeStep) const
+{
+ if (segResults->GetTimeGeometry()->CountTimeSteps() > 1)
+ {
+ mitkThrow() << "Invalid time geometry found while writing back segmentation. "
+ " Expected static segmentation output from model.";
+ }
+ mitk::ImageReadAccessor newMitkImgAcc(segResults);
+ previewImage->SetVolume(newMitkImgAcc.GetData(), timeStep);
+}
diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.h b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.h
new file mode 100644
index 0000000000..31452cf166
--- /dev/null
+++ b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.h
@@ -0,0 +1,52 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+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 mitkMonaiLabel3DTool_h
+#define mitkMonaiLabel3DTool_h
+
+#include "mitkMonaiLabelTool.h"
+#include <MitkSegmentationExports.h>
+
+namespace us
+{
+ class ModuleResource;
+}
+
+namespace mitk
+{
+ /**
+ \brief MonaiLabel segmentation 3D tool.
+
+ \ingroup Interaction
+ \ingroup ToolManagerEtAl
+
+ \warning Only to be instantiated by mitk::ToolManager.
+ */
+ class MITKSEGMENTATION_EXPORT MonaiLabel3DTool : public MonaiLabelTool
+ {
+ public:
+ mitkClassMacro(MonaiLabel3DTool, MonaiLabelTool);
+ itkFactorylessNewMacro(Self);
+ itkCloneMacro(Self);
+
+ const char *GetName() const override;
+ const char **GetXPM() const override;
+ us::ModuleResource GetIconResource() const override;
+ void Activated() override;
+ void WriteImage(const Image *inputAtTimeStep, const std::string &inputImagePath) const override;
+ void WriteBackResults(LabelSetImage *previewImage, LabelSetImage *segResults, TimeStepType timeStep) const override;
+
+ protected:
+ MonaiLabel3DTool() = default;
+ ~MonaiLabel3DTool() = default;
+ };
+} // namespace mitk
+#endif
diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp
new file mode 100644
index 0000000000..b1b1c8f614
--- /dev/null
+++ b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp
@@ -0,0 +1,660 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+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 "mitkMonaiLabelTool.h"
+
+#ifndef CPPHTTPLIB_OPENSSL_SUPPORT
+#define CPPHTTPLIB_OPENSSL_SUPPORT
+#endif
+#include <mitkFileSystem.h>
+#include <httplib.h>
+#include <mitkIOUtil.h>
+#include <mitkLabelSetImageHelper.h>
+#include <mitkPointSetShapeProperty.h>
+#include <mitkProperties.h>
+#include <mitkToolManager.h>
+#include <itkIntensityWindowingImageFilter.h>
+#include "mitkImageAccessByItk.h"
+
+mitk::MonaiLabelTool::MonaiLabelTool() : SegWithPreviewTool(true, "PressMoveReleaseAndPointSetting")
+{
+ this->ResetsToEmptyPreviewOn();
+ this->IsTimePointChangeAwareOff();
+}
+
+mitk::MonaiLabelTool::~MonaiLabelTool()
+{
+ fs::remove_all(this->GetTempDir());
+}
+
+void mitk::MonaiLabelTool::ConnectActionsAndFunctions()
+{
+ CONNECT_FUNCTION("ShiftSecondaryButtonPressed", OnAddNegativePoint);
+ CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnAddPositivePoint);
+ CONNECT_FUNCTION("DeletePoint", OnDelete);
+}
+
+void mitk::MonaiLabelTool::Activated()
+{
+ Superclass::Activated();
+ m_PointSetPositive = mitk::PointSet::New();
+ m_PointSetNodePositive = mitk::DataNode::New();
+ m_PointSetNodePositive->SetData(m_PointSetPositive);
+ m_PointSetNodePositive->SetName(std::string(this->GetName()) + "_PointSetPositive");
+ m_PointSetNodePositive->SetBoolProperty("helper object", true);
+ m_PointSetNodePositive->SetColor(0.0, 1.0, 0.0);
+ m_PointSetNodePositive->SetVisibility(true);
+ m_PointSetNodePositive->SetProperty("Pointset.2D.shape",
+ mitk::PointSetShapeProperty::New(mitk::PointSetShapeProperty::CIRCLE));
+ m_PointSetNodePositive->SetProperty("Pointset.2D.fill shape", mitk::BoolProperty::New(true));
+ this->GetDataStorage()->Add(m_PointSetNodePositive, this->GetToolManager()->GetWorkingData(0));
+
+ m_PointSetNegative = mitk::PointSet::New();
+ m_PointSetNodeNegative = mitk::DataNode::New();
+ m_PointSetNodeNegative->SetData(m_PointSetNegative);
+ m_PointSetNodeNegative->SetName(std::string(this->GetName()) + "_PointSetNegative");
+ m_PointSetNodeNegative->SetBoolProperty("helper object", true);
+ m_PointSetNodeNegative->SetColor(1.0, 0.0, 0.0);
+ m_PointSetNodeNegative->SetVisibility(true);
+ m_PointSetNodeNegative->SetProperty("Pointset.2D.shape",
+ mitk::PointSetShapeProperty::New(mitk::PointSetShapeProperty::CIRCLE));
+ m_PointSetNodeNegative->SetProperty("Pointset.2D.fill shape", mitk::BoolProperty::New(true));
+ this->GetDataStorage()->Add(m_PointSetNodeNegative, this->GetToolManager()->GetWorkingData(0));
+}
+
+void mitk::MonaiLabelTool::Deactivated()
+{
+ this->ClearSeeds();
+ this->GetDataStorage()->Remove(m_PointSetNodePositive);
+ this->GetDataStorage()->Remove(m_PointSetNodeNegative);
+ m_PointSetNodePositive = nullptr;
+ m_PointSetNodeNegative = nullptr;
+ m_PointSetPositive = nullptr;
+ m_PointSetNegative = nullptr;
+ Superclass::Deactivated();
+}
+
+void mitk::MonaiLabelTool::UpdatePrepare()
+{
+ Superclass::UpdatePrepare();
+ auto preview = this->GetPreviewSegmentation();
+ preview->RemoveLabels(preview->GetAllLabelValues());
+}
+
+void mitk::MonaiLabelTool::OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent)
+{
+ if (m_RequestParameters->model.IsInteractive())
+ {
+ if (m_RequestParameters->model.Is2D() && ((nullptr == this->GetWorkingPlaneGeometry()) ||
+ !mitk::Equal(*(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()),
+ *(this->GetWorkingPlaneGeometry()))))
+ {
+ this->ClearSeeds();
+ this->SetWorkingPlaneGeometry(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone());
+ this->ResetPreviewContent();
+ }
+ if (!this->IsUpdating() && m_PointSetPositive.IsNotNull())
+ {
+ const auto positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
+ if (positionEvent != nullptr)
+ {
+ m_PointSetPositive->InsertPoint(m_PointSetCount, positionEvent->GetPositionInWorld());
+ ++m_PointSetCount;
+ this->UpdatePreview();
+ }
+ }
+ }
+}
+
+void mitk::MonaiLabelTool::OnAddNegativePoint(StateMachineAction *, InteractionEvent *interactionEvent)
+{
+ if ("deepgrow" != m_RequestParameters->model.type)
+ {
+ return;
+ }
+ if (m_RequestParameters->model.Is2D() && (m_PointSetPositive->GetSize() == 0 ||
+ nullptr == this->GetWorkingPlaneGeometry() ||
+ !mitk::Equal(*(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()),
+ *(this->GetWorkingPlaneGeometry()))))
+ {
+ return;
+ }
+ if (!this->IsUpdating() && m_PointSetNegative.IsNotNull())
+ {
+ const auto positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
+ if (positionEvent != nullptr)
+ {
+ m_PointSetNegative->InsertPoint(m_PointSetCount, positionEvent->GetPositionInWorld());
+ m_PointSetCount++;
+ this->UpdatePreview();
+ }
+ }
+}
+
+void mitk::MonaiLabelTool::OnDelete(StateMachineAction *, InteractionEvent *)
+{
+ if (!this->IsUpdating() && m_PointSetPositive.IsNotNull())
+ {
+ PointSet::Pointer removeSet = m_PointSetPositive;
+ itk::IdentifierType maxId = 0;
+ if (m_PointSetPositive->GetSize() > 0)
+ {
+ maxId = m_PointSetPositive->GetMaxId().Index();
+ }
+ if (m_PointSetNegative->GetSize() > 0 && (maxId < m_PointSetNegative->GetMaxId().Index()))
+ {
+ removeSet = m_PointSetNegative;
+ }
+ removeSet->RemovePointAtEnd(0);
+ --m_PointSetCount;
+ this->UpdatePreview();
+ }
+}
+
+void mitk::MonaiLabelTool::ClearPicks()
+{
+ this->ClearSeeds();
+ this->UpdatePreview();
+}
+
+bool mitk::MonaiLabelTool::HasPicks() const
+{
+ return this->m_PointSetPositive.IsNotNull() && this->m_PointSetPositive->GetSize() > 0;
+}
+
+void mitk::MonaiLabelTool::ClearSeeds()
+{
+ if (this->m_PointSetPositive.IsNotNull())
+ {
+ m_PointSetCount -= m_PointSetPositive->GetSize();
+ this->m_PointSetPositive = mitk::PointSet::New(); // renew pointset
+ this->m_PointSetNodePositive->SetData(this->m_PointSetPositive);
+ }
+ if (this->m_PointSetNegative.IsNotNull())
+ {
+ m_PointSetCount -= m_PointSetNegative->GetSize();
+ this->m_PointSetNegative = mitk::PointSet::New(); // renew pointset
+ this->m_PointSetNodeNegative->SetData(this->m_PointSetNegative);
+ }
+}
+
+bool mitk::MonaiLabelTool::IsMonaiServerOn(const std::string &hostName, const int &port) const
+{
+ httplib::SSLClient cli(hostName, port);
+ cli.enable_server_certificate_verification(false);
+ while (cli.is_socket_open());
+ return cli.Get("/info/");
+}
+
+namespace mitk
+{
+ // Converts the json GET response from the MonaiLabel server to MonaiAppMetadata object.
+ void from_json(const nlohmann::json &jsonObj, mitk::MonaiAppMetadata &appData)
+ {
+ jsonObj["name"].get_to(appData.name);
+ jsonObj["description"].get_to(appData.description);
+ jsonObj["labels"].get_to(appData.labels);
+ auto modelJsonMap = jsonObj["models"].get<std::map<std::string, nlohmann::json>>();
+ for (const auto &[_name, _jsonObj] : modelJsonMap)
+ {
+ if (_jsonObj.is_discarded() || !_jsonObj.is_object())
+ {
+ MITK_ERROR << "Could not parse JSON object.";
+ }
+ mitk::MonaiModelInfo modelInfo;
+ modelInfo.name = _name;
+ try
+ {
+ auto labels = _jsonObj["labels"].get<std::unordered_map<std::string, int>>();
+ modelInfo.labels = labels;
+ }
+ catch (const std::exception &)
+ {
+ auto labels = _jsonObj["labels"].get<std::vector<std::string>>();
+ for (const auto &label : labels)
+ {
+ modelInfo.labels[label] = -1; // Hardcode -1 as label id
+ }
+ }
+ _jsonObj["type"].get_to(modelInfo.type);
+ _jsonObj["dimension"].get_to(modelInfo.dimension);
+ _jsonObj["description"].get_to(modelInfo.description);
+ appData.models.push_back(modelInfo);
+ }
+ }
+}
+
+namespace
+{
+ /**
+ * @brief Returns boundary string from Httplib response.
+ */
+ std::string GetBoundaryString(const httplib::Result &response)
+ {
+ httplib::Headers headers = response->headers;
+ std::string contentType = headers.find("content-type")->second;
+ std::string delimiter = "boundary=";
+ std::string boundaryString =
+ contentType.substr(contentType.find(delimiter) + delimiter.length(), std::string::npos);
+ boundaryString.insert(0, "--");
+ return boundaryString;
+ }
+
+ /**
+ * @brief Returns image data string from monai label overall response.
+ */
+ std::string GetResponseImageString(const std::string &imagePart)
+ {
+ std::string contentTypeStream = "Content-Type: application/octet-stream";
+ size_t ctPos = imagePart.find(contentTypeStream) + contentTypeStream.length();
+ std::string imageDataPart = imagePart.substr(ctPos);
+ std::string whitespaces = " \n\r";
+ imageDataPart.erase(0, imageDataPart.find_first_not_of(whitespaces)); // clean up
+ return imageDataPart;
+ }
+
+ /**
+ * @brief Helper function to get the Parts of the POST response.
+ */
+ std::vector<std::string> GetPartsBetweenBoundary(const std::string &body, const std::string &boundary)
+ {
+ std::vector<std::string> retVal;
+ std::string master = body;
+ size_t boundaryPos = master.find(boundary);
+ size_t beginPos = 0;
+ while (boundaryPos != std::string::npos)
+ {
+ std::string part = master.substr(beginPos, boundaryPos);
+ if (!part.empty())
+ {
+ retVal.push_back(part);
+ }
+ master.erase(beginPos, boundaryPos + boundary.length());
+ boundaryPos = master.find(boundary);
+ }
+ return retVal;
+ }
+
+ /**
+ * @brief Applies the give std::map lookup table on the preview segmentation LabelSetImage.
+ */
+ void MapLabelsToSegmentation(const mitk::LabelSetImage *source,
+ mitk::LabelSetImage *dest,
+ const std::map<std::string, mitk::Label::PixelType> &labelMap)
+ {
+ if (labelMap.empty())
+ {
+ auto label = mitk::LabelSetImageHelper::CreateNewLabel(dest, "object");
+ label->SetValue(1);
+ dest->AddLabel(label, false);
+ return;
+ }
+ std::map<mitk::Label::PixelType, std::string> flippedLabelMap;
+ for (auto const &[key, val] : labelMap)
+ {
+ flippedLabelMap[val] = key;
+ }
+ auto lookupTable = mitk::LookupTable::New();
+ lookupTable->SetType(mitk::LookupTable::LookupTableType::MULTILABEL);
+ for (auto const &[key, val] : flippedLabelMap)
+ {
+ if (source->ExistLabel(key, source->GetActiveLayer()))
+ {
+ auto label = mitk::Label::New(key, val);
+ std::array<double, 3> lookupTableColor;
+ lookupTable->GetColor(key, lookupTableColor.data());
+ mitk::Color color;
+ color.SetRed(lookupTableColor[0]);
+ color.SetGreen(lookupTableColor[1]);
+ color.SetBlue(lookupTableColor[2]);
+ label->SetColor(color);
+ dest->AddLabel(label, false);
+ }
+ else
+ {
+ MITK_INFO << "Label not found for " << val;
+ }
+ }
+ }
+
+ template <typename TPixel, unsigned int VImageDimension>
+ void ITKWindowing(const itk::Image<TPixel, VImageDimension> *inputImage,
+ mitk::Image *mitkImage, mitk::ScalarType min, mitk::ScalarType max)
+ {
+ typedef itk::Image<TPixel, VImageDimension> ImageType;
+ typedef itk::IntensityWindowingImageFilter<ImageType, ImageType> IntensityFilterType;
+ typename IntensityFilterType::Pointer filter = IntensityFilterType::New();
+ filter->SetInput(inputImage);
+ filter->SetWindowMinimum(min);
+ filter->SetWindowMaximum(max);
+ filter->SetOutputMinimum(min);
+ filter->SetOutputMaximum(max);
+ filter->Update();
+
+ mitkImage->SetImportVolume(
+ (void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), 0, 0, mitk::Image::ManageMemory);
+ filter->GetOutput()->GetPixelContainer()->ContainerManageMemoryOff();
+ }
+}
+
+mitk::Image::Pointer mitk::MonaiLabelTool::ApplyLevelWindowEffect(const Image *inputAtTimeStep) const
+{
+ mitk::LevelWindow levelWindow;
+ this->GetToolManager()->GetReferenceData(0)->GetLevelWindow(levelWindow);
+ auto filteredImage = mitk::Image::New();
+ filteredImage->Initialize(inputAtTimeStep);
+ AccessByItk_n(inputAtTimeStep,
+ ::ITKWindowing, // apply level window filter
+ (filteredImage, levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()));
+ return filteredImage;
+}
+
+void mitk::MonaiLabelTool::DoUpdatePreview(const Image *inputAtTimeStep,
+ const Image * /*oldSegAtTimeStep*/,
+ LabelSetImage *previewImage,
+ TimeStepType timeStep)
+{
+ if (nullptr == m_RequestParameters || (m_RequestParameters->model.IsInteractive() && !this->HasPicks()))
+ {
+ this->ResetPreviewContentAtTimeStep(timeStep);
+ RenderingManager::GetInstance()->ForceImmediateUpdateAll();
+ return;
+ }
+ const std::string &hostName = m_RequestParameters->hostName;
+ const int port = m_RequestParameters->port;
+ if (!IsMonaiServerOn(hostName, port))
+ {
+ mitkThrow() << m_SERVER_503_ERROR_TEXT;
+ }
+ if (this->m_TempDir.empty())
+ {
+ this->SetTempDir(IOUtil::CreateTemporaryDirectory("mitk-XXXXXX"));
+ }
+
+ std::string inputImagePath, outputImagePath;
+ std::tie(inputImagePath, outputImagePath) = this->CreateTempDirs(m_TEMPLATE_FILENAME);
+
+ try
+ {
+ mitk::Image::Pointer filteredImage = this->ApplyLevelWindowEffect(inputAtTimeStep);
+ this->WriteImage(filteredImage, inputImagePath);
+ this->PostInferRequest(hostName, port, inputImagePath, outputImagePath, inputAtTimeStep->GetGeometry());
+ auto outputImage = IOUtil::Load<Image>(outputImagePath);
+ auto outputBuffer = mitk::LabelSetImage::New();
+ outputBuffer->InitializeByLabeledImage(outputImage);
+ std::map<std::string, mitk::Label::PixelType> labelMap; // empty map
+ if (m_RequestParameters->model.IsInteractive())
+ {
+ this->SetLabelTransferMode(LabelTransferMode::MapLabel);
+ this->SetSelectedLabels({MASK_VALUE});
+ }
+ else
+ {
+ outputBuffer->SetGeometry(inputAtTimeStep->GetGeometry());
+ labelMap = m_ResultMetadata["label_names"];
+ this->SetLabelTransferMode(LabelTransferMode::AddLabel);
+ }
+ ::MapLabelsToSegmentation(outputBuffer, previewImage, labelMap);
+ this->WriteBackResults(previewImage, outputBuffer.GetPointer(), timeStep);
+ MonaiStatusEvent.Send(true);
+ }
+ catch (const mitk::Exception &e)
+ {
+ MITK_ERROR << e.GetDescription();
+ mitkThrow() << e.GetDescription();
+ MonaiStatusEvent.Send(false);
+ }
+}
+
+void mitk::MonaiLabelTool::FetchOverallInfo(const std::string &hostName, const int &port)
+{
+ m_InfoParameters.reset();
+ if (!IsMonaiServerOn(hostName, port))
+ {
+ Tool::ErrorMessage.Send(m_SERVER_503_ERROR_TEXT);
+ mitkThrow() << m_SERVER_503_ERROR_TEXT;
+ }
+ httplib::SSLClient cli(hostName, port);
+ cli.enable_server_certificate_verification(false);
+ if (auto response = cli.Get("/info/"))
+ {
+ if (response->status == 200)
+ {
+ auto jsonObj = nlohmann::json::parse(response->body);
+ if (jsonObj.is_discarded() || !jsonObj.is_object())
+ {
+ MITK_ERROR << "Could not parse response from MONAILabel server as JSON object!";
+ return;
+ }
+ auto appData = jsonObj.template get<mitk::MonaiAppMetadata>();
+ m_InfoParameters = std::make_unique<mitk::MonaiAppMetadata>(appData);
+ if (nullptr != m_InfoParameters)
+ {
+ m_InfoParameters->hostName = hostName;
+ m_InfoParameters->port = port;
+ }
+ }
+ }
+ else
+ {
+ Tool::ErrorMessage.Send(httplib::to_string(response.error()) + " error occured.");
+ }
+}
+
+void mitk::MonaiLabelTool::PostInferRequest(const std::string &hostName,
+ const int &port,
+ const std::string &filePath,
+ const std::string &outFile,
+ const mitk::BaseGeometry *baseGeometry)
+{
+ std::string &modelName = m_RequestParameters->model.name; // Get this from args as well.
+ std::string postPath = "/infer/"; // make this separate class of constants
+ postPath.append(modelName);
+ std::ifstream input(filePath, std::ios::binary);
+ if (!input)
+ {
+ MITK_WARN << "could not read file to POST";
+ }
+ std::stringstream buffer_lf_img;
+ buffer_lf_img << input.rdbuf();
+ input.close();
+ httplib::MultipartFormDataItems items;
+ if (m_RequestParameters->model.IsInteractive())
+ {
+ std::string foreground = this->ConvertPointsAsListString(baseGeometry, m_PointSetPositive);
+ std::string background = this->ConvertPointsAsListString(baseGeometry, m_PointSetNegative);
+ std::stringstream paramString;
+ paramString << "{"
+ << "\"foreground\":" << foreground
+ << ",\"background\":" << background
+ << "}";
+ MITK_DEBUG << paramString.str();
+ items.push_back({"params", paramString.str(), "", ""});
+ }
+ else // Auto models
+ {
+ items.push_back({"params", "{\"restore_label_idx\": true}", "", ""});
+ }
+ items.push_back({"file", buffer_lf_img.str(), "post_from_mitk.nii.gz", "application/octet-stream"});
+ httplib::SSLClient cli(hostName, port);
+ cli.set_read_timeout(60); // arbitary 1 minute time-out to avoid corner cases.
+ cli.enable_server_certificate_verification(false);
+ if (auto response = cli.Post(postPath, items))
+ {
+ if (response->status == 200)
+ {
+ // Find boundary
+ std::string boundaryString = ::GetBoundaryString(response);
+ MITK_DEBUG << "boundary hash: " << boundaryString;
+
+ // Parse metadata JSON
+ std::string resBody = response->body;
+ std::vector<std::string> multiPartResponse = ::GetPartsBetweenBoundary(resBody, boundaryString);
+
+ std::string metaData = multiPartResponse[0];
+ std::string contentTypeJson = "Content-Type: application/json";
+ size_t ctPos = metaData.find(contentTypeJson) + contentTypeJson.length();
+ std::string metaDataPart = metaData.substr(ctPos);
+ MITK_DEBUG << metaDataPart;
+ auto jsonObj = nlohmann::json::parse(metaDataPart);
+ if (jsonObj.is_discarded() || !jsonObj.is_object())
+ {
+ MITK_ERROR << "Could not parse response from MONAILabel server as JSON object!";
+ return;
+ }
+ else // use the metadata
+ {
+ m_ResultMetadata = jsonObj;
+ }
+ // Parse response image string
+ std::string imagePart = multiPartResponse[1];
+ std::string imageData = ::GetResponseImageString(imagePart);
+ std::ofstream output(outFile, std::ios::out | std::ios::app | std::ios::binary);
+ output << imageData;
+ output.unsetf(std::ios::skipws);
+ output.flush();
+ }
+ else
+ {
+ auto err = response.error();
+ MITK_ERROR << "An HTTP POST error: " << httplib::to_string(err) << " occured.";
+ mitkThrow() << "An HTTP POST error: " << httplib::to_string(err) << " occured.";
+ }
+ }
+}
+
+const std::vector<mitk::MonaiModelInfo> mitk::MonaiLabelTool::GetAutoSegmentationModels(const int dim) const
+{
+ std::vector<mitk::MonaiModelInfo> autoModels;
+ if (nullptr != m_InfoParameters)
+ {
+ for (mitk::MonaiModelInfo &model : m_InfoParameters->models)
+ {
+ if (m_AUTO_SEG_TYPE_NAME.find(model.type) != m_AUTO_SEG_TYPE_NAME.end() && (!(dim > -1) ||
+ model.dimension == dim))
+ {
+ autoModels.push_back(model);
+ }
+ }
+ }
+ return autoModels;
+}
+
+const std::vector<mitk::MonaiModelInfo> mitk::MonaiLabelTool::GetInteractiveSegmentationModels(const int dim) const
+{
+ std::vector<mitk::MonaiModelInfo> interactiveModels;
+ if (nullptr != m_InfoParameters)
+ {
+ for (mitk::MonaiModelInfo &model : m_InfoParameters->models)
+ {
+ if (m_INTERACTIVE_SEG_TYPE_NAME.find(model.type) != m_INTERACTIVE_SEG_TYPE_NAME.end() &&
+ (!(dim > -1) || model.dimension == dim))
+ {
+ interactiveModels.push_back(model);
+ }
+ }
+ }
+ return interactiveModels;
+}
+
+const std::vector<mitk::MonaiModelInfo> mitk::MonaiLabelTool::GetScribbleSegmentationModels(const int dim) const
+{
+ std::vector<mitk::MonaiModelInfo> scribbleModels;
+ if (nullptr != m_InfoParameters)
+ {
+ for (mitk::MonaiModelInfo &model : m_InfoParameters->models)
+ {
+ if (m_SCRIBBLE_SEG_TYPE_NAME.find(model.type) != m_SCRIBBLE_SEG_TYPE_NAME.end() &&
+ (!(dim > -1) || model.dimension == dim))
+ {
+ scribbleModels.push_back(model);
+ }
+ }
+ }
+ return scribbleModels;
+}
+
+mitk::MonaiModelInfo mitk::MonaiLabelTool::GetModelInfoFromName(const std::string modelName) const
+{
+ if (nullptr == m_InfoParameters)
+ {
+ mitkThrow() << "No model information found.";
+ }
+ mitk::MonaiModelInfo retVal;
+ for (mitk::MonaiModelInfo &model : m_InfoParameters->models)
+ {
+ if (model.name == modelName)
+ {
+ retVal = model;
+ break;
+ }
+ }
+ return retVal;
+}
+
+std::pair<std::string, std::string> mitk::MonaiLabelTool::CreateTempDirs(const std::string &filePattern) const
+{
+ std::string inDir, outDir, inputImagePath, outputImagePath;
+ inDir = IOUtil::CreateTemporaryDirectory("monai-in-XXXXXX", this->GetTempDir());
+ std::ofstream tmpStream;
+ inputImagePath = IOUtil::CreateTemporaryFile(tmpStream, filePattern, 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("_"));
+ outDir = IOUtil::CreateTemporaryDirectory("monai-out-XXXXXX", this->GetTempDir());
+ outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz";
+ return std::make_pair(inputImagePath, outputImagePath);
+}
+
+std::string mitk::MonaiLabelTool::ConvertPointsAsListString(const mitk::BaseGeometry *baseGeometry,
+ const PointSet::Pointer pointSet) const
+{
+ bool is3DMode = nullptr == this->GetWorkingPlaneGeometry();
+ MITK_INFO << "No.of points: " << pointSet->GetSize();
+ std::stringstream pointsAndLabels;
+ pointsAndLabels << "[";
+ mitk::PointSet::PointsConstIterator pointSetIter = pointSet->Begin();
+ const char COMMA = ',';
+ while (pointSetIter != pointSet->End())
+ {
+ mitk::Point3D point3d = pointSetIter.Value();
+ if (baseGeometry->IsInside(point3d))
+ {
+ mitk::Point3D index3D;
+ baseGeometry->WorldToIndex(point3d, index3D);
+ pointsAndLabels << "[";
+ pointsAndLabels << static_cast<int>(index3D[0]) << COMMA << static_cast<int>(index3D[1]) << COMMA;
+ if (is3DMode)
+ {
+ pointsAndLabels << static_cast<int>(index3D[2]);
+ }
+ else
+ {
+ pointsAndLabels << 0;
+ }
+ pointsAndLabels << "],";
+ }
+ ++pointSetIter;
+ }
+ if (pointsAndLabels.tellp() > 1)
+ {
+ pointsAndLabels.seekp(-1, pointsAndLabels.end); // remove last added comma character
+ }
+ pointsAndLabels << "]";
+ return pointsAndLabels.str();
+}
+
+const mitk::MonaiAppMetadata *mitk::MonaiLabelTool::GetInfoParameters() const
+{
+ return m_InfoParameters.get();
+}
diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h
new file mode 100644
index 0000000000..1bf4e4bc0b
--- /dev/null
+++ b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h
@@ -0,0 +1,264 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+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 mitkMonaiLabelTool_h
+#define mitkMonaiLabelTool_h
+
+#include "mitkSegWithPreviewTool.h"
+#include <MitkSegmentationExports.h>
+#include <memory>
+#include <unordered_map>
+#include <set>
+#include <nlohmann/json.hpp>
+#include <mitkPointSet.h>
+#include <mitkInteractionPositionEvent.h>
+
+
+namespace us
+{
+ class ModuleResource;
+}
+
+namespace mitk
+{
+ /**
+ * @brief Struct to hold featured models individual info
+ *
+ */
+ struct MonaiModelInfo
+ {
+ std::string name;
+ std::string type;
+ std::unordered_map<std::string, int> labels;
+ int dimension;
+ std::string description;
+ std::unordered_map<bool, std::string> config;
+
+ inline bool operator==(const MonaiModelInfo &rhs) const
+ {
+ return (this->name == rhs.name && this->type == rhs.type); // Comparing only name and type, for now.
+ }
+
+ inline bool IsInteractive() const
+ {
+ return ("deepgrow" == type || "deepedit" == type);
+ }
+
+ inline bool Is2D() const
+ {
+ return dimension == 2;
+ }
+ };
+
+ /**
+ * @brief Struct to store MonaiLabel server metadata including all model infos
+ *
+ */
+ struct MonaiAppMetadata
+ {
+ std::string name;
+ std::string description;
+ std::vector<std::string> labels;
+ std::string version;
+ std::string hostName;
+ int port;
+ std::string origin;
+ std::vector<MonaiModelInfo> models;
+ };
+
+ /**
+ * @brief Request class to pack model and other necessary server information
+ * from GUI.
+ *
+ */
+ struct MonaiLabelRequest
+ {
+ MonaiModelInfo model;
+ std::string hostName;
+ std::string requestLabel;
+ int port;
+
+ inline bool operator==(const MonaiLabelRequest &rhs) const
+ {
+ return (this->model == rhs.model && this->hostName == rhs.hostName && this->port == rhs.port &&
+ this->requestLabel == rhs.requestLabel);
+ }
+ };
+
+ /**
+ \brief MonaiLabel segmentation tool base class.
+
+ \ingroup Interaction
+ \ingroup ToolManagerEtAl
+
+ \warning Only to be instantiated by mitk::ToolManager.
+ */
+ class MITKSEGMENTATION_EXPORT MonaiLabelTool : public SegWithPreviewTool
+ {
+ public:
+ mitkClassMacro(MonaiLabelTool, SegWithPreviewTool);
+ itkCloneMacro(Self);
+ void Activated() override;
+ void Deactivated() override;
+ void UpdatePrepare() override;
+
+ /**
+ * @brief Method does the GET Rest call to fetch MonaiLabel
+ * server metadata including all models' info.
+ */
+ void FetchOverallInfo(const std::string &hostName, const int &port);
+
+ /**
+ * @brief Variable to set selected model's and other data needed
+ * for the POST call.
+ */
+ std::unique_ptr<MonaiLabelRequest> m_RequestParameters;
+
+ /**
+ * @brief Get the Auto Segmentation Models info for the given
+ * dimension.
+ */
+ const std::vector<MonaiModelInfo> GetAutoSegmentationModels(const int dim = -1) const;
+
+ /**
+ * @brief Get the Interactive Segmentation Models info for the given
+ * dimension.
+ */
+ const std::vector<MonaiModelInfo> GetInteractiveSegmentationModels(const int dim = -1) const;
+
+ /**
+ * @brief Get the Scribble Segmentation Models info for the given
+ * dimension.
+ */
+ const std::vector<MonaiModelInfo> GetScribbleSegmentationModels(const int dim = -1) const;
+
+ /**
+ * @brief Helper function to get full model info object from model name.
+ */
+ MonaiModelInfo GetModelInfoFromName(const std::string) const;
+
+ itkSetMacro(ModelName, std::string);
+ itkGetConstMacro(ModelName, std::string);
+ itkSetMacro(URL, std::string);
+ itkGetConstMacro(URL, std::string);
+ itkSetMacro(TempDir, std::string);
+ itkGetConstMacro(TempDir, std::string);
+ const MonaiAppMetadata *GetInfoParameters() const;
+
+
+ /**
+ * @brief Clears all picks and updates the preview.
+ */
+ void ClearPicks();
+
+ /**
+ * @brief Checks if any point exists in the either of the PointSetPositive
+ * or PointSetNegative.
+ */
+ bool HasPicks() const;
+
+ Message1<const bool> MonaiStatusEvent;
+
+ protected:
+ MonaiLabelTool();
+ ~MonaiLabelTool();
+ void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) override;
+ void ConnectActionsAndFunctions() override;
+
+ /**
+ * @brief Writes image to disk as the tool desires.
+ * Default implementation does nothing.
+ */
+ virtual void WriteImage(const Image *, const std::string &) const = 0;
+
+ /*
+ * @brief Add positive seed point action of StateMachine pattern.
+ */
+ virtual void OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent);
+
+ /*
+ * @brief Add negative seed point action of StateMachine pattern
+ */
+ virtual void OnAddNegativePoint(StateMachineAction *, InteractionEvent *interactionEvent);
+
+ /*
+ * @brief Delete action of StateMachine pattern
+ */
+ virtual void OnDelete(StateMachineAction *, InteractionEvent *);
+
+ /*
+ * @brief Clear all seed points and call UpdatePreview to reset the segmentation Preview
+ */
+ void ClearSeeds();
+
+ /**
+ * @brief Get the Points from given pointset as csv string.
+ *
+ */
+ virtual std::string ConvertPointsAsListString(const mitk::BaseGeometry *baseGeometry,
+ const PointSet::Pointer pointSet) const;
+
+ /**
+ * @brief Writes back segmentation results in 3D or 2D shape to preview LabelSetImage.
+ */
+ virtual void WriteBackResults(LabelSetImage *, LabelSetImage *, TimeStepType) const = 0;
+
+ PointSet::Pointer m_PointSetPositive;
+ PointSet::Pointer m_PointSetNegative;
+ DataNode::Pointer m_PointSetNodePositive;
+ DataNode::Pointer m_PointSetNodeNegative;
+ int m_PointSetCount = 0;
+
+ private:
+
+ /**
+ * @brief Holds all parameters of the server to serve the UI
+ *
+ */
+ std::unique_ptr<MonaiAppMetadata> m_InfoParameters;
+
+ /**
+ * @brief Helper function to create temp directory for writing/reading images
+ * Returns input and output file names expected as a pair.
+ */
+ std::pair<std::string, std::string> CreateTempDirs(const std::string &filePattern) const;
+
+ /**
+ * @brief Checks if MonaiLabel server is alive.
+ */
+ bool IsMonaiServerOn(const std::string &hostName, const int &port) const;
+
+ /**
+ * @brief Applies level window filter on the input image. Current Level window bounds
+ * are captured from the tool manager.
+ */
+ mitk::Image::Pointer ApplyLevelWindowEffect(const Image *inputAtTimeStep) const;
+
+ /**
+ * @brief Function to prepare the Rest request and does the POST call.
+ * Writes the POST responses back to disk.
+ */
+ void PostInferRequest(const std::string &hostName, const int &port, const std::string &filePath, const std::string &outFile,
+ const mitk::BaseGeometry *baseGeometry);
+
+ std::string m_TempDir;
+ std::string m_ModelName;
+ std::string m_URL;
+ nlohmann::json m_ResultMetadata;
+ const std::set<std::string> m_AUTO_SEG_TYPE_NAME = {"segmentation"};
+ const std::set<std::string> m_SCRIBBLE_SEG_TYPE_NAME = {"scribbles"};
+ const std::set<std::string> m_INTERACTIVE_SEG_TYPE_NAME = {"deepgrow"}; // deepedit not supported yet
+ const std::string m_TEMPLATE_FILENAME = "XXXXXX_000_0000.nii.gz";
+ const std::string m_SERVER_503_ERROR_TEXT = "A connection to MonaiLabel server cannot be established.";
+ const Label::PixelType MASK_VALUE = 1;
+ };
+}
+#endif
diff --git a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp
index 1dd6ab7418..0709830eb9 100644
--- a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp
@@ -1,616 +1,616 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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"
#include "mitkImageWriteAccessor.h"
-int mitk::PaintbrushTool::m_Size = 1;
+int mitk::PaintbrushTool::m_Size = 10;
mitk::PaintbrushTool::PaintbrushTool(bool startWithFillMode)
: FeedbackContourTool("PressMoveReleaseWithCTRLInversionAllMouseMoves"),
m_FillMode(startWithFillMode),
m_LastContourSize(0) // other than initial mitk::PaintbrushTool::m_Size (around l. 28)
{
m_MasterContour = ContourModel::New();
m_MasterContour->Initialize();
m_CurrentPlane = nullptr;
}
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();
SizeChanged.Send(m_Size);
this->GetToolManager()->WorkingDataChanged +=
mitk::MessageDelegate<mitk::PaintbrushTool>(this, &mitk::PaintbrushTool::OnToolManagerWorkingDataModified);
m_PaintingNode = DataNode::New();
m_PaintingNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(mitk::LevelWindow(0, m_InternalFillValue)));
m_PaintingNode->SetProperty("binary", mitk::BoolProperty::New(true));
m_PaintingNode->SetProperty("outline binary", mitk::BoolProperty::New(true));
m_PaintingNode->SetProperty("name", mitk::StringProperty::New("Paintbrush_Node"));
m_PaintingNode->SetProperty("helper object", mitk::BoolProperty::New(true));
m_PaintingNode->SetProperty("opacity", mitk::FloatProperty::New(0.8));
m_PaintingNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false));
auto allRenderWindows = BaseRenderer::GetAll3DRenderWindows();
for (auto mapit = allRenderWindows.begin(); mapit != allRenderWindows.end(); ++mapit)
{
m_PaintingNode->SetVisibility(false, mapit->second);
}
this->UpdateFeedbackColor();
FeedbackContourTool::SetFeedbackContourVisible(true);
this->GetToolManager()->GetDataStorage()->Add(m_PaintingNode);
}
void mitk::PaintbrushTool::Deactivated()
{
FeedbackContourTool::SetFeedbackContourVisible(false);
if (this->GetToolManager()->GetDataStorage()->Exists(m_PaintingNode))
this->GetToolManager()->GetDataStorage()->Remove(m_PaintingNode);
m_WorkingSlice = nullptr;
m_PaintingSlice = nullptr;
m_CurrentPlane = nullptr;
m_PaintingNode = 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
+ // but its upper right 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
+ // fill contour with points 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;
}
void mitk::PaintbrushTool::OnMousePressed(StateMachineAction *, InteractionEvent *interactionEvent)
{
if (m_WorkingSlice.IsNull())
return;
auto* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>(interactionEvent);
if (!positionEvent)
return;
this->ResetWorkingSlice(positionEvent);
m_WorkingSlice->GetGeometry()->WorldToIndex(positionEvent->GetPositionInWorld(), m_LastPosition);
this->m_PaintingNode->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);
bool newSlice = CheckIfCurrentSliceHasChanged(positionEvent);
if (newSlice)
{
this->ResetWorkingSlice(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)
{
ContourModelUtils::FillContourInSlice2(contour, m_PaintingSlice, m_InternalFillValue);
const double dist = indexCoordinates.EuclideanDistanceTo(m_LastPosition);
const double radius = static_cast<double>(m_Size) / 2.0;
// 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];
auto gapContour = ContourModel::New();
// upper left corner
vertex[0] = m_LastPosition[0] + (normal[0] * radius);
vertex[1] = m_LastPosition[1] + (normal[1] * radius);
gapContour->AddVertex(vertex);
// upper right corner
vertex[0] = currentPos[0] + (normal[0] * radius);
vertex[1] = currentPos[1] + (normal[1] * radius);
gapContour->AddVertex(vertex);
// lower right corner
vertex[0] = currentPos[0] - (normal[0] * radius);
vertex[1] = currentPos[1] - (normal[1] * radius);
gapContour->AddVertex(vertex);
// lower left corner
vertex[0] = m_LastPosition[0] - (normal[0] * radius);
vertex[1] = m_LastPosition[1] - (normal[1] * radius);
gapContour->AddVertex(vertex);
ContourModelUtils::FillContourInSlice2(gapContour, m_PaintingSlice, m_InternalFillValue);
}
}
else
{
// switched from different renderwindow
// no activate hover highlighting. Otherwise undo / redo wont work
this->m_PaintingNode->SetVisibility(false);
}
m_LastPosition = indexCoordinates;
// visualize contour
ContourModel::Pointer tmp =
FeedbackContourTool::BackProjectContourFrom2DSlice(m_WorkingSlice->GetGeometry(), contour);
this->UpdateCurrentFeedbackContour(tmp);
if (newSlice)
{
RenderingManager::GetInstance()->RequestUpdateAll();
}
else
{
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;
DataNode* workingNode(this->GetToolManager()->GetWorkingData(0));
auto workingImage = dynamic_cast<LabelSetImage*>(workingNode->GetData());
Label::PixelType activePixelValue = ContourModelUtils::GetActivePixelValue(workingImage);
if (!m_FillMode)
{
activePixelValue = LabelSetImage::UNLABELED_VALUE;
}
//as paintbrush tools should always allow to manipulate active label
//(that is what the user expects/knows when using tools so far:
//the active label can always be changed even if locked)
//we realize that by cloning the relevant label and changing the lock state
//this fillLabelSet is used for the transfer.
auto destinationLabels = workingImage->GetConstLabelsByValue(workingImage->GetLabelValuesByGroup(workingImage->GetActiveLayer()));
auto activeLabelClone = workingImage->GetActiveLabel()->Clone();
if (nullptr != activeLabelClone)
{
activeLabelClone->SetLocked(false);
auto activeIter = std::find(destinationLabels.begin(), destinationLabels.end(), workingImage->GetActiveLabel());
if (activeIter == destinationLabels.end()) mitkThrow() << "Application is in an invalid state. Active label is not contained in the labelset, but its group was requested.";
*activeIter = activeLabelClone;
}
TransferLabelContentAtTimeStep(m_PaintingSlice, m_WorkingSlice, destinationLabels, 0, LabelSetImage::UNLABELED_VALUE, LabelSetImage::UNLABELED_VALUE, false, { {m_InternalFillValue, activePixelValue} }, mitk::MultiLabelSegmentation::MergeStyle::Merge);
this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice->Clone());
// deactivate visibility of helper node
m_PaintingNode->SetVisibility(false);
m_PaintingNode->SetData(nullptr);
m_PaintingSlice = nullptr;
m_WorkingSlice = nullptr;
RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::PaintbrushTool::UpdateFeedbackColor()
{
mitk::Color currentColor;
if (m_FillMode)
{
FeedbackContourTool::SetFeedbackContourColorDefault();
currentColor.Set(0.0, 1.0, 0.);
}
else
{
FeedbackContourTool::SetFeedbackContourColor(1.0, 0.0, 0.0);
currentColor.Set(1.0, 0.0, 0.);
}
if (m_PaintingNode.IsNotNull())
{
m_PaintingNode->SetProperty("color", mitk::ColorProperty::New(currentColor[0], currentColor[1], currentColor[2]));
}
}
/**
Called when the CTRL key is pressed.
*/
void mitk::PaintbrushTool::OnInvertLogic(StateMachineAction *, InteractionEvent *)
{
m_FillMode = !m_FillMode;
UpdateFeedbackColor();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
bool mitk::PaintbrushTool::CheckIfCurrentSliceHasChanged(const InteractionPositionEvent *event)
{
const PlaneGeometry* planeGeometry((event->GetSender()->GetCurrentWorldPlaneGeometry()));
const auto* abstractTransformGeometry(
dynamic_cast<const AbstractTransformGeometry *>(event->GetSender()->GetCurrentWorldPlaneGeometry()));
if (nullptr == planeGeometry || nullptr != abstractTransformGeometry)
{
return false;
}
bool newPlane = false;
if (m_CurrentPlane.IsNull() || m_WorkingSlice.IsNull()
//or not the same slice
|| !mitk::MatrixEqualElementWise(planeGeometry->GetIndexToWorldTransform()->GetMatrix(),
m_CurrentPlane->GetIndexToWorldTransform()->GetMatrix())
|| !mitk::Equal(planeGeometry->GetIndexToWorldTransform()->GetOffset(),
m_CurrentPlane->GetIndexToWorldTransform()->GetOffset()))
{
m_CurrentPlane = planeGeometry;
newPlane = true;
}
return newPlane;
}
void mitk::PaintbrushTool::ResetWorkingSlice(const InteractionPositionEvent* event)
{
const PlaneGeometry* planeGeometry((event->GetSender()->GetCurrentWorldPlaneGeometry()));
const auto* abstractTransformGeometry(
dynamic_cast<const AbstractTransformGeometry*>(event->GetSender()->GetCurrentWorldPlaneGeometry()));
if (nullptr == planeGeometry || nullptr != abstractTransformGeometry)
{
return;
}
m_WorkingSlice = nullptr;
m_PaintingSlice = nullptr;
m_PaintingNode->SetData(nullptr);
DataNode* workingNode = this->GetToolManager()->GetWorkingData(0);
if (nullptr == workingNode)
{
return;
}
Image::Pointer image = dynamic_cast<Image*>(workingNode->GetData());
if (nullptr == image)
{
return;
}
m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone();
m_PaintingSlice = Image::New();
m_PaintingSlice->Initialize(m_WorkingSlice);
unsigned int byteSize = m_PaintingSlice->GetPixelType().GetSize();
for (unsigned int dim = 0; dim < m_PaintingSlice->GetDimension(); ++dim)
{
byteSize *= m_PaintingSlice->GetDimension(dim);
}
mitk::ImageWriteAccessor writeAccess(m_PaintingSlice.GetPointer(), m_PaintingSlice->GetVolumeData(0));
memset(writeAccess.GetData(), 0, byteSize);
m_PaintingNode->SetData(m_PaintingSlice);
}
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;
m_PaintingSlice = nullptr;
}
diff --git a/Modules/Segmentation/Interactions/mitkProcessExecutor.h b/Modules/Segmentation/Interactions/mitkProcessExecutor.h
index f67567de25..11df031b0b 100644
--- a/Modules/Segmentation/Interactions/mitkProcessExecutor.h
+++ b/Modules/Segmentation/Interactions/mitkProcessExecutor.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 mitkProcessExecutor_h
#define mitkProcessExecutor_h
#include <MitkSegmentationExports.h>
#include <itkObject.h>
#include <vector>
#include <itksys/Process.h>
namespace mitk
{
// Class is adapted from MatchPoint ProcessExecutor
class ExternalProcessOutputEvent : public itk::AnyEvent
{
public:
typedef ExternalProcessOutputEvent Self;
typedef itk::AnyEvent Superclass;
explicit ExternalProcessOutputEvent(const std::string &output = "") : m_Output(output) {}
~ExternalProcessOutputEvent() override {}
const char *GetEventName() const override { return "ExternalProcessOutputEvent"; }
bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast<const Self *>(e); }
itk::EventObject *MakeObject() const override { return new Self(m_Output); }
std::string GetOutput() const { return m_Output; }
private:
std::string m_Output;
};
#define mitkProcessExecutorEventMacro(classname) \
class classname : public ExternalProcessOutputEvent \
{ \
public: \
typedef classname Self; \
typedef ExternalProcessOutputEvent Superclass; \
\
explicit classname(const std::string &output) : Superclass(output) {} \
~classname() override {} \
\
- 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(this->GetOutput()); } \
+ virtual const char *GetEventName() const override { return #classname; } \
+ virtual bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast<const Self *>(e); } \
+ virtual ::itk::EventObject *MakeObject() const override { return new Self(this->GetOutput()); } \
};
mitkProcessExecutorEventMacro(ExternalProcessStdOutEvent);
mitkProcessExecutorEventMacro(ExternalProcessStdErrEvent);
/**
* @brief You may register an observer for an ExternalProcessOutputEvent, ExternalProcessStdOutEvent or
* ExternalProcessStdErrEvent in order to get notified of any output.
* @remark The events will only be invoked if the pipes are NOT(!) shared. By default the pipes are not shared.
*
*/
class MITKSEGMENTATION_EXPORT ProcessExecutor : public itk::Object
{
public:
using Self = ProcessExecutor;
using Superclass = ::itk::Object;
using Pointer = ::itk::SmartPointer<Self>;
using ConstPointer = ::itk::SmartPointer<const Self>;
itkTypeMacro(ProcessExecutor, ::itk::Object);
itkFactorylessNewMacro(Self);
itkSetMacro(SharedOutputPipes, bool);
itkGetConstMacro(SharedOutputPipes, bool);
using ArgumentListType = std::vector<std::string>;
bool Execute(const std::string &executionPath, const std::string &executableName, ArgumentListType &argumentList);
/**
* @brief Executes the process. This version assumes that the executable name is the first argument in the argument
* list and has already been converted to its OS dependent name via the static convert function of this class.
*/
virtual bool Execute(const std::string &executionPath, const ArgumentListType &argumentList);
int GetExitValue();
static std::string EnsureCorrectOSPathSeparator(const std::string &);
static std::string GetOSDependendExecutableName(const std::string &name);
void KillProcess();
protected:
ProcessExecutor();
~ProcessExecutor() override;
int m_ExitValue;
/**
* @brief Specifies if the child process should share the output pipes (true) or not (false).
* If pipes are not shared the output will be passed by invoking ExternalProcessOutputEvents
* @remark The events will only be invoked if the pipes are NOT(!) shared.
*/
bool m_SharedOutputPipes;
private:
itksysProcess *m_ProcessID = nullptr;
};
} // namespace mitk
#endif
diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp
index d0063c907d..5bf3d97430 100644
--- a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp
+++ b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp
@@ -1,795 +1,795 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkSegTool2D.h"
#include "mitkToolManager.h"
#include "mitkBaseRenderer.h"
#include "mitkDataStorage.h"
#include "mitkPlaneGeometry.h"
#include <mitkTimeNavigationController.h>
#include "mitkImageAccessByItk.h"
// Include of the new ImageExtractor
#include "mitkMorphologicalOperations.h"
#include "mitkPlanarCircle.h"
#include "usGetModuleContext.h"
// Includes for 3DSurfaceInterpolation
#include "mitkImageTimeSelector.h"
#include "mitkImageToContourFilter.h"
#include "mitkSurfaceInterpolationController.h"
// includes for resling and overwriting
#include <mitkExtractSliceFilter.h>
#include <mitkVtkImageOverwrite.h>
#include <vtkImageData.h>
#include <vtkSmartPointer.h>
#include "mitkOperationEvent.h"
#include "mitkUndoController.h"
#include <mitkDiffSliceOperationApplier.h>
#include "mitkAbstractTransformGeometry.h"
#include "mitkLabelSetImage.h"
#include "mitkContourModelUtils.h"
// #include <itkImageRegionIterator.h>
#include <vtkAbstractArray.h>
#include <vtkFieldData.h>
#define ROUND(a) ((a) > 0 ? (int)((a) + 0.5) : -(int)(0.5 - (a)))
bool mitk::SegTool2D::m_SurfaceInterpolationEnabled = true;
mitk::SegTool2D::SliceInformation::SliceInformation(const mitk::Image* aSlice, const mitk::PlaneGeometry* aPlane, mitk::TimeStepType aTimestep) :
slice(aSlice), plane(aPlane), timestep(aTimestep)
{
}
mitk::SegTool2D::SegTool2D(const char *type, const us::Module *interactorModule)
: Tool(type, interactorModule), m_Contourmarkername("Position")
{
Tool::m_EventConfig = "DisplayConfigBlockLMB.xml";
}
mitk::SegTool2D::~SegTool2D()
{
}
bool mitk::SegTool2D::FilterEvents(InteractionEvent *interactionEvent, DataNode *)
{
const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
bool isValidEvent =
(positionEvent && // Only events of type mitk::InteractionPositionEvent
interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard2D // Only events from the 2D renderwindows
);
return isValidEvent;
}
bool mitk::SegTool2D::DetermineAffectedImageSlice(const Image *image,
const PlaneGeometry *plane,
int &affectedDimension,
int &affectedSlice)
{
assert(image);
assert(plane);
// compare normal of plane to the three axis vectors of the image
Vector3D normal = plane->GetNormal();
Vector3D imageNormal0 = image->GetSlicedGeometry()->GetAxisVector(0);
Vector3D imageNormal1 = image->GetSlicedGeometry()->GetAxisVector(1);
Vector3D imageNormal2 = image->GetSlicedGeometry()->GetAxisVector(2);
normal.Normalize();
imageNormal0.Normalize();
imageNormal1.Normalize();
imageNormal2.Normalize();
imageNormal0.SetVnlVector(vnl_cross_3d<ScalarType>(normal.GetVnlVector(), imageNormal0.GetVnlVector()));
imageNormal1.SetVnlVector(vnl_cross_3d<ScalarType>(normal.GetVnlVector(), imageNormal1.GetVnlVector()));
imageNormal2.SetVnlVector(vnl_cross_3d<ScalarType>(normal.GetVnlVector(), imageNormal2.GetVnlVector()));
double eps(0.00001);
// axial
if (imageNormal2.GetNorm() <= eps)
{
affectedDimension = 2;
}
// sagittal
else if (imageNormal1.GetNorm() <= eps)
{
affectedDimension = 1;
}
// coronal
else if (imageNormal0.GetNorm() <= eps)
{
affectedDimension = 0;
}
else
{
affectedDimension = -1; // no idea
return false;
}
// determine slice number in image
BaseGeometry *imageGeometry = image->GetGeometry(0);
Point3D testPoint = imageGeometry->GetCenter();
Point3D projectedPoint;
plane->Project(testPoint, projectedPoint);
Point3D indexPoint;
imageGeometry->WorldToIndex(projectedPoint, indexPoint);
affectedSlice = ROUND(indexPoint[affectedDimension]);
MITK_DEBUG << "indexPoint " << indexPoint << " affectedDimension " << affectedDimension << " affectedSlice "
<< affectedSlice;
// check if this index is still within the image
if (affectedSlice < 0 || affectedSlice >= static_cast<int>(image->GetDimension(affectedDimension)))
return false;
return true;
}
void mitk::SegTool2D::UpdateAllSurfaceInterpolations(const LabelSetImage *workingImage,
TimeStepType timeStep,
const PlaneGeometry *plane,
bool detectIntersection)
{
if (nullptr == workingImage) mitkThrow() << "Cannot update surface interpolation. Invalid working image passed.";
if (nullptr == plane) mitkThrow() << "Cannot update surface interpolation. Invalid plane passed.";
auto affectedLabels = mitk::SurfaceInterpolationController::GetInstance()->GetAffectedLabels(workingImage, timeStep, plane);
for (auto affectedLabel : affectedLabels)
{
auto groupID = workingImage->GetGroupIndexOfLabel(affectedLabel);
auto slice = GetAffectedImageSliceAs2DImage(plane, workingImage->GetGroupImage(groupID), timeStep);
std::vector<SliceInformation> slices = { SliceInformation(slice, plane, timeStep) };
Self::UpdateSurfaceInterpolation(slices, workingImage, detectIntersection, affectedLabel, true);
}
if(!affectedLabels.empty()) mitk::SurfaceInterpolationController::GetInstance()->Modified();
}
void mitk::SegTool2D::RemoveContourFromInterpolator(const SliceInformation& sliceInfo, LabelSetImage::LabelValueType labelValue)
{
mitk::SurfaceInterpolationController::ContourPositionInformation contourInfo;
contourInfo.LabelValue = labelValue;
contourInfo.TimeStep = sliceInfo.timestep;
contourInfo.Plane = sliceInfo.plane;
mitk::SurfaceInterpolationController::GetInstance()->RemoveContour(contourInfo, true);
}
template <typename ImageType>
void ClearBufferProcessing(ImageType* itkImage)
{
itkImage->FillBuffer(0);
}
void mitk::SegTool2D::UpdateSurfaceInterpolation(const std::vector<SliceInformation>& sliceInfos,
const Image* workingImage,
bool detectIntersection,
mitk::Label::PixelType activeLabelValue, bool silent)
{
if (!m_SurfaceInterpolationEnabled)
return;
//Remark: the ImageTimeSelector is just needed to extract a timestep/channel of
//the image in order to get the image dimension (time dimension and channel dimension
//stripped away). Therfore it is OK to always use time step 0 and channel 0
mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New();
timeSelector->SetInput(workingImage);
timeSelector->SetTimeNr(0);
timeSelector->SetChannelNr(0);
timeSelector->Update();
const auto dimRefImg = timeSelector->GetOutput()->GetDimension();
if (dimRefImg != 3)
return;
std::vector<mitk::Surface::Pointer> contourList;
contourList.reserve(sliceInfos.size());
ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New();
std::vector<SliceInformation> relevantSlices = sliceInfos;
if (detectIntersection)
{
relevantSlices.clear();
for (const auto& sliceInfo : sliceInfos)
{
// Test whether there is something to extract or whether the slice just contains intersections of others
//Remark we cannot just errode the clone of sliceInfo.slice, because Erode currently only
//works on pixel value 1. But we need to erode active label. Therefore we use TransferLabelContent
//as workarround.
//If MorphologicalOperations::Erode is supports user defined pixel values, the workarround
//can be removed.
//Workarround starts
mitk::Image::Pointer slice2 = Image::New();
slice2->Initialize(sliceInfo.slice);
AccessByItk(slice2, ClearBufferProcessing);
LabelSetImage::LabelValueType erodeValue = 1;
auto label = Label::New(erodeValue, "");
TransferLabelContent(sliceInfo.slice, slice2, { label }, LabelSetImage::UNLABELED_VALUE, LabelSetImage::UNLABELED_VALUE, false, { {activeLabelValue, erodeValue} });
//Workarround ends
mitk::MorphologicalOperations::Erode(slice2, 2, mitk::MorphologicalOperations::Ball);
contourExtractor->SetInput(slice2);
contourExtractor->SetContourValue(erodeValue);
contourExtractor->Update();
mitk::Surface::Pointer contour = contourExtractor->GetOutput();
if (contour->GetVtkPolyData()->GetNumberOfPoints() == 0)
{
Self::RemoveContourFromInterpolator(sliceInfo, activeLabelValue);
}
else
{
relevantSlices.push_back(sliceInfo);
}
}
}
SurfaceInterpolationController::CPIVector cpis;
for (const auto& sliceInfo : relevantSlices)
{
contourExtractor->SetInput(sliceInfo.slice);
contourExtractor->SetContourValue(activeLabelValue);
contourExtractor->Update();
mitk::Surface::Pointer contour = contourExtractor->GetOutput();
if (contour->GetVtkPolyData()->GetNumberOfPoints() == 0)
{
Self::RemoveContourFromInterpolator(sliceInfo, activeLabelValue);
}
else
{
cpis.emplace_back(contour, sliceInfo.plane->Clone(), activeLabelValue, sliceInfo.timestep);
}
}
//this call is relevant even if cpis is empty to ensure SurfaceInterpolationController::Modified is triggered if silent==false;
mitk::SurfaceInterpolationController::GetInstance()->AddNewContours(cpis, false, silent);
}
mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const InteractionPositionEvent *positionEvent, const Image *image, unsigned int component /*= 0*/)
{
if (!positionEvent)
{
return nullptr;
}
assert(positionEvent->GetSender()); // sure, right?
const auto timeStep = positionEvent->GetSender()->GetTimeStep(image); // get the timestep of the visible part (time-wise) of the image
return GetAffectedImageSliceAs2DImage(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry(), image, timeStep, component);
}
mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(const PlaneGeometry* planeGeometry, const Image* image, TimePointType timePoint, unsigned int component /*= 0*/)
{
if (!image || !planeGeometry)
{
return nullptr;
}
if (!image->GetTimeGeometry()->IsValidTimePoint(timePoint))
return nullptr;
return SegTool2D::GetAffectedImageSliceAs2DImage(planeGeometry, image, image->GetTimeGeometry()->TimePointToTimeStep(timePoint), component);
}
mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PlaneGeometry *planeGeometry, const Image *image, TimeStepType timeStep, unsigned int component /*= 0*/)
{
if (!image || !planeGeometry)
{
return nullptr;
}
- // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer
+ // Make sure that for reslicing and overwriting the same algorithm is used. We can specify the mode of the vtk reslicer
vtkSmartPointer<mitkVtkImageOverwrite> reslice = vtkSmartPointer<mitkVtkImageOverwrite>::New();
// set to false to extract a slice
reslice->SetOverwriteMode(false);
reslice->Modified();
// use ExtractSliceFilter with our specific vtkImageReslice for overwriting and extracting
mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice);
extractor->SetInput(image);
extractor->SetTimeStep(timeStep);
extractor->SetWorldGeometry(planeGeometry);
extractor->SetVtkOutputRequest(false);
extractor->SetResliceTransformByGeometry(image->GetTimeGeometry()->GetGeometryForTimeStep(timeStep));
// additionally extract the given component
// default is 0; the extractor checks for multi-component images
extractor->SetComponent(component);
extractor->Modified();
extractor->Update();
Image::Pointer slice = extractor->GetOutput();
return slice;
}
mitk::Image::Pointer mitk::SegTool2D::GetAffectedWorkingSlice(const InteractionPositionEvent *positionEvent) const
{
const auto workingNode = this->GetWorkingDataNode();
if (!workingNode)
{
return nullptr;
}
const auto *workingImage = dynamic_cast<Image *>(workingNode->GetData());
if (!workingImage)
{
return nullptr;
}
return GetAffectedImageSliceAs2DImage(positionEvent, workingImage);
}
mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const InteractionPositionEvent *positionEvent) const
{
DataNode* referenceNode = this->GetReferenceDataNode();
if (!referenceNode)
{
return nullptr;
}
auto *referenceImage = dynamic_cast<Image *>(referenceNode->GetData());
if (!referenceImage)
{
return nullptr;
}
int displayedComponent = 0;
if (referenceNode->GetIntProperty("Image.Displayed Component", displayedComponent))
{
// found the displayed component
return GetAffectedImageSliceAs2DImage(positionEvent, referenceImage, displayedComponent);
}
else
{
return GetAffectedImageSliceAs2DImage(positionEvent, referenceImage);
}
}
mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const PlaneGeometry* planeGeometry, TimeStepType timeStep) const
{
DataNode* referenceNode = this->GetReferenceDataNode();
if (!referenceNode)
{
return nullptr;
}
auto* referenceImage = dynamic_cast<Image*>(referenceNode->GetData());
if (!referenceImage)
{
return nullptr;
}
int displayedComponent = 0;
if (referenceNode->GetIntProperty("Image.Displayed Component", displayedComponent))
{
// found the displayed component
return GetAffectedImageSliceAs2DImage(planeGeometry, referenceImage, timeStep, displayedComponent);
}
else
{
return GetAffectedImageSliceAs2DImage(planeGeometry, referenceImage, timeStep);
}
}
void mitk::SegTool2D::Activated()
{
Superclass::Activated();
this->GetToolManager()->SelectedTimePointChanged +=
mitk::MessageDelegate<mitk::SegTool2D>(this, &mitk::SegTool2D::OnTimePointChangedInternal);
m_LastTimePointTriggered = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
}
void mitk::SegTool2D::Deactivated()
{
this->GetToolManager()->SelectedTimePointChanged -=
mitk::MessageDelegate<mitk::SegTool2D>(this, &mitk::SegTool2D::OnTimePointChangedInternal);
Superclass::Deactivated();
}
void mitk::SegTool2D::OnTimePointChangedInternal()
{
if (m_IsTimePointChangeAware && nullptr != this->GetWorkingDataNode())
{
const TimePointType timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
if (timePoint != m_LastTimePointTriggered)
{
m_LastTimePointTriggered = timePoint;
this->OnTimePointChanged();
}
}
}
void mitk::SegTool2D::OnTimePointChanged()
{
//default implementation does nothing
}
mitk::DataNode* mitk::SegTool2D::GetWorkingDataNode() const
{
if (nullptr != this->GetToolManager())
{
return this->GetToolManager()->GetWorkingData(0);
}
return nullptr;
}
mitk::Image* mitk::SegTool2D::GetWorkingData() const
{
auto node = this->GetWorkingDataNode();
if (nullptr != node)
{
return dynamic_cast<Image*>(node->GetData());
}
return nullptr;
}
mitk::DataNode* mitk::SegTool2D::GetReferenceDataNode() const
{
if (nullptr != this->GetToolManager())
{
return this->GetToolManager()->GetReferenceData(0);
}
return nullptr;
}
mitk::Image* mitk::SegTool2D::GetReferenceData() const
{
auto node = this->GetReferenceDataNode();
if (nullptr != node)
{
return dynamic_cast<Image*>(node->GetData());
}
return nullptr;
}
void mitk::SegTool2D::WriteBackSegmentationResult(const InteractionPositionEvent *positionEvent, const Image * segmentationResult)
{
if (!positionEvent)
return;
const PlaneGeometry *planeGeometry((positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()));
const auto *abstractTransformGeometry(
dynamic_cast<const AbstractTransformGeometry *>(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()));
if (planeGeometry && segmentationResult && !abstractTransformGeometry)
{
const auto workingNode = this->GetWorkingDataNode();
auto *image = dynamic_cast<Image *>(workingNode->GetData());
const auto timeStep = positionEvent->GetSender()->GetTimeStep(image);
this->WriteBackSegmentationResult(planeGeometry, segmentationResult, timeStep);
}
}
void mitk::SegTool2D::WriteBackSegmentationResult(const DataNode* workingNode, const PlaneGeometry* planeGeometry, const Image* segmentationResult, TimeStepType timeStep)
{
if (!planeGeometry || !segmentationResult)
return;
SliceInformation sliceInfo(segmentationResult, const_cast<mitk::PlaneGeometry*>(planeGeometry), timeStep);
Self::WriteBackSegmentationResults(workingNode, { sliceInfo }, true);
}
void mitk::SegTool2D::WriteBackSegmentationResult(const PlaneGeometry *planeGeometry,
const Image * segmentationResult,
TimeStepType timeStep)
{
if (!planeGeometry || !segmentationResult)
return;
if(m_LastEventSender == nullptr)
{
return;
}
unsigned int currentSlicePosition = m_LastEventSender->GetSliceNavigationController()->GetStepper()->GetPos();
SliceInformation sliceInfo(segmentationResult, const_cast<mitk::PlaneGeometry *>(planeGeometry), timeStep);
sliceInfo.slicePosition = currentSlicePosition;
WriteBackSegmentationResults({ sliceInfo }, true);
}
void mitk::SegTool2D::WriteBackSegmentationResults(const std::vector<SegTool2D::SliceInformation> &sliceList,
bool writeSliceToVolume)
{
if (sliceList.empty())
{
return;
}
if (nullptr == m_LastEventSender)
{
- MITK_WARN << "Cannot write tool results. Tool seems to be in an invalid state, as no interaction event was recieved but is expected.";
+ MITK_WARN << "Cannot write tool results. Tool seems to be in an invalid state, as no interaction event was received but is expected.";
return;
}
const auto workingNode = this->GetWorkingDataNode();
// the first geometry is needed otherwise restoring the position is not working
const auto* plane3 =
dynamic_cast<const PlaneGeometry*>(dynamic_cast<const mitk::SlicedGeometry3D*>(
m_LastEventSender->GetSliceNavigationController()->GetCurrentGeometry3D())
->GetPlaneGeometry(0));
const unsigned int slicePosition = m_LastEventSender->GetSliceNavigationController()->GetStepper()->GetPos();
mitk::SegTool2D::WriteBackSegmentationResults(workingNode, sliceList, writeSliceToVolume);
/* A cleaner solution would be to add a contour marker for each slice info. It currently
does not work as the contour markers expect that the plane is always the plane of slice 0.
Had not the time to do it properly no. Should be solved by T28146*/
this->AddContourmarker(plane3, slicePosition);
}
void mitk::SegTool2D::WriteBackSegmentationResults(const DataNode* workingNode, const std::vector<SliceInformation>& sliceList, bool writeSliceToVolume)
{
if (sliceList.empty())
{
return;
}
if (nullptr == workingNode)
{
mitkThrow() << "Cannot write slice to working node. Working node is invalid.";
}
auto image = dynamic_cast<Image*>(workingNode->GetData());
mitk::Label::PixelType activeLabelValue = 0;
try{
auto labelSetImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
activeLabelValue = labelSetImage->GetActiveLabel()->GetValue();
}
catch(...)
{
mitkThrow() << "Working node does not contain labelSetImage.";
}
if (nullptr == image)
{
mitkThrow() << "Cannot write slice to working node. Working node does not contain an image.";
}
for (const auto& sliceInfo : sliceList)
{
if (writeSliceToVolume && nullptr != sliceInfo.plane && sliceInfo.slice.IsNotNull())
{
SegTool2D::WriteSliceToVolume(image, sliceInfo, true);
}
}
SegTool2D::UpdateSurfaceInterpolation(sliceList, image, false, activeLabelValue);
// also mark its node as modified (T27308). Can be removed if T27307
// is properly solved
if (workingNode != nullptr) workingNode->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void mitk::SegTool2D::WriteSliceToVolume(Image* workingImage, const PlaneGeometry* planeGeometry, const Image* slice, TimeStepType timeStep, bool allowUndo)
{
SliceInformation sliceInfo(slice, planeGeometry, timeStep);
WriteSliceToVolume(workingImage, sliceInfo, allowUndo);
}
void mitk::SegTool2D::WriteSliceToVolume(Image* workingImage, const SliceInformation &sliceInfo, bool allowUndo)
{
if (nullptr == workingImage)
{
mitkThrow() << "Cannot write slice to working node. Working node does not contain an image.";
}
DiffSliceOperation* undoOperation = nullptr;
if (allowUndo)
{
/*============= BEGIN undo/redo feature block ========================*/
// Create undo operation by caching the not yet modified slices
mitk::Image::Pointer originalSlice = GetAffectedImageSliceAs2DImage(sliceInfo.plane, workingImage, sliceInfo.timestep);
undoOperation =
new DiffSliceOperation(workingImage,
originalSlice,
dynamic_cast<SlicedGeometry3D*>(originalSlice->GetGeometry()),
sliceInfo.timestep,
sliceInfo.plane);
/*============= END undo/redo feature block ========================*/
}
- // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk
+ // Make sure that for reslicing and overwriting the same algorithm is used. We can specify the mode of the vtk
// reslicer
vtkSmartPointer<mitkVtkImageOverwrite> reslice = vtkSmartPointer<mitkVtkImageOverwrite>::New();
// Set the slice as 'input'
// casting const away is needed and OK as long the OverwriteMode of
// mitkVTKImageOverwrite is true.
// Reason: because then the input slice is not touched but
// used to overwrite the input of the ExtractSliceFilter.
auto noneConstSlice = const_cast<Image*>(sliceInfo.slice.GetPointer());
reslice->SetInputSlice(noneConstSlice->GetVtkImageData());
// set overwrite mode to true to write back to the image volume
reslice->SetOverwriteMode(true);
reslice->Modified();
mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice);
extractor->SetInput(workingImage);
extractor->SetTimeStep(sliceInfo.timestep);
extractor->SetWorldGeometry(sliceInfo.plane);
extractor->SetVtkOutputRequest(false);
extractor->SetResliceTransformByGeometry(workingImage->GetGeometry(sliceInfo.timestep));
extractor->Modified();
extractor->Update();
// the image was modified within the pipeline, but not marked so
workingImage->Modified();
workingImage->GetVtkImageData()->Modified();
if (allowUndo)
{
/*============= BEGIN undo/redo feature block ========================*/
// specify the redo operation with the edited slice
auto* doOperation =
new DiffSliceOperation(workingImage,
extractor->GetOutput(),
dynamic_cast<SlicedGeometry3D*>(sliceInfo.slice->GetGeometry()),
sliceInfo.timestep,
sliceInfo.plane);
// create an operation event for the undo stack
OperationEvent* undoStackItem =
new OperationEvent(DiffSliceOperationApplier::GetInstance(), doOperation, undoOperation, "Segmentation");
// add it to the undo controller
UndoStackItem::IncCurrObjectEventId();
UndoStackItem::IncCurrGroupEventId();
UndoController::GetCurrentUndoModel()->SetOperationEvent(undoStackItem);
/*============= END undo/redo feature block ========================*/
}
}
void mitk::SegTool2D::SetShowMarkerNodes(bool status)
{
m_ShowMarkerNodes = status;
}
void mitk::SegTool2D::SetEnable3DInterpolation(bool enabled)
{
m_SurfaceInterpolationEnabled = enabled;
}
int mitk::SegTool2D::AddContourmarker(const PlaneGeometry* planeGeometry, unsigned int sliceIndex)
{
if (planeGeometry == nullptr)
return -1;
us::ServiceReference<PlanePositionManagerService> serviceRef =
us::GetModuleContext()->GetServiceReference<PlanePositionManagerService>();
PlanePositionManagerService *service = us::GetModuleContext()->GetService(serviceRef);
unsigned int size = service->GetNumberOfPlanePositions();
unsigned int id = service->AddNewPlanePosition(planeGeometry, sliceIndex);
mitk::PlanarCircle::Pointer contourMarker = mitk::PlanarCircle::New();
mitk::Point2D p1;
planeGeometry->Map(planeGeometry->GetCenter(), p1);
contourMarker->SetPlaneGeometry(planeGeometry->Clone());
contourMarker->PlaceFigure(p1);
contourMarker->SetCurrentControlPoint(p1);
contourMarker->SetProperty("initiallyplaced", mitk::BoolProperty::New(true));
std::stringstream markerStream;
auto workingNode = this->GetWorkingDataNode();
markerStream << m_Contourmarkername;
markerStream << " ";
markerStream << id + 1;
DataNode::Pointer rotatedContourNode = DataNode::New();
rotatedContourNode->SetData(contourMarker);
rotatedContourNode->SetProperty("name", StringProperty::New(markerStream.str()));
rotatedContourNode->SetProperty("isContourMarker", BoolProperty::New(true));
rotatedContourNode->SetBoolProperty("PlanarFigureInitializedWindow", true, m_LastEventSender);
rotatedContourNode->SetProperty("includeInBoundingBox", BoolProperty::New(false));
rotatedContourNode->SetProperty("helper object", mitk::BoolProperty::New(!m_ShowMarkerNodes));
rotatedContourNode->SetProperty("planarfigure.drawcontrolpoints", BoolProperty::New(false));
rotatedContourNode->SetProperty("planarfigure.drawname", BoolProperty::New(false));
rotatedContourNode->SetProperty("planarfigure.drawoutline", BoolProperty::New(false));
rotatedContourNode->SetProperty("planarfigure.drawshadow", BoolProperty::New(false));
if (planeGeometry)
{
if (id == size)
{
this->GetToolManager()->GetDataStorage()->Add(rotatedContourNode, workingNode);
}
else
{
mitk::NodePredicateProperty::Pointer isMarker =
mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true));
mitk::DataStorage::SetOfObjects::ConstPointer markers =
this->GetToolManager()->GetDataStorage()->GetDerivations(workingNode, isMarker);
for (auto iter = markers->begin(); iter != markers->end(); ++iter)
{
std::string nodeName = (*iter)->GetName();
unsigned int t = nodeName.find_last_of(" ");
unsigned int markerId = atof(nodeName.substr(t + 1).c_str()) - 1;
if (id == markerId)
{
return id;
}
}
this->GetToolManager()->GetDataStorage()->Add(rotatedContourNode, workingNode);
}
}
return id;
}
void mitk::SegTool2D::InteractiveSegmentationBugMessage(const std::string &message) const
{
MITK_ERROR << "********************************************************************************" << std::endl
<< " " << message << std::endl
<< "********************************************************************************" << std::endl
<< " " << std::endl
<< " If your image is rotated or the 2D views don't really contain the patient image, try to press the "
"button next to the image selection. "
<< std::endl
<< " " << std::endl
<< " Please file a BUG REPORT: " << std::endl
<< " https://phabricator.mitk.org/" << std::endl
<< " Contain the following information:" << std::endl
<< " - What image were you working on?" << std::endl
<< " - Which region of the image?" << std::endl
<< " - Which tool did you use?" << std::endl
<< " - What did you do?" << std::endl
<< " - What happened (not)? What did you expect?" << std::endl;
}
bool mitk::SegTool2D::IsPositionEventInsideImageRegion(mitk::InteractionPositionEvent* positionEvent,
const mitk::BaseData* data)
{
bool isPositionEventInsideImageRegion =
nullptr != data && data->GetGeometry()->IsInside(positionEvent->GetPositionInWorld());
if (!isPositionEventInsideImageRegion)
MITK_WARN("EditableContourTool") << "PositionEvent is outside ImageRegion!";
return isPositionEventInsideImageRegion;
}
diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.h b/Modules/Segmentation/Interactions/mitkSegTool2D.h
index da50f33417..5d63e66a15 100644
--- a/Modules/Segmentation/Interactions/mitkSegTool2D.h
+++ b/Modules/Segmentation/Interactions/mitkSegTool2D.h
@@ -1,288 +1,288 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkSegTool2D_h
#define mitkSegTool2D_h
#include <mitkCommon.h>
#include <mitkImage.h>
#include <mitkTool.h>
#include <MitkSegmentationExports.h>
#include <mitkInteractionPositionEvent.h>
#include <mitkInteractionConst.h>
#include <mitkPlanePositionManager.h>
#include <mitkRestorePlanePositionOperation.h>
#include <mitkDiffSliceOperation.h>
namespace mitk
{
class BaseRenderer;
/**
\brief Abstract base class for segmentation tools.
\sa Tool
\ingroup Interaction
\ingroup ToolManagerEtAl
Implements 2D segmentation specific helper methods, that might be of use to
all kind of 2D segmentation tools. At the moment these are:
- Determination of the slice where the user paints upon (DetermineAffectedImageSlice)
- Projection of a 3D contour onto a 2D plane/slice
SegTool2D tries to structure the interaction a bit. If you pass "PressMoveRelease" as the interaction type
of your derived tool, you might implement the methods OnMousePressed, OnMouseMoved, and OnMouseReleased.
Yes, your guess about when they are called is correct.
\warning Only to be instantiated by mitk::ToolManager.
$Author$
*/
class MITKSEGMENTATION_EXPORT SegTool2D : public Tool
{
public:
mitkClassMacro(SegTool2D, Tool);
/**
- \brief Calculates for a given Image and PlaneGeometry, which slice of the image (in index corrdinates) is meant by
+ \brief Calculates for a given Image and PlaneGeometry, which slice of the image (in index coordinates) is meant by
the plane.
\return false, if no slice direction seems right (e.g. rotated planes)
\param image
\param plane
\param affectedDimension The image dimension, which is constant for all points in the plane, e.g. Axial --> 2
\param affectedSlice The index of the image slice
*/
static bool DetermineAffectedImageSlice(const Image *image,
const PlaneGeometry *plane,
int &affectedDimension,
int &affectedSlice);
/**
* @brief Updates the surface interpolations by extracting the contour form the given slice for all labels
* that have a surface contour information stored for the given plane at the given timestep.
* @param workingImage the segmentation image
* @param timeStep the time step for wich the surface interpolation information should be updated.
* @param plane the plane in which the slice lies
* @param detectIntersection if true the slice is eroded before contour extraction. If the slice is empty after the
* erosion it is most likely an intersecting contour an will not be added to the SurfaceInterpolationController
*/
static void UpdateAllSurfaceInterpolations(const LabelSetImage* workingImage,
TimeStepType timeStep,
const PlaneGeometry *plane,
bool detectIntersection);
/**
* \brief Extract the slice of an image that the user just scribbles on. The given component denotes the vector component of an vector image.
*
* \param positionEvent Event that specifies the plane that should be used to slice
* \param image Image that should be sliced
* \param component The component to be extracted of a given multi-component image. -1 is the default parameter to denote an invalid component.
*
* \return 'nullptr' if SegTool2D is either unable to determine which slice was affected, or if there was some problem
* getting the image data at that position.
*/
static Image::Pointer GetAffectedImageSliceAs2DImage(const InteractionPositionEvent* positionEvent, const Image* image, unsigned int component = 0);
/**
* \brief Extract the slice of an image cut by given plane. The given component denotes the vector component of a vector image.
*
* \param planeGeometry Geometry defining the slice that should be cut out.
* \param image Image that should be sliced
- * \param timeStep TimeStep of the image that shold be sliced
+ * \param timeStep TimeStep of the image that should be sliced
* \param component The component to be extracted of a given multi-component image. -1 is the default parameter to denote an invalid component.
*
* \return 'nullptr' if SegTool2D is either unable to determine which slice was affected, or if there was some problem
* getting the image data at that position.
*/
static Image::Pointer GetAffectedImageSliceAs2DImage(const PlaneGeometry* planeGeometry,
const Image* image,
TimeStepType timeStep,
unsigned int component = 0);
static Image::Pointer GetAffectedImageSliceAs2DImageByTimePoint(const PlaneGeometry* planeGeometry,
const Image* image,
TimePointType timePoint,
unsigned int component = 0);
/** Convenience overloaded version that can be called for a given planeGeometry, slice image and time step.
* Calls static WriteBackSegmentationResults*/
static void WriteBackSegmentationResult(const DataNode* workingNode, const PlaneGeometry* planeGeometry, const Image* segmentationResult, TimeStepType timeStep);
/** Convenience overloaded version that can be called for a given planeGeometry, slice image and time step.
* For more details see protected WriteSliceToVolume version.*/
static void WriteSliceToVolume(Image* workingImage, const PlaneGeometry* planeGeometry, const Image* slice, TimeStepType timeStep, bool allowUndo);
void SetShowMarkerNodes(bool);
/**
* \brief Enables or disables the 3D interpolation after writing back the 2D segmentation result, and defaults to
* true.
*/
void SetEnable3DInterpolation(bool);
void Activated() override;
void Deactivated() override;
itkSetMacro(IsTimePointChangeAware, bool);
itkGetMacro(IsTimePointChangeAware, bool);
itkBooleanMacro(IsTimePointChangeAware);
protected:
SegTool2D(); // purposely hidden
SegTool2D(const char *, const us::Module *interactorModule = nullptr); // purposely hidden
~SegTool2D() override;
/**
* @brief returns the segmentation node that should be modified by the tool.
*/
DataNode* GetWorkingDataNode() const;
Image* GetWorkingData() const;
DataNode* GetReferenceDataNode() const;
Image* GetReferenceData() const;
/**
* This function can be reimplemented by derived classes to react on changes of the current
* time point. Default implementation does nothing.*/
virtual void OnTimePointChanged();
struct SliceInformation
{
mitk::Image::ConstPointer slice;
const mitk::PlaneGeometry *plane = nullptr;
mitk::TimeStepType timestep = 0;
unsigned int slicePosition;
SliceInformation() = default;
SliceInformation(const mitk::Image* aSlice, const mitk::PlaneGeometry* aPlane, mitk::TimeStepType aTimestep);
};
/**
* @brief Updates the surface interpolation by extracting the contour form the given slice.
* @param sliceInfos vector of slice information instances from which the contours should be extracted
* @param workingImage the segmentation image
* @param detectIntersection if true the slice is eroded before contour extraction. If the slice is empty after the
* @param activeLabelValue The label value of the active label.
* @param silent Indicates if the modification event of the SurfaceInterpolationController should be triggered.
* erosion it is most
* likely an intersecting contour an will not be added to the SurfaceInterpolationController
*/
static void UpdateSurfaceInterpolation(const std::vector<SliceInformation>& sliceInfos,
const Image* workingImage,
bool detectIntersection,
mitk::Label::PixelType activeLabelValue, bool silent = false);
/**
* \brief Filters events that cannot be handled by 2D segmentation tools
*
* Currently an event is discarded if it was not sent by a 2D renderwindow and if it is
* not of type InteractionPositionEvent
*/
bool FilterEvents(InteractionEvent *interactionEvent, DataNode *dataNode) override;
/**
\brief Extract the slice of the currently selected working image that the user just scribbles on.
\return nullptr if SegTool2D is either unable to determine which slice was affected, or if there was some problem
getting the image data at that position,
or just no working image is selected.
*/
Image::Pointer GetAffectedWorkingSlice(const InteractionPositionEvent *) const;
/**
\brief Extract the slice of the currently selected reference image that the user just scribbles on.
\return nullptr if SegTool2D is either unable to determine which slice was affected, or if there was some problem
getting the image data at that position,
or just no reference image is selected.
*/
Image::Pointer GetAffectedReferenceSlice(const InteractionPositionEvent *) const;
/** Overload version that gets the reference slice passed on the passed plane geometry and timestep.*/
Image::Pointer GetAffectedReferenceSlice(const PlaneGeometry* planeGeometry, TimeStepType timeStep) const;
/** Convenience version that can be called for a given event (which is used to deduce timepoint and plane) and a slice image.
* Calls non static WriteBackSegmentationResults*/
void WriteBackSegmentationResult(const InteractionPositionEvent *, const Image* segmentationResult);
/** Convenience version that can be called for a given planeGeometry, slice image and time step.
* Calls non static WriteBackSegmentationResults*/
void WriteBackSegmentationResult(const PlaneGeometry *planeGeometry, const Image* segmentationResult, TimeStepType timeStep);
/** Overloaded version that calls the static version and also adds the contour markers.
* @remark If the sliceList is empty, this function does nothing.*/
void WriteBackSegmentationResults(const std::vector<SliceInformation> &sliceList, bool writeSliceToVolume = true);
/** \brief Writes all provided source slices into the data of the passed workingNode.
* The function does the following: 1) for every passed slice write it to workingNode (and generate and undo/redo step);
* 2) update the surface interpolation and 3) mark the node as modified.
* @param workingNode Pointer to the node that contains the working image.
* @param sliceList Vector of all slices that should be written into the workingNode. If the list is
* empty, the function call does nothing.
* @param writeSliceToVolume If set to false the write operation (WriteSliceToVolume will be skipped)
* and only the surface interpolation will be updated.
* @pre workingNode must point to a valid instance and contain an image instance as data.*/
static void WriteBackSegmentationResults(const DataNode* workingNode, const std::vector<SliceInformation>& sliceList, bool writeSliceToVolume = true);
/** Writes a provided slice into the passed working image. The content of working image that is covered
- * by the slice will be completly overwritten. If asked for it also generates the needed
+ * by the slice will be completely overwritten. If asked for it also generates the needed
* undo/redo steps.
* @param workingImage Pointer to the image that is the target of the write operation.
- * @param sliceInfo SliceInfo instance that containes the slice image, the defining plane geometry and time step.
+ * @param sliceInfo SliceInfo instance that contains the slice image, the defining plane geometry and time step.
* @param allowUndo Indicates if undo/redo operations should be registered for the write operation
* performed by this call. true: undo/redo will be generated; false: no undo/redo will be generated, so
* this operation cannot be revoked by the user.
* @pre workingImage must point to a valid instance.*/
static void WriteSliceToVolume(Image* workingImage, const SliceInformation &sliceInfo, bool allowUndo);
/**
\brief Adds a new node called Contourmarker to the datastorage which holds a mitk::PlanarFigure.
By selecting this node the slicestack will be reoriented according to the passed
PlanarFigure's Geometry
*/
int AddContourmarker(const PlaneGeometry* planeGeometry, unsigned int sliceIndex);
void InteractiveSegmentationBugMessage(const std::string &message) const;
/** Helper function to check if a position events points to a point inside the boundingbox of a passed
data instance.*/
static bool IsPositionEventInsideImageRegion(InteractionPositionEvent* positionEvent, const BaseData* data);
BaseRenderer *m_LastEventSender = nullptr;
unsigned int m_LastEventSlice = 0;
itkGetMacro(LastTimePointTriggered, TimePointType);
private:
/** Internal method that gets triggered as soon as the tool manager indicates a
* time point change. If the time point has changed since last time and tool
* is set to be time point change aware, OnTimePointChanged() will be called.*/
void OnTimePointChangedInternal();
static void RemoveContourFromInterpolator(const SliceInformation& sliceInfo, LabelSetImage::LabelValueType labelValue);
// The prefix of the contourmarkername. Suffix is a consecutive number
const std::string m_Contourmarkername;
bool m_ShowMarkerNodes = false;
static bool m_SurfaceInterpolationEnabled;
bool m_IsTimePointChangeAware = true;
TimePointType m_LastTimePointTriggered = 0.;
};
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.cpp b/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.cpp
index 314b7b9fee..6070914a34 100644
--- a/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.cpp
@@ -1,851 +1,852 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkSegWithPreviewTool.h"
#include "mitkToolManager.h"
#include "mitkColorProperty.h"
#include "mitkProperties.h"
#include "mitkDataStorage.h"
#include "mitkRenderingManager.h"
#include <mitkTimeNavigationController.h>
#include "mitkImageAccessByItk.h"
#include "mitkImageCast.h"
#include "mitkLabelSetImage.h"
#include "mitkMaskAndCutRoiImageFilter.h"
#include "mitkPadImageFilter.h"
#include "mitkNodePredicateGeometry.h"
#include "mitkSegTool2D.h"
mitk::SegWithPreviewTool::SegWithPreviewTool(bool lazyDynamicPreviews): Tool("dummy"), m_LazyDynamicPreviews(lazyDynamicPreviews)
{
m_ProgressCommand = ToolCommand::New();
}
mitk::SegWithPreviewTool::SegWithPreviewTool(bool lazyDynamicPreviews, const char* interactorType, const us::Module* interactorModule) : Tool(interactorType, interactorModule), m_LazyDynamicPreviews(lazyDynamicPreviews)
{
m_ProgressCommand = ToolCommand::New();
}
mitk::SegWithPreviewTool::~SegWithPreviewTool()
{
}
void mitk::SegWithPreviewTool::SetMergeStyle(MultiLabelSegmentation::MergeStyle mergeStyle)
{
m_MergeStyle = mergeStyle;
this->Modified();
}
void mitk::SegWithPreviewTool::SetOverwriteStyle(MultiLabelSegmentation::OverwriteStyle overwriteStyle)
{
m_OverwriteStyle = overwriteStyle;
this->Modified();
}
void mitk::SegWithPreviewTool::SetLabelTransferScope(LabelTransferScope labelTransferScope)
{
m_LabelTransferScope = labelTransferScope;
this->Modified();
}
void mitk::SegWithPreviewTool::SetLabelTransferMode(LabelTransferMode labelTransferMode)
{
m_LabelTransferMode = labelTransferMode;
this->Modified();
}
void mitk::SegWithPreviewTool::SetSelectedLabels(const SelectedLabelVectorType& labelsToTransfer)
{
m_SelectedLabels = labelsToTransfer;
this->Modified();
}
bool mitk::SegWithPreviewTool::CanHandle(const BaseData* referenceData, const BaseData* workingData) const
{
if (!Superclass::CanHandle(referenceData, workingData))
return false;
if (workingData == nullptr)
return false;
auto* referenceImage = dynamic_cast<const Image*>(referenceData);
if (referenceImage == nullptr)
return false;
auto* labelSet = dynamic_cast<const LabelSetImage*>(workingData);
if (labelSet != nullptr)
return true;
auto* workingImage = dynamic_cast<const Image*>(workingData);
if (workingImage == nullptr)
return false;
// If the working image is a normal image and not a label set image
// it must have the same pixel type as a label set.
return MakeScalarPixelType< DefaultSegmentationDataType >() == workingImage->GetPixelType();
}
void mitk::SegWithPreviewTool::Activated()
{
Superclass::Activated();
this->GetToolManager()->RoiDataChanged +=
MessageDelegate<SegWithPreviewTool>(this, &SegWithPreviewTool::OnRoiDataChanged);
this->GetToolManager()->SelectedTimePointChanged +=
MessageDelegate<SegWithPreviewTool>(this, &SegWithPreviewTool::OnTimePointChanged);
m_ReferenceDataNode = this->GetToolManager()->GetReferenceData(0);
m_SegmentationInputNode = m_ReferenceDataNode;
m_LastTimePointOfUpdate = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
if (m_PreviewSegmentationNode.IsNull())
{
m_PreviewSegmentationNode = DataNode::New();
m_PreviewSegmentationNode->SetProperty("color", ColorProperty::New(0.0, 1.0, 0.0));
m_PreviewSegmentationNode->SetProperty("name", StringProperty::New(std::string(this->GetName())+" preview"));
m_PreviewSegmentationNode->SetProperty("opacity", FloatProperty::New(0.3));
m_PreviewSegmentationNode->SetProperty("binary", BoolProperty::New(true));
m_PreviewSegmentationNode->SetProperty("helper object", BoolProperty::New(true));
}
if (m_SegmentationInputNode.IsNotNull())
{
this->ResetPreviewNode();
this->InitiateToolByInput();
}
else
{
this->GetToolManager()->ActivateTool(-1);
}
m_IsPreviewGenerated = false;
}
void mitk::SegWithPreviewTool::Deactivated()
{
this->GetToolManager()->RoiDataChanged -=
MessageDelegate<SegWithPreviewTool>(this, &SegWithPreviewTool::OnRoiDataChanged);
this->GetToolManager()->SelectedTimePointChanged -=
MessageDelegate<SegWithPreviewTool>(this, &SegWithPreviewTool::OnTimePointChanged);
m_SegmentationInputNode = nullptr;
m_ReferenceDataNode = nullptr;
m_WorkingPlaneGeometry = nullptr;
try
{
if (DataStorage *storage = this->GetToolManager()->GetDataStorage())
{
storage->Remove(m_PreviewSegmentationNode);
RenderingManager::GetInstance()->RequestUpdateAll();
}
}
catch (...)
{
// don't care
}
if (m_PreviewSegmentationNode.IsNotNull())
{
m_PreviewSegmentationNode->SetData(nullptr);
}
Superclass::Deactivated();
}
void mitk::SegWithPreviewTool::ConfirmSegmentation()
{
bool labelChanged = this->EnsureUpToDateUserDefinedActiveLabel();
if ((m_LazyDynamicPreviews && m_CreateAllTimeSteps) || labelChanged)
{ // The tool should create all time steps but is currently in lazy mode,
// thus ensure that a preview for all time steps is available.
this->UpdatePreview(true);
}
CreateResultSegmentationFromPreview();
RenderingManager::GetInstance()->RequestUpdateAll();
if (!m_KeepActiveAfterAccept)
{
this->GetToolManager()->ActivateTool(-1);
}
this->ConfirmCleanUp();
}
void mitk::SegWithPreviewTool::InitiateToolByInput()
{
//default implementation does nothing.
//implement in derived classes to change behavior
}
mitk::LabelSetImage* mitk::SegWithPreviewTool::GetPreviewSegmentation()
{
if (m_PreviewSegmentationNode.IsNull())
{
return nullptr;
}
return dynamic_cast<LabelSetImage*>(m_PreviewSegmentationNode->GetData());
}
const mitk::LabelSetImage* mitk::SegWithPreviewTool::GetPreviewSegmentation() const
{
if (m_PreviewSegmentationNode.IsNull())
{
return nullptr;
}
return dynamic_cast<LabelSetImage*>(m_PreviewSegmentationNode->GetData());
}
mitk::DataNode* mitk::SegWithPreviewTool::GetPreviewSegmentationNode()
{
return m_PreviewSegmentationNode;
}
const mitk::Image* mitk::SegWithPreviewTool::GetSegmentationInput() const
{
if (m_SegmentationInputNode.IsNull())
{
return nullptr;
}
return dynamic_cast<const Image*>(m_SegmentationInputNode->GetData());
}
const mitk::Image* mitk::SegWithPreviewTool::GetReferenceData() const
{
if (m_ReferenceDataNode.IsNull())
{
return nullptr;
}
return dynamic_cast<const Image*>(m_ReferenceDataNode->GetData());
}
template <typename ImageType>
void ClearBufferProcessing(ImageType* itkImage)
{
itkImage->FillBuffer(0);
}
void mitk::SegWithPreviewTool::ResetPreviewContentAtTimeStep(unsigned int timeStep)
{
auto previewImage = GetImageByTimeStep(this->GetPreviewSegmentation(), timeStep);
if (nullptr != previewImage)
{
AccessByItk(previewImage, ClearBufferProcessing);
previewImage->Modified();
}
}
void mitk::SegWithPreviewTool::ResetPreviewContent()
{
auto previewImage = this->GetPreviewSegmentation();
if (nullptr != previewImage)
{
auto castedPreviewImage =
dynamic_cast<LabelSetImage*>(previewImage);
if (nullptr == castedPreviewImage) mitkThrow() << "Application is on wrong state / invalid tool implementation. Preview image should always be of type LabelSetImage now.";
castedPreviewImage->ClearBuffer();
}
}
void mitk::SegWithPreviewTool::ResetPreviewNode()
{
if (m_IsUpdating)
{
mitkThrow() << "Used tool is implemented incorrectly. ResetPreviewNode is called while preview update is ongoing. Check implementation!";
}
itk::RGBPixel<float> previewColor;
previewColor[0] = 0.0f;
previewColor[1] = 1.0f;
previewColor[2] = 0.0f;
const auto image = this->GetSegmentationInput();
if (nullptr != image)
{
LabelSetImage::ConstPointer workingImage =
dynamic_cast<const LabelSetImage *>(this->GetToolManager()->GetWorkingData(0)->GetData());
if (workingImage.IsNotNull())
{
auto newPreviewImage = workingImage->Clone();
if (this->GetResetsToEmptyPreview())
{
newPreviewImage->ClearBuffer();
}
if (newPreviewImage.IsNull())
{
MITK_ERROR << "Cannot create preview helper objects. Unable to clone working image";
return;
}
m_PreviewSegmentationNode->SetData(newPreviewImage);
if (newPreviewImage->GetNumberOfLayers() == 0)
{
newPreviewImage->AddLayer();
newPreviewImage->SetActiveLayer(0);
}
auto* activeLabel = newPreviewImage->GetActiveLabel();
if (nullptr == activeLabel)
{
activeLabel = newPreviewImage->AddLabel("toolresult", previewColor, newPreviewImage->GetActiveLayer());
newPreviewImage->UpdateLookupTable(activeLabel->GetValue());
}
else if (m_UseSpecialPreviewColor)
{
// Let's paint the feedback node green...
activeLabel->SetColor(previewColor);
newPreviewImage->UpdateLookupTable(activeLabel->GetValue());
}
+ newPreviewImage->GetLookupTable()->Modified();
activeLabel->SetVisible(true);
}
else
{
Image::ConstPointer workingImageBin = dynamic_cast<const Image*>(this->GetToolManager()->GetWorkingData(0)->GetData());
if (workingImageBin.IsNotNull())
{
Image::Pointer newPreviewImage;
if (this->GetResetsToEmptyPreview())
{
newPreviewImage = Image::New();
newPreviewImage->Initialize(workingImageBin);
}
else
{
auto newPreviewImage = workingImageBin->Clone();
}
if (newPreviewImage.IsNull())
{
MITK_ERROR << "Cannot create preview helper objects. Unable to clone working image";
return;
}
m_PreviewSegmentationNode->SetData(newPreviewImage);
}
else
{
mitkThrow() << "Tool is an invalid state. Cannot setup preview node. Working data is an unsupported class and should have not been accepted by CanHandle().";
}
}
m_PreviewSegmentationNode->SetColor(previewColor);
m_PreviewSegmentationNode->SetOpacity(0.5);
int layer(50);
m_ReferenceDataNode->GetIntProperty("layer", layer);
m_PreviewSegmentationNode->SetIntProperty("layer", layer + 1);
if (DataStorage *ds = this->GetToolManager()->GetDataStorage())
{
if (!ds->Exists(m_PreviewSegmentationNode))
ds->Add(m_PreviewSegmentationNode, m_ReferenceDataNode);
}
}
}
mitk::SegWithPreviewTool::LabelMappingType mitk::SegWithPreviewTool::GetLabelMapping() const
{
LabelSetImage::LabelValueType offset = 0;
if (LabelTransferMode::AddLabel == m_LabelTransferMode && LabelTransferScope::ActiveLabel!=m_LabelTransferScope)
{
//If we are not just working on active label and transfer mode is add, we need to compute an offset for adding the
//preview labels instat of just mapping them to existing segmentation labels.
const auto segmentation = this->GetTargetSegmentation();
if (nullptr == segmentation)
mitkThrow() << "Invalid state of SegWithPreviewTool. Cannot GetLabelMapping if no target segmentation is set.";
auto labels = segmentation->GetLabels();
auto maxLabelIter = std::max_element(std::begin(labels), std::end(labels), [](const Label::Pointer& a, const Label::Pointer& b) {
return a->GetValue() < b->GetValue();
});
if (maxLabelIter != labels.end())
{
offset = maxLabelIter->GetPointer()->GetValue();
}
}
LabelMappingType labelMapping = {};
switch (this->m_LabelTransferScope)
{
case LabelTransferScope::SelectedLabels:
{
for (auto label : this->m_SelectedLabels)
{
labelMapping.push_back({label, label + offset});
}
}
break;
case LabelTransferScope::AllLabels:
{
const auto labelValues = this->GetPreviewSegmentation()->GetLabelValuesByGroup(this->GetPreviewSegmentation()->GetActiveLayer());
for (auto labelValue : labelValues)
{
labelMapping.push_back({ labelValue, labelValue + offset});
}
}
break;
default:
{
if (m_SelectedLabels.empty())
mitkThrow() << "Failed to generate label transfer mapping. Tool is in an invalid state, as "
"LabelTransferScope==ActiveLabel but no label is indicated as selected label. Check "
"implementation of derived tool class.";
if (m_SelectedLabels.size() > 1)
mitkThrow() << "Failed to generate label transfer mapping. Tool is in an invalid state, as "
"LabelTransferScope==ActiveLabel but more then one selected label is indicated."
"Should be only one. Check implementation of derived tool class.";
labelMapping.push_back({m_SelectedLabels.front(), this->GetUserDefinedActiveLabel()});
}
break;
}
return labelMapping;
}
void mitk::SegWithPreviewTool::TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep, const LabelMappingType& labelMapping)
{
try
{
Image::ConstPointer sourceImageAtTimeStep = this->GetImageByTimeStep(sourceImage, timeStep);
if (sourceImageAtTimeStep->GetPixelType() != destinationImage->GetPixelType())
{
mitkThrow() << "Cannot transfer images. Tool is in an invalid state, source image and destination image do not have the same pixel type. "
<< "Source pixel type: " << sourceImage->GetPixelType().GetTypeAsString()
<< "; destination pixel type: " << destinationImage->GetPixelType().GetTypeAsString();
}
if (!Equal(*(sourceImage->GetGeometry(timeStep)), *(destinationImage->GetGeometry(timeStep)), NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, false))
{
mitkThrow() << "Cannot transfer images. Tool is in an invalid state, source image and destination image do not have the same geometry.";
}
if (nullptr != this->GetWorkingPlaneGeometry())
{
auto sourceSlice = SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), sourceImage, timeStep);
auto resultSlice =
SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), destinationImage, timeStep)->Clone();
auto destLSImage = dynamic_cast<LabelSetImage *>(destinationImage);
//We need to transfer explictly to a copy of the current working image to ensure that labelMapping is done and things
//like merge style, overwrite style and locks are regarded.
TransferLabelContentAtTimeStep(sourceSlice,
resultSlice,
destLSImage->GetConstLabelsByValue(destLSImage->GetLabelValuesByGroup(destLSImage->GetActiveLayer())),
timeStep,
0,
0,
destLSImage->GetUnlabeledLabelLock(),
labelMapping,
m_MergeStyle,
m_OverwriteStyle);
//We use WriteBackSegmentationResult to ensure undo/redo is supported also by derived tools of this class.
SegTool2D::WriteBackSegmentationResult(this->GetTargetSegmentationNode(), m_WorkingPlaneGeometry, resultSlice, timeStep);
}
else
{ //take care of the full segmentation volume
auto sourceLSImage = dynamic_cast<const LabelSetImage*>(sourceImage);
auto destLSImage = dynamic_cast<LabelSetImage*>(destinationImage);
TransferLabelContentAtTimeStep(sourceLSImage, destLSImage, timeStep, labelMapping, m_MergeStyle, m_OverwriteStyle);
}
}
catch (mitk::Exception& e)
{
Tool::ErrorMessage(e.GetDescription());
mitkReThrow(e);
}
}
void mitk::SegWithPreviewTool::CreateResultSegmentationFromPreview()
{
const auto segInput = this->GetSegmentationInput();
auto previewImage = this->GetPreviewSegmentation();
if (nullptr != segInput && nullptr != previewImage)
{
DataNode::Pointer resultSegmentationNode = GetTargetSegmentationNode();
if (resultSegmentationNode.IsNotNull())
{
const TimePointType timePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
auto resultSegmentation = dynamic_cast<Image*>(resultSegmentationNode->GetData());
// REMARK: the following code in this scope assumes that previewImage and resultSegmentation
// are clones of the working referenceImage (segmentation provided to the tool). Therefore they have
// the same time geometry.
if (previewImage->GetTimeSteps() != resultSegmentation->GetTimeSteps())
{
mitkThrow() << "Cannot confirm/transfer segmentation. Internal tool state is invalid."
<< " Preview segmentation and segmentation result image have different time geometries.";
}
auto labelMapping = this->GetLabelMapping();
this->PreparePreviewToResultTransfer(labelMapping);
if (m_CreateAllTimeSteps)
{
for (unsigned int timeStep = 0; timeStep < previewImage->GetTimeSteps(); ++timeStep)
{
this->TransferImageAtTimeStep(previewImage, resultSegmentation, timeStep, labelMapping);
}
}
else
{
const auto timeStep = resultSegmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint);
this->TransferImageAtTimeStep(previewImage, resultSegmentation, timeStep, labelMapping);
}
// since we are maybe working on a smaller referenceImage, pad it to the size of the original referenceImage
if (m_ReferenceDataNode.GetPointer() != m_SegmentationInputNode.GetPointer())
{
PadImageFilter::Pointer padFilter = PadImageFilter::New();
padFilter->SetInput(0, resultSegmentation);
padFilter->SetInput(1, dynamic_cast<Image*>(m_ReferenceDataNode->GetData()));
padFilter->SetBinaryFilter(true);
padFilter->SetUpperThreshold(1);
padFilter->SetLowerThreshold(1);
padFilter->Update();
resultSegmentationNode->SetData(padFilter->GetOutput());
}
this->EnsureTargetSegmentationNodeInDataStorage();
}
}
}
void mitk::SegWithPreviewTool::OnRoiDataChanged()
{
DataNode::ConstPointer node = this->GetToolManager()->GetRoiData(0);
if (node.IsNotNull())
{
MaskAndCutRoiImageFilter::Pointer roiFilter = MaskAndCutRoiImageFilter::New();
Image::Pointer image = dynamic_cast<Image *>(m_SegmentationInputNode->GetData());
if (image.IsNull())
return;
roiFilter->SetInput(image);
roiFilter->SetRegionOfInterest(node->GetData());
roiFilter->Update();
DataNode::Pointer tmpNode = DataNode::New();
tmpNode->SetData(roiFilter->GetOutput());
m_SegmentationInputNode = tmpNode;
}
else
m_SegmentationInputNode = m_ReferenceDataNode;
this->ResetPreviewNode();
this->InitiateToolByInput();
this->UpdatePreview();
}
void mitk::SegWithPreviewTool::OnTimePointChanged()
{
if (m_IsTimePointChangeAware && m_PreviewSegmentationNode.IsNotNull() && m_SegmentationInputNode.IsNotNull())
{
const TimePointType timePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
const bool isStaticSegOnDynamicImage = m_PreviewSegmentationNode->GetData()->GetTimeSteps() == 1 && m_SegmentationInputNode->GetData()->GetTimeSteps() > 1;
if (timePoint!=m_LastTimePointOfUpdate && (isStaticSegOnDynamicImage || m_LazyDynamicPreviews))
{ //we only need to update either because we are lazzy
//or because we have a static segmentation with a dynamic referenceImage
this->UpdatePreview();
}
}
}
bool mitk::SegWithPreviewTool::EnsureUpToDateUserDefinedActiveLabel()
{
bool labelChanged = true;
const auto workingImage = dynamic_cast<const Image*>(this->GetToolManager()->GetWorkingData(0)->GetData());
if (const auto& labelSetImage = dynamic_cast<const LabelSetImage*>(workingImage))
{
// this is a fix for T28131 / T28986, which should be refactored if T28524 is being worked on
auto newLabel = labelSetImage->GetActiveLabel()->GetValue();
labelChanged = newLabel != m_UserDefinedActiveLabel;
m_UserDefinedActiveLabel = newLabel;
}
else
{
m_UserDefinedActiveLabel = 1;
labelChanged = false;
}
return labelChanged;
}
void mitk::SegWithPreviewTool::UpdatePreview(bool ignoreLazyPreviewSetting)
{
const auto inputImage = this->GetSegmentationInput();
auto previewImage = this->GetPreviewSegmentation();
int progress_steps = 200;
const auto workingImage = dynamic_cast<const Image*>(this->GetToolManager()->GetWorkingData(0)->GetData());
this->EnsureUpToDateUserDefinedActiveLabel();
this->CurrentlyBusy.Send(true);
m_IsUpdating = true;
m_IsPreviewGenerated = false;
this->UpdatePrepare();
const TimePointType timePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint();
try
{
if (nullptr != inputImage && nullptr != previewImage)
{
m_ProgressCommand->AddStepsToDo(progress_steps);
if (previewImage->GetTimeSteps() > 1 && (ignoreLazyPreviewSetting || !m_LazyDynamicPreviews))
{
for (unsigned int timeStep = 0; timeStep < previewImage->GetTimeSteps(); ++timeStep)
{
Image::ConstPointer feedBackImage;
Image::ConstPointer currentSegImage;
auto previewTimePoint = previewImage->GetTimeGeometry()->TimeStepToTimePoint(timeStep);
auto inputTimeStep = inputImage->GetTimeGeometry()->TimePointToTimeStep(previewTimePoint);
if (nullptr != this->GetWorkingPlaneGeometry())
{ //only extract a specific slice defined by the working plane as feedback referenceImage.
feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), inputImage, inputTimeStep);
currentSegImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), workingImage, previewTimePoint);
}
else
{ //work on the whole feedback referenceImage
feedBackImage = this->GetImageByTimeStep(inputImage, inputTimeStep);
currentSegImage = this->GetImageByTimePoint(workingImage, previewTimePoint);
}
this->DoUpdatePreview(feedBackImage, currentSegImage, previewImage, timeStep);
}
}
else
{
Image::ConstPointer feedBackImage;
Image::ConstPointer currentSegImage;
if (nullptr != this->GetWorkingPlaneGeometry())
{
feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), inputImage, timePoint);
currentSegImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), workingImage, timePoint);
}
else
{
feedBackImage = this->GetImageByTimePoint(inputImage, timePoint);
currentSegImage = this->GetImageByTimePoint(workingImage, timePoint);
}
auto timeStep = previewImage->GetTimeGeometry()->TimePointToTimeStep(timePoint);
this->DoUpdatePreview(feedBackImage, currentSegImage, previewImage, timeStep);
}
RenderingManager::GetInstance()->RequestUpdateAll();
if (!previewImage->GetAllLabelValues().empty())
{ // check if labels exits for the preview
m_IsPreviewGenerated = true;
}
}
}
catch (itk::ExceptionObject & excep)
{
MITK_ERROR << "Exception caught: " << excep.GetDescription();
m_ProgressCommand->SetProgress(progress_steps);
std::string msg = excep.GetDescription();
ErrorMessage.Send(msg);
}
catch (...)
{
m_ProgressCommand->SetProgress(progress_steps);
m_IsUpdating = false;
CurrentlyBusy.Send(false);
throw;
}
this->UpdateCleanUp();
m_LastTimePointOfUpdate = timePoint;
m_ProgressCommand->SetProgress(progress_steps);
m_IsUpdating = false;
CurrentlyBusy.Send(false);
}
bool mitk::SegWithPreviewTool::IsUpdating() const
{
return m_IsUpdating;
}
void mitk::SegWithPreviewTool::UpdatePrepare()
{
// default implementation does nothing
//reimplement in derived classes for special behavior
}
void mitk::SegWithPreviewTool::UpdateCleanUp()
{
// default implementation does nothing
//reimplement in derived classes for special behavior
}
void mitk::SegWithPreviewTool::ConfirmCleanUp()
{
// default implementation does nothing
// reimplement in derived classes for special behavior
}
void mitk::SegWithPreviewTool::TransferLabelInformation(const LabelMappingType& labelMapping,
const mitk::LabelSetImage* source, mitk::LabelSetImage* target)
{
for (const auto& [sourceLabel, targetLabel] : labelMapping)
{
if (LabelSetImage::UNLABELED_VALUE != sourceLabel &&
LabelSetImage::UNLABELED_VALUE != targetLabel &&
!target->ExistLabel(targetLabel, target->GetActiveLayer()))
{
if (!source->ExistLabel(sourceLabel, source->GetActiveLayer()))
{
mitkThrow() << "Cannot prepare segmentation for preview transfer. Preview seems invalid as label is missing. Missing label: " << sourceLabel;
}
auto clonedLabel = source->GetLabel(sourceLabel)->Clone();
clonedLabel->SetValue(targetLabel);
target->AddLabel(clonedLabel,target->GetActiveLayer(), false, false);
}
}
}
void mitk::SegWithPreviewTool::PreparePreviewToResultTransfer(const LabelMappingType& labelMapping)
{
DataNode::Pointer resultSegmentationNode = GetTargetSegmentationNode();
if (resultSegmentationNode.IsNotNull())
{
auto resultSegmentation = dynamic_cast<LabelSetImage*>(resultSegmentationNode->GetData());
if (nullptr == resultSegmentation)
{
mitkThrow() << "Cannot prepare segmentation for preview transfer. Tool is in invalid state as segmentation is not existing or of right type";
}
auto preview = this->GetPreviewSegmentation();
TransferLabelInformation(labelMapping, preview, resultSegmentation);
}
}
mitk::TimePointType mitk::SegWithPreviewTool::GetLastTimePointOfUpdate() const
{
return m_LastTimePointOfUpdate;
}
mitk::LabelSetImage::LabelValueType mitk::SegWithPreviewTool::GetActiveLabelValueOfPreview() const
{
const auto previewImage = this->GetPreviewSegmentation();
const auto activeLabel = previewImage->GetActiveLabel();
if (nullptr == activeLabel)
mitkThrow() << this->GetNameOfClass() <<" is in an invalid state, as "
"preview has no active label indicated. Check "
"implementation of the class.";
return activeLabel->GetValue();
}
const char* mitk::SegWithPreviewTool::GetGroup() const
{
return "autoSegmentation";
}
mitk::Image::ConstPointer mitk::SegWithPreviewTool::GetImageByTimeStep(const mitk::Image* image, TimeStepType timestep)
{
return SelectImageByTimeStep(image, timestep);
}
mitk::Image::Pointer mitk::SegWithPreviewTool::GetImageByTimeStep(mitk::Image* image, TimeStepType timestep)
{
return SelectImageByTimeStep(image, timestep);
}
mitk::Image::ConstPointer mitk::SegWithPreviewTool::GetImageByTimePoint(const mitk::Image* image, TimePointType timePoint)
{
return SelectImageByTimePoint(image, timePoint);
}
void mitk::SegWithPreviewTool::EnsureTargetSegmentationNodeInDataStorage() const
{
auto targetNode = this->GetTargetSegmentationNode();
auto dataStorage = this->GetToolManager()->GetDataStorage();
if (!dataStorage->Exists(targetNode))
{
dataStorage->Add(targetNode, this->GetToolManager()->GetReferenceData(0));
}
}
std::string mitk::SegWithPreviewTool::GetCurrentSegmentationName()
{
auto workingData = this->GetToolManager()->GetWorkingData(0);
return nullptr != workingData
? workingData->GetName()
: "";
}
mitk::DataNode* mitk::SegWithPreviewTool::GetTargetSegmentationNode() const
{
return this->GetToolManager()->GetWorkingData(0);
}
mitk::LabelSetImage* mitk::SegWithPreviewTool::GetTargetSegmentation() const
{
auto node = this->GetTargetSegmentationNode();
if (nullptr == node)
return nullptr;
return dynamic_cast<LabelSetImage*>(node->GetData());
}
void mitk::SegWithPreviewTool::TransferLabelSetImageContent(const LabelSetImage* source, LabelSetImage* target, TimeStepType timeStep)
{
mitk::ImageReadAccessor newMitkImgAcc(source);
LabelMappingType labelMapping;
const auto labelValues = source->GetLabelValuesByGroup(source->GetActiveLayer());
for (const auto& labelValue : labelValues)
{
labelMapping.push_back({ labelValue,labelValue });
}
TransferLabelInformation(labelMapping, source, target);
target->SetVolume(newMitkImgAcc.GetData(), timeStep);
}
bool mitk::SegWithPreviewTool::ConfirmBeforeDeactivation()
{
return m_IsPreviewGenerated && m_RequestDeactivationConfirmation;
}
diff --git a/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.h b/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.h
index 4aa461a30e..ac7d7a7bb1 100644
--- a/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.h
+++ b/Modules/Segmentation/Interactions/mitkSegWithPreviewTool.h
@@ -1,339 +1,339 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkSegWithPreviewTool_h
#define mitkSegWithPreviewTool_h
#include "mitkTool.h"
#include "mitkCommon.h"
#include "mitkDataNode.h"
#include "mitkToolCommand.h"
#include <MitkSegmentationExports.h>
namespace mitk
{
/**
\brief Base class for any auto segmentation tool that provides a preview of the new segmentation.
This tool class implements a lot basic logic to handle auto segmentation tools with preview,
Time point and ROI support. Derived classes will ask to update the segmentation preview if needed
(e.g. because the ROI or the current time point has changed) or because derived tools
indicated the need to update themselves.
- This class also takes care to properly transfer a confirmed preview into the segementation
+ This class also takes care to properly transfer a confirmed preview into the segmentation
result.
\ingroup ToolManagerEtAl
\sa mitk::Tool
\sa QmitkInteractiveSegmentation
*/
class MITKSEGMENTATION_EXPORT SegWithPreviewTool : public Tool
{
public:
mitkClassMacro(SegWithPreviewTool, Tool);
void Activated() override;
void Deactivated() override;
void ConfirmSegmentation();
itkSetMacro(CreateAllTimeSteps, bool);
itkGetMacro(CreateAllTimeSteps, bool);
itkBooleanMacro(CreateAllTimeSteps);
itkSetMacro(KeepActiveAfterAccept, bool);
itkGetMacro(KeepActiveAfterAccept, bool);
itkBooleanMacro(KeepActiveAfterAccept);
itkSetMacro(IsTimePointChangeAware, bool);
itkGetMacro(IsTimePointChangeAware, bool);
itkBooleanMacro(IsTimePointChangeAware);
itkSetMacro(ResetsToEmptyPreview, bool);
itkGetMacro(ResetsToEmptyPreview, bool);
itkBooleanMacro(ResetsToEmptyPreview);
itkSetMacro(UseSpecialPreviewColor, bool);
itkGetMacro(UseSpecialPreviewColor, bool);
itkBooleanMacro(UseSpecialPreviewColor);
itkSetMacro(RequestDeactivationConfirmation, bool);
itkGetMacro(RequestDeactivationConfirmation, bool);
itkBooleanMacro(RequestDeactivationConfirmation);
- /*itk macro was not used on purpose, to aviod the change of mtime.*/
+ /*itk macro was not used on purpose, to avoid the change of mtime.*/
void SetMergeStyle(MultiLabelSegmentation::MergeStyle mergeStyle);
itkGetMacro(MergeStyle, MultiLabelSegmentation::MergeStyle);
- /*itk macro was not used on purpose, to aviod the change of mtime.*/
+ /*itk macro was not used on purpose, to avoid the change of mtime.*/
void SetOverwriteStyle(MultiLabelSegmentation::OverwriteStyle overwriteStyle);
itkGetMacro(OverwriteStyle, MultiLabelSegmentation::OverwriteStyle);
enum class LabelTransferScope
{
- ActiveLabel, //Only the selected label will be transfered from the preview segmentation
+ ActiveLabel, //Only the selected label will be transferred from the preview segmentation
//to the result segmentation.
//If this mode is selected the class expects that GetSelectedLabels indicate
//the label in the preview.
- SelectedLabels, //The labels defined as selected labels will be transfered.
+ SelectedLabels, //The labels defined as selected labels will be transferred.
AllLabels //Transfer all labels of the preview
};
- /*itk macro was not used on purpose, to aviod the change of mtime.*/
+ /*itk macro was not used on purpose, to avoid the change of mtime.*/
void SetLabelTransferScope(LabelTransferScope labelTransferScope);
itkGetMacro(LabelTransferScope, LabelTransferScope);
using SelectedLabelVectorType = std::vector<Label::PixelType>;
- /** Specifies the labels that should be transfered form preview to the working image,
+ /** Specifies the labels that should be transferred form preview to the working image,
if the segmentation is confirmed. The setting will be used, if LabelTransferScope is set to "ActiveLabel"
or "SelectedLabels".
@remark If the LabelTransferScope=="ActiveLabel", the class expects only one label to be selected.
- @remark The selected label IDs corespond to the labels of the preview image.*/
+ @remark The selected label IDs correspond to the labels of the preview image.*/
void SetSelectedLabels(const SelectedLabelVectorType& labelsToTransfer);
itkGetMacro(SelectedLabels, SelectedLabelVectorType);
enum class LabelTransferMode
{
- MapLabel, //Only the active label will be transfered from preview to segmentation.
- AddLabel //The labels defined as selected labels will be transfered.
+ MapLabel, //Only the active label will be transferred from preview to segmentation.
+ AddLabel //The labels defined as selected labels will be transferred.
};
- /*itk macro was not used on purpose, to aviod the change of mtime.*/
+ /*itk macro was not used on purpose, to avoid the change of mtime.*/
void SetLabelTransferMode(LabelTransferMode labelTransferMode);
itkGetMacro(LabelTransferMode, LabelTransferMode);
bool CanHandle(const BaseData* referenceData, const BaseData* workingData) const override;
/** Triggers the actualization of the preview
* @param ignoreLazyPreviewSetting If set true UpdatePreview will always
* generate the preview for all time steps. If set to false, UpdatePreview
* will regard the setting specified by the constructor.
* To define the update generation for time steps implement DoUpdatePreview.
* To alter what should be done directly before or after the update of the preview,
* reimplement UpdatePrepare() or UpdateCleanUp().*/
void UpdatePreview(bool ignoreLazyPreviewSetting = false);
/** Indicate if currently UpdatePreview is triggered (true) or not (false).*/
bool IsUpdating() const;
/**
* @brief Gets the name of the currently selected segmentation node
* @return the name of the segmentation node or an empty string if
* none is selected
*/
std::string GetCurrentSegmentationName();
/**
* @brief Returns the currently selected segmentation node
* @return a mitk::DataNode which contains a segmentation image
*/
virtual DataNode* GetTargetSegmentationNode() const;
LabelSetImage* GetTargetSegmentation() const;
/** Returns the image that contains the preview of the current segmentation.
* Returns null if the node is not set or does not contain an image.*/
LabelSetImage* GetPreviewSegmentation();
const LabelSetImage* GetPreviewSegmentation() const;
DataNode* GetPreviewSegmentationNode();
protected:
ToolCommand::Pointer m_ProgressCommand;
SegWithPreviewTool(bool lazyDynamicPreviews = false); // purposely hidden
SegWithPreviewTool(bool lazyDynamicPreviews, const char* interactorType, const us::Module* interactorModule = nullptr); // purposely hidden
~SegWithPreviewTool() override;
const char* GetGroup() const override;
/** Helper that extracts the image for the passed timestep, if the image has multiple time steps.*/
static Image::ConstPointer GetImageByTimeStep(const Image* image, TimeStepType timestep);
/** Helper that extracts the image for the passed timestep, if the image has multiple time steps.*/
static Image::Pointer GetImageByTimeStep(Image* image, TimeStepType timestep);
/** Helper that extracts the image for the passed time point, if the image has multiple time steps.*/
static Image::ConstPointer GetImageByTimePoint(const Image* image, TimePointType timePoint);
void EnsureTargetSegmentationNodeInDataStorage() const;
/** Member is always called if GetSegmentationInput() has changed
* (e.g. because a new ROI was defined, or on activation) to give derived
- * classes the posibility to initiate their state accordingly.
+ * classes the possibility to initiate their state accordingly.
* Reimplement this function to implement special behavior.
*/
virtual void InitiateToolByInput();
/** This member function offers derived classes the possibility to alter what should
happen directly before the update of the preview is performed. It is called by
UpdatePreview. Default implementation does nothing.*/
virtual void UpdatePrepare();
/** This member function offers derived classes the possibility to alter what should
happen directly after the update of the preview is performed. It is called by
UpdatePreview. Default implementation does nothing.*/
virtual void UpdateCleanUp();
/** This member function offers derived classes the possibility to alter what should
happen directly after the Confirmation of the preview is performed. It is called by
ConfirmSegmentation. Default implementation does nothing.*/
virtual void ConfirmCleanUp();
using LabelMappingType = std::vector<std::pair<Label::PixelType, Label::PixelType> >;
/** This member function offers derived classes the possibility to alter what should
- happen directly before the content of the preview is transfered to the segmentation,
+ happen directly before the content of the preview is transferred to the segmentation,
when the segmentation is confirmed. It is called by CreateResultSegmentationFromPreview.
- Default implementation ensure that all labels that will be transfered, exist in the
+ Default implementation ensure that all labels that will be transferred, exist in the
segmentation. If they are not existing before the transfer, the will be added by
cloning the label information of the preview.
- @param labelMapping the mapping that should be used for transfering the labels.
+ @param labelMapping the mapping that should be used for transferring the labels.
*/
virtual void PreparePreviewToResultTransfer(const LabelMappingType& labelMapping);
static void TransferLabelInformation(const LabelMappingType& labelMapping,
const mitk::LabelSetImage* source, mitk::LabelSetImage* target);
/**Helper function that can be used to move the content of an LabelSetImage (the pixels of the active source layer and the labels).
- This is e.g. helpfull if you generate an LabelSetImage content in DoUpdatePreview and you want to transfer it into the preview image.*/
+ This is e.g. helpful if you generate an LabelSetImage content in DoUpdatePreview and you want to transfer it into the preview image.*/
static void TransferLabelSetImageContent(const LabelSetImage* source, LabelSetImage* target, TimeStepType timeStep);
/** This function does the real work. Here the preview for a given
* input image should be computed and stored in the also passed
* preview image at the passed time step.
* It also provides the current/old segmentation at the time point,
- * which can be used, if the preview depends on the the segmenation so far.
+ * which can be used, if the preview depends on the the segmentation so far.
*/
virtual void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) = 0;
/** Returns the input that should be used for any segmentation/preview or tool update.
* It is either the data of ReferenceDataNode itself or a part of it defined by a ROI mask
* provided by the tool manager. Derived classes should regard this as the relevant
* input data for any processing.
* Returns null if the node is not set or does not contain an image.*/
const Image* GetSegmentationInput() const;
/** Returns the image that is provided by the ReferenceDataNode.
* Returns null if the node is not set or does not contain an image.*/
const Image* GetReferenceData() const;
/** Resets the preview node so it is empty and ready to be filled by the tool
@remark Calling this function will generate a new preview image, and the old
might be invalidated. Therefore this function should not be used within the
scope of UpdatePreview (m_IsUpdating == true).*/
void ResetPreviewNode();
/** Resets the complete content of the preview image. The instance of the preview image and its settings
* stay the same.*/
void ResetPreviewContent();
/** Resets only the image content of the specified timeStep of the preview image. If the preview image or the specified
time step does not exist, nothing happens.*/
void ResetPreviewContentAtTimeStep(unsigned int timeStep);
TimePointType GetLastTimePointOfUpdate() const;
LabelSetImage::LabelValueType GetActiveLabelValueOfPreview() const;
itkGetConstMacro(UserDefinedActiveLabel, Label::PixelType);
itkSetObjectMacro(WorkingPlaneGeometry, PlaneGeometry);
itkGetConstObjectMacro(WorkingPlaneGeometry, PlaneGeometry);
bool ConfirmBeforeDeactivation() override;
private:
void TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep, const LabelMappingType& labelMapping);
void CreateResultSegmentationFromPreview();
void OnRoiDataChanged();
void OnTimePointChanged();
/**Internal helper that ensures that the stored active label is up to date.
This is a fix for T28131 / T28986. It should be refactored if T28524 is being worked on.
On the long run, the active label will be communicated/set by the user/toolmanager as a
state of the tool and the tool should react accordingly (like it does for other external
state changes).
@return indicates if the label has changed (true) or not.
*/
bool EnsureUpToDateUserDefinedActiveLabel();
/**Returns that label mapping between preview segmentation (first element of pair) and
result segmentation (second element of pair).
The content depends on the settings of LabelTransferMode and LabelTransferScope*/
LabelMappingType GetLabelMapping() const;
- /** Node that containes the preview data generated and managed by this class or derived ones.*/
+ /** Node that contains the preview data generated and managed by this class or derived ones.*/
DataNode::Pointer m_PreviewSegmentationNode;
- /** The reference data recieved from ToolManager::GetReferenceData when tool was activated.*/
+ /** The reference data received from ToolManager::GetReferenceData when tool was activated.*/
DataNode::Pointer m_ReferenceDataNode;
- /** Node that containes the data that should be used as input for any auto segmentation. It might
+ /** Node that contains the data that should be used as input for any auto segmentation. It might
* be the same like m_ReferenceDataNode (if no ROI is set) or a sub region (if ROI is set).*/
DataNode::Pointer m_SegmentationInputNode;
/** Indicates if Accepting the threshold should transfer/create the segmentations
of all time steps (true) or only of the currently selected timepoint (false).*/
bool m_CreateAllTimeSteps = false;
/** Indicates if the tool should kept active after accepting the segmentation or not.*/
bool m_KeepActiveAfterAccept = false;
/** Relevant if the working data / preview image has multiple time steps (dynamic segmentations).
* This flag has to be set by derived classes accordingly to there way to generate dynamic previews.
* If LazyDynamicPreview is true, the tool generates only the preview for the current time step.
* Therefore it always has to update the preview if current time point has changed and it has to (re)compute
* all timeframes if ConfirmSegmentation() is called.*/
bool m_LazyDynamicPreviews = false;
bool m_IsTimePointChangeAware = true;
/** Controls if ResetPreviewNode generates an empty content (true) or clones the current
segmentation (false).*/
bool m_ResetsToEmptyPreview = false;
/** Controls if for the preview of the active label a special preview color is used.
* If set to false, coloring will stay in the preview like it is in the working image.*/
bool m_UseSpecialPreviewColor = true;
TimePointType m_LastTimePointOfUpdate = 0.;
bool m_IsUpdating = false;
Label::PixelType m_UserDefinedActiveLabel = 1;
/** This variable indicates if for the tool a working plane geometry is defined.
* If a working plane is defined the tool will only work an the slice of the input
* and the segmentation. Thus only the relevant input slice will be passed to
- * DoUpdatePreview(...) and only the relevant slice of the preview will be transfered when
+ * DoUpdatePreview(...) and only the relevant slice of the preview will be transferred when
* ConfirmSegmentation() is called.*/
PlaneGeometry::Pointer m_WorkingPlaneGeometry;
- /** This variable controles how the label pixel content of the preview should be transfered into the
+ /** This variable controls how the label pixel content of the preview should be transferred into the
segmentation- For more details of the behavior see documentation of MultiLabelSegmentation::MergeStyle. */
MultiLabelSegmentation::MergeStyle m_MergeStyle = MultiLabelSegmentation::MergeStyle::Replace;
- /** This variable controles how the label pixel content of the preview should be transfered into the
+ /** This variable controls how the label pixel content of the preview should be transferred into the
segmentation- For more details of the behavior see documentation of MultiLabelSegmentation::OverwriteStyle. */
MultiLabelSegmentation::OverwriteStyle m_OverwriteStyle = MultiLabelSegmentation::OverwriteStyle::RegardLocks;
LabelTransferScope m_LabelTransferScope = LabelTransferScope::ActiveLabel;
SelectedLabelVectorType m_SelectedLabels = {};
LabelTransferMode m_LabelTransferMode = LabelTransferMode::MapLabel;
bool m_IsPreviewGenerated = false;
/** This variable tracks if there should be a user-confirmation before a tool is deactivated or not.
* Call RequestDeactivationConfirmationOn() in the tool class to avail this feature.
*/
bool m_RequestDeactivationConfirmation = false;
};
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitkSegmentAnythingPythonService.cpp b/Modules/Segmentation/Interactions/mitkSegmentAnythingPythonService.cpp
index 08d8e86494..0b90a0a377 100644
--- a/Modules/Segmentation/Interactions/mitkSegmentAnythingPythonService.cpp
+++ b/Modules/Segmentation/Interactions/mitkSegmentAnythingPythonService.cpp
@@ -1,265 +1,265 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "mitkSegmentAnythingPythonService.h"
#include "mitkIOUtil.h"
#include <mitkSegmentAnythingProcessExecutor.h>
#include <itksys/SystemTools.hxx>
#include <chrono>
#include <thread>
-#include <filesystem>
+#include <mitkFileSystem.h>
#include <itkImageFileWriter.h>
#include "mitkImageAccessByItk.h"
#include <mitkLocaleSwitch.h>
using namespace std::chrono_literals;
using sys_clock = std::chrono::system_clock;
namespace mitk
{
const std::string SIGNALCONSTANTS::READY = "READY";
const std::string SIGNALCONSTANTS::KILL = "KILL";
const std::string SIGNALCONSTANTS::OFF = "OFF";
const std::string SIGNALCONSTANTS::CUDA_OUT_OF_MEMORY_ERROR = "CudaOutOfMemoryError";
const std::string SIGNALCONSTANTS::TIMEOUT_ERROR = "TimeOut";
SegmentAnythingPythonService::Status SegmentAnythingPythonService::CurrentStatus =
SegmentAnythingPythonService::Status::OFF;
}
mitk::SegmentAnythingPythonService::SegmentAnythingPythonService(
std::string workingDir, std::string modelType, std::string checkPointPath, unsigned int gpuId, std::string backend)
: m_PythonPath(workingDir),
m_ModelType(modelType),
m_CheckpointPath(checkPointPath),
m_Backend(backend),
m_GpuId(gpuId)
{
this->CreateTempDirs(PARENT_TEMP_DIR_PATTERN);
}
mitk::SegmentAnythingPythonService::~SegmentAnythingPythonService()
{
if (CurrentStatus == Status::READY)
{
this->StopAsyncProcess();
}
CurrentStatus = Status::OFF;
- std::filesystem::remove_all(this->GetMitkTempDir());
+ fs::remove_all(this->GetMitkTempDir());
}
void mitk::SegmentAnythingPythonService::onPythonProcessEvent(itk::Object*, const itk::EventObject &e, void*)
{
std::string testCOUT,testCERR;
const auto *pEvent = dynamic_cast<const mitk::ExternalProcessStdOutEvent *>(&e);
if (pEvent)
{
testCOUT = testCOUT + pEvent->GetOutput();
testCOUT.erase(std::find_if(testCOUT.rbegin(), testCOUT.rend(), [](unsigned char ch) {
return !std::isspace(ch);}).base(), testCOUT.end()); // remove trailing whitespaces, if any
if (SIGNALCONSTANTS::READY == testCOUT)
{
CurrentStatus = Status::READY;
}
if (SIGNALCONSTANTS::KILL == testCOUT)
{
CurrentStatus = Status::KILLED;
}
if (SIGNALCONSTANTS::CUDA_OUT_OF_MEMORY_ERROR == testCOUT)
{
CurrentStatus = Status::CUDAError;
}
MITK_INFO << testCOUT;
}
const auto *pErrEvent = dynamic_cast<const mitk::ExternalProcessStdErrEvent *>(&e);
if (pErrEvent)
{
testCERR = testCERR + pErrEvent->GetOutput();
MITK_ERROR << testCERR;
}
}
void mitk::SegmentAnythingPythonService::StopAsyncProcess()
{
this->WriteControlFile(SIGNALCONSTANTS::KILL);
m_DaemonExec->SetStop(true);
m_Future.get();
}
void mitk::SegmentAnythingPythonService::StartAsyncProcess()
{
if (nullptr != m_DaemonExec)
{
this->StopAsyncProcess();
}
if (this->GetMitkTempDir().empty())
{
this->CreateTempDirs(PARENT_TEMP_DIR_PATTERN);
}
this->WriteControlFile(SIGNALCONSTANTS::READY);
double timeout = 1;
m_DaemonExec = SegmentAnythingProcessExecutor::New(timeout);
itk::CStyleCommand::Pointer spCommand = itk::CStyleCommand::New();
spCommand->SetCallback(&mitk::SegmentAnythingPythonService::onPythonProcessEvent);
m_DaemonExec->AddObserver(ExternalProcessOutputEvent(), spCommand);
m_Future = std::async(std::launch::async, &mitk::SegmentAnythingPythonService::start_python_daemon, this);
}
void mitk::SegmentAnythingPythonService::TransferPointsToProcess(const std::string &triggerCSV) const
{
this->CheckStatus();
std::string triggerFilePath = m_InDir + IOUtil::GetDirectorySeparator() + TRIGGER_FILENAME;
std::ofstream csvfile;
csvfile.open(triggerFilePath, std::ofstream::out | std::ofstream::trunc);
csvfile << triggerCSV;
csvfile.close();
}
void mitk::SegmentAnythingPythonService::WriteControlFile(const std::string &statusString) const
{
std::string controlFilePath = m_InDir + IOUtil::GetDirectorySeparator() + "control.txt";
std::ofstream controlFile;
controlFile.open(controlFilePath, std::ofstream::out | std::ofstream::trunc);
controlFile << statusString;
controlFile.close();
}
void mitk::SegmentAnythingPythonService::start_python_daemon() const
{
ProcessExecutor::ArgumentListType args;
std::string command = "python";
args.push_back("-u");
args.push_back(SAM_PYTHON_FILE_NAME);
args.push_back("--input-folder");
args.push_back(m_InDir);
args.push_back("--output-folder");
args.push_back(m_OutDir);
args.push_back("--trigger-file");
args.push_back(TRIGGER_FILENAME);
args.push_back("--model-type");
args.push_back(m_ModelType);
args.push_back("--checkpoint");
args.push_back(m_CheckpointPath);
args.push_back("--backend");
args.push_back(m_Backend);
args.push_back("--device");
if (m_GpuId == -1)
{
args.push_back("cpu");
}
else
{
args.push_back("cuda");
std::string cudaEnv = "CUDA_VISIBLE_DEVICES=" + std::to_string(m_GpuId);
itksys::SystemTools::PutEnv(cudaEnv.c_str());
}
try
{
std::stringstream logStream;
for (const auto &arg : args)
logStream << arg << " ";
logStream << m_PythonPath;
MITK_INFO << logStream.str();
m_DaemonExec->Execute(m_PythonPath, command, args);
}
catch (const mitk::Exception &e)
{
MITK_ERROR << e.GetDescription();
return;
}
MITK_INFO << "Python process ended.";
}
bool mitk::SegmentAnythingPythonService::CheckStatus()
{
switch (CurrentStatus)
{
case mitk::SegmentAnythingPythonService::Status::READY:
return true;
case mitk::SegmentAnythingPythonService::Status::CUDAError:
mitkThrow() << "Error: Cuda Out of Memory. Change your model type in Preferences and Activate Segment Anything tool again.";
case mitk::SegmentAnythingPythonService::Status::KILLED:
mitkThrow() << "Error: Python process is already terminated. Cannot load requested segmentation. Activate Segment Anything tool again.";
default:
return false;
}
}
void mitk::SegmentAnythingPythonService::CreateTempDirs(const std::string &dirPattern)
{
this->SetMitkTempDir(IOUtil::CreateTemporaryDirectory(dirPattern));
m_InDir = IOUtil::CreateTemporaryDirectory("sam-in-XXXXXX", m_MitkTempDir);
m_OutDir = IOUtil::CreateTemporaryDirectory("sam-out-XXXXXX", m_MitkTempDir);
}
mitk::LabelSetImage::Pointer mitk::SegmentAnythingPythonService::RetrieveImageFromProcess(long timeOut) const
{
std::string outputImagePath = m_OutDir + IOUtil::GetDirectorySeparator() + m_CurrentUId + ".nrrd";
auto start = sys_clock::now();
- while (!std::filesystem::exists(outputImagePath))
+ while (!fs::exists(outputImagePath))
{
this->CheckStatus();
std::this_thread::sleep_for(100ms);
if (timeOut != -1 && std::chrono::duration_cast<std::chrono::seconds>(sys_clock::now() - start).count() > timeOut)
{
CurrentStatus = Status::OFF;
m_DaemonExec->SetStop(true);
mitkThrow() << SIGNALCONSTANTS::TIMEOUT_ERROR;
}
}
LabelSetImage::Pointer outputBuffer = mitk::IOUtil::Load<LabelSetImage>(outputImagePath);
return outputBuffer;
}
void mitk::SegmentAnythingPythonService::TransferImageToProcess(const Image *inputAtTimeStep, std::string &UId)
{
std::string inputImagePath = m_InDir + IOUtil::GetDirectorySeparator() + UId + ".nrrd";
if (inputAtTimeStep->GetPixelType().GetNumberOfComponents() < 2)
{
AccessByItk_n(inputAtTimeStep, ITKWriter, (inputImagePath));
}
else
{
mitk::IOUtil::Save(inputAtTimeStep, inputImagePath);
}
m_CurrentUId = UId;
}
template <typename TPixel, unsigned int VImageDimension>
void mitk::SegmentAnythingPythonService::ITKWriter(const itk::Image<TPixel, VImageDimension> *image, std::string& outputFilename) const
{
typedef itk::Image<TPixel, VImageDimension> ImageType;
typedef itk::ImageFileWriter<ImageType> WriterType;
typename WriterType::Pointer writer = WriterType::New();
mitk::LocaleSwitch localeSwitch("C");
writer->SetFileName(outputFilename);
writer->SetInput(image);
try
{
writer->Update();
}
catch (const itk::ExceptionObject &error)
{
MITK_ERROR << "Error: " << error << std::endl;
mitkThrow() << "Error: " << error;
}
}
diff --git a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h
index 652f414dfc..5774404e4d 100644
--- a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h
+++ b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h
@@ -1,221 +1,220 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkSegmentAnythingTool_h
#define mitkSegmentAnythingTool_h
#include "mitkSegWithPreviewTool.h"
#include "mitkPointSet.h"
#include "mitkProcessExecutor.h"
#include "mitkSegmentAnythingPythonService.h"
#include <MitkSegmentationExports.h>
#include <itkImage.h>
#include <mitkLevelWindow.h>
namespace us
{
class ModuleResource;
}
namespace mitk
{
/**
\brief Segment Anything Model interactive 2D tool class.
\ingroup ToolManagerEtAl
\sa mitk::Tool
\sa QmitkInteractiveSegmentation
*/
class MITKSEGMENTATION_EXPORT SegmentAnythingTool : public SegWithPreviewTool
{
public:
mitkClassMacro(SegmentAnythingTool, SegWithPreviewTool);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
const char **GetXPM() const override;
const char *GetName() const override;
us::ModuleResource GetIconResource() const override;
void Activated() override;
void Deactivated() override;
/**
* @brief Clears all picks and updates the preview.
*/
virtual void ClearPicks();
/**
* @brief Checks if any point exists in the either
* of the pointsets
*
* @return bool
*/
virtual bool HasPicks() const;
itkSetMacro(MitkTempDir, std::string);
itkGetConstMacro(MitkTempDir, std::string);
itkSetMacro(PythonPath, std::string);
itkGetConstMacro(PythonPath, std::string);
itkSetMacro(ModelType, std::string);
itkGetConstMacro(ModelType, std::string);
itkSetMacro(CheckpointPath, std::string);
itkGetConstMacro(CheckpointPath, std::string);
itkSetMacro(Backend, std::string);
itkGetConstMacro(Backend, std::string);
itkSetMacro(GpuId, int);
itkGetConstMacro(GpuId, int);
itkSetMacro(TimeOutLimit, long);
itkGetConstMacro(TimeOutLimit, long);
itkSetMacro(IsReady, bool);
itkGetConstMacro(IsReady, bool);
itkBooleanMacro(IsReady);
/**
* @brief Initializes python service and
* starts async python daemon of SegmentAnything model.
*
*/
void InitSAMPythonProcess();
/**
* @brief Checks if Python daemon is ready to accept inputs.
*
* @return bool
*/
bool IsPythonReady() const;
Message1<const std::string&> SAMStatusMessageEvent;
protected:
SegmentAnythingTool();
~SegmentAnythingTool() = default;
void ConnectActionsAndFunctions() override;
/*
* @brief Add positive seed point action of StateMachine pattern
*/
virtual void OnAddPositivePoint(StateMachineAction*, InteractionEvent *interactionEvent);
/*
* @brief Add negative seed point action of StateMachine pattern
*/
virtual void OnAddNegativePoint(StateMachineAction*, InteractionEvent *interactionEvent);
/*
* @brief Delete action of StateMachine pattern. The function deletes positive or negative points in
the reverse order of creation. This is done by finding & deleting the Point having the highest
PointIdentifier value from either of the PointSets m_PointSetPositive & m_PointSetNegative.
*/
virtual void OnDelete(StateMachineAction*, InteractionEvent*);
/*
* @brief Clear all seed points and call UpdatePreview to reset the segmentation Preview
*/
void ClearSeeds();
/**
* @brief Overriden method from the tool manager to execute the segmentation
* Implementation:
* 1. Creates Hash for input image from current plane geometry.
* 2. Transfers image pointer to python service along with the hash code.
* 3. Creates seed points as CSV string & transfers to python service
* 3. Retrieves resulting segmentation Image pointer from python service and sets to previewImage.
*
* @param inputAtTimeStep
* @param oldSegAtTimeStep
* @param previewImage
* @param timeStep
*/
void DoUpdatePreview(const Image *inputAtTimeStep, const Image *oldSegAtTimeStep, LabelSetImage *previewImage, TimeStepType timeStep) override;
/**
* @brief Get the Points from positive and negative pointsets as std::vector.
*
* @return std::vector<std::pair<mitk::Point2D, std::string>>
*/
std::vector<std::pair<mitk::Point2D, std::string>> GetPointsAsVector(const mitk::BaseGeometry *baseGeometry) const;
/**
* @brief Get the Points from positive and negative pointsets as csv string.
*
* @param baseGeometry
* @return std::string
*/
virtual std::string GetPointsAsCSVString(const mitk::BaseGeometry *baseGeometry) const;
/**
* @brief Get the Hash For Current Plane from current working plane geometry.
*
* @return std::string
*/
std::string GetHashForCurrentPlane(const mitk::LevelWindow &levelWindow) const;
/**
* @brief Emits message to connected Listnerers.
*
*/
void EmitSAMStatusMessageEvent(const std::string &status);
/**
* @brief Cleans up segmentation preview and clears all seeds.
*
*/
void ConfirmCleanUp() override;
/**
* @brief Applies ITK IntensityWindowing Filter to input image;
*
*/
template <typename TPixel, unsigned int VImageDimension>
void ITKWindowing(const itk::Image<TPixel, VImageDimension>*, mitk::Image*, ScalarType, ScalarType);
/**
* @brief Convert 3D world coordinates to 2D indices.
*
* @param baseGeometry Base Geometry of image
- * @param mitk::Point3D 3D world coordinates
- * @return mitk::Point2D
+ * @param point3d 3D world coordinates
*/
static mitk::Point2D Get2DIndicesfrom3DWorld(const mitk::BaseGeometry *baseGeometry, const mitk::Point3D &point3d);
std::unique_ptr<SegmentAnythingPythonService> m_PythonService;
private:
std::string m_MitkTempDir;
std::string m_PythonPath;
std::string m_ModelType;
std::string m_CheckpointPath;
std::string m_Backend;
int m_GpuId = 0;
PointSet::Pointer m_PointSetPositive;
PointSet::Pointer m_PointSetNegative;
DataNode::Pointer m_PointSetNodePositive;
DataNode::Pointer m_PointSetNodeNegative;
bool m_IsGenerateEmbeddings = true;
bool m_IsReady = false;
int m_PointSetCount = 0;
long m_TimeOutLimit = -1;
const Label::PixelType MASK_VALUE = 1;
};
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.cpp b/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.cpp
index 9d93d41ccf..fe4f34e5e6 100644
--- a/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.cpp
+++ b/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.cpp
@@ -1,349 +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.
============================================================================*/
// MITK
#include "mitkTotalSegmentatorTool.h"
#include <mitkIOUtil.h>
#include <mitkImageReadAccessor.h>
#include <algorithm>
-#include <filesystem>
+#include <mitkFileSystem.h>
#include <itksys/SystemTools.hxx>
#include <regex>
// us
#include <usGetModuleContext.h>
#include <usModule.h>
#include <usModuleContext.h>
#include <usModuleResource.h>
#include <usServiceReference.h>
namespace mitk
{
MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, TotalSegmentatorTool, "Total Segmentator");
}
mitk::TotalSegmentatorTool::~TotalSegmentatorTool()
{
- std::filesystem::remove_all(this->GetMitkTempDir());
+ fs::remove_all(this->GetMitkTempDir());
}
mitk::TotalSegmentatorTool::TotalSegmentatorTool() : SegWithPreviewTool(true) // prevents auto-compute across all timesteps
{
this->IsTimePointChangeAwareOff();
this->RequestDeactivationConfirmationOn();
}
void mitk::TotalSegmentatorTool::Activated()
{
Superclass::Activated();
this->SetLabelTransferScope(LabelTransferScope::AllLabels);
this->SetLabelTransferMode(LabelTransferMode::AddLabel);
}
const char **mitk::TotalSegmentatorTool::GetXPM() const
{
return nullptr;
}
us::ModuleResource mitk::TotalSegmentatorTool::GetIconResource() const
{
us::Module *module = us::GetModuleContext()->GetModule();
us::ModuleResource resource = module->GetResource("AI.svg");
return resource;
}
const char *mitk::TotalSegmentatorTool::GetName() const
{
return "TotalSegmentator";
}
void mitk::TotalSegmentatorTool::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;
}
}
void mitk::TotalSegmentatorTool::DoUpdatePreview(const Image *inputAtTimeStep,
const Image * /*oldSegAtTimeStep*/,
LabelSetImage *previewImage,
TimeStepType timeStep)
{
if (this->m_MitkTempDir.empty())
{
this->SetMitkTempDir(IOUtil::CreateTemporaryDirectory("mitk-XXXXXX"));
}
ProcessExecutor::Pointer spExec = ProcessExecutor::New();
itk::CStyleCommand::Pointer spCommand = itk::CStyleCommand::New();
spCommand->SetCallback(&onPythonProcessEvent);
spExec->AddObserver(ExternalProcessOutputEvent(), spCommand);
m_ProgressCommand->SetProgress(5);
std::string inDir, outDir, inputImagePath, outputImagePath, scriptPath;
inDir = IOUtil::CreateTemporaryDirectory("totalseg-in-XXXXXX", this->GetMitkTempDir());
std::ofstream tmpStream;
inputImagePath = IOUtil::CreateTemporaryFile(tmpStream, TEMPLATE_FILENAME, 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("_"));
outDir = IOUtil::CreateTemporaryDirectory("totalseg-out-XXXXXX", this->GetMitkTempDir());
LabelSetImage::Pointer outputBuffer;
m_ProgressCommand->SetProgress(20);
IOUtil::Save(inputAtTimeStep, inputImagePath);
m_ProgressCommand->SetProgress(50);
outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz";
- const bool isSubTask = (this->GetSubTask() != DEFAULT_TOTAL_TASK);
+ const bool isSubTask = (this->GetSubTask() != DEFAULT_TOTAL_TASK) && (this->GetSubTask() != DEFAULT_TOTAL_TASK_MRI);
if (isSubTask)
{
outputImagePath = outDir;
this->run_totalsegmentator(
spExec, inputImagePath, outputImagePath, !isSubTask, !isSubTask, this->GetGpuId(), this->GetSubTask());
// Construct Label Id map
std::vector<std::string> files = SUBTASKS_MAP.at(this->GetSubTask());
// Agglomerate individual mask files into one multi-label image.
std::for_each(files.begin(),
files.end(),
[&](std::string &fileName) { fileName = (outDir + IOUtil::GetDirectorySeparator() + fileName); });
outputBuffer = AgglomerateLabelFiles(files, inputAtTimeStep->GetDimensions(), inputAtTimeStep->GetGeometry());
}
else
{
this->run_totalsegmentator(
- spExec, inputImagePath, outputImagePath, this->GetFast(), !isSubTask, this->GetGpuId(), DEFAULT_TOTAL_TASK);
+ spExec, inputImagePath, outputImagePath, this->GetFast(), !isSubTask, this->GetGpuId(), this->GetSubTask());
Image::Pointer outputImage = IOUtil::Load<Image>(outputImagePath);
outputBuffer = mitk::LabelSetImage::New();
outputBuffer->InitializeByLabeledImage(outputImage);
outputBuffer->SetGeometry(inputAtTimeStep->GetGeometry());
}
m_ProgressCommand->SetProgress(180);
mitk::ImageReadAccessor newMitkImgAcc(outputBuffer.GetPointer());
this->MapLabelsToSegmentation(outputBuffer, previewImage, m_LabelMapTotal);
previewImage->SetVolume(newMitkImgAcc.GetData(), timeStep);
}
void mitk::TotalSegmentatorTool::UpdatePrepare()
{
Superclass::UpdatePrepare();
auto preview = this->GetPreviewSegmentation();
preview->RemoveLabels(preview->GetAllLabelValues());
if (m_LabelMapTotal.empty())
{
this->ParseLabelMapTotalDefault();
}
- const bool isSubTask = (this->GetSubTask() != DEFAULT_TOTAL_TASK);
+ const bool isSubTask = (this->GetSubTask() != DEFAULT_TOTAL_TASK) && (this->GetSubTask() != DEFAULT_TOTAL_TASK_MRI);
if (isSubTask)
{
std::vector<std::string> files = SUBTASKS_MAP.at(this->GetSubTask());
m_LabelMapTotal.clear();
mitk::Label::PixelType labelId = 1;
for (auto const &file : files)
{
std::string labelName = file.substr(0, file.find('.'));
m_LabelMapTotal[labelId] = labelName;
labelId++;
}
}
}
mitk::LabelSetImage::Pointer mitk::TotalSegmentatorTool::AgglomerateLabelFiles(std::vector<std::string> &filePaths,
const unsigned int *dimensions,
mitk::BaseGeometry *geometry)
{
Label::PixelType labelId = 1;
auto aggloLabelImage = mitk::LabelSetImage::New();
auto initImage = mitk::Image::New();
initImage->Initialize(mitk::MakeScalarPixelType<mitk::Label::PixelType>(), 3, dimensions);
aggloLabelImage->Initialize(initImage);
aggloLabelImage->SetGeometry(geometry);
const auto layerIndex = aggloLabelImage->AddLayer();
aggloLabelImage->SetActiveLayer(layerIndex);
for (auto const &outputImagePath : filePaths)
{
double rgba[4];
aggloLabelImage->GetLookupTable()->GetTableValue(labelId, rgba);
mitk::Color color;
color.SetRed(rgba[0]);
color.SetGreen(rgba[1]);
color.SetBlue(rgba[2]);
auto label = mitk::Label::New();
label->SetName("object-" + std::to_string(labelId));
label->SetValue(labelId);
label->SetColor(color);
label->SetOpacity(rgba[3]);
aggloLabelImage->AddLabel(label, layerIndex, false, false);
Image::Pointer outputImage = IOUtil::Load<Image>(outputImagePath);
auto source = mitk::LabelSetImage::New();
source->InitializeByLabeledImage(outputImage);
source->SetGeometry(geometry);
mitk::TransferLabelContent(source, aggloLabelImage, aggloLabelImage->GetConstLabelsByValue(aggloLabelImage->GetLabelValuesByGroup(layerIndex)), 0, 0, false, {{1, labelId}});
labelId++;
}
return aggloLabelImage;
}
void mitk::TotalSegmentatorTool::run_totalsegmentator(ProcessExecutor* spExec,
const std::string &inputImagePath,
const std::string &outputImagePath,
bool isFast,
bool isMultiLabel,
unsigned int gpuId,
const std::string &subTask)
{
ProcessExecutor::ArgumentListType args;
std::string command = "TotalSegmentator";
#ifdef _WIN32
command += ".exe";
#endif
args.clear();
args.push_back("-i");
args.push_back(inputImagePath);
args.push_back("-o");
args.push_back(outputImagePath);
- if (subTask != DEFAULT_TOTAL_TASK)
+ if (subTask == DEFAULT_TOTAL_TASK_MRI)
+ {
+ args.push_back("--task");
+ args.push_back(subTask);
+ }
+ else if (subTask != DEFAULT_TOTAL_TASK)
{
args.push_back("-ta");
args.push_back(subTask);
}
if (isMultiLabel)
{
args.push_back("--ml");
}
if (isFast)
{
args.push_back("--fast");
}
try
{
std::string cudaEnv = "CUDA_VISIBLE_DEVICES=" + std::to_string(gpuId);
itksys::SystemTools::PutEnv(cudaEnv.c_str());
std::stringstream logStream;
for (const auto &arg : args)
logStream << arg << " ";
logStream << this->GetPythonPath();
MITK_INFO << logStream.str();
spExec->Execute(this->GetPythonPath(), command, args);
}
catch (const mitk::Exception &e)
{
MITK_ERROR << e.GetDescription();
return;
}
}
void mitk::TotalSegmentatorTool::ParseLabelMapTotalDefault()
{
if (!this->GetLabelMapPath().empty())
{
+ int start_line = 0, end_line = 0;
+ if (this->GetSubTask() == DEFAULT_TOTAL_TASK)
+ start_line = 111, end_line = 229;
+ else if (this->GetSubTask() == DEFAULT_TOTAL_TASK_MRI)
+ start_line = 231, end_line = 288;
std::regex sanitizer(R"([^A-Za-z0-9_])");
std::fstream newfile;
newfile.open(this->GetLabelMapPath(), ios::in);
std::stringstream buffer;
if (newfile.is_open())
{
int line = 0;
std::string temp;
while (std::getline(newfile, temp))
{
- if (line > 111 && line < 229)
+ if (line > start_line && line < end_line)
{
buffer << temp;
}
++line;
}
}
std::string key, val;
while (std::getline(std::getline(buffer, key, ':'), val, ','))
{
std::string sanitized = std::regex_replace(val, sanitizer, "");
m_LabelMapTotal[std::stoi(key)] = sanitized;
}
}
}
void mitk::TotalSegmentatorTool::MapLabelsToSegmentation(const mitk::LabelSetImage* source,
mitk::LabelSetImage* dest,
std::map<mitk::Label::PixelType, std::string> &labelMap)
{
auto lookupTable = mitk::LookupTable::New();
lookupTable->SetType(mitk::LookupTable::LookupTableType::MULTILABEL);
for (auto const &[key, val] : labelMap)
{
if (source->ExistLabel(key, source->GetActiveLayer()))
{
Label::Pointer label = Label::New(key, val);
std::array<double, 3> lookupTableColor;
lookupTable->GetColor(key, lookupTableColor.data());
Color color;
color.SetRed(lookupTableColor[0]);
color.SetGreen(lookupTableColor[1]);
color.SetBlue(lookupTableColor[2]);
label->SetColor(color);
dest->AddLabel(label, 0,false);
}
}
}
std::string mitk::TotalSegmentatorTool::GetLabelMapPath()
{
std::string pythonFileName;
- std::filesystem::path pathToLabelMap(this->GetPythonPath());
+ fs::path pathToLabelMap(this->GetPythonPath());
pathToLabelMap = pathToLabelMap.parent_path();
#ifdef _WIN32
pythonFileName = pathToLabelMap.string() + "/Lib/site-packages/totalsegmentator/map_to_binary.py";
#else
pathToLabelMap.append("lib");
- for (auto const &dir_entry : std::filesystem::directory_iterator{pathToLabelMap})
+ for (auto const &dir_entry : fs::directory_iterator{pathToLabelMap})
{
if (dir_entry.is_directory())
{
auto dirName = dir_entry.path().filename().string();
if (dirName.rfind("python", 0) == 0)
{
pathToLabelMap.append(dir_entry.path().filename().string());
break;
}
}
}
pythonFileName = pathToLabelMap.string() + "/site-packages/totalsegmentator/map_to_binary.py";
#endif
return pythonFileName;
}
diff --git a/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.h b/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.h
index 6d75fe8d34..3e86e95220 100644
--- a/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.h
+++ b/Modules/Segmentation/Interactions/mitkTotalSegmentatorTool.h
@@ -1,150 +1,151 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 MITKTOTALSEGMENTATORTOOL_H
#define MITKTOTALSEGMENTATORTOOL_H
#include "mitkSegWithPreviewTool.h"
#include <MitkSegmentationExports.h>
#include "mitkProcessExecutor.h"
namespace us
{
class ModuleResource;
}
namespace mitk
{
/**
\brief TotalSegmentator segmentation tool.
\ingroup Interaction
\ingroup ToolManagerEtAl
\warning Only to be instantiated by mitk::ToolManager.
*/
class MITKSEGMENTATION_EXPORT TotalSegmentatorTool : public SegWithPreviewTool
{
public:
mitkClassMacro(TotalSegmentatorTool, SegWithPreviewTool);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
const char *GetName() const override;
const char **GetXPM() const override;
us::ModuleResource GetIconResource() const override;
void Activated() override;
itkSetMacro(MitkTempDir, std::string);
itkGetConstMacro(MitkTempDir, std::string);
itkSetMacro(SubTask, std::string);
itkGetConstMacro(SubTask, std::string);
itkSetMacro(PythonPath, std::string);
itkGetConstMacro(PythonPath, std::string);
itkSetMacro(GpuId, unsigned int);
itkGetConstMacro(GpuId, unsigned int);
itkSetMacro(Fast, bool);
itkGetConstMacro(Fast, bool);
itkBooleanMacro(Fast);
/**
* @brief Static function to print out everything from itk::EventObject.
* Used as callback in mitk::ProcessExecutor object.
*
*/
static void onPythonProcessEvent(itk::Object *, const itk::EventObject &e, void *);
protected:
TotalSegmentatorTool();
~TotalSegmentatorTool();
/**
- * @brief Overriden method from the tool manager to execute the segmentation
+ * @brief Overridden method from the tool manager to execute the segmentation
* Implementation:
* 1. Creates temp directory, if not done already.
* 2. Parses Label names from map_to_binary.py for using later on.
* 3. Calls "run_totalsegmentator" method.
- * 4. Expects an output image to be saved in the temporary directory by the python proces. Loads it as
+ * 4. Expects an output image to be saved in the temporary directory by the python process. Loads it as
* LabelSetImage and sets to previewImage.
*
* @param inputAtTimeStep
* @param oldSegAtTimeStep
* @param previewImage
* @param timeStep
*/
void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) override;
void UpdatePrepare() override;
private:
/**
* @brief Runs Totalsegmentator python process with desired arguments
*
*/
void run_totalsegmentator(ProcessExecutor*, const std::string&, const std::string&, bool, bool, unsigned int, const std::string&);
/**
* @brief Applies the m_LabelMapTotal lookup table on the output segmentation LabelSetImage.
*
*/
void MapLabelsToSegmentation(const mitk::LabelSetImage*, mitk::LabelSetImage*, std::map<mitk::Label::PixelType, std::string>&);
/**
* @brief Parses map_to_binary.py file to extract label ids and names
* and stores as a map for reference in m_LabelMapTotal
*
*/
void ParseLabelMapTotalDefault();
/**
* @brief Get the Label Map Path from the virtual environment location
*
* @return std::string
*/
std::string GetLabelMapPath();
/**
* @brief Agglomerate many individual mask image files into one multi-label LabelSetImage in the
* given filePath order.
*
* @param filePaths
* @param dimension
* @param geometry
* @return LabelSetImage::Pointer
*/
LabelSetImage::Pointer AgglomerateLabelFiles(std::vector<std::string>& filePaths, const unsigned int* dimension, mitk::BaseGeometry* geometry);
std::string m_MitkTempDir;
std::string m_PythonPath;
std::string m_SubTask = "total";
unsigned int m_GpuId = 0;
std::map<mitk::Label::PixelType, std::string> m_LabelMapTotal;
bool m_Fast = true;
const std::string TEMPLATE_FILENAME = "XXXXXX_000_0000.nii.gz";
const std::string DEFAULT_TOTAL_TASK = "total";
+ const std::string DEFAULT_TOTAL_TASK_MRI = "total_mr";
const std::unordered_map<std::string, std::vector<std::string>> SUBTASKS_MAP =
{
{"body", { "body.nii.gz", "body_trunc.nii.gz", "body_extremities.nii.gz", "skin.nii.gz"}},
{"hip_implant", {"hip_implant.nii.gz"}},
{"cerebral_bleed", {"intracerebral_hemorrhage.nii.gz"}},
{"coronary_arteries", {"coronary_arteries.nii.gz"}},
{"lung_vessels", {"lung_vessels.nii.gz", "lung_trachea_bronchia.nii.gz"}},
{"pleural_pericard_effusion", {"pleural_effusion.nii.gz", "pericardial_effusion.nii.gz"}}
};
}; // class
} // namespace
#endif
diff --git a/Modules/Segmentation/Interactions/mitknnUnetTool.cpp b/Modules/Segmentation/Interactions/mitknnUnetTool.cpp
index 6af435b28e..2bbb425592 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>
-#include <filesystem>
+#include <mitkFileSystem.h>
namespace mitk
{
MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, nnUNetTool, "nnUNet tool");
}
mitk::nnUNetTool::~nnUNetTool()
{
- std::filesystem::remove_all(this->GetMitkTempDir());
+ fs::remove_all(this->GetMitkTempDir());
}
void mitk::nnUNetTool::Activated()
{
Superclass::Activated();
this->SetLabelTransferScope(LabelTransferScope::AllLabels);
this->SetLabelTransferMode(LabelTransferMode::AddLabel);
}
void mitk::nnUNetTool::RenderOutputBuffer()
{
if (m_OutputBuffer != nullptr)
{
try
{
if (nullptr != this->GetPreviewSegmentationNode())
{
auto previewImage = this->GetPreviewSegmentation();
previewImage->InitializeByLabeledImage(m_OutputBuffer);
}
}
catch (const mitk::Exception &e)
{
MITK_INFO << e.GetDescription();
}
}
}
void mitk::nnUNetTool::SetOutputBuffer(LabelSetImage::Pointer segmentation)
{
m_OutputBuffer = segmentation;
}
mitk::LabelSetImage::Pointer mitk::nnUNetTool::GetOutputBuffer()
{
return m_OutputBuffer;
}
void mitk::nnUNetTool::ClearOutputBuffer()
{
m_OutputBuffer = nullptr;
}
us::ModuleResource mitk::nnUNetTool::GetIconResource() const
{
us::Module *module = us::GetModuleContext()->GetModule();
us::ModuleResource resource = module->GetResource("AI.svg");
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);
}
void mitk::nnUNetTool::UpdatePrepare()
{
Superclass::UpdatePrepare();
auto preview = this->GetPreviewSegmentation();
preview->RemoveLabels(preview->GetLabelValuesByGroup(preview->GetActiveLayer()));
}
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
void mitk::nnUNetTool::DoUpdatePreview(const Image* inputAtTimeStep, const Image* /*oldSegAtTimeStep*/, LabelSetImage* previewImage, TimeStepType /*timeStep*/)
{
if (this->GetMitkTempDir().empty())
{
this->SetMitkTempDir(IOUtil::CreateTemporaryDirectory("mitk-nnunet-XXXXXX"));
}
std::string inDir, outDir, inputImagePath, outputImagePath, scriptPath;
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, m_TEMPLATE_FILENAME, 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
{
if (this->GetMultiModal())
{
const std::string fileFormat(".nii.gz");
const std::string fileNamePart("_000_000");
std::string outModalFile;
size_t len = inDir.length() + 1 + token.length() + fileNamePart.length() + 1 + fileFormat.length();
outModalFile.reserve(len); // The 1(s) indicates a directory separator char and an underscore.
for (size_t i = 0; i < m_OtherModalPaths.size(); ++i)
{
mitk::Image::ConstPointer modalImage = m_OtherModalPaths[i];
outModalFile.append(inDir);
outModalFile.push_back(IOUtil::GetDirectorySeparator());
outModalFile.append(token);
outModalFile.append(fileNamePart);
outModalFile.append(std::to_string(i));
outModalFile.append(fileFormat);
IOUtil::Save(modalImage.GetPointer(), outModalFile);
outModalFile.clear();
}
}
else
{
IOUtil::Save(inputAtTimeStep, inputImagePath);
}
}
catch (const mitk::Exception &e)
{
MITK_ERROR << e.GetDescription();
return;
}
// 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())
{
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;
}
}
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);
if (!this->GetPostProcessingJsonDirectory().empty())
{
args.push_back("-pp");
args.push_back(this->GetPostProcessingJsonDirectory());
}
spExec->Execute(this->GetPythonPath(), command, args);
}
try
{
Image::Pointer outputImage = IOUtil::Load<Image>(outputImagePath);
previewImage->InitializeByLabeledImage(outputImage);
previewImage->SetGeometry(inputAtTimeStep->GetGeometry());
m_InputBuffer = inputAtTimeStep;
m_OutputBuffer = mitk::LabelSetImage::New();
m_OutputBuffer->InitializeByLabeledImage(outputImage);
m_OutputBuffer->SetGeometry(inputAtTimeStep->GetGeometry());
}
catch (const mitk::Exception &e)
{
MITK_ERROR << e.GetDescription();
return;
}
}
diff --git a/Modules/Segmentation/Interactions/mitknnUnetTool.h b/Modules/Segmentation/Interactions/mitknnUnetTool.h
index e6e3fc9bb1..8172cca4b7 100644
--- a/Modules/Segmentation/Interactions/mitknnUnetTool.h
+++ b/Modules/Segmentation/Interactions/mitknnUnetTool.h
@@ -1,215 +1,215 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitknnUnetTool_h
#define mitknnUnetTool_h
#include "mitkSegWithPreviewTool.h"
#include "mitkCommon.h"
#include "mitkToolManager.h"
#include <MitkSegmentationExports.h>
#include <mitkStandardFileLocations.h>
#include <numeric>
#include <utility>
namespace us
{
class ModuleResource;
}
namespace mitk
{
/**
* @brief nnUNet parameter request object holding all model parameters for input.
* Also holds output temporary directory path.
*/
struct ModelParams
{
std::string task;
std::vector<std::string> folds;
std::string model;
std::string trainer;
std::string planId;
std::string outputDir;
std::string inputName;
std::string timeStamp;
size_t generateHash() const
{
std::string toHash;
std::string foldsConcatenated = std::accumulate(folds.begin(), folds.end(), std::string(""));
toHash += this->task;
toHash += this->model;
toHash += this->inputName;
toHash += foldsConcatenated;
toHash += this->timeStamp;
size_t hashVal = std::hash<std::string>{}(toHash);
return hashVal;
}
};
/**
\brief nnUNet segmentation tool.
\ingroup Interaction
\ingroup ToolManagerEtAl
\warning Only to be instantiated by mitk::ToolManager.
*/
class MITKSEGMENTATION_EXPORT nnUNetTool : public SegWithPreviewTool
{
public:
mitkClassMacro(nnUNetTool, SegWithPreviewTool);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
const char **GetXPM() const override;
const char *GetName() const override;
us::ModuleResource GetIconResource() const override;
void Activated() override;
itkSetMacro(nnUNetDirectory, std::string);
itkGetConstMacro(nnUNetDirectory, std::string);
itkSetMacro(ModelDirectory, std::string);
itkGetConstMacro(ModelDirectory, std::string);
itkSetMacro(PythonPath, std::string);
itkGetConstMacro(PythonPath, std::string);
itkSetMacro(MitkTempDir, std::string);
itkGetConstMacro(MitkTempDir, std::string);
itkSetMacro(PostProcessingJsonDirectory, std::string);
itkGetConstMacro(PostProcessingJsonDirectory, std::string);
itkSetMacro(MixedPrecision, bool);
itkGetConstMacro(MixedPrecision, bool);
itkBooleanMacro(MixedPrecision);
itkSetMacro(Mirror, bool);
itkGetConstMacro(Mirror, bool);
itkBooleanMacro(Mirror);
itkSetMacro(MultiModal, bool);
itkGetConstMacro(MultiModal, bool);
itkBooleanMacro(MultiModal);
itkSetMacro(NoPip, bool);
itkGetConstMacro(NoPip, bool);
itkBooleanMacro(NoPip);
itkSetMacro(Ensemble, bool);
itkGetConstMacro(Ensemble, bool);
itkBooleanMacro(Ensemble);
itkSetMacro(Predict, bool);
itkGetConstMacro(Predict, bool);
itkBooleanMacro(Predict);
itkSetMacro(GpuId, unsigned int);
itkGetConstMacro(GpuId, unsigned int);
/**
* @brief vector of ModelParams.
* Size > 1 only for ensemble prediction.
*/
std::vector<ModelParams> m_ParamQ;
/**
* @brief Holds paths to other input image modalities.
*
*/
std::vector<mitk::Image::ConstPointer> m_OtherModalPaths;
mitk::Image::ConstPointer m_InputBuffer;
/**
* @brief Renders the output LabelSetImage.
* To called in the main thread.
*/
void RenderOutputBuffer();
/**
* @brief Get the Output Buffer object
*
* @return LabelSetImage::Pointer
*/
LabelSetImage::Pointer GetOutputBuffer();
/**
* @brief Sets the outputBuffer to nullptr
*
*/
void ClearOutputBuffer();
/**
* @brief Returns the DataStorage from the ToolManager
*/
mitk::DataStorage *GetDataStorage();
mitk::DataNode *GetRefNode();
void SetOutputBuffer(LabelSetImage::Pointer);
protected:
/**
* @brief Construct a new nnUNet Tool object.
*
*/
nnUNetTool() = default;
/**
* @brief Destroy the nnUNet Tool object and deletes the temp directory.
*
*/
~nnUNetTool();
/**
- * @brief Overriden method from the tool manager to execute the segmentation
+ * @brief Overridden method from the tool manager to execute the segmentation
* Implementation:
* 1. Saves the inputAtTimeStep in a temporary directory.
* 2. Copies other modalities, renames and saves in the temporary directory, if required.
* 3. Sets RESULTS_FOLDER and CUDA_VISIBLE_DEVICES variables in the environment.
* 3. Iterates through the parameter queue (m_ParamQ) and executes "nnUNet_predict" command with the parameters
- * 4. Expects an output image to be saved in the temporary directory by the python proces. Loads it as
+ * 4. Expects an output image to be saved in the temporary directory by the python process. Loads it as
* LabelSetImage and sets to previewImage.
*
* @param inputAtTimeStep
* @param oldSegAtTimeStep
* @param previewImage
* @param timeStep
*/
void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) override;
void UpdatePrepare() override;
private:
std::string m_MitkTempDir;
std::string m_nnUNetDirectory;
std::string m_ModelDirectory;
std::string m_PythonPath;
std::string m_PostProcessingJsonDirectory;
// bool m_UseGPU; kept for future
// bool m_AllInGPU;
bool m_MixedPrecision;
bool m_Mirror;
bool m_NoPip;
bool m_MultiModal;
bool m_Ensemble = false;
bool m_Predict;
LabelSetImage::Pointer m_OutputBuffer;
unsigned int m_GpuId;
const std::string m_TEMPLATE_FILENAME = "XXXXXX_000_0000.nii.gz";
};
} // namespace mitk
#endif
diff --git a/Modules/Segmentation/Rendering/mitkContourMapper2D.h b/Modules/Segmentation/Rendering/mitkContourMapper2D.h
index 568a7b4b12..cedd39905a 100644
--- a/Modules/Segmentation/Rendering/mitkContourMapper2D.h
+++ b/Modules/Segmentation/Rendering/mitkContourMapper2D.h
@@ -1,60 +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 mitkContourMapper2D_h
#define mitkContourMapper2D_h
#include "mitkCommon.h"
#include "mitkMapper.h"
#include <MitkSegmentationExports.h>
namespace mitk
{
class BaseRenderer;
class Contour;
/**
* @brief OpenGL-based mapper to display a mitk::Contour object in a 2D render window
*
*
* @ingroup Mapper
*/
class MITKSEGMENTATION_EXPORT ContourMapper2D : public Mapper
{
public:
mitkClassMacro(ContourMapper2D, Mapper);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* reimplemented from Baseclass
*/
void MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) override;
void ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * actor=nullptr) override;
/**
- * return a refernce of the rendered data object
+ * return a reference of the rendered data object
*/
const Contour *GetInput(void);
protected:
ContourMapper2D();
~ContourMapper2D() override;
};
} // namespace mitk
#endif
diff --git a/Modules/Segmentation/Rendering/mitkContourSetMapper2D.h b/Modules/Segmentation/Rendering/mitkContourSetMapper2D.h
index a913f2137b..d4692fcfa5 100644
--- a/Modules/Segmentation/Rendering/mitkContourSetMapper2D.h
+++ b/Modules/Segmentation/Rendering/mitkContourSetMapper2D.h
@@ -1,60 +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 mitkContourSetMapper2D_h
#define mitkContourSetMapper2D_h
#include "mitkCommon.h"
#include "mitkGLMapper.h"
#include <MitkSegmentationExports.h>
namespace mitk
{
class BaseRenderer;
class ContourSet;
/**
* @brief OpenGL-based mapper to display a mitk::Contour object in a 2D render window
*
*
* @ingroup Mapper
*/
class MITKSEGMENTATION_EXPORT ContourSetMapper2D : public Mapper
{
public:
mitkClassMacro(ContourSetMapper2D, Mapper);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
/**
* reimplemented from Baseclass
*/
void MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) override;
void ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * actor = nullptr) override;
/**
- * return a refernce of the rendered data object
+ * return a reference of the rendered data object
*/
const mitk::ContourSet *GetInput(void);
protected:
ContourSetMapper2D();
~ContourSetMapper2D() override;
};
} // namespace mitk
#endif
diff --git a/Modules/Segmentation/SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp b/Modules/Segmentation/SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp
index 6c45ebb90b..3088a4af05 100644
--- a/Modules/Segmentation/SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp
+++ b/Modules/Segmentation/SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp
@@ -1,414 +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 "mitkMorphologicalOperations.h"
#include <itkBinaryBallStructuringElement.h>
#include <itkBinaryCrossStructuringElement.h>
#include <itkBinaryDilateImageFilter.h>
#include <itkBinaryErodeImageFilter.h>
#include <itkBinaryFillholeImageFilter.h>
#include <itkBinaryMorphologicalClosingImageFilter.h>
#include <itkBinaryMorphologicalOpeningImageFilter.h>
#include <mitkImageAccessByItk.h>
#include <mitkImageCast.h>
#include <mitkImageReadAccessor.h>
#include <mitkImageTimeSelector.h>
void mitk::MorphologicalOperations::Closing(mitk::Image::Pointer &image,
int factor,
mitk::MorphologicalOperations::StructuralElementType structuralElement)
{
MITK_INFO << "Start Closing...";
auto timeSteps = static_cast<int>(image->GetTimeSteps());
if (timeSteps > 1)
{
mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New();
timeSelector->SetInput(image);
for (int t = 0; t < timeSteps; ++t)
{
MITK_INFO << " Processing time step " << t;
timeSelector->SetTimeNr(t);
timeSelector->Update();
mitk::Image::Pointer img3D = timeSelector->GetOutput();
img3D->DisconnectPipeline();
- AccessByItk_3(img3D, itkClosing, img3D, factor, structuralElement);
+ AccessFixedDimensionByItk_3(img3D, itkClosing, 3, img3D, factor, structuralElement);
mitk::ImageReadAccessor accessor(img3D);
image->SetVolume(accessor.GetData(), t);
}
}
else
{
- AccessByItk_3(image, itkClosing, image, factor, structuralElement);
+ AccessFixedDimensionByItk_3(image, itkClosing, 3, image, factor, structuralElement);
}
MITK_INFO << "Finished Closing";
}
void mitk::MorphologicalOperations::Erode(mitk::Image::Pointer &image,
int factor,
mitk::MorphologicalOperations::StructuralElementType structuralElement)
{
MITK_INFO << "Start Erode...";
auto timeSteps = static_cast<int>(image->GetTimeSteps());
if (timeSteps > 1)
{
mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New();
timeSelector->SetInput(image);
for (int t = 0; t < timeSteps; ++t)
{
MITK_INFO << " Processing time step " << t;
timeSelector->SetTimeNr(t);
timeSelector->Update();
mitk::Image::Pointer img3D = timeSelector->GetOutput();
img3D->DisconnectPipeline();
- AccessByItk_3(img3D, itkErode, img3D, factor, structuralElement);
+ AccessFixedDimensionByItk_3(img3D, itkErode, 3, img3D, factor, structuralElement);
mitk::ImageReadAccessor accessor(img3D);
image->SetVolume(accessor.GetData(), t);
}
}
else
{
- AccessByItk_3(image, itkErode, image, factor, structuralElement);
+ AccessFixedDimensionByItk_3(image, itkErode, 3, image, factor, structuralElement);
}
MITK_INFO << "Finished Erode";
}
void mitk::MorphologicalOperations::Dilate(mitk::Image::Pointer &image,
int factor,
mitk::MorphologicalOperations::StructuralElementType structuralElement)
{
MITK_INFO << "Start Dilate...";
auto timeSteps = static_cast<int>(image->GetTimeSteps());
if (timeSteps > 1)
{
mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New();
timeSelector->SetInput(image);
for (int t = 0; t < timeSteps; ++t)
{
MITK_INFO << " Processing time step " << t;
timeSelector->SetTimeNr(t);
timeSelector->Update();
mitk::Image::Pointer img3D = timeSelector->GetOutput();
img3D->DisconnectPipeline();
- AccessByItk_3(img3D, itkDilate, img3D, factor, structuralElement);
+ AccessFixedDimensionByItk_3(img3D, itkDilate, 3, img3D, factor, structuralElement);
mitk::ImageReadAccessor accessor(img3D);
image->SetVolume(accessor.GetData(), t);
}
}
else
{
- AccessByItk_3(image, itkDilate, image, factor, structuralElement);
+ AccessFixedDimensionByItk_3(image, itkDilate, 3, image, factor, structuralElement);
}
MITK_INFO << "Finished Dilate";
}
void mitk::MorphologicalOperations::Opening(mitk::Image::Pointer &image,
int factor,
mitk::MorphologicalOperations::StructuralElementType structuralElement)
{
MITK_INFO << "Start Opening...";
auto timeSteps = static_cast<int>(image->GetTimeSteps());
if (timeSteps > 1)
{
mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New();
timeSelector->SetInput(image);
for (int t = 0; t < timeSteps; ++t)
{
MITK_INFO << " Processing time step " << t;
timeSelector->SetTimeNr(t);
timeSelector->Update();
mitk::Image::Pointer img3D = timeSelector->GetOutput();
img3D->DisconnectPipeline();
- AccessByItk_3(img3D, itkOpening, img3D, factor, structuralElement);
+ AccessFixedDimensionByItk_3(img3D, itkOpening, 3, img3D, factor, structuralElement);
mitk::ImageReadAccessor accessor(img3D);
image->SetVolume(accessor.GetData(), t);
}
}
else
{
- AccessByItk_3(image, itkOpening, image, factor, structuralElement);
+ AccessFixedDimensionByItk_3(image, itkOpening, 3, image, factor, structuralElement);
}
MITK_INFO << "Finished Opening";
}
void mitk::MorphologicalOperations::FillHoles(mitk::Image::Pointer &image)
{
MITK_INFO << "Start FillHole...";
auto timeSteps = static_cast<int>(image->GetTimeSteps());
if (timeSteps > 1)
{
mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New();
timeSelector->SetInput(image);
for (int t = 0; t < timeSteps; ++t)
{
MITK_INFO << " Processing time step " << t;
timeSelector->SetTimeNr(t);
timeSelector->Update();
mitk::Image::Pointer img3D = timeSelector->GetOutput();
img3D->DisconnectPipeline();
- AccessByItk_1(img3D, itkFillHoles, img3D);
+ AccessFixedDimensionByItk_1(img3D, itkFillHoles, 3, img3D);
mitk::ImageReadAccessor accessor(img3D);
image->SetVolume(accessor.GetData(), t);
}
}
else
{
- AccessByItk_1(image, itkFillHoles, image);
+ AccessFixedDimensionByItk_1(image, itkFillHoles, 3, image);
}
MITK_INFO << "Finished FillHole";
}
template <typename TPixel, unsigned int VDimension>
void mitk::MorphologicalOperations::itkClosing(
itk::Image<TPixel, VDimension> *sourceImage,
mitk::Image::Pointer &resultImage,
int factor,
mitk::MorphologicalOperations::StructuralElementType structuralElementFlags)
{
typedef itk::Image<TPixel, VDimension> ImageType;
typedef itk::BinaryBallStructuringElement<TPixel, VDimension> BallType;
typedef itk::BinaryCrossStructuringElement<TPixel, VDimension> CrossType;
typedef typename itk::BinaryMorphologicalClosingImageFilter<ImageType, ImageType, BallType> BallClosingFilterType;
typedef typename itk::BinaryMorphologicalClosingImageFilter<ImageType, ImageType, CrossType> CrossClosingFilterType;
if (structuralElementFlags & (Ball_Axial | Ball_Coronal | Ball_Sagittal))
{
BallType ball = CreateStructuringElement<BallType>(structuralElementFlags, factor);
typename BallClosingFilterType::Pointer closingFilter = BallClosingFilterType::New();
closingFilter->SetKernel(ball);
closingFilter->SetInput(sourceImage);
closingFilter->SetForegroundValue(1);
closingFilter->UpdateLargestPossibleRegion();
resultImage->SetVolume(closingFilter->GetOutput()->GetBufferPointer());
}
else
{
CrossType cross = CreateStructuringElement<CrossType>(structuralElementFlags, factor);
typename CrossClosingFilterType::Pointer closingFilter = CrossClosingFilterType::New();
closingFilter->SetKernel(cross);
closingFilter->SetInput(sourceImage);
closingFilter->SetForegroundValue(1);
closingFilter->UpdateLargestPossibleRegion();
resultImage->SetVolume(closingFilter->GetOutput()->GetBufferPointer());
}
}
template <typename TPixel, unsigned int VDimension>
void mitk::MorphologicalOperations::itkErode(
itk::Image<TPixel, VDimension> *sourceImage,
mitk::Image::Pointer &resultImage,
int factor,
mitk::MorphologicalOperations::StructuralElementType structuralElementFlags)
{
typedef itk::Image<TPixel, VDimension> ImageType;
typedef itk::BinaryBallStructuringElement<TPixel, VDimension> BallType;
typedef itk::BinaryCrossStructuringElement<TPixel, VDimension> CrossType;
typedef typename itk::BinaryErodeImageFilter<ImageType, ImageType, BallType> BallErodeFilterType;
typedef typename itk::BinaryErodeImageFilter<ImageType, ImageType, CrossType> CrossErodeFilterType;
if (structuralElementFlags & (Ball_Axial | Ball_Coronal | Ball_Sagittal))
{
BallType ball = CreateStructuringElement<BallType>(structuralElementFlags, factor);
typename BallErodeFilterType::Pointer erodeFilter = BallErodeFilterType::New();
erodeFilter->SetKernel(ball);
erodeFilter->SetInput(sourceImage);
erodeFilter->SetErodeValue(1);
erodeFilter->UpdateLargestPossibleRegion();
resultImage->SetVolume(erodeFilter->GetOutput()->GetBufferPointer());
}
else
{
CrossType cross = CreateStructuringElement<CrossType>(structuralElementFlags, factor);
typename CrossErodeFilterType::Pointer erodeFilter = CrossErodeFilterType::New();
erodeFilter->SetKernel(cross);
erodeFilter->SetInput(sourceImage);
erodeFilter->SetErodeValue(1);
erodeFilter->UpdateLargestPossibleRegion();
resultImage->SetVolume(erodeFilter->GetOutput()->GetBufferPointer());
}
}
template <typename TPixel, unsigned int VDimension>
void mitk::MorphologicalOperations::itkDilate(
itk::Image<TPixel, VDimension> *sourceImage,
mitk::Image::Pointer &resultImage,
int factor,
mitk::MorphologicalOperations::StructuralElementType structuralElementFlags)
{
typedef itk::Image<TPixel, VDimension> ImageType;
typedef itk::BinaryBallStructuringElement<TPixel, VDimension> BallType;
typedef itk::BinaryCrossStructuringElement<TPixel, VDimension> CrossType;
typedef typename itk::BinaryDilateImageFilter<ImageType, ImageType, BallType> BallDilateFilterType;
typedef typename itk::BinaryDilateImageFilter<ImageType, ImageType, CrossType> CrossDilateFilterType;
if (structuralElementFlags & (Ball_Axial | Ball_Coronal | Ball_Sagittal))
{
BallType ball = CreateStructuringElement<BallType>(structuralElementFlags, factor);
typename BallDilateFilterType::Pointer dilateFilter = BallDilateFilterType::New();
dilateFilter->SetKernel(ball);
dilateFilter->SetInput(sourceImage);
dilateFilter->SetDilateValue(1);
dilateFilter->UpdateLargestPossibleRegion();
resultImage->SetVolume(dilateFilter->GetOutput()->GetBufferPointer());
}
else
{
CrossType cross = CreateStructuringElement<CrossType>(structuralElementFlags, factor);
typename CrossDilateFilterType::Pointer dilateFilter = CrossDilateFilterType::New();
dilateFilter->SetKernel(cross);
dilateFilter->SetInput(sourceImage);
dilateFilter->SetDilateValue(1);
dilateFilter->UpdateLargestPossibleRegion();
resultImage->SetVolume(dilateFilter->GetOutput()->GetBufferPointer());
}
}
template <typename TPixel, unsigned int VDimension>
void mitk::MorphologicalOperations::itkOpening(
itk::Image<TPixel, VDimension> *sourceImage,
mitk::Image::Pointer &resultImage,
int factor,
mitk::MorphologicalOperations::StructuralElementType structuralElementFlags)
{
typedef itk::Image<TPixel, VDimension> ImageType;
typedef itk::BinaryBallStructuringElement<TPixel, VDimension> BallType;
typedef itk::BinaryCrossStructuringElement<TPixel, VDimension> CrossType;
typedef typename itk::BinaryMorphologicalOpeningImageFilter<ImageType, ImageType, BallType> BallOpeningFiltertype;
typedef typename itk::BinaryMorphologicalOpeningImageFilter<ImageType, ImageType, CrossType> CrossOpeningFiltertype;
if (structuralElementFlags & (Ball_Axial | Ball_Coronal | Ball_Sagittal))
{
BallType ball = CreateStructuringElement<BallType>(structuralElementFlags, factor);
typename BallOpeningFiltertype::Pointer openingFilter = BallOpeningFiltertype::New();
openingFilter->SetKernel(ball);
openingFilter->SetInput(sourceImage);
openingFilter->SetForegroundValue(1);
openingFilter->SetBackgroundValue(0);
openingFilter->UpdateLargestPossibleRegion();
resultImage->SetVolume(openingFilter->GetOutput()->GetBufferPointer());
}
else
{
CrossType cross = CreateStructuringElement<CrossType>(structuralElementFlags, factor);
typename CrossOpeningFiltertype::Pointer openingFilter = CrossOpeningFiltertype::New();
openingFilter->SetKernel(cross);
openingFilter->SetInput(sourceImage);
openingFilter->SetForegroundValue(1);
openingFilter->SetBackgroundValue(0);
openingFilter->UpdateLargestPossibleRegion();
resultImage->SetVolume(openingFilter->GetOutput()->GetBufferPointer());
}
}
template <typename TPixel, unsigned int VDimension>
void mitk::MorphologicalOperations::itkFillHoles(itk::Image<TPixel, VDimension> *sourceImage,
mitk::Image::Pointer &resultImage)
{
typedef itk::Image<TPixel, VDimension> ImageType;
typedef typename itk::BinaryFillholeImageFilter<ImageType> FillHoleFilterType;
typename FillHoleFilterType::Pointer fillHoleFilter = FillHoleFilterType::New();
fillHoleFilter->SetInput(sourceImage);
fillHoleFilter->SetForegroundValue(1);
fillHoleFilter->UpdateLargestPossibleRegion();
resultImage->SetVolume(fillHoleFilter->GetOutput()->GetBufferPointer());
}
template <class TStructuringElement>
TStructuringElement mitk::MorphologicalOperations::CreateStructuringElement(StructuralElementType structuralElementFlag, int factor)
{
+ using SizeType = typename TStructuringElement::SizeType;
+ static_assert(SizeType::Dimension == 3);
+
TStructuringElement strElem;
- typename TStructuringElement::SizeType size;
+ SizeType size;
+
size.Fill(0);
switch (structuralElementFlag)
{
case Ball_Axial:
case Cross_Axial:
size.SetElement(0, factor);
size.SetElement(1, factor);
break;
case Ball_Coronal:
case Cross_Coronal:
size.SetElement(0, factor);
size.SetElement(2, factor);
break;
case Ball_Sagittal:
case Cross_Sagittal:
size.SetElement(1, factor);
size.SetElement(2, factor);
break;
case Ball:
case Cross:
size.Fill(factor);
break;
}
strElem.SetRadius(size);
strElem.CreateStructuringElement();
return strElem;
}
diff --git a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp
index e5839fa022..3e9277df26 100644
--- a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp
+++ b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.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 <mitkTestingMacros.h>
#include <mitkExtractSliceFilter.h>
#include <mitkGeometry3D.h>
#include <mitkImageCast.h>
#include <mitkImagePixelReadAccessor.h>
#include <mitkInteractionConst.h>
#include <mitkRotationOperation.h>
#include <mitkVtkImageOverwrite.h>
#include <itkImage.h>
#include <itkImageRegionIterator.h>
#include <vtkImageData.h>
#include <vtkSmartPointer.h>
int ObliquePlaneTestVolumeSize = 128;
static void OverwriteObliquePlaneTest(mitk::Image *workingImage, mitk::Image *refImg)
{
- /*==============TEST WITHOUT MITK CONVERTION=============================*/
+ /*==============TEST WITHOUT MITK CONVERSION=============================*/
/* ============= setup plane ============*/
auto sliceindex = (int)(ObliquePlaneTestVolumeSize / 2); // rand() % 32;
bool isFrontside = true;
bool isRotated = false;
mitk::PlaneGeometry::Pointer obliquePlane = mitk::PlaneGeometry::New();
obliquePlane->InitializeStandardPlane(
workingImage->GetGeometry(), mitk::AnatomicalPlane::Axial, sliceindex, isFrontside, isRotated);
mitk::Point3D origin = obliquePlane->GetOrigin();
mitk::Vector3D normal;
normal = obliquePlane->GetNormal();
normal.Normalize();
origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5
obliquePlane->SetOrigin(origin);
mitk::Vector3D rotationVector = obliquePlane->GetAxisVector(0);
rotationVector.Normalize();
float degree = 45.0;
auto op = new mitk::RotationOperation(mitk::OpROTATE, obliquePlane->GetCenter(), rotationVector, degree);
obliquePlane->ExecuteOperation(op);
delete op;
/* ============= extract slice ============*/
mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New();
slicer->SetInput(workingImage);
slicer->SetWorldGeometry(obliquePlane);
slicer->SetVtkOutputRequest(true);
slicer->Modified();
slicer->Update();
vtkSmartPointer<vtkImageData> slice = vtkSmartPointer<vtkImageData>::New();
slice = slicer->GetVtkOutput();
/* ============= overwrite slice ============*/
vtkSmartPointer<mitkVtkImageOverwrite> resliceIdx = vtkSmartPointer<mitkVtkImageOverwrite>::New();
mitk::ExtractSliceFilter::Pointer overwriter = mitk::ExtractSliceFilter::New(resliceIdx);
resliceIdx->SetOverwriteMode(true);
resliceIdx->SetInputSlice(slice);
resliceIdx->Modified();
overwriter->SetInput(workingImage);
overwriter->SetWorldGeometry(obliquePlane);
overwriter->SetVtkOutputRequest(true);
overwriter->Modified();
overwriter->Update();
typedef mitk::ImagePixelReadAccessor<unsigned short, 3> ReadAccessorType;
ReadAccessorType refImgReadAccessor(refImg);
ReadAccessorType workingImgReadAccessor(workingImage);
/* ============= check ref == working ============*/
bool areSame = true;
itk::Index<3> id;
id[0] = id[1] = id[2] = 0;
for (int x = 0; x < ObliquePlaneTestVolumeSize; ++x)
{
id[0] = x;
for (int y = 0; y < ObliquePlaneTestVolumeSize; ++y)
{
id[1] = y;
for (int z = 0; z < ObliquePlaneTestVolumeSize; ++z)
{
id[2] = z;
areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id);
if (!areSame)
goto stop;
}
}
}
stop:
- MITK_TEST_CONDITION(areSame, "comparing images (no mitk convertion) [oblique]");
+ MITK_TEST_CONDITION(areSame, "comparing images (no mitk conversion) [oblique]");
- /*==============TEST WITH MITK CONVERTION=============================*/
+ /*==============TEST WITH MITK CONVERSION=============================*/
/* ============= extract slice ============*/
mitk::ExtractSliceFilter::Pointer slicer2 = mitk::ExtractSliceFilter::New();
slicer2->SetInput(workingImage);
slicer2->SetWorldGeometry(obliquePlane);
slicer2->Modified();
slicer2->Update();
mitk::Image::Pointer sliceInMitk = slicer2->GetOutput();
vtkSmartPointer<vtkImageData> slice2 = vtkSmartPointer<vtkImageData>::New();
slice2 = sliceInMitk->GetVtkImageData();
/* ============= overwrite slice ============*/
vtkSmartPointer<mitkVtkImageOverwrite> resliceIdx2 = vtkSmartPointer<mitkVtkImageOverwrite>::New();
mitk::ExtractSliceFilter::Pointer overwriter2 = mitk::ExtractSliceFilter::New(resliceIdx2);
resliceIdx2->SetOverwriteMode(true);
resliceIdx2->SetInputSlice(slice2);
resliceIdx2->Modified();
overwriter2->SetInput(workingImage);
overwriter2->SetWorldGeometry(obliquePlane);
overwriter2->SetVtkOutputRequest(true);
overwriter2->Modified();
overwriter2->Update();
/* ============= check ref == working ============*/
areSame = true;
id[0] = id[1] = id[2] = 0;
for (int x = 0; x < ObliquePlaneTestVolumeSize; ++x)
{
id[0] = x;
for (int y = 0; y < ObliquePlaneTestVolumeSize; ++y)
{
id[1] = y;
for (int z = 0; z < ObliquePlaneTestVolumeSize; ++z)
{
id[2] = z;
areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id);
if (!areSame)
goto stop2;
}
}
}
stop2:
- MITK_TEST_CONDITION(areSame, "comparing images (with mitk convertion) [oblique]");
+ MITK_TEST_CONDITION(areSame, "comparing images (with mitk conversion) [oblique]");
- /*==============TEST EDIT WITHOUT MITK CONVERTION=============================*/
+ /*==============TEST EDIT WITHOUT MITK CONVERSION=============================*/
/* ============= edit slice ============*/
int idX = std::abs(ObliquePlaneTestVolumeSize - 59);
int idY = std::abs(ObliquePlaneTestVolumeSize - 23);
int idZ = 0;
int component = 0;
double val = 33.0;
slice->SetScalarComponentFromDouble(idX, idY, idZ, component, val);
mitk::Vector3D indx;
indx[0] = idX;
indx[1] = idY;
indx[2] = idZ;
sliceInMitk->GetGeometry()->IndexToWorld(indx, indx);
/* ============= overwrite slice ============*/
vtkSmartPointer<mitkVtkImageOverwrite> resliceIdx3 = vtkSmartPointer<mitkVtkImageOverwrite>::New();
resliceIdx3->SetOverwriteMode(true);
resliceIdx3->SetInputSlice(slice);
mitk::ExtractSliceFilter::Pointer overwriter3 = mitk::ExtractSliceFilter::New(resliceIdx3);
overwriter3->SetInput(workingImage);
overwriter3->SetWorldGeometry(obliquePlane);
overwriter3->SetVtkOutputRequest(true);
overwriter3->Modified();
overwriter3->Update();
/* ============= check ============*/
areSame = true;
int x = 0;
int y = 0;
int z = 0;
for (x = 0; x < ObliquePlaneTestVolumeSize; ++x)
{
id[0] = x;
for (y = 0; y < ObliquePlaneTestVolumeSize; ++y)
{
id[1] = y;
for (z = 0; z < ObliquePlaneTestVolumeSize; ++z)
{
id[2] = z;
areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id);
if (!areSame)
goto stop3;
}
}
}
stop3:
- MITK_TEST_CONDITION(x == idX && y == z, "overwrited the right index [oblique]");
+ MITK_TEST_CONDITION(x == idX && y == z, "overwrote the right index [oblique]");
}
/*================ #BEGIN test main ================*/
int mitkOverwriteSliceFilterObliquePlaneTest(int, char *[])
{
MITK_TEST_BEGIN("mitkOverwriteSliceFilterObliquePlaneTest")
typedef itk::Image<unsigned short, 3> ImageType;
typedef itk::ImageRegionConstIterator<ImageType> ImageIterator;
ImageType::Pointer image = ImageType::New();
ImageType::IndexType start;
start[0] = start[1] = start[2] = 0;
ImageType::SizeType size;
size[0] = size[1] = size[2] = ObliquePlaneTestVolumeSize;
ImageType::RegionType imgRegion;
imgRegion.SetSize(size);
imgRegion.SetIndex(start);
image->SetRegions(imgRegion);
image->SetSpacing(mitk::Vector(1.0));
image->Allocate();
ImageIterator imageIterator(image, image->GetLargestPossibleRegion());
imageIterator.GoToBegin();
unsigned short pixelValue = 0;
// fill the image with distinct values
while (!imageIterator.IsAtEnd())
{
image->SetPixel(imageIterator.GetIndex(), pixelValue);
++imageIterator;
++pixelValue;
}
/* end setup itk image */
mitk::Image::Pointer refImage;
CastToMitkImage(image, refImage);
mitk::Image::Pointer workingImg;
CastToMitkImage(image, workingImg);
OverwriteObliquePlaneTest(workingImg, refImage);
MITK_TEST_END()
}
diff --git a/Modules/Segmentation/Testing/mitkToolInteractionTest.cpp b/Modules/Segmentation/Testing/mitkToolInteractionTest.cpp
index 50aaef5c35..294223ef94 100644
--- a/Modules/Segmentation/Testing/mitkToolInteractionTest.cpp
+++ b/Modules/Segmentation/Testing/mitkToolInteractionTest.cpp
@@ -1,237 +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 "mitkTestingMacros.h"
#include <mitkDataNode.h>
#include <mitkIOUtil.h>
#include <mitkInteractionTestHelper.h>
#include <mitkLabelSetImage.h>
#include <mitkStandaloneDataStorage.h>
#include <mitkTestFixture.h>
#include <mitkTestingConfig.h>
#include <mitkToolManager.h>
#include <vtkDebugLeaks.h>
class mitkToolInteractionTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkToolInteractionTestSuite);
/// \todo Fix VTK memory leaks. Bug 18098.
vtkDebugLeaks::SetExitError(0);
/* ####### example ######
MITK_TEST(AddToolInteraction_4D_Test);
#########################*/
/// \todo fix crash when wipe tool test is called after another test
// MITK_TEST(WipeToolInteractionTest);
// BUG 19274 - working if recorded again
// MITK_TEST(AddToolInteractionTest);
// MITK_TEST(SubtractToolInteractionTest);
// MITK_TEST(PaintToolInteractionTest);
// MITK_TEST(RegionGrowingToolInteractionTest);
// MITK_TEST(CorrectionToolInteractionTest);
// MITK_TEST(EraseToolInteractionTest);
// MITK_TEST(FillToolInteractionTest);
CPPUNIT_TEST_SUITE_END();
private:
mitk::DataStorage::Pointer m_DataStorage;
mitk::ToolManager::Pointer m_ToolManager;
public:
int GetToolIdByToolName(const std::string &toolName)
{
// find tool from toolname
int numberOfTools = m_ToolManager->GetTools().size();
int toolId = 0;
for (; toolId < numberOfTools; ++toolId)
{
mitk::Tool *currentTool = m_ToolManager->GetToolById(toolId);
if (toolName.compare(currentTool->GetNameOfClass()) == 0)
{
return toolId;
}
}
return -1;
}
void RunTestWithParameters(const std::string &patientImagePath,
const std::string &referenceSegmentationImage,
const std::string &toolName,
const std::string &interactionPattern,
unsigned int timestep = 0,
const std::string &preSegmentationImagePath = std::string())
{
// Create test helper to initialize all necessary objects for interaction
mitk::InteractionTestHelper interactionTestHelper(GetTestDataFilePath(interactionPattern));
// Use data storage of test helper
m_DataStorage = interactionTestHelper.GetDataStorage().GetPointer();
// create ToolManager
m_ToolManager = mitk::ToolManager::New(m_DataStorage);
m_ToolManager->InitializeTools();
m_ToolManager->RegisterClient(); // This is needed because there must be at least one registered. Otherwise tools
// can't be activated.
// Load patient image
mitk::Image::Pointer patientImage = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath(patientImagePath));
CPPUNIT_ASSERT(patientImage.IsNotNull());
mitk::DataNode::Pointer patientImageNode = mitk::DataNode::New();
patientImageNode->SetData(patientImage);
// Activate tool to work with
int toolID = GetToolIdByToolName(toolName);
mitk::Tool *tool = m_ToolManager->GetToolById(toolID);
CPPUNIT_ASSERT(tool != nullptr);
// Create empty segmentation working image
mitk::DataNode::Pointer workingImageNode = mitk::DataNode::New();
const std::string organName = "test";
- mitk::Color color; // actually it dosn't matter which color we are using
+ mitk::Color color; // actually it doesn't matter which color we are using
color.SetRed(1); // but CreateEmptySegmentationNode expects a color parameter
color.SetGreen(0);
color.SetBlue(0);
if (preSegmentationImagePath.empty())
{
workingImageNode = tool->CreateEmptySegmentationNode(patientImage, organName, color);
}
else
{
mitk::Image::Pointer preSegmentation = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath(preSegmentationImagePath));
workingImageNode = tool->CreateSegmentationNode(preSegmentation, organName, color);
}
CPPUNIT_ASSERT(workingImageNode.IsNotNull());
CPPUNIT_ASSERT(workingImageNode->GetData() != nullptr);
// add images to datastorage
interactionTestHelper.AddNodeToStorage(patientImageNode);
interactionTestHelper.AddNodeToStorage(workingImageNode);
// set reference and working image
m_ToolManager->SetWorkingData(workingImageNode);
m_ToolManager->SetReferenceData(patientImageNode);
// set time step
interactionTestHelper.SetTimeStep(timestep);
// load interaction events
m_ToolManager->ActivateTool(toolID);
CPPUNIT_ASSERT(m_ToolManager->GetActiveTool() != nullptr);
// Start Interaction
interactionTestHelper.PlaybackInteraction();
// load reference segmentation image
mitk::Image::Pointer segmentationReferenceImage =
mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath(referenceSegmentationImage));
mitk::Image::Pointer currentSegmentationImage = mitk::Image::New();
currentSegmentationImage = dynamic_cast<mitk::Image *>(workingImageNode->GetData());
// compare reference with interaction result
MITK_ASSERT_EQUAL(segmentationReferenceImage, currentSegmentationImage, "Reference equals interaction result.");
}
void setUp() {}
void tearDown()
{
m_ToolManager->ActivateTool(-1);
m_ToolManager = nullptr;
}
void AddToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_AddContourTool.nrrd",
"AddContourTool",
"InteractionTestData/Interactions/SegmentationInteractor_AddTool.xml");
}
void SubtractToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_SubtractContourTool.nrrd",
"SubtractContourTool",
"InteractionTestData/Interactions/SegmentationInteractor_SubtractTool.xml");
}
void PaintToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_DrawPaintbrushTool.nrrd",
"DrawPaintbrushTool",
"InteractionTestData/Interactions/SegmentationInteractor_PaintTool.xml");
}
void WipeToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_ErasePaintbrushTool.nrrd",
"ErasePaintbrushTool",
"InteractionTestData/Interactions/SegmentationInteractor_WipeTool.xml");
}
void RegionGrowingToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_RegionGrowingTool.nrrd",
"RegionGrowingTool",
"InteractionTestData/Interactions/SegmentationInteractor_RegionGrowingTool.xml");
}
void CorrectionToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_CorrectorTool2D.nrrd",
"CorrectorTool2D",
"InteractionTestData/Interactions/SegmentationInteractor_CorrectionTool.xml");
}
void EraseToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_EraseRegionTool.nrrd",
"EraseRegionTool",
"InteractionTestData/Interactions/SegmentationInteractor_EraseTool.xml",
0,
"InteractionTestData/ReferenceData/SegmentationInteractor_AddContourTool.nrrd");
}
void FillToolInteractionTest()
{
RunTestWithParameters("Pic3D.nrrd",
"InteractionTestData/ReferenceData/SegmentationInteractor_FillRegionTool.nrrd",
"FillRegionTool",
"InteractionTestData/Interactions/SegmentationInteractor_FillTool.xml",
0,
"InteractionTestData/InputData/SegmentationInteractor_FillTool_input.nrrd");
}
/*############ example ###################
void AddToolInteraction_4D_Test()
{
RunTestWithParameters("US4DCyl.nrrd", "Segmentation/ReferenceSegmentations/AddTool_4D.nrrd", "AddContourTool",
"Segmentation/InteractionPatterns/AddTool_4D.xml", 1);
}
#########################################*/
};
MITK_TEST_SUITE_REGISTRATION(mitkToolInteraction)
diff --git a/Modules/Segmentation/Testing/mitkToolManagerProviderTest.cpp b/Modules/Segmentation/Testing/mitkToolManagerProviderTest.cpp
index e737f37410..09bf1f093b 100644
--- a/Modules/Segmentation/Testing/mitkToolManagerProviderTest.cpp
+++ b/Modules/Segmentation/Testing/mitkToolManagerProviderTest.cpp
@@ -1,32 +1,32 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "itkLightObject.h"
#include "mitkTestingMacros.h"
#include "mitkToolManagerProvider.h"
#include <usGetModuleContext.h>
#include <usModuleContext.h>
#include <usServiceReference.h>
int mitkToolManagerProviderTest(int, char *[])
{
MITK_TEST_BEGIN("ToolManagerProvider")
// at this point the service is already registered by loading the Segmentation module.
mitk::ToolManagerProvider *service = mitk::ToolManagerProvider::GetInstance();
- MITK_TEST_CONDITION(service != nullptr, "Service was succesfully called");
+ MITK_TEST_CONDITION(service != nullptr, "Service was successfully called");
MITK_TEST_CONDITION((service->GetToolManager()) == (mitk::ToolManagerProvider::GetInstance()->GetToolManager()),
"Service singleton");
MITK_TEST_END()
}
diff --git a/Modules/Segmentation/cmdapps/ContoursToImage.cpp b/Modules/Segmentation/cmdapps/ContoursToImage.cpp
index 8d63fca2b1..424678ba01 100644
--- a/Modules/Segmentation/cmdapps/ContoursToImage.cpp
+++ b/Modules/Segmentation/cmdapps/ContoursToImage.cpp
@@ -1,312 +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 <mitkCommandLineParser.h>
#include <mitkContourModelSet.h>
#include <mitkContourModelSetToImageFilter.h>
#include <mitkDataStorage.h>
#include <mitkImageReadAccessor.h>
#include <mitkImageWriteAccessor.h>
#include <mitkIOUtil.h>
#include <mitkLabelSetImage.h>
#include <mitkLabelSetImageHelper.h>
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/replace.hpp>
-#include <filesystem>
+#include <mitkFileSystem.h>
enum class OutputFormat
{
Binary,
Label,
Multilabel
};
void InitializeCommandLineParser(mitkCommandLineParser& parser)
{
parser.setTitle("Contour to Image Converter");
parser.setCategory("Segmentation");
parser.setDescription("Converts contours (i. e. RTSTRUCT or MITK Contour Model Set) to binary image masks or (multi-)label segmentations.");
parser.setContributor("German Cancer Research Center (DKFZ)");
parser.setArgumentPrefix("--", "-");
parser.addArgument("input", "i", mitkCommandLineParser::File, "Input file:", "Input contour(s)", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("reference", "r", mitkCommandLineParser::Image, "Reference image:", "Input reference image", us::Any(), false, false, false, mitkCommandLineParser::Input);
parser.addArgument("output", "o", mitkCommandLineParser::Image, "Output file:", "Output image", us::Any(), false, false, false, mitkCommandLineParser::Output);
parser.addArgument("format", "f", mitkCommandLineParser::String, "Output format:", "Output format (binary, label, or multilabel)", std::string("binary"));
}
std::string GetSafeName(const mitk::IPropertyProvider* propertyProvider)
{
std::string name;
if (propertyProvider != nullptr)
{
name = propertyProvider->GetConstProperty("name")->GetValueAsString();
if (!name.empty())
{
boost::trim(name);
boost::replace_all(name, "/", "_");
boost::replace_all(name, "\\", "_");
// If you read this, feel free to handle invalid filename characters here. :)
}
}
return name;
}
-void CreateParentDirectories(const std::filesystem::path& path)
+void CreateParentDirectories(const fs::path& path)
{
if (path.has_parent_path())
{
auto parentPath = path.parent_path();
- if (!std::filesystem::exists(parentPath))
- std::filesystem::create_directories(parentPath);
+ if (!fs::exists(parentPath))
+ fs::create_directories(parentPath);
}
}
bool SetLabelName(const mitk::IPropertyProvider* propertyProvider, mitk::Label* label)
{
if (propertyProvider != nullptr)
{
if (auto property = propertyProvider->GetConstProperty("name"); property.IsNotNull())
{
if (auto nameProperty = dynamic_cast<const mitk::StringProperty*>(property.GetPointer()); nameProperty != nullptr)
{
if (auto name = nameProperty->GetValueAsString(); !name.empty())
{
label->SetName(name);
return true;
}
}
}
}
return false;
}
bool SetLabelColor(const mitk::IPropertyProvider* propertyProvider, mitk::Label* label)
{
if (propertyProvider != nullptr)
{
if (auto property = propertyProvider->GetConstProperty("color"); property.IsNotNull())
{
if (auto colorProperty = dynamic_cast<const mitk::ColorProperty*>(property.GetPointer()); colorProperty != nullptr)
{
label->SetColor(colorProperty->GetColor());
return true;
}
}
}
return false;
}
void CopyImageToActiveLayerImage(const mitk::Image* image, mitk::LabelSetImage* labelSetImage)
{
mitk::ImageReadAccessor readAccessor(image);
mitk::ImageWriteAccessor writeAccessor(labelSetImage);
auto size = sizeof(mitk::Label::PixelType);
for (size_t dim = 0; dim < image->GetDimension(); ++dim)
size *= image->GetDimension(dim);
memcpy(writeAccessor.GetData(), readAccessor.GetData(), size);
}
OutputFormat ParseOutputFormat(const mitk::IFileIO::Options& args)
{
auto it = args.find("format");
if (it != args.end())
{
auto format = us::any_cast<std::string>(it->second);
if (format == "multilabel")
return OutputFormat::Multilabel;
if (format == "label")
return OutputFormat::Label;
if (format != "binary")
mitkThrow() << "Unknown output format \"" << format << "\" (must be \"binary\", \"label\" or \"multilabel\").";
}
return OutputFormat::Binary;
}
std::vector<mitk::ContourModelSet::Pointer> FilterValidInputs(const std::vector<mitk::BaseData::Pointer>& inputs)
{
std::vector<mitk::ContourModelSet::Pointer> validInputs;
for (auto input : inputs)
{
if (input.IsNull())
{
MITK_WARN << "Skipping null input.";
continue;
}
auto* validInput = dynamic_cast<mitk::ContourModelSet*>(input.GetPointer());
if (validInput == nullptr)
{
MITK_WARN << "Skipping input of type \"" << input->GetNameOfClass() << "\".";
continue;
}
validInputs.push_back(validInput);
}
return validInputs;
}
int main(int argc, char* argv[])
{
int returnValue = EXIT_SUCCESS;
mitkCommandLineParser parser;
InitializeCommandLineParser(parser);
auto args = parser.parseArguments(argc, argv);
if (args.empty())
return EXIT_FAILURE;
try
{
auto inputFilename = us::any_cast<std::string>(args["input"]);
auto referenceFilename = us::any_cast<std::string>(args["reference"]);
auto outputFilename = us::any_cast<std::string>(args["output"]);
auto format = ParseOutputFormat(args);
auto referenceImage = mitk::IOUtil::Load<mitk::Image>(referenceFilename);
auto inputs = FilterValidInputs(mitk::IOUtil::Load(inputFilename));
MITK_INFO << "Found " << inputs.size() << " input contour set(s)";
- std::filesystem::path outputPath(outputFilename);
+ fs::path outputPath(outputFilename);
CreateParentDirectories(outputPath);
mitk::LabelSetImage::Pointer labelSetImage; // Only used for "multilabel" output
unsigned int nonameCounter = 0; // Helper variable to generate placeholder names for nameless contour sets
for (auto input : inputs)
{
// If the input file contains multiple contour sets but the output format is not set to "multilabel",
// we create separate output files for each contour set. In this case the specified output filename
// is used only as a base filename and the names of the individual contour sets are appended accordingly.
if (inputs.size() > 1 && format != OutputFormat::Multilabel)
{
outputPath = outputFilename;
auto name = GetSafeName(input);
if (name.empty())
name = "nameless_" + std::to_string(nonameCounter++);
outputPath.replace_filename(outputPath.stem().string() + '_' + name + outputPath.extension().string());
}
// Do the actual conversion from a contour set to an image with a background pixel value of 0.
// - For "binary" output, use pixel value 1 and unsigned char as pixel type.
// - For "label" output, use pixel value 1 and our label pixel type.
// - For "multilabel" output, use the next available label value instead.
const auto labelValue = labelSetImage.IsNotNull()
? labelSetImage->GetUnusedLabelValue()
: 1;
auto filter = mitk::ContourModelSetToImageFilter::New();
filter->SetMakeOutputLabelPixelType(format != OutputFormat::Binary);
filter->SetPaintingPixelValue(labelValue);
filter->SetImage(referenceImage);
filter->SetInput(input);
filter->Update();
mitk::Image::Pointer image = filter->GetOutput();
filter = nullptr;
if (image.IsNull())
{
MITK_ERROR << "Contour set to image conversion failed without exception. Continue with next contour set... ";
returnValue = EXIT_FAILURE;
continue;
}
if (format == OutputFormat::Binary)
{
mitk::IOUtil::Save(image, outputPath.string());
}
else
{
if (labelSetImage.IsNull())
{
labelSetImage = mitk::LabelSetImage::New();
labelSetImage->Initialize(image);
CopyImageToActiveLayerImage(image, labelSetImage);
}
else
{
labelSetImage->AddLayer(image);
}
auto label = mitk::LabelSetImageHelper::CreateNewLabel(labelSetImage);
label->SetValue(labelValue);
SetLabelName(input, label);
SetLabelColor(input, label);
if (format == OutputFormat::Multilabel)
MITK_INFO << "Creating label: " << label->GetName() << " [" << labelValue << ']';
labelSetImage->AddLabel(label, labelSetImage->GetActiveLayer(), false, false);
if (format == OutputFormat::Label)
{
mitk::IOUtil::Save(labelSetImage, outputPath.string());
labelSetImage = nullptr;
}
}
}
// In case of the "multilabel" output format, eventually save the single output file.
// For all other output formats, the output file(s) have been saved already while iterating
// over the inputs.
if (labelSetImage.IsNotNull())
mitk::IOUtil::Save(labelSetImage, outputPath.string());
}
catch (const mitk::Exception& e)
{
MITK_ERROR << e.GetDescription();
return EXIT_FAILURE;
}
catch (const std::exception& e)
{
MITK_ERROR << e.what();
return EXIT_FAILURE;
}
catch (...)
{
return EXIT_FAILURE;
}
return returnValue;
}
diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake
index 31b461d399..fce1bc0f64 100644
--- a/Modules/Segmentation/files.cmake
+++ b/Modules/Segmentation/files.cmake
@@ -1,117 +1,120 @@
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/mitkGrowCutSegmentationFilter.cpp
Algorithms/mitkImageLiveWireContourModelFilter.cpp
Algorithms/mitkImageToContourFilter.cpp
Algorithms/mitkManualSegmentationToSurfaceFilter.cpp
Algorithms/mitkOtsuSegmentationFilter.cpp
Algorithms/mitkSegmentationHelper.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/mitkAddContourTool.cpp
Interactions/mitkAutoCropTool.cpp
Interactions/mitkSegWithPreviewTool.cpp
Interactions/mitkBinaryThresholdBaseTool.cpp
Interactions/mitkBinaryThresholdTool.cpp
Interactions/mitkBinaryThresholdULTool.cpp
Interactions/mitkCloseRegionTool.cpp
Interactions/mitkContourModelInteractor.cpp
Interactions/mitkContourModelLiveWireInteractor.cpp
Interactions/mitkEditableContourTool.cpp
Interactions/mitkLiveWireTool2D.cpp
Interactions/mitkLassoTool.cpp
Interactions/mitkContourTool.cpp
Interactions/mitkDrawPaintbrushTool.cpp
Interactions/mitkErasePaintbrushTool.cpp
Interactions/mitkEraseRegionTool.cpp
Interactions/mitkFeedbackContourTool.cpp
Interactions/mitkFillRegionBaseTool.cpp
Interactions/mitkFillRegionTool.cpp
Interactions/mitkGrowCutTool.cpp
Interactions/mitkOtsuTool3D.cpp
Interactions/mitkPaintbrushTool.cpp
Interactions/mitkRegionGrowingTool.cpp
Interactions/mitkSegmentationsProcessingTool.cpp
Interactions/mitkSegTool2D.cpp
Interactions/mitkSubtractContourTool.cpp
Interactions/mitkTool.cpp
Interactions/mitkToolCommand.cpp
Interactions/mitkPickingTool.cpp
Interactions/mitknnUnetTool.cpp
Interactions/mitkProcessExecutor.cpp
Interactions/mitkSegmentAnythingProcessExecutor.cpp
+ Interactions/mitkMonaiLabelTool.cpp
+ Interactions/mitkMonaiLabel2DTool.cpp
+ Interactions/mitkMonaiLabel3DTool.cpp
Interactions/mitkTotalSegmentatorTool.cpp
Interactions/mitkSegmentAnythingTool.cpp
Interactions/mitkMedSAMTool.cpp
Interactions/mitkSegmentAnythingPythonService.cpp
Rendering/mitkContourMapper2D.cpp
Rendering/mitkContourSetMapper2D.cpp
Rendering/mitkContourSetVtkMapper3D.cpp
Rendering/mitkContourVtkMapper3D.cpp
SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp
SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp
#Added from ML
Algorithms/mitkSurfaceStampImageFilter.cpp
)
set(RESOURCE_FILES
Add.svg
Add_Cursor.svg
AI.svg
AI_Cursor.svg
Close.svg
Close_Cursor.svg
Erase.svg
Erase_Cursor.svg
Fill.svg
Fill_Cursor.svg
LiveWire.svg
LiveWire_Cursor.svg
Lasso.svg
GrowCut.svg
Lasso_Cursor.svg
Otsu.svg
Paint.svg
Paint_Cursor.svg
Picking.svg
RegionGrowing.svg
RegionGrowing_Cursor.svg
Subtract.svg
Subtract_Cursor.svg
Threshold.svg
ULThreshold.svg
Wipe.svg
Wipe_Cursor.svg
Interactions/dummy.xml
Interactions/EditableContourTool.xml
Interactions/PickingTool.xml
Interactions/MouseReleaseOnly.xml
Interactions/PressMoveRelease.xml
Interactions/PressMoveReleaseAndPointSetting.xml
Interactions/PressMoveReleaseWithCTRLInversion.xml
Interactions/PressMoveReleaseWithCTRLInversionAllMouseMoves.xml
Interactions/SegmentationConfig.xml
Interactions/SegmentationInteraction.xml
Interactions/SegmentationToolsConfig.xml
Interactions/ContourModelModificationConfig.xml
Interactions/ContourModelModificationInteractor.xml
)
diff --git a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.cpp b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.cpp
index f6231dea49..eb2a20bbc9 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.cpp
@@ -1,187 +1,193 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkBinaryThresholdToolGUIBase.h"
#include "mitkBinaryThresholdBaseTool.h"
#include "mitkBinaryThresholdTool.h"
+#include <ctkDoubleSpinBox.h>
+
#include <qlabel.h>
#include <qlayout.h>
#include <qslider.h>
#include <QApplication>
+#include <QSpinBox>
QmitkBinaryThresholdToolGUIBase::QmitkBinaryThresholdToolGUIBase(bool ulMode) : QmitkSegWithPreviewToolGUIBase(false), m_ULMode(ulMode)
{
}
QmitkBinaryThresholdToolGUIBase::~QmitkBinaryThresholdToolGUIBase()
{
auto tool = this->GetConnectedToolAs<mitk::BinaryThresholdBaseTool>();
if (nullptr != tool)
{
tool->IntervalBordersChanged -=
mitk::MessageDelegate3<QmitkBinaryThresholdToolGUIBase, double, double, bool>(
this, &QmitkBinaryThresholdToolGUIBase::OnThresholdingIntervalBordersChanged);
tool->ThresholdingValuesChanged -=
mitk::MessageDelegate2<QmitkBinaryThresholdToolGUIBase, mitk::ScalarType, mitk::ScalarType>(
this, &QmitkBinaryThresholdToolGUIBase::OnThresholdingValuesChanged);
}
}
void QmitkBinaryThresholdToolGUIBase::OnThresholdingIntervalBordersChanged(double lower, double upper, bool isFloat)
{
m_InternalUpdate = true;
if (m_ULMode)
{
if (!isFloat)
{
m_ThresholdRange->setRange(int(lower), int(upper));
m_ThresholdRange->setSingleStep(1);
m_ThresholdRange->setDecimals(0);
}
else
{
m_ThresholdRange->setRange(lower, upper);
}
}
else
{
if (!isFloat)
{
m_ThresholdSlider->setRange(int(lower), int(upper));
m_ThresholdSlider->setSingleStep(1);
m_ThresholdSlider->setDecimals(0);
}
else
{
m_ThresholdSlider->setRange(lower, upper);
}
}
m_InternalUpdate = false;
}
void QmitkBinaryThresholdToolGUIBase::OnThresholdingValuesChanged(mitk::ScalarType lower, mitk::ScalarType upper)
{
if (m_ULMode)
{
m_ThresholdRange->setValues(lower, upper);
}
else
{
m_ThresholdSlider->setValue(lower);
}
}
void QmitkBinaryThresholdToolGUIBase::OnThresholdRangeChanged(double min, double max)
{
auto tool = this->GetConnectedToolAs<mitk::BinaryThresholdBaseTool>();
if (nullptr != tool && !m_InternalUpdate)
{
tool->SetThresholdValues(min, max);
}
}
void QmitkBinaryThresholdToolGUIBase::OnThresholdSliderChanged(double value)
{
auto tool = this->GetConnectedToolAs<mitk::BinaryThresholdTool>();
if (nullptr != tool && !m_InternalUpdate)
{
tool->SetThresholdValue(value);
}
}
void QmitkBinaryThresholdToolGUIBase::DisconnectOldTool(mitk::SegWithPreviewTool* oldTool)
{
Superclass::DisconnectOldTool(oldTool);
auto tool = dynamic_cast<mitk::BinaryThresholdBaseTool*>(oldTool);
if (nullptr != tool)
{
tool->IntervalBordersChanged -=
mitk::MessageDelegate3<QmitkBinaryThresholdToolGUIBase, double, double, bool>(
this, &QmitkBinaryThresholdToolGUIBase::OnThresholdingIntervalBordersChanged);
tool->ThresholdingValuesChanged -=
mitk::MessageDelegate2<QmitkBinaryThresholdToolGUIBase, mitk::ScalarType, mitk::ScalarType>(
this, &QmitkBinaryThresholdToolGUIBase::OnThresholdingValuesChanged);
}
}
void QmitkBinaryThresholdToolGUIBase::ConnectNewTool(mitk::SegWithPreviewTool* newTool)
{
Superclass::ConnectNewTool(newTool);
auto tool = dynamic_cast<mitk::BinaryThresholdBaseTool*>(newTool);
if (nullptr != tool)
{
tool->IntervalBordersChanged +=
mitk::MessageDelegate3<QmitkBinaryThresholdToolGUIBase, double, double, bool>(
this, &QmitkBinaryThresholdToolGUIBase::OnThresholdingIntervalBordersChanged);
tool->ThresholdingValuesChanged +=
mitk::MessageDelegate2<QmitkBinaryThresholdToolGUIBase, mitk::ScalarType, mitk::ScalarType>(
this, &QmitkBinaryThresholdToolGUIBase::OnThresholdingValuesChanged);
}
}
void QmitkBinaryThresholdToolGUIBase::InitializeUI(QBoxLayout* mainLayout)
{
QLabel* label = new QLabel("Threshold :", this);
QFont f = label->font();
f.setBold(false);
label->setFont(f);
mainLayout->addWidget(label);
QBoxLayout* layout = new QHBoxLayout();
if (m_ULMode)
{
m_ThresholdRange = new ctkRangeWidget();
connect(
m_ThresholdRange, SIGNAL(valuesChanged(double, double)), this, SLOT(OnThresholdRangeChanged(double, double)));
layout->addWidget(m_ThresholdRange);
m_ThresholdRange->setSingleStep(0.01);
+ m_ThresholdRange->minimumSpinBox()->spinBox()->setKeyboardTracking(false);
+ m_ThresholdRange->maximumSpinBox()->spinBox()->setKeyboardTracking(false);
}
else
{
m_ThresholdSlider = new ctkSliderWidget();
connect(
m_ThresholdSlider, SIGNAL(valueChanged(double)), this, SLOT(OnThresholdSliderChanged(double)));
layout->addWidget(m_ThresholdSlider);
m_ThresholdSlider->setSingleStep(0.01);
+ m_ThresholdSlider->spinBox()->spinBox()->setKeyboardTracking(false);
}
mainLayout->addLayout(layout);
Superclass::InitializeUI(mainLayout);
}
void QmitkBinaryThresholdToolGUIBase::BusyStateChanged(bool value)
{
Superclass::BusyStateChanged(value);
if (m_ThresholdRange)
{
m_ThresholdRange->setEnabled(!value);
}
if (m_ThresholdSlider)
{
m_ThresholdSlider->setEnabled(!value);
}
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.h b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.h
index 5e14d89449..2d42e7bc3a 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkBinaryThresholdToolGUIBase.h
@@ -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.
============================================================================*/
#ifndef QmitkBinaryThresholdToolGUIBase_h
#define QmitkBinaryThresholdToolGUIBase_h
#include "QmitkSegWithPreviewToolGUIBase.h"
#include "ctkRangeWidget.h"
#include "ctkSliderWidget.h"
#include <MitkSegmentationUIExports.h>
/**
\ingroup org_mitk_gui_qt_interactivesegmentation_internal
\brief Base GUI for mitk::BinaryThresholdTool.
This GUI shows a slider to change the tool's threshold and an OK button to accept a preview for actual thresholding.
*/
class MITKSEGMENTATIONUI_EXPORT QmitkBinaryThresholdToolGUIBase : public QmitkSegWithPreviewToolGUIBase
{
Q_OBJECT
public:
mitkClassMacro(QmitkBinaryThresholdToolGUIBase, QmitkSegWithPreviewToolGUIBase);
void OnThresholdingIntervalBordersChanged(double lower, double upper, bool isFloat);
void OnThresholdingValuesChanged(mitk::ScalarType lower, mitk::ScalarType upper);
protected slots:
void OnThresholdRangeChanged(double min, double max);
void OnThresholdSliderChanged(double value);
protected:
QmitkBinaryThresholdToolGUIBase(bool ulMode);
~QmitkBinaryThresholdToolGUIBase() override;
void DisconnectOldTool(mitk::SegWithPreviewTool* oldTool) override;
void ConnectNewTool(mitk::SegWithPreviewTool* newTool) override;
void InitializeUI(QBoxLayout* mainLayout) override;
void BusyStateChanged(bool) override;
ctkRangeWidget* m_ThresholdRange = nullptr;
ctkSliderWidget* m_ThresholdSlider = nullptr;
/** Indicates if the tool UI is used for a tool with upper an lower threshold (true)
- ore only with one threshold (false)*/
+ or only with one threshold (false)*/
bool m_ULMode;
bool m_InternalUpdate = false;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkGrowCutToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkGrowCutToolGUI.cpp
index 03a607b838..c6006c540e 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkGrowCutToolGUI.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkGrowCutToolGUI.cpp
@@ -1,149 +1,150 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkGrowCutToolGUI.h"
#include <mitkGrowCutTool.h>
#include <mitkToolManagerProvider.h>
// Qt
#include <QBoxLayout>
#include <QLabel>
#include <QRect>
#include <QPushButton>
#include <QRadioButton>
#include <QMessageBox>
MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkGrowCutToolGUI, "")
QmitkGrowCutToolGUI::QmitkGrowCutToolGUI() : QmitkMultiLabelSegWithPreviewToolGUIBase()
{
auto enableConfirmSegBtnFnc = [this](bool enabled) { return enabled && m_FirstPreviewComputation; };
m_EnableConfirmSegBtnFnc = enableConfirmSegBtnFnc;
}
QmitkGrowCutToolGUI::~QmitkGrowCutToolGUI() {}
void QmitkGrowCutToolGUI::ConnectNewTool(mitk::SegWithPreviewTool *newTool)
{
Superclass::ConnectNewTool(newTool);
newTool->IsTimePointChangeAwareOff();
}
void QmitkGrowCutToolGUI::InitializeUI(QBoxLayout *mainLayout)
{
m_Controls.setupUi(this);
this->OnAdvancedSettingsButtonToggled(false);
std::function<bool()> isPreviewAvailable;
isPreviewAvailable = [this]()
{
auto tool = this->GetConnectedToolAs<mitk::GrowCutTool>();
if (nullptr != tool)
{
return tool->SeedImageIsValid();
}
return false;
};
auto previewAvailable = isPreviewAvailable();
m_Controls.m_previewButton->setEnabled(previewAvailable);
m_Controls.m_warningLabel->setVisible(!previewAvailable);
mainLayout->addLayout(m_Controls.verticalLayout);
connect(m_Controls.m_previewButton, &QPushButton::clicked, this, &QmitkGrowCutToolGUI::OnPreviewBtnClicked);
connect(m_Controls.m_advancedSettingsButton,
&ctkExpandButton::toggled,
this,
&QmitkGrowCutToolGUI::OnAdvancedSettingsButtonToggled);
connect(m_Controls.m_distancePenaltyDoubleSpinBox,
qOverload<double>(&QDoubleSpinBox::valueChanged),
this,
&QmitkGrowCutToolGUI::SetValueOfDistancePenaltySlider);
connect(m_Controls.m_distancePenaltySlider,
&QSlider::valueChanged,
this,
&QmitkGrowCutToolGUI::SetValueOfDistancePenaltyDoubleSpinBox);
Superclass::InitializeUI(mainLayout);
}
void QmitkGrowCutToolGUI::SetValueOfDistancePenaltySlider(double val)
{
m_Controls.m_distancePenaltySlider->setValue(val * 100);
}
void QmitkGrowCutToolGUI::SetValueOfDistancePenaltyDoubleSpinBox(int val)
{
m_Controls.m_distancePenaltyDoubleSpinBox->setValue((static_cast<double>(val) / 100));
}
void QmitkGrowCutToolGUI::OnAdvancedSettingsButtonToggled(bool toggled)
{
m_Controls.m_distancePenaltyLabel->setVisible(toggled);
m_Controls.m_distancePenaltyDoubleSpinBox->setVisible(toggled);
m_Controls.m_distancePenaltySlider->setVisible(toggled);
double distancePenaltyMinium = 0.0;
double distancePenaltyMaximum = 1.0;
m_Controls.m_distancePenaltyDoubleSpinBox->setMinimum(distancePenaltyMinium);
m_Controls.m_distancePenaltyDoubleSpinBox->setMaximum(distancePenaltyMaximum);
m_Controls.m_distancePenaltySlider->setMinimum(distancePenaltyMinium);
m_Controls.m_distancePenaltySlider->setMaximum(distancePenaltyMaximum * 100);
m_Controls.m_distancePenaltyDoubleSpinBox->setSingleStep(0.01);
}
void QmitkGrowCutToolGUI::OnPreviewBtnClicked()
{
auto tool = this->GetConnectedToolAs<mitk::GrowCutTool>();
if (nullptr != tool)
{
try
{
tool->SetDistancePenalty(m_Controls.m_distancePenaltyDoubleSpinBox->value());
tool->UpdatePreview();
}
catch (...)
{
this->setCursor(Qt::ArrowCursor);
QMessageBox *messageBox =
new QMessageBox(QMessageBox::Critical,
nullptr,
"itkGrowCutFilter error.");
messageBox->exec();
delete messageBox;
return;
}
m_FirstPreviewComputation = true;
this->SetLabelSetPreview(tool->GetPreviewSegmentation());
tool->IsTimePointChangeAwareOn();
this->ActualizePreviewLabelVisibility();
+ this->EnableWidgets(true);
}
}
void QmitkGrowCutToolGUI::EnableWidgets(bool enabled)
{
Superclass::EnableWidgets(enabled);
m_Controls.m_distancePenaltyLabel->setEnabled(enabled);
m_Controls.m_distancePenaltyDoubleSpinBox->setEnabled(enabled);
m_Controls.m_distancePenaltySlider->setEnabled(enabled);
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMedSAMGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkMedSAMGUIControls.ui
index af5d1b7f3f..13b4eee81a 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMedSAMGUIControls.ui
+++ b/Modules/SegmentationUI/Qmitk/QmitkMedSAMGUIControls.ui
@@ -1,173 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmitkMedSAMGUIControls</class>
<widget class="QWidget" name="QmitkMedSAMGUIControls">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>699</width>
<height>490</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100000</width>
<height>100000</height>
</size>
</property>
<property name="windowTitle">
<string>QmitkMedSAMToolWidget</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<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>
<layout class="QGridLayout" name="basicSettingsLayout">
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="welcomeNote">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Welcome to Segment anything in medical images (MedSAM) tool in MITK. [Experimental]&lt;/p&gt;&lt;p&gt;Please note that this is only an interface to MedSAM. MITK does not ship with MedSAM. Make sure to have a working internet connection to install MedSAM via MITK. &lt;/p&gt;&lt;p&gt;Refer to &lt;a href=&quot;https://www.nature.com/articles/s41467-024-44824-z&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://www.nature.com/articles/s41467-024-44824-z&lt;/span&gt;&lt;/a&gt; to learn everything about the Segment anything in medical images.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="instructLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>
Press SHIFT+Left-click and drag for RoI on the render windows.
- </string>
+Use level window slider to adjust image contrast.
+</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="resetButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100000</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Reset RoI</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="activateButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100000</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Initialize MedSAM</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="statusLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="samProgressBar">
<property name="maximum">
<number>1</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="previewButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100000</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Preview</string>
</property>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.cpp
index ad8a1f4e9a..f0abdfb05b 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkMedSAMToolGUI.cpp
@@ -1,284 +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.
============================================================================*/
#include "QmitkMedSAMToolGUI.h"
#include <QApplication>
#include <QmitkStyleManager.h>
#include <mitkCoreServices.h>
#include <mitkIPreferencesService.h>
#include <mitkMedSAMTool.h>
MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkMedSAMToolGUI, "")
namespace
{
mitk::IPreferences *GetPreferences()
{
auto *preferencesService = mitk::CoreServices::GetPreferencesService();
return preferencesService->GetSystemPreferences()->Node("org.mitk.views.segmentation");
}
}
QmitkMedSAMToolGUI::QmitkMedSAMToolGUI() : QmitkSegWithPreviewToolGUIBase(true)
{
m_EnableConfirmSegBtnFnc = [this](bool enabled)
{
bool result = false;
auto tool = this->GetConnectedToolAs<mitk::MedSAMTool>();
if (nullptr != tool)
{
result = enabled && tool->HasPicks();
}
return result;
};
m_Preferences = GetPreferences();
m_Preferences->OnPropertyChanged +=
mitk::MessageDelegate1<QmitkMedSAMToolGUI, const mitk::IPreferences::ChangeEvent &>(
this, &QmitkMedSAMToolGUI::OnPreferenceChangedEvent);
}
QmitkMedSAMToolGUI::~QmitkMedSAMToolGUI()
{
auto tool = this->GetConnectedToolAs<mitk::SegmentAnythingTool>();
if (nullptr != tool)
{
tool->SAMStatusMessageEvent -=
mitk::MessageDelegate1<QmitkMedSAMToolGUI, const std::string &>(this, &QmitkMedSAMToolGUI::StatusMessageListener);
}
}
void QmitkMedSAMToolGUI::EnableAll(bool isEnable)
{
m_Controls.activateButton->setEnabled(isEnable);
}
void QmitkMedSAMToolGUI::WriteStatusMessage(const QString &message)
{
m_Controls.statusLabel->setText(message);
m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: white");
qApp->processEvents();
}
void QmitkMedSAMToolGUI::WriteErrorMessage(const QString &message)
{
m_Controls.statusLabel->setText(message);
m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: red");
qApp->processEvents();
}
void QmitkMedSAMToolGUI::ShowProgressBar(bool enabled)
{
m_Controls.samProgressBar->setEnabled(enabled);
m_Controls.samProgressBar->setVisible(enabled);
}
void QmitkMedSAMToolGUI::ShowErrorMessage(const std::string &message, QMessageBox::Icon icon)
{
this->setCursor(Qt::ArrowCursor);
QMessageBox *messageBox = new QMessageBox(icon, nullptr, message.c_str());
messageBox->exec();
delete messageBox;
MITK_WARN << message;
}
void QmitkMedSAMToolGUI::InitializeUI(QBoxLayout *mainLayout)
{
m_Controls.setupUi(this);
m_Controls.statusLabel->setTextFormat(Qt::RichText);
QString welcomeText;
- if (m_GpuLoader.GetGPUCount() != 0)
- {
- welcomeText = "<b>STATUS: </b><i>Welcome to MedSAM Anything tool. You're in luck: " +
+ welcomeText = "<b>STATUS: </b><i>Welcome to MedSAM tool. " +
QString::number(m_GpuLoader.GetGPUCount()) + " GPU(s) were detected.</i>";
- }
- else
- {
- welcomeText = "<b>STATUS: </b><i>Welcome to MedSAM Anything tool. Sorry, " +
- QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected.</i>";
- }
connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewBtnClicked()));
connect(m_Controls.activateButton, SIGNAL(clicked()), this, SLOT(OnActivateBtnClicked()));
connect(m_Controls.resetButton, SIGNAL(clicked()), this, SLOT(OnResetPicksClicked()));
QIcon arrowIcon = QmitkStyleManager::ThemeIcon(
QStringLiteral(":/org_mitk_icons/icons/tango/scalable/actions/media-playback-start.svg"));
m_Controls.activateButton->setIcon(arrowIcon);
bool isInstalled = this->ValidatePrefences();
if (isInstalled)
{
QString modelType = QString::fromStdString(m_Preferences->Get("sam modeltype", ""));
welcomeText += " MedSAM is already found installed.";
}
else
{
welcomeText += " MedSAM tool is not configured correctly. Please go to Preferences (Cntl+P) > Segment Anything to "
"configure and/or install SAM & MedSAM.";
}
this->EnableAll(isInstalled);
this->WriteStatusMessage(welcomeText);
this->ShowProgressBar(false);
m_Controls.samProgressBar->setMaximum(0);
mainLayout->addLayout(m_Controls.verticalLayout);
Superclass::InitializeUI(mainLayout);
}
bool QmitkMedSAMToolGUI::ValidatePrefences()
{
const QString storageDir = QString::fromStdString(m_Preferences->Get("sam python path", ""));
bool isInstalled = QmitkSegmentAnythingToolGUI::IsSAMInstalled(storageDir);
std::string modelType = m_Preferences->Get("sam modeltype", "");
std::string path = m_Preferences->Get("sam parent path", "");
return (isInstalled && !modelType.empty() && !path.empty());
}
void QmitkMedSAMToolGUI::StatusMessageListener(const std::string &message)
{
if (message.rfind("Error", 0) == 0)
{
this->EnableAll(true);
this->WriteErrorMessage(QString::fromStdString(message));
}
else if (message == "TimeOut")
{ // trying to re init the daemon
this->WriteErrorMessage(QString("<b>STATUS: </b><i>Sorry, operation timed out. Reactivating MedSAM tool...</i>"));
if (this->ActivateSAMDaemon())
{
this->WriteStatusMessage(QString("<b>STATUS: </b><i>MedSAM tool re-initialized.</i>"));
}
else
{
this->WriteErrorMessage(QString("<b>STATUS: </b><i>Couldn't init tool backend.</i>"));
this->EnableAll(true);
}
}
else
{
this->WriteStatusMessage(QString::fromStdString(message));
}
}
bool QmitkMedSAMToolGUI::ActivateSAMDaemon()
{
auto tool = this->GetConnectedToolAs<mitk::MedSAMTool>();
if (nullptr == tool)
{
return false;
}
this->ShowProgressBar(true);
qApp->processEvents();
try
{
tool->InitSAMPythonProcess();
while (!tool->IsPythonReady())
{
qApp->processEvents();
}
tool->IsReadyOn();
}
catch (...)
{
tool->IsReadyOff();
}
this->ShowProgressBar(false);
return tool->GetIsReady();
}
void QmitkMedSAMToolGUI::OnActivateBtnClicked()
{
auto tool = this->GetConnectedToolAs<mitk::MedSAMTool>();
if (nullptr == tool)
{
return;
}
try
{
this->EnableAll(false);
qApp->processEvents();
QString pythonPath = QString::fromStdString(m_Preferences->Get("sam python path", ""));
if (!QmitkSegmentAnythingToolGUI::IsSAMInstalled(pythonPath))
{
throw std::runtime_error(WARNING_SAM_NOT_FOUND);
}
tool->SetPythonPath(pythonPath.toStdString());
tool->SetGpuId(m_Preferences->GetInt("sam gpuid", -1));
tool->SetModelType("vit_b"); // MedSAM only works with vit_b
tool->SetTimeOutLimit(m_Preferences->GetInt("sam timeout", 300));
tool->SetCheckpointPath(m_Preferences->Get("sam parent path", ""));
tool->SetBackend("MedSAM");
this->WriteStatusMessage(QString("<b>STATUS: </b><i>Initializing MedSAM...</i>"));
tool->SAMStatusMessageEvent +=
mitk::MessageDelegate1<QmitkMedSAMToolGUI, const std::string &>(this, &QmitkMedSAMToolGUI::StatusMessageListener);
if (this->ActivateSAMDaemon())
{
this->WriteStatusMessage(QString("<b>STATUS: </b><i>MedSAM tool initialized.</i>"));
}
else
{
this->WriteErrorMessage(QString("<b>STATUS: </b><i>Couldn't init tool backend.</i>"));
this->EnableAll(true);
}
}
catch (const std::exception &e)
{
std::stringstream errorMsg;
errorMsg << "<b>STATUS: </b>Error while processing parameters for MedSAM segmentation. Reason: " << e.what();
this->ShowErrorMessage(errorMsg.str());
this->WriteErrorMessage(QString::fromStdString(errorMsg.str()));
this->EnableAll(true);
return;
}
catch (...)
{
std::string errorMsg = "Unkown error occured while generation MedSAM segmentation.";
this->ShowErrorMessage(errorMsg);
this->EnableAll(true);
return;
}
}
void QmitkMedSAMToolGUI::OnPreviewBtnClicked()
{
auto tool = this->GetConnectedToolAs<mitk::MedSAMTool>();
if (nullptr != tool)
{
tool->UpdatePreview();
}
}
void QmitkMedSAMToolGUI::OnResetPicksClicked()
{
auto tool = this->GetConnectedToolAs<mitk::MedSAMTool>();
if (nullptr != tool)
{
tool->ClearPicks();
}
}
void QmitkMedSAMToolGUI::OnPreferenceChangedEvent(const mitk::IPreferences::ChangeEvent &event)
{
const std::string property = event.GetProperty();
const std::string modelType = "modeltype";
if (property.compare(property.size() - modelType.size(), modelType.size(), modelType) == 0)
return; // Model type change ignored.
this->EnableAll(true);
this->WriteStatusMessage("A Preference change was detected. Please initialize the tool again.");
auto tool = this->GetConnectedToolAs<mitk::MedSAMTool>();
if (nullptr != tool)
{
tool->IsReadyOff();
}
}
diff --git a/Modules/Core/include/mitkAffineTransform3D.h b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel2DToolGUI.cpp
similarity index 61%
copy from Modules/Core/include/mitkAffineTransform3D.h
copy to Modules/SegmentationUI/Qmitk/QmitkMonaiLabel2DToolGUI.cpp
index e6f535cd75..43fb118247 100644
--- a/Modules/Core/include/mitkAffineTransform3D.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel2DToolGUI.cpp
@@ -1,24 +1,17 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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
+#include "QmitkMonaiLabel2DToolGUI.h"
-#include <mitkNumericConstants.h>
-#include <itkScalableAffineTransform.h>
+MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkMonaiLabel2DToolGUI, "")
-namespace mitk
-{
- using AffineTransform3D = itk::ScalableAffineTransform<ScalarType, 3>;
-}
-
-#endif
+QmitkMonaiLabel2DToolGUI::QmitkMonaiLabel2DToolGUI(): QmitkMonaiLabelToolGUI(2) {}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel2DToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel2DToolGUI.h
new file mode 100644
index 0000000000..9f5bd5c824
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel2DToolGUI.h
@@ -0,0 +1,33 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+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 QmitkMonaiLabelTool2DGUI_h
+#define QmitkMonaiLabelTool2DGUI_h
+
+#include <MitkSegmentationUIExports.h>
+#include "QmitkMonaiLabelToolGUI.h"
+
+class MITKSEGMENTATIONUI_EXPORT QmitkMonaiLabel2DToolGUI : public QmitkMonaiLabelToolGUI
+{
+ Q_OBJECT
+
+public:
+ mitkClassMacro(QmitkMonaiLabel2DToolGUI, QmitkMonaiLabelToolGUI);
+ itkFactorylessNewMacro(Self);
+ itkCloneMacro(Self);
+
+protected:
+ QmitkMonaiLabel2DToolGUI();
+ ~QmitkMonaiLabel2DToolGUI() = default;
+};
+
+#endif
diff --git a/Modules/Core/include/mitkAffineTransform3D.h b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel3DToolGUI.cpp
similarity index 61%
copy from Modules/Core/include/mitkAffineTransform3D.h
copy to Modules/SegmentationUI/Qmitk/QmitkMonaiLabel3DToolGUI.cpp
index e6f535cd75..15857ff290 100644
--- a/Modules/Core/include/mitkAffineTransform3D.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel3DToolGUI.cpp
@@ -1,24 +1,17 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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
+#include "QmitkMonaiLabel3DToolGUI.h"
-#include <mitkNumericConstants.h>
-#include <itkScalableAffineTransform.h>
+MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkMonaiLabel3DToolGUI, "")
-namespace mitk
-{
- using AffineTransform3D = itk::ScalableAffineTransform<ScalarType, 3>;
-}
-
-#endif
+QmitkMonaiLabel3DToolGUI::QmitkMonaiLabel3DToolGUI(): QmitkMonaiLabelToolGUI(3) {}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel3DToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel3DToolGUI.h
new file mode 100644
index 0000000000..20254534d3
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel3DToolGUI.h
@@ -0,0 +1,32 @@
+/*============================================================================
+
+The Medical Imaging Interaction Toolkit (MITK)
+
+Copyright (c) German Cancer Research Center (DKFZ)
+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 QmitkMonaiLabelTool3DGUI_h
+#define QmitkMonaiLabelTool3DGUI_h
+
+#include <MitkSegmentationUIExports.h>
+#include "QmitkMonaiLabelToolGUI.h"
+
+class MITKSEGMENTATIONUI_EXPORT QmitkMonaiLabel3DToolGUI : public QmitkMonaiLabelToolGUI
+{
+ Q_OBJECT
+
+public:
+ mitkClassMacro(QmitkMonaiLabel3DToolGUI, QmitkMonaiLabelToolGUI);
+ itkFactorylessNewMacro(Self);
+ itkCloneMacro(Self);
+
+protected:
+ QmitkMonaiLabel3DToolGUI();
+ ~QmitkMonaiLabel3DToolGUI() = default;
+};
+#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.cpp
new file mode 100644
index 0000000000..256f783b59
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.cpp
@@ -0,0 +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 "QmitkMonaiLabelToolGUI.h"
+
+#include <QIcon>
+#include <QMessageBox>
+#include <QUrl>
+#include <QmitkStyleManager.h>
+#include <mitkCoreServices.h>
+#include <mitkIPreferencesService.h>
+
+namespace
+{
+ mitk::IPreferences *GetPreferences()
+ {
+ auto *preferencesService = mitk::CoreServices::GetPreferencesService();
+ return preferencesService->GetSystemPreferences()->Node("org.mitk.views.segmentation");
+ }
+
+ const static QString CONFIRM_QUESTION_TEXT =
+ "Data will be sent to the processing server devoid of any patient information. Are you sure you want continue?";
+
+ /* Pretested models in MITK and corresponding app */
+ const static QMap<QString, QString> WHITELISTED_MODELS = {
+ {"deepgrow_2d", "Radiology"},
+ {"deepgrow_3d", "Radiology"},
+ {"deepedit_seg", "Radiology"},
+ {"localization_vertebra", "Radiology"},
+ {"segmentation", "Radiology"},
+ {"segmentation_spleen", "Radiology"},
+ {"segmentation_vertebra", "Radiology"},
+ {"deepgrow_pipeline", "Radiology"},
+ {"vertebra_pipeline", "Radiology"}};
+
+ /* Strictly unsupported models in MITK and corresponding app */
+ const static QMap<QString, QString> BLACKLISTED_MODELS = {
+ {"deepedit", "Radiology"}, // interaction type not yet supported
+ {"localization_spine", "Radiology"}}; // Metadata discrepancy with label names
+}
+
+QmitkMonaiLabelToolGUI::QmitkMonaiLabelToolGUI(int dimension)
+ : QmitkMultiLabelSegWithPreviewToolGUIBase(),
+ m_SuperclassEnableConfirmSegBtnFnc(m_EnableConfirmSegBtnFnc),
+ m_Dimension(dimension)
+{
+ m_EnableConfirmSegBtnFnc = [this](bool enabled)
+ { return !m_FirstPreviewComputation ? m_SuperclassEnableConfirmSegBtnFnc(enabled) : false; };
+ m_Preferences = GetPreferences();
+ m_Preferences->OnPropertyChanged +=
+ mitk::MessageDelegate1<QmitkMonaiLabelToolGUI, const mitk::IPreferences::ChangeEvent &>(
+ this, &QmitkMonaiLabelToolGUI::OnPreferenceChangedEvent);
+}
+
+QmitkMonaiLabelToolGUI::~QmitkMonaiLabelToolGUI()
+{
+ auto tool = this->GetConnectedToolAs<mitk::MonaiLabelTool>();
+ if (nullptr != tool)
+ {
+ tool->MonaiStatusEvent -=
+ mitk::MessageDelegate1<QmitkMonaiLabelToolGUI, const bool>(this, &QmitkMonaiLabelToolGUI::StatusMessageListener);
+ }
+ m_Preferences->OnPropertyChanged -=
+ mitk::MessageDelegate1<QmitkMonaiLabelToolGUI, const mitk::IPreferences::ChangeEvent &>(
+ this, &QmitkMonaiLabelToolGUI::OnPreferenceChangedEvent);
+}
+
+void QmitkMonaiLabelToolGUI::ConnectNewTool(mitk::SegWithPreviewTool *newTool)
+{
+ Superclass::ConnectNewTool(newTool);
+ m_FirstPreviewComputation = true;
+}
+
+void QmitkMonaiLabelToolGUI::InitializeUI(QBoxLayout *mainLayout)
+{
+ m_Controls.setupUi(this);
+ mainLayout->addLayout(m_Controls.verticalLayout);
+
+ connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewBtnClicked()));
+ connect(m_Controls.fetchUrl, SIGNAL(clicked()), this, SLOT(OnFetchBtnClicked()));
+ connect(m_Controls.modelBox,
+ QOverload<int>::of(&QComboBox::activated),
+ [=](int index) { OnModelChanged(m_Controls.modelBox->itemText(index)); });
+ QIcon refreshIcon =
+ QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/view-refresh.svg"));
+ m_Controls.fetchUrl->setIcon(refreshIcon);
+ m_Controls.previewButton->setEnabled(false);
+ Superclass::InitializeUI(mainLayout);
+}
+
+void QmitkMonaiLabelToolGUI::EnableWidgets(bool enabled)
+{
+ Superclass::EnableWidgets(enabled);
+}
+
+void QmitkMonaiLabelToolGUI::StatusMessageListener(const bool status)
+{
+ if (!status)
+ {
+ return;
+ }
+ auto tool = this->GetConnectedToolAs<mitk::MonaiLabelTool>();
+ if (nullptr == tool)
+ {
+ return;
+ }
+ this->SetLabelSetPreview(tool->GetPreviewSegmentation());
+ this->ActualizePreviewLabelVisibility();
+ m_FirstPreviewComputation = false;
+}
+
+void QmitkMonaiLabelToolGUI::DisplayWidgets(bool enabled)
+{
+ Superclass::DisplayTransferWidgets(enabled);
+ m_Controls.previewButton->setVisible(enabled);
+}
+
+void QmitkMonaiLabelToolGUI::OnModelChanged(const QString &modelName)
+{
+ auto tool = this->GetConnectedToolAs<mitk::MonaiLabelTool>();
+ if (nullptr == tool)
+ {
+ return;
+ }
+ m_Controls.labelListLabel->clear();
+ mitk::MonaiModelInfo model = tool->GetModelInfoFromName(modelName.toStdString());
+ if (model.IsInteractive())
+ {
+ this->WriteStatusMessage("Interactive model selected. Please press SHIFT + click on the render windows.\n");
+ m_Controls.previewButton->setEnabled(false);
+ this->DisplayWidgets(false);
+ }
+ else
+ {
+ this->WriteStatusMessage("Auto-segmentation model selected. Please click on Preview.\n");
+ m_Controls.previewButton->setEnabled(true);
+ this->DisplayWidgets(true);
+ }
+ auto selectedModel = m_Controls.modelBox->currentText().toStdString();
+ for (const auto &modelObject : tool->GetInfoParameters()->models)
+ {
+ if (modelObject.name == selectedModel)
+ {
+ auto requestObject = std::make_unique<mitk::MonaiLabelRequest>();
+ requestObject->model = modelObject;
+ requestObject->hostName = tool->GetInfoParameters()->hostName;
+ requestObject->port = tool->GetInfoParameters()->port;
+ if (modelObject.IsInteractive()) // set only if interactive model
+ {
+ tool->m_RequestParameters = std::move(requestObject);
+ }
+ QStringList supportedLabels;
+ for (const auto &label : modelObject.labels)
+ {
+ supportedLabels << QString::fromStdString(label.first);
+ }
+ m_Controls.labelListLabel->setText(supportedLabels.join(QStringLiteral(", ")));
+ break;
+ }
+ }
+ tool->MonaiStatusEvent +=
+ mitk::MessageDelegate1<QmitkMonaiLabelToolGUI, const bool>(this, &QmitkMonaiLabelToolGUI::StatusMessageListener);
+}
+
+void QmitkMonaiLabelToolGUI::OnFetchBtnClicked()
+{
+ m_Controls.previewButton->setEnabled(false);
+ m_Controls.labelListLabel->clear();
+ auto reply = QMessageBox::question(this, "Confirm", ::CONFIRM_QUESTION_TEXT, QMessageBox::Yes | QMessageBox::No);
+ if (reply == QMessageBox::No)
+ {
+ MITK_INFO << "Didn't went ahead with Monai Label inferencing";
+ return;
+ }
+ auto tool = this->GetConnectedToolAs<mitk::MonaiLabelTool>();
+ if (nullptr == tool)
+ {
+ return;
+ }
+ QString urlString = m_Controls.urlBox->text();
+ QUrl url(urlString);
+ if (url.isValid() && !url.isLocalFile() && !url.hasFragment() && !url.hasQuery()) // sanity check
+ {
+ std::string hostName = url.host().toStdString();
+ int port = url.port();
+ try
+ {
+ tool->FetchOverallInfo(hostName, port);
+ bool allowAllModels = m_Preferences->GetBool("monailabel allow all models", false);
+ this->PopulateUI(allowAllModels);
+ }
+ catch (const mitk::Exception &e)
+ {
+ m_Controls.appBox->clear();
+ m_Controls.modelBox->clear();
+ MITK_ERROR << e.GetDescription();
+ this->WriteErrorMessage(e.GetDescription());
+ }
+ }
+ else
+ {
+ std::string invalidURLMessage = "Invalid URL entered: " + urlString.toStdString();
+ MITK_ERROR << invalidURLMessage;
+ this->ShowErrorMessage(invalidURLMessage);
+ }
+}
+
+void QmitkMonaiLabelToolGUI::OnPreviewBtnClicked()
+{
+ auto tool = this->GetConnectedToolAs<mitk::MonaiLabelTool>();
+ if (nullptr == tool)
+ {
+ return;
+ }
+ tool->ClearPicks(); // clear any interactive segmentation from before
+ auto selectedModel = m_Controls.modelBox->currentText().toStdString();
+ for (const auto &modelObject : tool->GetInfoParameters()->models)
+ {
+ if (modelObject.name == selectedModel)
+ {
+ auto requestObject = std::make_unique<mitk::MonaiLabelRequest>();
+ requestObject->model = modelObject;
+ requestObject->hostName = tool->GetInfoParameters()->hostName;
+ requestObject->port = tool->GetInfoParameters()->port;
+ tool->m_RequestParameters = std::move(requestObject);
+ break;
+ }
+ }
+ try
+ {
+ tool->UpdatePreview();
+ }
+ catch (const std::exception &e)
+ {
+ std::stringstream errorMsg;
+ errorMsg << "<b>STATUS: </b>Error while processing parameters for MONAI Label segmentation. Reason: "
+ << e.what();
+ this->ShowErrorMessage(errorMsg.str());
+ this->WriteErrorMessage(QString::fromStdString(errorMsg.str()));
+ m_Controls.previewButton->setEnabled(true);
+ return;
+ }
+ catch (...)
+ {
+ std::string errorMsg = "Unkown error occured while generation MONAI Label segmentation.";
+ this->ShowErrorMessage(errorMsg);
+ m_Controls.previewButton->setEnabled(true);
+ return;
+ }
+}
+
+void QmitkMonaiLabelToolGUI::PopulateUI(bool allowAllModels)
+{
+ auto tool = this->GetConnectedToolAs<mitk::MonaiLabelTool>();
+ if (nullptr == tool)
+ {
+ return;
+ }
+ m_Controls.appBox->clear();
+ m_Controls.labelListLabel->clear();
+ if (nullptr != tool->GetInfoParameters())
+ {
+ QString appName = QString::fromStdString(tool->GetInfoParameters()->name);
+ auto autoModels = tool->GetAutoSegmentationModels(m_Dimension);
+ auto interactiveModels = tool->GetInteractiveSegmentationModels(m_Dimension);
+ autoModels.insert(autoModels.end(), interactiveModels.begin(), interactiveModels.end());
+ this->WriteStatusMessage(appName);
+ m_Controls.appBox->addItem(appName);
+ this->PopulateModelBox(appName, autoModels, allowAllModels);
+ m_Controls.modelBox->setCurrentIndex(-1);
+ }
+}
+
+void QmitkMonaiLabelToolGUI::PopulateModelBox(QString appName, std::vector<mitk::MonaiModelInfo> models, bool allowAllModels)
+{
+ m_Controls.modelBox->clear();
+ for (const auto &model : models)
+ {
+ QString modelName = QString::fromStdString(model.name);
+ if (allowAllModels)
+ {
+ if (::BLACKLISTED_MODELS.contains(modelName) && appName.contains(::BLACKLISTED_MODELS[modelName]))
+ {
+ continue;
+ }
+ m_Controls.modelBox->addItem(modelName);
+ }
+ else
+ {
+ if (::WHITELISTED_MODELS.contains(modelName))
+ {
+ m_Controls.modelBox->addItem(modelName);
+ }
+ }
+ }
+}
+
+void QmitkMonaiLabelToolGUI::WriteStatusMessage(const QString &message)
+{
+ m_Controls.responseNote->setText(message);
+ m_Controls.responseNote->setStyleSheet("font-weight: bold; color: white");
+ qApp->processEvents();
+}
+
+void QmitkMonaiLabelToolGUI::WriteErrorMessage(const QString &message)
+{
+ m_Controls.responseNote->setText(message);
+ m_Controls.responseNote->setStyleSheet("font-weight: bold; color: red");
+ qApp->processEvents();
+}
+
+void QmitkMonaiLabelToolGUI::ShowErrorMessage(const std::string &message)
+{
+ this->setCursor(Qt::ArrowCursor);
+ QMessageBox::critical(nullptr, "MONAI Label", message.c_str());
+ MITK_WARN << message;
+}
+
+void QmitkMonaiLabelToolGUI::OnPreferenceChangedEvent(const mitk::IPreferences::ChangeEvent& event)
+{
+ if (event.GetProperty().rfind("monai", 0) == 0)
+ {
+ bool allowAllModels = m_Preferences->GetBool("monailabel allow all models", false);
+ this->PopulateUI(allowAllModels);
+ }
+}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.h
new file mode 100644
index 0000000000..58739ff2f4
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.h
@@ -0,0 +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 QmitkMonaiLabelToolGUI_h
+#define QmitkMonaiLabelToolGUI_h
+
+#include "QmitkMultiLabelSegWithPreviewToolGUIBase.h"
+#include "ui_QmitkMonaiLabelToolGUIControls.h"
+#include <MitkSegmentationUIExports.h>
+#include <QMessageBox>
+#include <mitkIPreferences.h>
+#include <mitkMonaiLabelTool.h>
+#include <QMap>
+
+class MITKSEGMENTATIONUI_EXPORT QmitkMonaiLabelToolGUI : public QmitkMultiLabelSegWithPreviewToolGUIBase
+{
+ Q_OBJECT
+
+public:
+ mitkClassMacro(QmitkMonaiLabelToolGUI, QmitkMultiLabelSegWithPreviewToolGUIBase);
+ itkCloneMacro(Self);
+
+protected slots:
+
+ void OnPreviewBtnClicked();
+ void OnFetchBtnClicked();
+ void OnModelChanged(const QString &);
+
+protected:
+ QmitkMonaiLabelToolGUI(int dimension);
+ ~QmitkMonaiLabelToolGUI();
+
+ void ConnectNewTool(mitk::SegWithPreviewTool *newTool) override;
+ void InitializeUI(QBoxLayout *mainLayout) override;
+
+ void EnableWidgets(bool enabled) override;
+
+ virtual void DisplayWidgets(bool enabled);
+
+ /**
+ * @brief Writes any message in white on the tool pane.
+ */
+ void WriteStatusMessage(const QString &);
+
+ /**
+ * @brief Writes any message in red on the tool pane.
+ */
+ void WriteErrorMessage(const QString &);
+
+ /**
+ * @brief Creates a QMessage object and shows on screen.
+ */
+ void ShowErrorMessage(const std::string &message);
+
+ /**
+ * @brief Function to listen to tool class status emitters.
+ */
+ void StatusMessageListener(const bool status);
+
+ /**
+ * @brief Function to listen to Preference changes
+ */
+ void OnPreferenceChangedEvent(const mitk::IPreferences::ChangeEvent &event);
+
+ /**
+ * @brief Helper function to write MONAI model info in to model combo box
+ */
+ void PopulateModelBox(QString appName, std::vector<mitk::MonaiModelInfo> models, bool allowAllModels);
+
+ /**
+ * @brief Helper function to populate required server metadata into UI
+ */
+ void PopulateUI(bool allowAllModels);
+
+private:
+ mitk::IPreferences *m_Preferences;
+ Ui_QmitkMonaiLabelToolGUIControls m_Controls;
+ bool m_FirstPreviewComputation = true;
+ EnableConfirmSegBtnFunctionType m_SuperclassEnableConfirmSegBtnFnc;
+ int m_Dimension;
+};
+
+#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUIControls.ui
new file mode 100644
index 0000000000..3344e86c13
--- /dev/null
+++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUIControls.ui
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QmitkMonaiLabelToolGUIControls</class>
+ <widget class="QWidget" name="QmitkMonaiLabelToolGUIControls">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>699</width>
+ <height>352</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Ignored" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100000</width>
+ <height>100000</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>QmitkMonaiToolWidget</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</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>
+ <layout class="QGridLayout" name="basicSettingsLayout">
+ <item row="1" column="0">
+ <widget class="QLabel" name="monaiUrlLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>MONAI Label Server URL:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" colspan="3">
+ <widget class="QLineEdit" name="urlBox">
+ <property name="text">
+ <string>https://localhost:8000</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QPushButton" name="fetchUrl"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="appLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Available Apps:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" colspan="4">
+ <widget class="QLabel" name="welcomeNote">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Welcome to MONAI Label App in MITK. [Experimental]&lt;/p&gt;&lt;p&gt;Please note that this is only an interface to MONAI Label. MITK does not ship with any apps. Make sure to have a started a MONAI Label server beforehand. &lt;/p&gt;&lt;p&gt;Refer to &lt;a href=&quot;https://docs.monai.io/projects/label/en/latest/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://docs.monai.io/projects/label/en/latest/&lt;/span&gt;&lt;/a&gt; to learn everything about the MONAI Label App.&lt;/p&gt;&lt;p&gt;Provide a valid URL (eg. https://localhost:8000) to the server &amp;amp; start the workflow.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" colspan="4">
+ <widget class="ctkComboBox" name="appBox"/>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="modelLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Models:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" colspan="4">
+ <widget class="ctkComboBox" name="modelBox"/>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="supportedLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Supported Classes:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" colspan="4">
+ <widget class="QLabel" name="labelListLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="responseNote">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Welcome to MONAI Label in MITK. [Experimental]&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="previewButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100000</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Preview</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <customwidgets>
+ <customwidget>
+ <class>ctkComboBox</class>
+ <extends>QComboBox</extends>
+ <header location="global">ctkComboBox.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp
index f20cfe0426..644094ca74 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp
@@ -1,1416 +1,1426 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <QmitkMultiLabelInspector.h>
// mitk
#include <mitkRenderingManager.h>
#include <mitkLabelSetImageHelper.h>
#include <mitkDICOMSegmentationPropertyHelper.h>
-#include <mitkVectorProperty.h>
// Qmitk
#include <QmitkMultiLabelTreeModel.h>
#include <QmitkLabelColorItemDelegate.h>
#include <QmitkLabelToggleItemDelegate.h>
#include <QmitkStyleManager.h>
// Qt
#include <QMenu>
#include <QLabel>
#include <QWidgetAction>
#include <QMessageBox>
+#include <QKeyEvent>
#include <ui_QmitkMultiLabelInspectorControls.h>
-namespace
-{
- void ActivateLabelHighlights(mitk::DataNode* node, const mitk::LabelSetImage::LabelValueVectorType highlightedValues)
- {
- const std::string propertyName = "org.mitk.multilabel.labels.highlighted";
-
- mitk::IntVectorProperty::Pointer prop = dynamic_cast<mitk::IntVectorProperty*>(node->GetNonConstProperty(propertyName));
- if (nullptr == prop)
- {
- prop = mitk::IntVectorProperty::New();
- node->SetProperty(propertyName, prop);
- }
-
- mitk::IntVectorProperty::VectorType intValues(highlightedValues.begin(), highlightedValues.end());
- prop->SetValue(intValues);
- prop->Modified(); //see T30386; needed because VectorProperty::SetValue does currently trigger no modified
- mitk::RenderingManager::GetInstance()->RequestUpdateAll();
- }
-
- void DeactivateLabelHighlights(mitk::DataNode* node)
- {
- std::string propertyName = "org.mitk.multilabel.labels.highlighted";
-
- mitk::IntVectorProperty::Pointer prop = dynamic_cast<mitk::IntVectorProperty*>(node->GetNonConstProperty(propertyName));
- if (nullptr != prop)
- {
- prop->SetValue({});
- prop->Modified(); //see T30386; needed because VectorProperty::SetValue does currently trigger no modified
-
- mitk::RenderingManager::GetInstance()->RequestUpdateAll();
- }
- }
-}
QmitkMultiLabelInspector::QmitkMultiLabelInspector(QWidget* parent/* = nullptr*/)
: QWidget(parent), m_Controls(new Ui::QmitkMultiLabelInspector), m_SegmentationNodeDataMTime(0)
{
m_Controls->setupUi(this);
m_Model = new QmitkMultiLabelTreeModel(this);
m_Controls->view->setModel(m_Model);
m_ColorItemDelegate = new QmitkLabelColorItemDelegate(this);
auto visibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg"));
auto invisibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/invisible.svg"));
m_VisibilityItemDelegate = new QmitkLabelToggleItemDelegate(visibleIcon, invisibleIcon, this);
auto lockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg"));
auto unlockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg"));
m_LockItemDelegate = new QmitkLabelToggleItemDelegate(lockIcon, unlockIcon, this);
auto* view = this->m_Controls->view;
view->setItemDelegateForColumn(1, m_LockItemDelegate);
view->setItemDelegateForColumn(2, m_ColorItemDelegate);
view->setItemDelegateForColumn(3, m_VisibilityItemDelegate);
auto* header = view->header();
header->setSectionResizeMode(0,QHeaderView::Stretch);
header->setSectionResizeMode(1, QHeaderView::ResizeToContents);
header->setSectionResizeMode(2, QHeaderView::ResizeToContents);
header->setSectionResizeMode(3, QHeaderView::ResizeToContents);
view->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_Model, &QAbstractItemModel::modelReset, this, &QmitkMultiLabelInspector::OnModelReset);
connect(m_Model, &QAbstractItemModel::dataChanged, this, &QmitkMultiLabelInspector::OnDataChanged);
connect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(OnChangeModelSelection(const QItemSelection&, const QItemSelection&)));
connect(view, &QAbstractItemView::customContextMenuRequested, this, &QmitkMultiLabelInspector::OnContextMenuRequested);
connect(view, &QAbstractItemView::doubleClicked, this, &QmitkMultiLabelInspector::OnItemDoubleClicked);
connect(view, &QAbstractItemView::entered, this, &QmitkMultiLabelInspector::OnEntered);
connect(view, &QmitkMultiLabelTreeView::MouseLeave, this, &QmitkMultiLabelInspector::OnMouseLeave);
}
QmitkMultiLabelInspector::~QmitkMultiLabelInspector()
{
delete m_Controls;
}
void QmitkMultiLabelInspector::Initialize()
{
m_LastValidSelectedLabels = {};
m_ModelManipulationOngoing = false;
m_Model->SetSegmentation(m_Segmentation);
m_Controls->view->expandAll();
m_LastValidSelectedLabels = {};
- //in singel selection mode, if at least one label exist select the first label of the mode.
+ //in single selection mode, if at least one label exist select the first label of the mode.
if (m_Segmentation.IsNotNull() && !this->GetMultiSelectionMode() && m_Segmentation->GetTotalNumberOfLabels() > 0)
{
auto firstIndex = m_Model->FirstLabelInstanceIndex(QModelIndex());
auto labelVariant = firstIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
if (labelVariant.isValid())
{
this->SetSelectedLabel(labelVariant.value<LabelValueType>());
m_Controls->view->selectionModel()->setCurrentIndex(firstIndex, QItemSelectionModel::NoUpdate);
}
}
}
void QmitkMultiLabelInspector::SetMultiSelectionMode(bool multiMode)
{
m_Controls->view->setSelectionMode(multiMode
? QAbstractItemView::SelectionMode::MultiSelection
: QAbstractItemView::SelectionMode::SingleSelection);
}
bool QmitkMultiLabelInspector::GetMultiSelectionMode() const
{
return QAbstractItemView::SelectionMode::MultiSelection == m_Controls->view->selectionMode();
}
void QmitkMultiLabelInspector::SetAllowVisibilityModification(bool visibilityMod)
{
m_AllowVisibilityModification = visibilityMod;
this->m_Model->SetAllowVisibilityModification(visibilityMod);
}
void QmitkMultiLabelInspector::SetAllowLabelModification(bool labelMod)
{
m_AllowLabelModification = labelMod;
}
bool QmitkMultiLabelInspector::GetAllowVisibilityModification() const
{
return m_AllowVisibilityModification;
}
void QmitkMultiLabelInspector::SetAllowLockModification(bool lockMod)
{
m_AllowLockModification = lockMod;
this->m_Model->SetAllowLockModification(lockMod);
}
bool QmitkMultiLabelInspector::GetAllowLockModification() const
{
return m_AllowLockModification;
}
bool QmitkMultiLabelInspector::GetAllowLabelModification() const
{
return m_AllowLabelModification;
}
void QmitkMultiLabelInspector::SetDefaultLabelNaming(bool defaultLabelNaming)
{
m_DefaultLabelNaming = defaultLabelNaming;
}
void QmitkMultiLabelInspector::SetMultiLabelSegmentation(mitk::LabelSetImage* segmentation)
{
if (segmentation != m_Segmentation)
{
m_Segmentation = segmentation;
this->Initialize();
emit SegmentationChanged();
}
}
mitk::LabelSetImage* QmitkMultiLabelInspector::GetMultiLabelSegmentation() const
{
return m_Segmentation;
}
void QmitkMultiLabelInspector::SetMultiLabelNode(mitk::DataNode* node)
{
if (node != this->m_SegmentationNode.GetPointer())
{
m_SegmentationObserver.Reset();
m_SegmentationNode = node;
+ m_LabelHighlightGuard.SetSegmentationNode(m_SegmentationNode);
m_SegmentationNodeDataMTime = 0;
if (m_SegmentationNode.IsNotNull())
{
auto& widget = *this;
auto checkAndSetSeg = [&widget, node](const itk::EventObject&)
{
if (widget.m_SegmentationNodeDataMTime < node->GetDataReferenceChangedTime())
{
auto newSeg = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
if (nullptr == newSeg) mitkThrow() << "Invalid usage. Node set does not contain a segmentation.";
widget.m_SegmentationNodeDataMTime = node->GetDataReferenceChangedTime();
widget.SetMultiLabelSegmentation(newSeg);
}
};
m_SegmentationObserver.Reset(node, itk::ModifiedEvent(), checkAndSetSeg);
checkAndSetSeg(itk::ModifiedEvent());
}
else
{
this->SetMultiLabelSegmentation(nullptr);
}
}
}
mitk::DataNode* QmitkMultiLabelInspector::GetMultiLabelNode() const
{
return m_SegmentationNode;
}
bool QmitkMultiLabelInspector::GetModelManipulationOngoing() const
{
return m_ModelManipulationOngoing;
}
void QmitkMultiLabelInspector::OnModelReset()
{
m_LastValidSelectedLabels = {};
m_ModelManipulationOngoing = false;
}
void QmitkMultiLabelInspector::OnDataChanged(const QModelIndex& topLeft, const QModelIndex& /*bottomRight*/,
const QList<int>& /*roles*/)
{
if (!m_ModelManipulationOngoing && topLeft.isValid())
m_Controls->view->expand(topLeft);
}
bool EqualLabelSelections(const QmitkMultiLabelInspector::LabelValueVectorType& selection1, const QmitkMultiLabelInspector::LabelValueVectorType& selection2)
{
if (selection1.size() == selection2.size())
{
// lambda to compare node pointer inside both lists
return std::is_permutation(selection1.begin(), selection1.end(), selection2.begin());
}
return false;
}
void QmitkMultiLabelInspector::SetSelectedLabels(const LabelValueVectorType& selectedLabels)
{
if (EqualLabelSelections(this->GetSelectedLabels(), selectedLabels))
{
return;
}
this->UpdateSelectionModel(selectedLabels);
m_LastValidSelectedLabels = selectedLabels;
}
void QmitkMultiLabelInspector::UpdateSelectionModel(const LabelValueVectorType& selectedLabels)
{
// create new selection by retrieving the corresponding indexes of the labels
QItemSelection newCurrentSelection;
for (const auto& labelID : selectedLabels)
{
QModelIndexList matched = m_Model->match(m_Model->index(0, 0), QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole, QVariant(labelID), 1, Qt::MatchRecursive);
if (!matched.empty())
{
newCurrentSelection.select(matched.front(), matched.front());
}
}
m_Controls->view->selectionModel()->select(newCurrentSelection, QItemSelectionModel::ClearAndSelect|QItemSelectionModel::Current);
}
void QmitkMultiLabelInspector::SetSelectedLabel(mitk::LabelSetImage::LabelValueType selectedLabel)
{
this->SetSelectedLabels({ selectedLabel });
}
QmitkMultiLabelInspector::LabelValueVectorType QmitkMultiLabelInspector::GetSelectedLabelsFromSelectionModel() const
{
LabelValueVectorType result;
QModelIndexList selectedIndexes = m_Controls->view->selectionModel()->selectedIndexes();
for (const auto& index : std::as_const(selectedIndexes))
{
QVariant qvariantDataNode = m_Model->data(index, QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
if (qvariantDataNode.canConvert<mitk::LabelSetImage::LabelValueType>())
{
result.push_back(qvariantDataNode.value<mitk::LabelSetImage::LabelValueType>());
}
}
return result;
}
QmitkMultiLabelInspector::LabelValueVectorType QmitkMultiLabelInspector::GetSelectedLabels() const
{
return m_LastValidSelectedLabels;
}
mitk::Label* QmitkMultiLabelInspector::GetFirstSelectedLabelObject() const
{
if (m_LastValidSelectedLabels.empty() || m_Segmentation.IsNull())
return nullptr;
return m_Segmentation->GetLabel(m_LastValidSelectedLabels.front());
}
void QmitkMultiLabelInspector::OnChangeModelSelection(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/)
{
if (!m_ModelManipulationOngoing)
{
auto internalSelection = GetSelectedLabelsFromSelectionModel();
if (internalSelection.empty())
{
//empty selections are not allowed by UI interactions, there should always be at least on label selected.
//but selections are e.g. also cleared if the model is updated (e.g. due to addition of labels)
UpdateSelectionModel(m_LastValidSelectedLabels);
}
else
{
m_LastValidSelectedLabels = internalSelection;
emit CurrentSelectionChanged(GetSelectedLabels());
}
}
}
void QmitkMultiLabelInspector::WaitCursorOn() const
{
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
}
void QmitkMultiLabelInspector::WaitCursorOff() const
{
this->RestoreOverrideCursor();
}
void QmitkMultiLabelInspector::RestoreOverrideCursor() const
{
QApplication::restoreOverrideCursor();
}
mitk::Label* QmitkMultiLabelInspector::GetCurrentLabel() const
{
auto currentIndex = this->m_Controls->view->currentIndex();
auto labelVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelDataRole);
mitk::Label::Pointer currentIndexLabel = nullptr;
if (labelVariant.isValid())
{
auto uncastedLabel = labelVariant.value<void*>();
currentIndexLabel = static_cast<mitk::Label*>(uncastedLabel);
}
return currentIndexLabel;
}
QmitkMultiLabelInspector::IndexLevelType QmitkMultiLabelInspector::GetCurrentLevelType() const
{
auto currentIndex = this->m_Controls->view->currentIndex();
auto labelInstanceVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceDataRole);
auto labelVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelDataRole);
if (labelInstanceVariant.isValid() )
{
return IndexLevelType::LabelInstance;
}
else if (labelVariant.isValid())
{
return IndexLevelType::LabelClass;
}
return IndexLevelType::Group;
}
QmitkMultiLabelInspector::LabelValueVectorType QmitkMultiLabelInspector::GetCurrentlyAffactedLabelInstances() const
{
auto currentIndex = m_Controls->view->currentIndex();
return m_Model->GetLabelsInSubTree(currentIndex);
}
QmitkMultiLabelInspector::LabelValueVectorType QmitkMultiLabelInspector::GetLabelInstancesOfSelectedFirstLabel() const
{
if (m_Segmentation.IsNull())
return {};
if (this->GetSelectedLabels().empty())
return {};
const auto index = m_Model->indexOfLabel(this->GetSelectedLabels().front());
return m_Model->GetLabelInstancesOfSameLabelClass(index);
}
mitk::Label* QmitkMultiLabelInspector::AddNewLabelInstanceInternal(mitk::Label* templateLabel)
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of AddNewLabelInstance.";
if (nullptr == templateLabel)
mitkThrow() << "QmitkMultiLabelInspector is in an invalid state. AddNewLabelInstanceInternal was called with a non existing label as template";
auto groupID = m_Segmentation->GetGroupIndexOfLabel(templateLabel->GetValue());
m_ModelManipulationOngoing = true;
auto newLabel = m_Segmentation->AddLabel(templateLabel, groupID, true);
m_ModelManipulationOngoing = false;
this->SetSelectedLabel(newLabel->GetValue());
auto index = m_Model->indexOfLabel(newLabel->GetValue());
if (index.isValid())
{
m_Controls->view->expand(index.parent());
}
else
{
mitkThrow() << "Segmentation or QmitkMultiLabelTreeModel is in an invalid state. Label is not present in the model after adding it to the segmentation. Label value: " << newLabel->GetValue();
}
emit ModelUpdated();
return newLabel;
}
mitk::Label* QmitkMultiLabelInspector::AddNewLabelInstance()
{
auto currentLabel = this->GetFirstSelectedLabelObject();
if (nullptr == currentLabel)
return nullptr;
auto result = this->AddNewLabelInstanceInternal(currentLabel);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
return result;
}
mitk::Label* QmitkMultiLabelInspector::AddNewLabelInternal(const mitk::LabelSetImage::GroupIndexType& containingGroup)
{
auto newLabel = mitk::LabelSetImageHelper::CreateNewLabel(m_Segmentation);
+ bool canceled = false;
if (!m_DefaultLabelNaming)
- emit LabelRenameRequested(newLabel, false);
+ emit LabelRenameRequested(newLabel, false, canceled);
+
+ if (canceled) return nullptr;
m_ModelManipulationOngoing = true;
m_Segmentation->AddLabel(newLabel, containingGroup, false);
m_ModelManipulationOngoing = false;
this->SetSelectedLabel(newLabel->GetValue());
auto index = m_Model->indexOfLabel(newLabel->GetValue());
if (!index.isValid())
mitkThrow() << "Segmentation or QmitkMultiLabelTreeModel is in an invalid state. Label is not present in the "
"model after adding it to the segmentation. Label value: " << newLabel->GetValue();
m_Controls->view->expand(index.parent());
emit ModelUpdated();
return newLabel;
}
mitk::Label* QmitkMultiLabelInspector::AddNewLabel()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of AddNewLabel.";
if (m_Segmentation.IsNull())
{
return nullptr;
}
auto currentLabel = this->GetFirstSelectedLabelObject();
mitk::LabelSetImage::GroupIndexType groupID = nullptr != currentLabel
? m_Segmentation->GetGroupIndexOfLabel(currentLabel->GetValue())
: 0;
auto result = AddNewLabelInternal(groupID);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
return result;
}
void QmitkMultiLabelInspector::DeleteLabelInstance()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of DeleteLabelInstance.";
if (m_Segmentation.IsNull())
return;
auto label = this->GetFirstSelectedLabelObject();
if (nullptr == label)
return;
auto index = m_Model->indexOfLabel(label->GetValue());
auto instanceName = index.data(Qt::DisplayRole);
auto question = "Do you really want to delete label instance \"" + instanceName.toString() + "\"?";
auto answer = QMessageBox::question(this, QString("Delete label instances"), question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answer == QMessageBox::Yes)
{
this->DeleteLabelInternal({ label->GetValue() });
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
void QmitkMultiLabelInspector::DeleteLabel()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of DeleteLabel.";
if (m_Segmentation.IsNull())
return;
const auto label = this->GetFirstSelectedLabelObject();
if (nullptr == label)
return;
const auto relevantLabels = this->GetLabelInstancesOfSelectedFirstLabel();
if (relevantLabels.empty())
return;
auto question = "Do you really want to delete label \"" + QString::fromStdString(label->GetName());
question = relevantLabels.size()==1 ? question + "\"?" : question + "\" with all "+QString::number(relevantLabels.size()) +" instances?";
auto answer = QMessageBox::question(this, QString("Delete label"), question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answer == QMessageBox::Yes)
{
this->DeleteLabelInternal(relevantLabels);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
void QmitkMultiLabelInspector::DeleteLabelInternal(const LabelValueVectorType& labelValues)
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of DeleteLabelInternal.";
if (m_Segmentation.IsNull())
{
return;
}
QVariant nextLabelVariant;
this->WaitCursorOn();
m_ModelManipulationOngoing = true;
for (auto labelValue : labelValues)
{
if (labelValue == labelValues.back())
{
auto currentIndex = m_Model->indexOfLabel(labelValue);
auto nextIndex = m_Model->ClosestLabelInstanceIndex(currentIndex);
nextLabelVariant = nextIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
}
m_Segmentation->RemoveLabel(labelValue);
}
m_ModelManipulationOngoing = false;
this->WaitCursorOff();
if (nextLabelVariant.isValid())
{
auto newLabelValue = nextLabelVariant.value<LabelValueType>();
this->SetSelectedLabel(newLabelValue);
auto index = m_Model->indexOfLabel(newLabelValue); //we have to get index again, because it could have changed due to remove operation.
if (index.isValid())
{
m_Controls->view->expand(index.parent());
}
else
{
mitkThrow() << "Segmentation or QmitkMultiLabelTreeModel is in an invalid state. Label is not present in the model after adding it to the segmentation. Label value: " << newLabelValue;
}
}
else
{
this->SetSelectedLabels({});
}
emit ModelUpdated();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
mitk::Label* QmitkMultiLabelInspector::AddNewGroup()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of AddNewLabel.";
if (m_Segmentation.IsNull())
{
return nullptr;
}
mitk::LabelSetImage::GroupIndexType groupID = 0;
mitk::Label* newLabel = nullptr;
m_ModelManipulationOngoing = true;
try
{
this->WaitCursorOn();
groupID = m_Segmentation->AddLayer();
m_Segmentation->SetActiveLayer(groupID);
this->WaitCursorOff();
newLabel = this->AddNewLabelInternal(groupID);
}
catch (mitk::Exception& e)
{
this->WaitCursorOff();
m_ModelManipulationOngoing = false;
MITK_ERROR << "Exception caught: " << e.GetDescription();
QMessageBox::information(this, "Add group", "Could not add a new group. See error log for details.");
}
m_ModelManipulationOngoing = false;
emit ModelUpdated();
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
return newLabel;
}
void QmitkMultiLabelInspector::RemoveGroupInternal(const mitk::LabelSetImage::GroupIndexType& groupID)
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of RemoveLabel.";
if (m_Segmentation.IsNull())
return;
if (m_Segmentation->GetNumberOfLayers() < 2)
return;
auto currentIndex = m_Model->indexOfGroup(groupID);
auto nextIndex = m_Model->ClosestLabelInstanceIndex(currentIndex);
auto labelVariant = nextIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
try
{
this->WaitCursorOn();
m_ModelManipulationOngoing = true;
m_Segmentation->RemoveGroup(groupID);
m_ModelManipulationOngoing = false;
this->WaitCursorOff();
}
catch (mitk::Exception& e)
{
m_ModelManipulationOngoing = false;
this->WaitCursorOff();
MITK_ERROR << "Exception caught: " << e.GetDescription();
QMessageBox::information(this, "Delete group", "Could not delete the currently active group. See error log for details.");
return;
}
if (labelVariant.isValid())
{
auto newLabelValue = labelVariant.value<LabelValueType>();
this->SetSelectedLabel(newLabelValue);
auto index = m_Model->indexOfLabel(newLabelValue); //we have to get index again, because it could have changed due to remove operation.
if (index.isValid())
{
m_Controls->view->expand(index.parent());
}
else
{
mitkThrow() << "Segmentation or QmitkMultiLabelTreeModel is in an invalid state. Label is not present in the model after adding it to the segmentation. Label value: " << newLabelValue;
}
}
else
{
this->SetSelectedLabels({});
}
emit ModelUpdated();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkMultiLabelInspector::RemoveGroup()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of RemoveLabel.";
if (m_Segmentation.IsNull())
return;
if (m_Segmentation->GetNumberOfLayers() < 2)
{
QMessageBox::information(this, "Delete group", "Cannot delete last remaining group. A segmentation must contain at least a single group.");
return;
}
const auto* selectedLabel = this->GetFirstSelectedLabelObject();
if (selectedLabel == nullptr)
return;
const auto groupID = m_Segmentation->GetGroupIndexOfLabel(selectedLabel->GetValue());
auto groupName = QString::fromStdString(mitk::LabelSetImageHelper::CreateDisplayGroupName(m_Segmentation, groupID));
auto question = QStringLiteral("Do you really want to delete group \"%1\" including all of its labels?").arg(groupName);
auto answer = QMessageBox::question(this, QString("Delete group \"%1\"").arg(groupName), question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (answer != QMessageBox::Yes)
return;
this->RemoveGroupInternal(groupID);
}
void QmitkMultiLabelInspector::OnDeleteGroup()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of RemoveLabel.";
if (m_Segmentation.IsNull())
return;
auto currentIndex = this->m_Controls->view->currentIndex();
auto groupIDVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::GroupIDRole);
if (groupIDVariant.isValid())
{
auto groupID = groupIDVariant.value<mitk::LabelSetImage::GroupIndexType>();
auto groupName = QString::fromStdString(mitk::LabelSetImageHelper::CreateDisplayGroupName(m_Segmentation, groupID));
auto question = QStringLiteral("Do you really want to delete group \"%1\" including all of its labels?").arg(groupName);
auto answer = QMessageBox::question(this, QString("Delete group \"%1\"").arg(groupName), question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (answer != QMessageBox::Yes)
return;
this->RemoveGroupInternal(groupID);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
};
void QmitkMultiLabelInspector::OnContextMenuRequested(const QPoint& /*pos*/)
{
if (m_Segmentation.IsNull() || !this->isEnabled())
return;
const auto indexLevel = this->GetCurrentLevelType();
auto currentIndex = this->m_Controls->view->currentIndex();
//this ensures correct highlighting is the context menu is triggered while
//another context menu is already open.
if (currentIndex.isValid() && this->m_AboutToShowContextMenu) this->OnEntered(this->m_Controls->view->currentIndex());
QMenu* menu = new QMenu(this);
if (IndexLevelType::Group == indexLevel)
{
if (m_AllowLabelModification)
{
QAction* addInstanceAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_add.svg")), "&Add label", this);
QObject::connect(addInstanceAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnAddLabel);
menu->addAction(addInstanceAction);
if (m_Segmentation->GetNumberOfLayers() > 1)
{
QAction* removeAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_group_delete.svg")), "Delete group", this);
QObject::connect(removeAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnDeleteGroup);
menu->addAction(removeAction);
}
}
if (m_AllowLockModification)
{
menu->addSeparator();
QAction* lockAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg")), "Lock group", this);
QObject::connect(lockAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnLockAffectedLabels);
menu->addAction(lockAllAction);
QAction* unlockAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg")), "Unlock group", this);
QObject::connect(unlockAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnUnlockAffectedLabels);
menu->addAction(unlockAllAction);
}
if (m_AllowVisibilityModification)
{
menu->addSeparator();
QAction* viewAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg")), "Show group", this);
QObject::connect(viewAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnSetAffectedLabelsVisible);
menu->addAction(viewAllAction);
QAction* hideAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/invisible.svg")), "Hide group", this);
QObject::connect(hideAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnSetAffectedLabelsInvisible);
menu->addAction(hideAllAction);
menu->addSeparator();
auto opacityAction = this->CreateOpacityAction();
if (nullptr != opacityAction)
menu->addAction(opacityAction);
}
}
else if (IndexLevelType::LabelClass == indexLevel)
{
if (m_AllowLabelModification)
{
QAction* addInstanceAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_add_instance.svg")), "Add label instance", this);
QObject::connect(addInstanceAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnAddLabelInstance);
menu->addAction(addInstanceAction);
QAction* renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "&Rename label", this);
QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool)));
menu->addAction(renameAction);
QAction* removeAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_delete.svg")), "&Delete label", this);
QObject::connect(removeAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnDeleteAffectedLabel);
menu->addAction(removeAction);
}
if (m_AllowLockModification)
{
menu->addSeparator();
QAction* lockAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg")), "Lock label instances", this);
QObject::connect(lockAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnLockAffectedLabels);
menu->addAction(lockAllAction);
QAction* unlockAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg")), "Unlock label instances", this);
QObject::connect(unlockAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnUnlockAffectedLabels);
menu->addAction(unlockAllAction);
}
if (m_AllowVisibilityModification)
{
menu->addSeparator();
QAction* viewAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg")), "Show label instances", this);
QObject::connect(viewAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnSetAffectedLabelsVisible);
menu->addAction(viewAllAction);
QAction* hideAllAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/invisible.svg")), "Hide label instances", this);
QObject::connect(hideAllAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnSetAffectedLabelsInvisible);
menu->addAction(hideAllAction);
menu->addSeparator();
auto opacityAction = this->CreateOpacityAction();
if (nullptr!=opacityAction)
menu->addAction(opacityAction);
}
}
else
{
auto selectedLabelValues = this->GetSelectedLabels();
if (selectedLabelValues.empty())
return;
if (this->GetMultiSelectionMode() && selectedLabelValues.size() > 1)
{
if (m_AllowLabelModification)
{
QAction* mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge selection on current label", this);
QObject::connect(mergeAction, SIGNAL(triggered(bool)), this, SLOT(OnMergeLabels(bool)));
menu->addAction(mergeAction);
QAction* removeLabelsAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_delete_instance.svg")), "&Delete selected labels", this);
QObject::connect(removeLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnDeleteLabels(bool)));
menu->addAction(removeLabelsAction);
QAction* clearLabelsAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "&Clear selected labels", this);
QObject::connect(clearLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnClearLabels(bool)));
menu->addAction(clearLabelsAction);
}
if (m_AllowVisibilityModification)
{
if (m_AllowLabelModification) menu->addSeparator();
QAction* viewOnlyAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg")), "Hide everything but this", this);
QObject::connect(viewOnlyAction, SIGNAL(triggered(bool)), this, SLOT(OnSetOnlyActiveLabelVisible(bool)));
menu->addAction(viewOnlyAction);
menu->addSeparator();
auto opacityAction = this->CreateOpacityAction();
if (nullptr != opacityAction)
menu->addAction(opacityAction);
}
}
else
{
if (m_AllowLabelModification)
{
QAction* addInstanceAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_add_instance.svg")), "&Add label instance", this);
QObject::connect(addInstanceAction, &QAction::triggered, this, &QmitkMultiLabelInspector::OnAddLabelInstance);
menu->addAction(addInstanceAction);
const auto selectedLabelIndex = m_Model->indexOfLabel(selectedLabelValues.front());
if (m_Model->GetLabelInstancesOfSameLabelClass(selectedLabelIndex).size() > 1) // Only labels that actually appear as instance (having additional instances)
{
QAction* renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "&Rename label instance", this);
QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool)));
menu->addAction(renameAction);
QAction* removeInstanceAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_delete_instance.svg")), "&Delete label instance", this);
QObject::connect(removeInstanceAction, &QAction::triggered, this, &QmitkMultiLabelInspector::DeleteLabelInstance);
menu->addAction(removeInstanceAction);
}
else
{
QAction* renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "&Rename label", this);
QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool)));
menu->addAction(renameAction);
}
QAction* removeLabelAction = new QAction(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_delete.svg")), "Delete &label", this);
QObject::connect(removeLabelAction, &QAction::triggered, this, &QmitkMultiLabelInspector::DeleteLabel);
menu->addAction(removeLabelAction);
QAction* clearAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "&Clear content", this);
QObject::connect(clearAction, SIGNAL(triggered(bool)), this, SLOT(OnClearLabel(bool)));
menu->addAction(clearAction);
}
if (m_AllowVisibilityModification)
{
if (m_AllowLabelModification) menu->addSeparator();
QAction* viewOnlyAction = new QAction(QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg")), "Hide everything but this", this);
QObject::connect(viewOnlyAction, SIGNAL(triggered(bool)), this, SLOT(OnSetOnlyActiveLabelVisible(bool)));
menu->addAction(viewOnlyAction);
menu->addSeparator();
auto opacityAction = this->CreateOpacityAction();
if (nullptr != opacityAction)
menu->addAction(opacityAction);
}
}
}
QObject::connect(menu, &QMenu::aboutToHide, this, &QmitkMultiLabelInspector::OnMouseLeave);
m_AboutToShowContextMenu = true;
menu->popup(QCursor::pos());
}
QWidgetAction* QmitkMultiLabelInspector::CreateOpacityAction()
{
auto selectedLabelValues = this->GetSelectedLabels();
auto relevantLabelValues = !this->GetMultiSelectionMode() || selectedLabelValues.size() <= 1
? this->GetCurrentlyAffactedLabelInstances()
: selectedLabelValues;
std::vector<mitk::Label*> relevantLabels;
if (!relevantLabelValues.empty())
{
for (auto value : relevantLabelValues)
{
auto label = this->m_Segmentation->GetLabel(value);
- if (nullptr == label)
+ if (label.IsNull())
mitkThrow() << "Invalid state. Internal model returned a label value that does not exist in segmentation. Invalid value:" << value;
relevantLabels.emplace_back(label);
}
auto* opacitySlider = new QSlider;
opacitySlider->setMinimum(0);
opacitySlider->setMaximum(100);
opacitySlider->setOrientation(Qt::Horizontal);
auto opacity = relevantLabels.front()->GetOpacity();
opacitySlider->setValue(static_cast<int>(opacity * 100));
auto segmentation = m_Segmentation;
- auto node = m_SegmentationNode;
+ auto guard = &m_LabelHighlightGuard;
auto onChangeLambda = [segmentation, relevantLabels](const int value)
{
auto opacity = static_cast<float>(value) / 100.0f;
for (auto label : relevantLabels)
{
label->SetOpacity(opacity);
segmentation->UpdateLookupTable(label->GetValue());
}
segmentation->GetLookupTable()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
};
QObject::connect(opacitySlider, &QSlider::valueChanged, this, onChangeLambda);
- auto onPressedLambda = [node]()
+ auto onPressedLambda = [guard]()
{
- DeactivateLabelHighlights(node);
+ guard->SetHighlightedLabels({});
};
QObject::connect(opacitySlider, &QSlider::sliderPressed, this, onPressedLambda);
- auto onReleasedLambda = [node, relevantLabelValues]()
+ auto onReleasedLambda = [relevantLabelValues, guard]()
{
- ActivateLabelHighlights(node, relevantLabelValues);
+ guard->SetHighlightedLabels(relevantLabelValues);
};
QObject::connect(opacitySlider, &QSlider::sliderReleased, this, onReleasedLambda);
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);
return opacityAction;
}
return nullptr;
}
void QmitkMultiLabelInspector::OnClearLabels(bool /*value*/)
{
QString question = "Do you really want to clear the selected labels?";
QMessageBox::StandardButton answerButton = QMessageBox::question(
this, "Clear selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
this->WaitCursorOn();
m_Segmentation->EraseLabels(this->GetSelectedLabels());
this->WaitCursorOff();
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
+ mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
}
void QmitkMultiLabelInspector::OnDeleteAffectedLabel()
{
if (!m_AllowLabelModification)
mitkThrow() << "QmitkMultiLabelInspector is configured incorrectly. Set AllowLabelModification to true to allow the usage of RemoveLabel.";
if (m_Segmentation.IsNull())
{
return;
}
auto affectedLabels = GetCurrentlyAffactedLabelInstances();
auto currentLabel = m_Segmentation->GetLabel(affectedLabels.front());
+
+ if (currentLabel.IsNull())
+ {
+ MITK_WARN << "Ignore operation. Try to delete non-existing label. Invalid ID: " << affectedLabels.front();
+ return;
+ }
+
QString question = "Do you really want to delete all instances of label \"" + QString::fromStdString(currentLabel->GetName()) + "\"?";
QMessageBox::StandardButton answerButton =
QMessageBox::question(this, "Delete label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
this->DeleteLabelInternal(affectedLabels);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
void QmitkMultiLabelInspector::OnDeleteLabels(bool /*value*/)
{
QString question = "Do you really want to remove the selected labels?";
QMessageBox::StandardButton answerButton = QMessageBox::question(
this, "Remove selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
this->WaitCursorOn();
m_Segmentation->RemoveLabels(this->GetSelectedLabels());
this->WaitCursorOff();
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
void QmitkMultiLabelInspector::OnMergeLabels(bool /*value*/)
{
auto currentLabel = GetCurrentLabel();
QString question = "Do you really want to merge selected labels into \"" + QString::fromStdString(currentLabel->GetName())+"\"?";
QMessageBox::StandardButton answerButton = QMessageBox::question(
this, "Merge selected label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
this->WaitCursorOn();
m_Segmentation->MergeLabels(currentLabel->GetValue(), this->GetSelectedLabels());
this->WaitCursorOff();
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
void QmitkMultiLabelInspector::OnAddLabel()
{
auto currentIndex = this->m_Controls->view->currentIndex();
auto groupIDVariant = currentIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::GroupIDRole);
if (groupIDVariant.isValid())
{
auto groupID = groupIDVariant.value<mitk::LabelSetImage::GroupIndexType>();
this->AddNewLabelInternal(groupID);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
void QmitkMultiLabelInspector::OnAddLabelInstance()
{
auto currentLabel = this->GetCurrentLabel();
if (nullptr == currentLabel)
return;
this->AddNewLabelInstanceInternal(currentLabel);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
+ mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkMultiLabelInspector::OnClearLabel(bool /*value*/)
{
auto currentLabel = GetFirstSelectedLabelObject();
QString question = "Do you really want to clear the contents of label \"" + QString::fromStdString(currentLabel->GetName())+"\"?";
QMessageBox::StandardButton answerButton =
QMessageBox::question(this, "Clear label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
if (answerButton == QMessageBox::Yes)
{
this->WaitCursorOn();
m_Segmentation->EraseLabel(currentLabel->GetValue());
this->WaitCursorOff();
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
+ mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
}
void QmitkMultiLabelInspector::OnRenameLabel(bool /*value*/)
{
auto relevantLabelValues = this->GetCurrentlyAffactedLabelInstances();
auto currentLabel = this->GetCurrentLabel();
- emit LabelRenameRequested(currentLabel, true);
+ bool canceled = false;
+ emit LabelRenameRequested(currentLabel, true, canceled);
+
+ if (canceled) return;
for (auto value : relevantLabelValues)
{
if (value != currentLabel->GetValue())
{
auto label = this->m_Segmentation->GetLabel(value);
if (nullptr == label)
mitkThrow() << "Invalid state. Internal model returned a label value that does not exist in segmentation. Invalid value:" << value;
label->SetName(currentLabel->GetName());
label->SetColor(currentLabel->GetColor());
m_Segmentation->UpdateLookupTable(label->GetValue());
+ m_Segmentation->GetLookupTable()->Modified();
mitk::DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(label);
// this is needed as workaround for (T27307). It circumvents the fact that modifications
// of data (here the segmentation) does not directly trigger the modification of the
// owning node (see T27307). Therefore other code (like renderers or model views) that e.g.
// listens to the datastorage for modification would not get notified.
if (m_SegmentationNode.IsNotNull())
{
m_SegmentationNode->Modified();
}
}
}
emit ModelUpdated();
}
void QmitkMultiLabelInspector::SetLockOfAffectedLabels(bool locked) const
{
auto relevantLabelValues = this->GetCurrentlyAffactedLabelInstances();
if (!relevantLabelValues.empty())
{
for (auto value : relevantLabelValues)
{
auto label = this->m_Segmentation->GetLabel(value);
- if (nullptr == label)
+ if (label.IsNull())
mitkThrow() << "Invalid state. Internal model returned a label value that does not exist in segmentation. Invalid value:" << value;
label->SetLocked(locked);
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkMultiLabelInspector::OnUnlockAffectedLabels()
{
this->SetLockOfAffectedLabels(false);
}
void QmitkMultiLabelInspector::OnLockAffectedLabels()
{
this->SetLockOfAffectedLabels(true);
}
void QmitkMultiLabelInspector::SetVisibilityOfAffectedLabels(bool visible) const
{
auto relevantLabelValues = this->GetCurrentlyAffactedLabelInstances();
if (!relevantLabelValues.empty())
{
for (auto value : relevantLabelValues)
{
auto label = this->m_Segmentation->GetLabel(value);
- if (nullptr == label)
+ if (label.IsNull())
mitkThrow() << "Invalid state. Internal model returned a label value that does not exist in segmentation. Invalid value:" << value;
label->SetVisible(visible);
m_Segmentation->UpdateLookupTable(label->GetValue());
}
m_Segmentation->GetLookupTable()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkMultiLabelInspector::OnSetAffectedLabelsVisible()
{
this->SetVisibilityOfAffectedLabels(true);
}
void QmitkMultiLabelInspector::OnSetAffectedLabelsInvisible()
{
this->SetVisibilityOfAffectedLabels(false);
}
void QmitkMultiLabelInspector::OnSetOnlyActiveLabelVisible(bool /*value*/)
{
auto selectedLabelValues = this->GetSelectedLabels();
if (selectedLabelValues.empty()) return;
m_Segmentation->SetAllLabelsVisible(false);
for (auto selectedValue : selectedLabelValues)
{
auto currentLabel = m_Segmentation->GetLabel(selectedValue);
currentLabel->SetVisible(true);
m_Segmentation->UpdateLookupTable(selectedValue);
}
+ m_Segmentation->GetLookupTable()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
this->PrepareGoToLabel(selectedLabelValues.front());
}
void QmitkMultiLabelInspector::OnItemDoubleClicked(const QModelIndex& index)
{
if (!index.isValid()) return;
if (index.column() > 0) return;
auto labelVariant = index.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
if (!labelVariant.isValid()) return;
const auto labelID = labelVariant.value<mitk::Label::PixelType>();
if (QApplication::queryKeyboardModifiers().testFlag(Qt::AltModifier))
{
this->OnRenameLabel(false);
return;
}
this->PrepareGoToLabel(labelID);
}
void QmitkMultiLabelInspector::PrepareGoToLabel(mitk::Label::PixelType labelID) const
{
this->WaitCursorOn();
m_Segmentation->UpdateCenterOfMass(labelID);
+ this->WaitCursorOff();
+
const auto currentLabel = m_Segmentation->GetLabel(labelID);
+ if (currentLabel.IsNull())
+ return;
+
const mitk::Point3D& pos = currentLabel->GetCenterOfMassCoordinates();
- this->WaitCursorOff();
if (pos.GetVnlVector().max_value() > 0.0)
{
emit GoToLabel(currentLabel->GetValue(), pos);
}
}
void QmitkMultiLabelInspector::OnEntered(const QModelIndex& index)
{
if (m_SegmentationNode.IsNotNull())
{
auto labelVariant = index.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole);
auto highlightedValues = m_Model->GetLabelsInSubTree(index);
- ActivateLabelHighlights(m_SegmentationNode, highlightedValues);
+ m_LabelHighlightGuard.SetHighlightedLabels(highlightedValues);
}
m_AboutToShowContextMenu = false;
}
void QmitkMultiLabelInspector::OnMouseLeave()
{
if (m_SegmentationNode.IsNotNull() && !m_AboutToShowContextMenu)
{
- DeactivateLabelHighlights(m_SegmentationNode);
+ m_LabelHighlightGuard.SetHighlightedLabels({});
}
else
{
m_AboutToShowContextMenu = false;
}
}
+
+void QmitkMultiLabelInspector::keyPressEvent(QKeyEvent* event)
+{
+ if (event->key() == Qt::Key_Shift)
+ {
+ m_LabelHighlightGuard.SetHighlightInvisibleLabels(true);
+ }
+
+ QWidget::keyPressEvent(event);
+}
+
+void QmitkMultiLabelInspector::keyReleaseEvent(QKeyEvent* event)
+{
+ if (event->key() == Qt::Key_Shift)
+ {
+ m_LabelHighlightGuard.SetHighlightInvisibleLabels(false);
+ }
+
+ QWidget::keyPressEvent(event);
+}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h
index 9d660a34d4..d2a7c44028 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.h
@@ -1,333 +1,339 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitkMultiLabelInspector_h
#define QmitkMultiLabelInspector_h
#include <MitkSegmentationUIExports.h>
#include <mitkWeakPointer.h>
#include <mitkLabelSetImage.h>
#include <mitkDataNode.h>
+#include <mitkLabelHighlightGuard.h>
#include <QWidget>
#include <QItemSelectionModel>
class QmitkMultiLabelTreeModel;
class QStyledItemDelegate;
class QWidgetAction;
namespace Ui
{
class QmitkMultiLabelInspector;
}
/*
* @brief This is an inspector that offers a tree view on the labels and groups of a MultiLabelSegmentation instance.
-* It also allows some manipulation operations an the labels/groups accordin to the UI/selection state.
+* It also allows some manipulation operations an the labels/groups according to the UI/selection state.
*/
class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelInspector : public QWidget
{
Q_OBJECT
public:
QmitkMultiLabelInspector(QWidget* parent = nullptr);
~QmitkMultiLabelInspector();
bool GetMultiSelectionMode() const;
bool GetAllowVisibilityModification() const;
bool GetAllowLockModification() const;
bool GetAllowLabelModification() const;
- /** Indicates if the inspector is currently modifiying the model/segmentation.
+ /** Indicates if the inspector is currently modifying the model/segmentation.
Thus as long as the manipulation is ongoing, one should assume the model to be in an invalid state.*/
bool GetModelManipulationOngoing() const;
using LabelValueType = mitk::LabelSetImage::LabelValueType;
using LabelValueVectorType = mitk::LabelSetImage::LabelValueVectorType;
/**
* @brief Retrieve the currently selected labels (equals the last CurrentSelectionChanged values).
*/
LabelValueVectorType GetSelectedLabels() const;
/** @brief Returns the label that currently has the focus in the tree view.
*
* The focus is indicated by QTreeView::currentIndex, thus the mouse is over it and it has a dashed border line.
*
* The current label must not equal the selected label(s). If the mouse is not hovering above a label
* (label class or instance item), the method will return nullptr.
*/
mitk::Label* GetCurrentLabel() const;
enum class IndexLevelType
{
Group,
LabelClass,
LabelInstance
};
/** @brief Returns the level of the index that currently has the focus in the tree view.
*
* The focus is indicated by QTreeView::currentIndex, thus the mouse is over it and it has a dashed border line.
*/
IndexLevelType GetCurrentLevelType() const;
/** @brief Returns all label values that are currently affected.
*
* Affected means that these labels (including the one returned by GetCurrentLabel) are in the subtree of the tree
* view element that currently has the focus (indicated by QTreeView::currentIndex, thus the mouse is over it and
* it has a dashed border line.
*/
LabelValueVectorType GetCurrentlyAffactedLabelInstances() const;
/** @brief Returns the values of all label instances that are of the same label (class) like the first selected label instance.
*
* If no label is selected an empty vector will be returned.
*/
LabelValueVectorType GetLabelInstancesOfSelectedFirstLabel() const;
Q_SIGNALS:
/**
* @brief A signal that will be emitted if the selected labels change.
*
* @param labels A list of label values that are now selected.
*/
void CurrentSelectionChanged(LabelValueVectorType labels) const;
/**
* @brief A signal that will be emitted if the user has requested to "go to" a certain label.
*
* Going to a label would be e.g. to focus the renderwindows on the centroid of the label.
* @param label The label that should be focused.
* @param point in World coordinate that should be focused.
*/
void GoToLabel(LabelValueType label, const mitk::Point3D& point) const;
/** @brief Signal that is emitted, if a label should be (re)named and default
* label naming is deactivated.
*
* The instance for which a new name is requested is passed with the signal.
* @param label Pointer to the instance that needs a (new) name.
* @param rename Indicates if it is a renaming or naming of a new label.
+ * @param [out] canceled Indicating if the request was canceled by the used.
*/
- void LabelRenameRequested(mitk::Label* label, bool rename) const;
+ void LabelRenameRequested(mitk::Label* label, bool rename, bool& canceled) const;
/** @brief Signal that is emitted, if the model was updated (e.g. by a delete or add operation).*/
void ModelUpdated() const;
/** @brief Signal is emitted, if the segmentation is changed that is observed by the inspector.*/
void SegmentationChanged() const;
public Q_SLOTS:
/**
* @brief Transform a list of label values into the new selection of the inspector.
* @param selectedLabels A list of selected label values.
* @remark Using this method to select labels will not trigger the CurrentSelectionChanged signal. Observers
* should regard that to avoid signal loops.
*/
void SetSelectedLabels(const LabelValueVectorType& selectedLabels);
/**
* @brief The passed label will be used as new selection in the widget
* @param selectedLabel Value of the selected label.
* @remark Using this method to select labels will not trigger the CurrentSelectionChanged signal. Observers
* should regard that to avoid signal loops.
*/
void SetSelectedLabel(mitk::LabelSetImage::LabelValueType selectedLabel);
/** @brief Sets the segmentation that will be used and monitored by the widget.
* @param segmentation A pointer to the segmentation to set.
* @remark You cannot set the segmentation directly if a segmentation node is
* also set. Reset the node (nullptr) if you want to change to direct segmentation
* setting.
* @pre Segmentation node is nullptr.
*/
void SetMultiLabelSegmentation(mitk::LabelSetImage* segmentation);
mitk::LabelSetImage* GetMultiLabelSegmentation() const;
/**
* @brief Sets the segmentation node that will be used /monitored by the widget.
*
* @param node A pointer to the segmentation node.
* @remark If not set some features (e.g. highlighting in render windows) of the inspectors
* are not active.
* @remark Currently it is also needed to circumvent the fact that
* modification of data does not directly trigger modification of the
* node (see T27307).
*/
void SetMultiLabelNode(mitk::DataNode* node);
mitk::DataNode* GetMultiLabelNode() const;
void SetMultiSelectionMode(bool multiMode);
void SetAllowVisibilityModification(bool visiblityMod);
void SetAllowLockModification(bool lockMod);
void SetAllowLabelModification(bool labelMod);
void SetDefaultLabelNaming(bool defaultLabelNaming);
/** @brief Adds an instance of the same label/class like the first label instance
* indicated by GetSelectedLabels() to the segmentation.
*
* This new label instance is returned by the function. If the inspector has no selected label,
* no new instance will be generated and nullptr will be returned.
*
* @remark The new label instance is a clone of the selected label instance.
* Therefore all properties but the LabelValue will be the same.
*
* @pre AllowLabeModification must be set to true.
*/
mitk::Label* AddNewLabelInstance();
/** @brief Adds a new label to the segmentation.
* Depending on the settings the name of
* the label will be either default generated or the rename delegate will be used. The label
* will be added to the same group as the first currently selected label.
*
* @pre AllowLabeModification must be set to true.*/
mitk::Label* AddNewLabel();
/** @brief Removes the first currently selected label instance of the segmentation.
* If no label is selected
* nothing will happen.
*
* @pre AllowLabeModification must be set to true.*/
void DeleteLabelInstance();
/** @brief Delete the first currently selected label and all its instances of the segmentation.
* If no label is selected
* nothing will happen.
*
* @pre AllowLabeModification must be set to true.*/
void DeleteLabel();
/** @brief Adds a new group with a new label to segmentation.
*
* @pre AllowLabeModification must be set to true.*/
mitk::Label* AddNewGroup();
/** @brief Removes the group of the first currently selected label of the segmentation.
*If no label is selected nothing will happen.
*
* @pre AllowLabeModification must be set to true.*/
void RemoveGroup();
void SetVisibilityOfAffectedLabels(bool visible) const;
void SetLockOfAffectedLabels(bool visible) const;
protected:
void Initialize();
void OnModelReset();
void OnDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight,
const QList<int>& roles = QList<int>());
QmitkMultiLabelTreeModel* m_Model;
mitk::LabelSetImage::Pointer m_Segmentation;
LabelValueVectorType m_LastValidSelectedLabels;
QStyledItemDelegate* m_LockItemDelegate;
QStyledItemDelegate* m_ColorItemDelegate;
QStyledItemDelegate* m_VisibilityItemDelegate;
Ui::QmitkMultiLabelInspector* m_Controls;
LabelValueVectorType GetSelectedLabelsFromSelectionModel() const;
void UpdateSelectionModel(const LabelValueVectorType& selectedLabels);
/** @brief Helper that returns the label object (if multiple labels are selected the first).
*/
mitk::Label* GetFirstSelectedLabelObject() const;
mitk::Label* AddNewLabelInternal(const mitk::LabelSetImage::GroupIndexType& containingGroup);
/**@brief Adds an instance of the same label/class like the passed label value
*/
mitk::Label* AddNewLabelInstanceInternal(mitk::Label* templateLabel);
void RemoveGroupInternal(const mitk::LabelSetImage::GroupIndexType& groupID);
void DeleteLabelInternal(const LabelValueVectorType& labelValues);
+ void keyPressEvent(QKeyEvent* event) override;
+ void keyReleaseEvent(QKeyEvent* event) override;
+
private Q_SLOTS:
/** @brief Transform a labels selection into a data node list and emit the 'CurrentSelectionChanged'-signal.
*
* The function adds the selected nodes from the original selection that could not be modified, if
* m_SelectOnlyVisibleNodes is false.
* This slot is internally connected to the 'selectionChanged'-signal of the selection model of the private member item view.
*
* @param selected The newly selected items.
* @param deselected The newly deselected items.
*/
void OnChangeModelSelection(const QItemSelection& selected, const QItemSelection& deselected);
void OnContextMenuRequested(const QPoint&);
void OnAddLabel();
void OnAddLabelInstance();
void OnDeleteGroup();
void OnDeleteAffectedLabel();
void OnDeleteLabels(bool);
void OnClearLabels(bool);
void OnMergeLabels(bool);
void OnRenameLabel(bool);
void OnClearLabel(bool);
void OnUnlockAffectedLabels();
void OnLockAffectedLabels();
void OnSetAffectedLabelsVisible();
void OnSetAffectedLabelsInvisible();
void OnSetOnlyActiveLabelVisible(bool);
void OnItemDoubleClicked(const QModelIndex& index);
void WaitCursorOn() const;
void WaitCursorOff() const;
void RestoreOverrideCursor() const;
void PrepareGoToLabel(LabelValueType labelID) const;
void OnEntered(const QModelIndex& index);
void OnMouseLeave();
QWidgetAction* CreateOpacityAction();
private:
bool m_ShowVisibility = true;
bool m_ShowLock = true;
bool m_ShowOther = false;
- /** @brief Indicates if the context menu allows changes in visiblity.
+ /** @brief Indicates if the context menu allows changes in visibility.
*
- * Visiblity includes also color
+ * Visibility includes also color
*/
bool m_AllowVisibilityModification = true;
/** @brief Indicates if the context menu allows changes in lock state.
*/
bool m_AllowLockModification = true;
/** @brief Indicates if the context menu allows label modifications (adding, removing, renaming ...)
*/
bool m_AllowLabelModification = false;
bool m_DefaultLabelNaming = true;
bool m_ModelManipulationOngoing = false;
bool m_AboutToShowContextMenu = false;
mitk::DataNode::Pointer m_SegmentationNode;
unsigned long m_SegmentationNodeDataMTime;
mitk::ITKEventObserverGuard m_SegmentationObserver;
+ mitk::LabelHighlightGuard m_LabelHighlightGuard;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.cpp
index e9d4f15e15..63fbd02b51 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.cpp
@@ -1,562 +1,581 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <QmitkMultiLabelManager.h>
// mitk
#include <mitkAutoCropImageFilter.h>
#include <mitkCoreObjectFactory.h>
#include <mitkIOUtil.h>
#include <mitkLabelSetImage.h>
#include <mitkLabelSetImageToSurfaceThreadedFilter.h>
#include <mitkLabelSetImageConverter.h>
#include <mitkRenderingManager.h>
#include <mitkShowSegmentationAsSurface.h>
#include <mitkStatusBar.h>
#include <mitkToolManagerProvider.h>
// Qmitk
#include <QmitkStyleManager.h>
#include <QmitkMultiLabelPresetHelper.h>
// Qt
#include <QLabel>
#include <QWidgetAction>
#include <QColorDialog>
#include <QCompleter>
#include <QDateTime>
#include <QFileDialog>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QShortcut>
#include <QStringListModel>
// itk
#include <itksys/SystemTools.hxx>
#include <ui_QmitkMultiLabelManagerControls.h>
QmitkMultiLabelManager::QmitkMultiLabelManager(QWidget *parent)
: QWidget(parent), m_Controls(new Ui::QmitkMultiLabelManagerControls), m_Completer(nullptr), m_ProcessingManualSelection(false), m_DataStorage(nullptr)
{
m_Controls->setupUi(this);
m_Controls->labelSearchBox->setAlwaysShowClearIcon(true);
m_Controls->labelSearchBox->setShowSearchIcon(true);
QStringList completionList;
completionList << "";
m_Completer = new QCompleter(completionList, this);
m_Completer->setCaseSensitivity(Qt::CaseInsensitive);
m_Controls->labelSearchBox->setCompleter(m_Completer);
m_Controls->labelInspector->SetAllowLabelModification(true);
connect(m_Controls->labelSearchBox, SIGNAL(returnPressed()), this, SLOT(OnSearchLabel()));
QStringListModel *completeModel = static_cast<QStringListModel *>(m_Completer->model());
completeModel->setStringList(GetLabelStringList());
// See T29549
m_Controls->labelSearchBox->hide();
m_Controls->btnSavePreset->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg")));
m_Controls->btnLoadPreset->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg")));
m_Controls->btnAddLabel->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_add.svg")));
m_Controls->btnAddInstance->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_add_instance.svg")));
m_Controls->btnAddGroup->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_group_add.svg")));
m_Controls->btnRemoveLabel->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_delete.svg")));
m_Controls->btnRemoveInstance->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_label_delete_instance.svg")));
m_Controls->btnRemoveGroup->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_group_delete.svg")));
connect(m_Controls->btnAddLabel, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::AddNewLabel);
connect(m_Controls->btnRemoveLabel, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::DeleteLabel);
connect(m_Controls->btnAddInstance, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::AddNewLabelInstance);
connect(m_Controls->btnRemoveInstance, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::DeleteLabelInstance);
connect(m_Controls->btnAddGroup, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::AddNewGroup);
connect(m_Controls->btnRemoveGroup, &QToolButton::clicked, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::RemoveGroup);
connect(m_Controls->btnSavePreset, &QToolButton::clicked, this, &QmitkMultiLabelManager::OnSavePreset);
connect(m_Controls->btnLoadPreset, &QToolButton::clicked, this, &QmitkMultiLabelManager::OnLoadPreset);
connect(this->m_Controls->labelInspector, &QmitkMultiLabelInspector::GoToLabel, this, &QmitkMultiLabelManager::OnGoToLabel);
connect(this->m_Controls->labelInspector, &QmitkMultiLabelInspector::LabelRenameRequested, this, &QmitkMultiLabelManager::OnLabelRenameRequested);
connect(this->m_Controls->labelInspector, &QmitkMultiLabelInspector::CurrentSelectionChanged, this, &QmitkMultiLabelManager::OnSelectedLabelChanged);
connect(this->m_Controls->labelInspector, &QmitkMultiLabelInspector::ModelUpdated, this, &QmitkMultiLabelManager::OnModelUpdated);
connect(this->m_Controls->labelInspector, &QmitkMultiLabelInspector::SegmentationChanged, this, &QmitkMultiLabelManager::OnSegmentationChanged);
auto* renameLabelShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_R), this);
connect(renameLabelShortcut, &QShortcut::activated, this, &QmitkMultiLabelManager::OnRenameLabelShortcutActivated);
auto* addLabelShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_A), this);
connect(addLabelShortcut, &QShortcut::activated, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::AddNewLabel);
auto* addLabelInstanceShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_I), this);
connect(addLabelInstanceShortcut, &QShortcut::activated, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::AddNewLabelInstance);
auto* deleteLabelShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_D), this);
connect(deleteLabelShortcut, &QShortcut::activated, this->m_Controls->labelInspector, &QmitkMultiLabelInspector::DeleteLabelInstance);
this->UpdateControls();
}
QmitkMultiLabelManager::~QmitkMultiLabelManager()
{
this->SetMultiLabelSegmentation(nullptr);
delete m_Controls;
}
QmitkMultiLabelManager::LabelValueVectorType QmitkMultiLabelManager::GetSelectedLabels() const
{
return m_Controls->labelInspector->GetSelectedLabels();
}
void QmitkMultiLabelManager::OnRenameLabelShortcutActivated()
{
auto selectedLabels = this->GetSelectedLabels();
for (auto labelValue : selectedLabels)
{
auto currentLabel = this->GetMultiLabelSegmentation()->GetLabel(labelValue);
- emit LabelRenameRequested(currentLabel, true);
+ if (currentLabel.IsNull())
+ continue;
+ bool canceled = false;
+ emit LabelRenameRequested(currentLabel, true, canceled);
}
}
void QmitkMultiLabelManager::OnSelectedLabelChanged(const LabelValueVectorType& labels)
{
this->UpdateControls();
if (labels.empty() || labels.size() > 1) return;
emit CurrentSelectionChanged(labels);
}
QStringList &QmitkMultiLabelManager::GetLabelStringList()
{
return m_LabelStringList;
}
void QmitkMultiLabelManager::SetDefaultLabelNaming(bool defaultLabelNaming)
{
this->m_Controls->labelInspector->SetDefaultLabelNaming(defaultLabelNaming);
}
void QmitkMultiLabelManager::setEnabled(bool enabled)
{
QWidget::setEnabled(enabled);
UpdateControls();
}
void QmitkMultiLabelManager::SetSelectedLabels(const LabelValueVectorType& selectedLabels)
{
this->m_Controls->labelInspector->SetSelectedLabels(selectedLabels);
UpdateControls();
}
void QmitkMultiLabelManager::SetSelectedLabel(mitk::LabelSetImage::LabelValueType selectedLabel)
{
this->m_Controls->labelInspector->SetSelectedLabel(selectedLabel);
UpdateControls();
}
void QmitkMultiLabelManager::SetMultiLabelSegmentation(mitk::LabelSetImage* segmentation)
{
this->m_Controls->labelInspector->SetMultiLabelSegmentation(segmentation);
//Update of controls and observers is done in OnSegmentationChanged()
// which is triggered by the inspector when setting the segmentation or node
}
mitk::LabelSetImage* QmitkMultiLabelManager::GetMultiLabelSegmentation() const
{
return this->m_Controls->labelInspector->GetMultiLabelSegmentation();
}
void QmitkMultiLabelManager::SetMultiLabelNode(mitk::DataNode* node)
{
this->m_Controls->labelInspector->SetMultiLabelNode(node);
}
mitk::DataNode* QmitkMultiLabelManager::GetMultiLabelNode() const
{
return this->m_Controls->labelInspector->GetMultiLabelNode();
}
void QmitkMultiLabelManager::SetDataStorage(mitk::DataStorage *storage)
{
m_DataStorage = storage;
}
void QmitkMultiLabelManager::OnSearchLabel()
{
//std::string text = m_Controls->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->selectRow(row);
//m_Controls->m_LabelSetTableWidget->scrollToItem(nameItem);
//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 QmitkMultiLabelManager::UpdateControls()
{
bool hasWorkingData = this->GetMultiLabelSegmentation() != nullptr;
auto labels = this->m_Controls->labelInspector->GetSelectedLabels();
bool hasMultipleInstances = this->m_Controls->labelInspector->GetLabelInstancesOfSelectedFirstLabel().size() > 1;
m_Controls->labelSearchBox->setEnabled(hasWorkingData);
m_Controls->btnAddGroup->setEnabled(hasWorkingData);
m_Controls->btnAddInstance->setEnabled(hasWorkingData && labels.size()==1);
m_Controls->btnAddLabel->setEnabled(hasWorkingData);
m_Controls->btnLoadPreset->setEnabled(hasWorkingData);
m_Controls->btnRemoveGroup->setEnabled(hasWorkingData && !labels.empty() && this->GetMultiLabelSegmentation()->GetNumberOfLayers()>1);
m_Controls->btnRemoveLabel->setEnabled(hasWorkingData && !labels.empty());
m_Controls->btnRemoveInstance->setEnabled(hasWorkingData && !labels.empty() && hasMultipleInstances);
m_Controls->btnSavePreset->setEnabled(hasWorkingData);
if (!hasWorkingData)
return;
QStringListModel *completeModel = dynamic_cast<QStringListModel *>(m_Completer->model());
completeModel->setStringList(GetLabelStringList());
}
void QmitkMultiLabelManager::OnCreateCroppedMask(bool)
{
mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1);
mitk::Image::Pointer maskImage;
auto currentLabel = this->GetMultiLabelSegmentation()->GetLabel(this->GetSelectedLabels().front());
- auto pixelValue = currentLabel->GetValue();
try
{
this->WaitCursorOn();
+ if (currentLabel.IsNull())
+ mitkThrow() << "Invalid state context menu action was triggered with an invalid label id. Label id: " << this->GetSelectedLabels().front();
+
+ auto pixelValue = currentLabel->GetValue();
+
mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New();
cropFilter->SetInput(mitk::CreateLabelMask(this->GetMultiLabelSegmentation(),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 = currentLabel->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(currentLabel->GetColor());
maskNode->SetOpacity(1.0);
m_DataStorage->Add(maskNode, this->GetMultiLabelNode());
}
void QmitkMultiLabelManager::OnCreateMask(bool /*triggered*/)
{
mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1);
auto currentLabel = this->GetMultiLabelSegmentation()->GetLabel(this->GetSelectedLabels().front());
- auto pixelValue = currentLabel->GetValue();
mitk::Image::Pointer maskImage;
try
{
this->WaitCursorOn();
+
+ if (currentLabel.IsNull())
+ mitkThrow() << "Invalid state context menu action was triggered with an invalid label id. Label id: " << this->GetSelectedLabels().front();
+ auto pixelValue = currentLabel->GetValue();
+
maskImage = mitk::CreateLabelMask(GetMultiLabelSegmentation(),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 = currentLabel->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(currentLabel->GetColor());
maskNode->SetOpacity(1.0);
m_DataStorage->Add(maskNode, this->GetMultiLabelNode());
}
void QmitkMultiLabelManager::OnCreateSmoothedSurface(bool /*triggered*/)
{
mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1);
auto currentLabel = this->GetMultiLabelSegmentation()->GetLabel(this->GetSelectedLabels().front());
- auto pixelValue = currentLabel->GetValue();
- mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New();
+ try
+ {
+ if (currentLabel.IsNull())
+ mitkThrow() << "Invalid state context menu action was triggered with an invalid label id. Label id: " << this->GetSelectedLabels().front();
- itk::SimpleMemberCommand<QmitkMultiLabelManager>::Pointer successCommand =
- itk::SimpleMemberCommand<QmitkMultiLabelManager>::New();
- successCommand->SetCallbackFunction(this, &QmitkMultiLabelManager::OnThreadedCalculationDone);
- surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand);
+ auto pixelValue = currentLabel->GetValue();
- itk::SimpleMemberCommand<QmitkMultiLabelManager>::Pointer errorCommand =
- itk::SimpleMemberCommand<QmitkMultiLabelManager>::New();
- errorCommand->SetCallbackFunction(this, &QmitkMultiLabelManager::OnThreadedCalculationDone);
- surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand);
+ mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New();
- mitk::DataNode::Pointer groupNode = this->GetMultiLabelNode();
- surfaceFilter->SetPointerParameter("Group node", groupNode);
- surfaceFilter->SetPointerParameter("Input", this->GetMultiLabelSegmentation());
- surfaceFilter->SetParameter("RequestedLabel", pixelValue);
- surfaceFilter->SetParameter("Smooth", true);
- surfaceFilter->SetDataStorage(*m_DataStorage);
+ itk::SimpleMemberCommand<QmitkMultiLabelManager>::Pointer successCommand =
+ itk::SimpleMemberCommand<QmitkMultiLabelManager>::New();
+ successCommand->SetCallbackFunction(this, &QmitkMultiLabelManager::OnThreadedCalculationDone);
+ surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand);
- mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background...");
+ itk::SimpleMemberCommand<QmitkMultiLabelManager>::Pointer errorCommand =
+ itk::SimpleMemberCommand<QmitkMultiLabelManager>::New();
+ errorCommand->SetCallbackFunction(this, &QmitkMultiLabelManager::OnThreadedCalculationDone);
+ surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand);
+
+ mitk::DataNode::Pointer groupNode = this->GetMultiLabelNode();
+ surfaceFilter->SetPointerParameter("Group node", groupNode);
+ surfaceFilter->SetPointerParameter("Input", this->GetMultiLabelSegmentation());
+ 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 QmitkMultiLabelManager::OnCreateDetailedSurface(bool /*triggered*/)
{
mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1);
auto currentLabel = this->GetMultiLabelSegmentation()->GetLabel(this->GetSelectedLabels().front());
- auto pixelValue = currentLabel->GetValue();
- mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New();
+ try
+ {
+ if (currentLabel.IsNull())
+ mitkThrow() << "Invalid state context menu action was triggered with an invalid label id. Label id: " << this->GetSelectedLabels().front();
+
+ auto pixelValue = currentLabel->GetValue();
- itk::SimpleMemberCommand<QmitkMultiLabelManager>::Pointer successCommand =
- itk::SimpleMemberCommand<QmitkMultiLabelManager>::New();
- successCommand->SetCallbackFunction(this, &QmitkMultiLabelManager::OnThreadedCalculationDone);
- surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand);
+ mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New();
- itk::SimpleMemberCommand<QmitkMultiLabelManager>::Pointer errorCommand =
- itk::SimpleMemberCommand<QmitkMultiLabelManager>::New();
- errorCommand->SetCallbackFunction(this, &QmitkMultiLabelManager::OnThreadedCalculationDone);
- surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand);
+ itk::SimpleMemberCommand<QmitkMultiLabelManager>::Pointer successCommand =
+ itk::SimpleMemberCommand<QmitkMultiLabelManager>::New();
+ successCommand->SetCallbackFunction(this, &QmitkMultiLabelManager::OnThreadedCalculationDone);
+ surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand);
- mitk::DataNode::Pointer groupNode = this->GetMultiLabelNode();
- surfaceFilter->SetPointerParameter("Group node", groupNode);
- surfaceFilter->SetPointerParameter("Input", this->GetMultiLabelSegmentation());
- surfaceFilter->SetParameter("RequestedLabel", pixelValue);
- surfaceFilter->SetParameter("Smooth", false);
- surfaceFilter->SetDataStorage(*m_DataStorage);
+ itk::SimpleMemberCommand<QmitkMultiLabelManager>::Pointer errorCommand =
+ itk::SimpleMemberCommand<QmitkMultiLabelManager>::New();
+ errorCommand->SetCallbackFunction(this, &QmitkMultiLabelManager::OnThreadedCalculationDone);
+ surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand);
- mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background...");
+ mitk::DataNode::Pointer groupNode = this->GetMultiLabelNode();
+ surfaceFilter->SetPointerParameter("Group node", groupNode);
+ surfaceFilter->SetPointerParameter("Input", this->GetMultiLabelSegmentation());
+ 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 QmitkMultiLabelManager::OnSavePreset()
{
QmitkSaveMultiLabelPreset(this->GetMultiLabelSegmentation());
}
void QmitkMultiLabelManager::OnLoadPreset()
{
QmitkLoadMultiLabelPreset({ this->GetMultiLabelSegmentation() });
}
void QmitkMultiLabelManager::OnGoToLabel(mitk::LabelSetImage::LabelValueType label, const mitk::Point3D& position) const
{
emit GoToLabel(label, position);
}
-void QmitkMultiLabelManager::OnLabelRenameRequested(mitk::Label* label, bool rename) const
+void QmitkMultiLabelManager::OnLabelRenameRequested(mitk::Label* label, bool rename, bool& canceled) const
{
- emit LabelRenameRequested(label, rename);
+ emit LabelRenameRequested(label, rename, canceled);
}
void QmitkMultiLabelManager::WaitCursorOn()
{
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
}
void QmitkMultiLabelManager::WaitCursorOff()
{
this->RestoreOverrideCursor();
}
void QmitkMultiLabelManager::RestoreOverrideCursor()
{
QApplication::restoreOverrideCursor();
}
void QmitkMultiLabelManager::OnThreadedCalculationDone()
{
mitk::StatusBar::GetInstance()->Clear();
}
void QmitkMultiLabelManager::AddSegmentationObserver()
{
if (this->GetMultiLabelSegmentation() != nullptr)
{
auto& widget = *this;
m_LabelAddedObserver.Reset(this->GetMultiLabelSegmentation(), mitk::LabelAddedEvent(), [&widget](const itk::EventObject& event)
{
auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&event);
widget.OnLabelEvent(labelEvent->GetLabelValue());
});
m_LabelModifiedObserver.Reset(this->GetMultiLabelSegmentation(), mitk::LabelModifiedEvent(), [&widget](const itk::EventObject& event)
{
auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&event);
widget.OnLabelEvent(labelEvent->GetLabelValue());
});
m_LabelRemovedObserver.Reset(this->GetMultiLabelSegmentation(), mitk::LabelRemovedEvent(), [&widget](const itk::EventObject& event)
{
auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&event);
widget.OnLabelEvent(labelEvent->GetLabelValue());
});
m_GroupAddedObserver.Reset(this->GetMultiLabelSegmentation(), mitk::GroupAddedEvent(), [&widget](const itk::EventObject& event)
{
auto groupEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&event);
widget.OnGroupEvent(groupEvent->GetGroupID());
});
m_GroupModifiedObserver.Reset(this->GetMultiLabelSegmentation(), mitk::GroupModifiedEvent(), [&widget](const itk::EventObject& event)
{
auto groupEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&event);
widget.OnGroupEvent(groupEvent->GetGroupID());
});
m_GroupRemovedObserver.Reset(this->GetMultiLabelSegmentation(), mitk::GroupRemovedEvent(), [&widget](const itk::EventObject& event)
{
auto groupEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&event);
widget.OnGroupEvent(groupEvent->GetGroupID());
});
}
}
void QmitkMultiLabelManager::RemoveSegmentationObserver()
{
m_LabelAddedObserver.Reset();
m_LabelModifiedObserver.Reset();
m_LabelRemovedObserver.Reset();
m_GroupAddedObserver.Reset();
m_GroupModifiedObserver.Reset();
m_GroupRemovedObserver.Reset();
}
void QmitkMultiLabelManager::OnLabelEvent(mitk::LabelSetImage::LabelValueType /*labelValue*/)
{
if (!m_Controls->labelInspector->GetModelManipulationOngoing())
this->UpdateControls();
}
void QmitkMultiLabelManager::OnGroupEvent(mitk::LabelSetImage::GroupIndexType /*groupIndex*/)
{
if (!m_Controls->labelInspector->GetModelManipulationOngoing())
this->UpdateControls();
}
void QmitkMultiLabelManager::OnModelUpdated()
{
this->UpdateControls();
}
void QmitkMultiLabelManager::OnSegmentationChanged()
{
this->RemoveSegmentationObserver();
this->AddSegmentationObserver();
UpdateControls();
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h
index 13461c642d..54b87bc561 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelManager.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 QmitkMultiLabelManager_h
#define QmitkMultiLabelManager_h
#include <MitkSegmentationUIExports.h>
#include <mitkLabelSetImage.h>
#include <mitkDataNode.h>
#include <mitkNumericTypes.h>
#include <mitkITKEventObserverGuard.h>
#include <QWidget>
class QmitkDataStorageComboBox;
class QCompleter;
namespace Ui
{
class QmitkMultiLabelManagerControls;
}
namespace mitk
{
class DataStorage;
}
class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelManager : public QWidget
{
Q_OBJECT
public:
explicit QmitkMultiLabelManager(QWidget *parent = nullptr);
~QmitkMultiLabelManager() override;
using LabelValueVectorType = mitk::LabelSetImage::LabelValueVectorType;
/**
* @brief Retrieve the currently selected labels (equals the last CurrentSelectionChanged values).
*/
LabelValueVectorType GetSelectedLabels() const;
mitk::LabelSetImage* GetMultiLabelSegmentation() const;
mitk::DataNode* GetMultiLabelNode() const;
Q_SIGNALS:
/**
* @brief A signal that will be emitted if the selected labels change.
*
* @param labels A list of label values that are now selected.
*/
void CurrentSelectionChanged(const LabelValueVectorType& labels);
/**
* @brief A signal that will be emitted if the user has requested to "go to" a certain label.
*
* Going to a label would be e.g. to focus the render windows on the centroid of the label.
* @param label The label that should be focused.
* @param point in World coordinate that should be focused.
*/
void GoToLabel(mitk::LabelSetImage::LabelValueType label, const mitk::Point3D& point) const;
/** @brief Signal that is emitted, if a label should be (re)named and default
* label naming is deactivated.
*
* The instance for which a new name is requested is passed with the signal.
* @param label Pointer to the instance that needs a (new) name.
- * @param rename Indicates if it is a renaming or naming of a new label.
- */
- void LabelRenameRequested(mitk::Label* label, bool rename) const;
+ * @param [out] canceled Indicating if the request was canceled by the used.
+ */
+ void LabelRenameRequested(mitk::Label* label, bool rename, bool& canceled) const;
public Q_SLOTS:
/**
* @brief Transform a list label values into a model selection and set this as a new selection of the view
*
* @param selectedLabels A list of data nodes that should be newly selected.
*/
void SetSelectedLabels(const LabelValueVectorType& selectedLabels);
/**
* @brief Selects the passed label instance and sets a new selection of the view
*
* @param selectedLabel Value of the label instance that should be selected.
*/
void SetSelectedLabel(mitk::LabelSetImage::LabelValueType selectedLabel);
/**
* @brief Sets the segmentation that will be used /monitored by the widget.
*
* @param segmentation A pointer to the segmentation to set.
* @remark You cannot set the segmentation directly if a segmentation node is
* also set. Reset the node (nullptr) if you want to change to direct segmentation
* setting.
* @pre Segmentation node is nullptr.
*/
void SetMultiLabelSegmentation(mitk::LabelSetImage* segmentation);
/**
* @brief Sets the segmentation node that will be used /monitored by the widget.
*
* @param node A pointer to the segmentation node.
* @remark If not set some features of the manager are not active
*/
void SetMultiLabelNode(mitk::DataNode* node);
void SetDataStorage(mitk::DataStorage *storage);
void UpdateControls();
virtual void setEnabled(bool enabled);
QStringList &GetLabelStringList();
void SetDefaultLabelNaming(bool defaultLabelNaming);
private Q_SLOTS:
// LabelSet dependent
void OnRenameLabelShortcutActivated();
// reaction to "returnPressed" signal from ...
void OnSearchLabel();
// reaction to the change of labels. If multiple labels are selected, it is ignored.
void OnSelectedLabelChanged(const LabelValueVectorType& labels);
- // LabelSetImage Dependet
+ // LabelSetImage Dependent
void OnCreateDetailedSurface(bool);
void OnCreateSmoothedSurface(bool);
// reaction to the signal "createMask" from QmitkLabelSetTableWidget
void OnCreateMask(bool);
// reaction to the signal "createCroppedMask" from QmitkLabelSetTableWidget
void OnCreateCroppedMask(bool);
void OnSavePreset();
void OnLoadPreset();
void OnGoToLabel(mitk::LabelSetImage::LabelValueType label, const mitk::Point3D& position) const;
- void OnLabelRenameRequested(mitk::Label* label, bool rename) const;
+ void OnLabelRenameRequested(mitk::Label* label, bool rename, bool& canceled) const;
void OnModelUpdated();
void OnSegmentationChanged();
private:
enum TableColumns
{
NAME_COL = 0,
LOCKED_COL,
COLOR_COL,
VISIBLE_COL
};
void WaitCursorOn();
void WaitCursorOff();
void RestoreOverrideCursor();
void OnThreadedCalculationDone();
void AddSegmentationObserver();
void RemoveSegmentationObserver();
void OnLabelEvent(mitk::LabelSetImage::LabelValueType labelValue);
void OnGroupEvent(mitk::LabelSetImage::GroupIndexType groupIndex);
Ui::QmitkMultiLabelManagerControls* m_Controls;
QCompleter *m_Completer;
QStringList m_OrganColors;
QStringList m_LabelStringList;
bool m_ProcessingManualSelection;
mitk::DataStorage* m_DataStorage;
mitk::ITKEventObserverGuard m_LabelAddedObserver;
mitk::ITKEventObserverGuard m_LabelModifiedObserver;
mitk::ITKEventObserverGuard m_LabelRemovedObserver;
mitk::ITKEventObserverGuard m_GroupAddedObserver;
mitk::ITKEventObserverGuard m_GroupModifiedObserver;
mitk::ITKEventObserverGuard m_GroupRemovedObserver;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp
index 004cd80513..94396a8a8c 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp
@@ -1,149 +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 "QmitkMultiLabelSegWithPreviewToolGUIBase.h"
#include "mitkSegWithPreviewTool.h"
#include <QRadioButton>
#include <QBoxLayout>
QmitkMultiLabelSegWithPreviewToolGUIBase::QmitkMultiLabelSegWithPreviewToolGUIBase() : QmitkSegWithPreviewToolGUIBase(false)
{
auto enableMLSelectedDelegate = [this](bool enabled)
{
auto tool = this->GetConnectedToolAs<mitk::SegWithPreviewTool>();
return nullptr != tool
? (tool->GetLabelTransferScope() == mitk::SegWithPreviewTool::LabelTransferScope::AllLabels || !tool->GetSelectedLabels().empty()) && enabled
: false;
};
m_EnableConfirmSegBtnFnc = enableMLSelectedDelegate;
}
void QmitkMultiLabelSegWithPreviewToolGUIBase::InitializeUI(QBoxLayout* mainLayout)
{
auto radioTransferAll = new QRadioButton("Transfer all labels", this);
radioTransferAll->setToolTip("Transfer all preview labels when confirmed.");
radioTransferAll->setChecked(true);
connect(radioTransferAll, &QAbstractButton::toggled, this, &QmitkMultiLabelSegWithPreviewToolGUIBase::OnRadioTransferAllClicked);
mainLayout->addWidget(radioTransferAll);
m_RadioTransferAll = radioTransferAll;
auto radioTransferSelected = new QRadioButton("Transfer selected labels", this);
radioTransferSelected->setToolTip("Transfer the selected preview labels when confirmed.");
radioTransferSelected->setChecked(false);
mainLayout->addWidget(radioTransferSelected);
m_RadioTransferSelected = radioTransferSelected;
m_LabelSelectionList = new QmitkSimpleLabelSetListWidget(this);
m_LabelSelectionList->setObjectName(QString::fromUtf8("m_LabelSelectionList"));
QSizePolicy sizePolicy2(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
sizePolicy2.setHorizontalStretch(0);
sizePolicy2.setVerticalStretch(0);
sizePolicy2.setHeightForWidth(m_LabelSelectionList->sizePolicy().hasHeightForWidth());
m_LabelSelectionList->setSizePolicy(sizePolicy2);
m_LabelSelectionList->setMaximumSize(QSize(10000000, 10000000));
m_LabelSelectionList->setVisible(false);
mainLayout->addWidget(m_LabelSelectionList);
connect(m_LabelSelectionList, &QmitkSimpleLabelSetListWidget::SelectedLabelsChanged, this, &QmitkMultiLabelSegWithPreviewToolGUIBase::OnLabelSelectionChanged);
this->OnRadioTransferAllClicked(true);
Superclass::InitializeUI(mainLayout);
}
void QmitkMultiLabelSegWithPreviewToolGUIBase::OnLabelSelectionChanged(const QmitkSimpleLabelSetListWidget::LabelVectorType& selectedLabels)
{
auto tool = this->GetConnectedToolAs<mitk::SegWithPreviewTool>();
if (nullptr != tool)
{
mitk::SegWithPreviewTool::SelectedLabelVectorType labelIDs;
for (const auto& label : selectedLabels)
{
labelIDs.push_back(label->GetValue());
}
tool->SetSelectedLabels(labelIDs);
this->ActualizePreviewLabelVisibility();
this->EnableWidgets(true); //used to actualize the ConfirmSeg btn via the delegate;
}
}
void QmitkMultiLabelSegWithPreviewToolGUIBase::ActualizePreviewLabelVisibility()
{
auto tool = this->GetConnectedToolAs<mitk::SegWithPreviewTool>();
if (nullptr != tool)
{
auto preview = tool->GetPreviewSegmentation();
if (nullptr != preview)
{
auto labels = preview->GetLabelsByValue(preview->GetLabelValuesByGroup(preview->GetActiveLayer()));
auto selectedLabels = tool->GetSelectedLabels();
for (auto label : labels)
{
bool isVisible = tool->GetLabelTransferScope() == mitk::SegWithPreviewTool::LabelTransferScope::AllLabels
|| (std::find(selectedLabels.begin(), selectedLabels.end(), label->GetValue()) != selectedLabels.end());
label->SetVisible(isVisible);
preview->UpdateLookupTable(label->GetValue());
}
+ preview->GetLookupTable()->Modified();
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkMultiLabelSegWithPreviewToolGUIBase::OnRadioTransferAllClicked(bool checked)
{
m_LabelSelectionList->setVisible(!checked);
auto tool = this->GetConnectedToolAs<mitk::SegWithPreviewTool>();
if (nullptr != tool)
{
if (checked)
{
tool->SetLabelTransferScope(mitk::SegWithPreviewTool::LabelTransferScope::AllLabels);
}
else
{
tool->SetLabelTransferScope(mitk::SegWithPreviewTool::LabelTransferScope::SelectedLabels);
}
}
this->ActualizePreviewLabelVisibility();
}
void QmitkMultiLabelSegWithPreviewToolGUIBase::EnableWidgets(bool enabled)
{
Superclass::EnableWidgets(enabled);
if (nullptr != m_LabelSelectionList)
{
m_LabelSelectionList->setEnabled(enabled);
}
if (nullptr != m_RadioTransferAll)
{
m_RadioTransferAll->setEnabled(enabled);
}
if (nullptr != m_RadioTransferSelected)
{
m_RadioTransferSelected->setEnabled(enabled);
}
}
void QmitkMultiLabelSegWithPreviewToolGUIBase::SetLabelSetPreview(const mitk::LabelSetImage* preview)
{
if (nullptr != m_LabelSelectionList)
{
m_LabelSelectionList->SetLabelSetImage(preview);
}
}
+
+void QmitkMultiLabelSegWithPreviewToolGUIBase::DisplayTransferWidgets(bool enabled)
+{
+ if (nullptr != m_RadioTransferAll)
+ {
+ m_RadioTransferAll->setVisible(enabled);
+ }
+
+ if (nullptr != m_RadioTransferSelected)
+ {
+ m_RadioTransferSelected->setVisible(enabled);
+ }
+}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h
index 57e419406c..f309330917 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h
@@ -1,56 +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 QmitkMultiLabelSegWithPreviewToolGUIBase_h
#define QmitkMultiLabelSegWithPreviewToolGUIBase_h
#include "QmitkSegWithPreviewToolGUIBase.h"
#include "QmitkSimpleLabelSetListWidget.h"
#include <MitkSegmentationUIExports.h>
/**
\ingroup org_mitk_gui_qt_interactivesegmentation_internal
\brief GUI for tools based on mitk::AutoMLSegmentationWithPreviewTool.
This GUI offers an additional list to select the label that should be confirmed.
*/
class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelSegWithPreviewToolGUIBase : public QmitkSegWithPreviewToolGUIBase
{
Q_OBJECT
public:
mitkClassMacro(QmitkMultiLabelSegWithPreviewToolGUIBase, QmitkSegWithPreviewToolGUIBase);
protected slots :
void OnLabelSelectionChanged(const QmitkSimpleLabelSetListWidget::LabelVectorType& selectedLabels);
void OnRadioTransferAllClicked(bool checked);
protected:
QmitkMultiLabelSegWithPreviewToolGUIBase();
~QmitkMultiLabelSegWithPreviewToolGUIBase() = default;
void InitializeUI(QBoxLayout* mainLayout) override;
void EnableWidgets(bool enabled) override;
void SetLabelSetPreview(const mitk::LabelSetImage* preview);
void ActualizePreviewLabelVisibility();
+ /**
+ * @brief To toggle visibility of "Transfer all labels" and
+ * "Transfer selected labels" radio buttons.
+ */
+ void DisplayTransferWidgets(bool enabled);
+
private:
QmitkSimpleLabelSetListWidget* m_LabelSelectionList = nullptr;
QWidget* m_RadioTransferAll = nullptr;
QWidget* m_RadioTransferSelected = nullptr;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp
index 23ec29058b..6e468f4857 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp
@@ -1,1060 +1,1061 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkMultiLabelTreeModel.h"
#include <mitkMultiLabelEvents.h>
#include <mitkRenderingManager.h>
#include <mitkLabelSetImageHelper.h>
#include <QmitkStyleManager.h>
class QmitkMultiLabelSegTreeItem
{
public:
enum class ItemType
{
Group,
Label,
Instance
};
QmitkMultiLabelSegTreeItem()
{
};
explicit QmitkMultiLabelSegTreeItem(ItemType type, QmitkMultiLabelSegTreeItem* parentItem,
mitk::Label* label = nullptr, std::string className = ""): m_parentItem(parentItem), m_ItemType(type), m_Label(label), m_ClassName(className)
{
};
~QmitkMultiLabelSegTreeItem()
{
for (auto item : m_childItems)
{
delete item;
}
};
void AppendChild(QmitkMultiLabelSegTreeItem* child)
{
m_childItems.push_back(child);
};
void RemoveChild(std::size_t row)
{
if (row < m_childItems.size())
{
delete m_childItems[row];
m_childItems.erase(m_childItems.begin() + row);
}
};
int Row() const
{
if (m_parentItem)
{
auto finding = std::find(m_parentItem->m_childItems.begin(), m_parentItem->m_childItems.end(), this);
if (finding != m_parentItem->m_childItems.end())
{
return std::distance(m_parentItem->m_childItems.begin(), finding);
}
}
return 0;
};
QmitkMultiLabelSegTreeItem* ParentItem()
{
return m_parentItem;
};
const QmitkMultiLabelSegTreeItem* ParentItem() const
{
return m_parentItem;
};
const QmitkMultiLabelSegTreeItem* NextSibblingItem() const
{
if (m_parentItem)
{
const std::vector<QmitkMultiLabelSegTreeItem*>::size_type row = this->Row();
if (row + 1 < m_parentItem->m_childItems.size())
return m_parentItem->m_childItems[row+1];
}
return nullptr;
};
const QmitkMultiLabelSegTreeItem* PrevSibblingItem() const
{
if (m_parentItem)
{
const std::vector<QmitkMultiLabelSegTreeItem*>::size_type row = this->Row();
if (row > 0)
return m_parentItem->m_childItems[row-1];
}
return nullptr;
};
const QmitkMultiLabelSegTreeItem* RootItem() const
{
auto item = this;
while (item->m_parentItem != nullptr)
{
item = item->m_parentItem;
}
return item;
};
std::size_t GetGroupID() const
{
auto root = this->RootItem();
auto item = this;
if (root == this) return 0;
while (root != item->m_parentItem)
{
item = item->m_parentItem;
}
auto iter = std::find(root->m_childItems.begin(), root->m_childItems.end(), item);
if (root->m_childItems.end() == iter) mitkThrow() << "Invalid internal state of QmitkMultiLabelTreeModel. Root does not have an currentItem as child that has root as parent.";
return std::distance(root->m_childItems.begin(), iter);
}
bool HandleAsInstance() const
{
return (ItemType::Instance == m_ItemType) || ((ItemType::Label == m_ItemType) && (m_childItems.size() == 1));
}
mitk::Label* GetLabel() const
{
if (ItemType::Instance == m_ItemType)
{
return m_Label;
}
if (ItemType::Label == m_ItemType)
{
if (m_childItems.empty()) mitkThrow() << "Invalid internal state of QmitkMultiLabelTreeModel. Internal label currentItem has no instance currentItem.";
return m_childItems[0]->GetLabel();
}
return nullptr;
};
mitk::LabelSetImage::LabelValueType GetLabelValue() const
{
auto label = this->GetLabel();
if (nullptr == label)
{
mitkThrow() << "Invalid internal state of QmitkMultiLabelTreeModel. Called GetLabelValue on an group currentItem.";
}
return label->GetValue();
};
/** returns a vector containing all label values of referenced by this item or its child items.*/
std::vector< mitk::LabelSetImage::LabelValueType> GetLabelsInSubTree() const
{
if (this->m_ItemType == ItemType::Instance)
{
return { this->GetLabelValue() };
}
std::vector< mitk::LabelSetImage::LabelValueType> result;
for (const auto child : this->m_childItems)
{
auto childresult = child->GetLabelsInSubTree();
result.reserve(result.size() + childresult.size());
result.insert(result.end(), childresult.begin(), childresult.end());
}
return result;
}
std::vector<QmitkMultiLabelSegTreeItem*> m_childItems;
QmitkMultiLabelSegTreeItem* m_parentItem = nullptr;
ItemType m_ItemType = ItemType::Group;
mitk::Label::Pointer m_Label;
std::string m_ClassName;
};
QModelIndex GetIndexByItem(const QmitkMultiLabelSegTreeItem* start, const QmitkMultiLabelTreeModel* model)
{
QModelIndex parentIndex = QModelIndex();
if (nullptr != start->m_parentItem)
{
parentIndex = GetIndexByItem(start->m_parentItem, model);
}
else
{
return parentIndex;
}
return model->index(start->Row(), 0, parentIndex);
}
QmitkMultiLabelSegTreeItem* GetGroupItem(QmitkMultiLabelTreeModel::GroupIndexType groupIndex, QmitkMultiLabelSegTreeItem* root)
{
if (nullptr != root && groupIndex < root->m_childItems.size())
{
return root->m_childItems[groupIndex];
}
return nullptr;
}
QmitkMultiLabelSegTreeItem* GetInstanceItem(QmitkMultiLabelTreeModel::LabelValueType labelValue, QmitkMultiLabelSegTreeItem* root)
{
QmitkMultiLabelSegTreeItem* result = nullptr;
for (auto item : root->m_childItems)
{
result = GetInstanceItem(labelValue, item);
if (nullptr != result) return result;
}
if (root->m_ItemType == QmitkMultiLabelSegTreeItem::ItemType::Instance && root->GetLabelValue() == labelValue)
{
return root;
}
return nullptr;
}
const QmitkMultiLabelSegTreeItem* GetFirstInstanceLikeItem(const QmitkMultiLabelSegTreeItem* startItem)
{
const QmitkMultiLabelSegTreeItem* result = nullptr;
if (nullptr != startItem)
{
if (startItem->HandleAsInstance())
{
result = startItem;
}
else if (!startItem->m_childItems.empty())
{
result = GetFirstInstanceLikeItem(startItem->m_childItems.front());
}
}
return result;
}
QmitkMultiLabelSegTreeItem* GetLabelItemInGroup(const std::string& labelName, QmitkMultiLabelSegTreeItem* group)
{
if (nullptr != group)
{
auto predicate = [labelName](const QmitkMultiLabelSegTreeItem* item) { return labelName == item->m_ClassName; };
auto finding = std::find_if(group->m_childItems.begin(), group->m_childItems.end(), predicate);
if (group->m_childItems.end() != finding)
{
return *finding;
}
}
return nullptr;
}
QmitkMultiLabelTreeModel::QmitkMultiLabelTreeModel(QObject *parent) : QAbstractItemModel(parent)
{
m_RootItem = std::make_unique<QmitkMultiLabelSegTreeItem>();
}
QmitkMultiLabelTreeModel ::~QmitkMultiLabelTreeModel()
{
this->SetSegmentation(nullptr);
};
int QmitkMultiLabelTreeModel::columnCount(const QModelIndex& /*parent*/) const
{
return 4;
}
int QmitkMultiLabelTreeModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0)
return 0;
if (m_Segmentation.IsNull())
return 0;
QmitkMultiLabelSegTreeItem* parentItem = m_RootItem.get();
if (parent.isValid())
parentItem = static_cast<QmitkMultiLabelSegTreeItem *>(parent.internalPointer());
if (parentItem->HandleAsInstance())
{
return 0;
}
return parentItem->m_childItems.size();
}
QVariant QmitkMultiLabelTreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
auto item = static_cast<QmitkMultiLabelSegTreeItem*>(index.internalPointer());
if (!item)
return QVariant();
if (role == Qt::DisplayRole||role == Qt::EditRole)
{
if (TableColumns::NAME_COL == index.column())
{
switch (item->m_ItemType)
{
case QmitkMultiLabelSegTreeItem::ItemType::Group:
{
const auto groupID = item->GetGroupID();
if (m_Segmentation->ExistGroup(groupID))
{
return QVariant(QString::fromStdString(mitk::LabelSetImageHelper::CreateDisplayGroupName(m_Segmentation, groupID)));
}
return QVariant(QString("unknown group"));
}
case QmitkMultiLabelSegTreeItem::ItemType::Label:
{
auto label = item->GetLabel();
if (nullptr == label)
- mitkThrow() << "Invalid internal state. QmitkMultiLabelTreeModel currentItem is refering to a label that does not exist.";
+ mitkThrow() << "Invalid internal state. QmitkMultiLabelTreeModel currentItem is referring to a label that does not exist.";
QString name = QString::fromStdString(label->GetName());
if (!item->HandleAsInstance())
name = name + QString(" (%1 instances)").arg(item->m_childItems.size());
return QVariant(name);
}
case QmitkMultiLabelSegTreeItem::ItemType::Instance:
{
auto label = item->GetLabel();
if (nullptr == label)
- mitkThrow() << "Invalid internal state. QmitkMultiLabelTreeModel currentItem is refering to a label that does not exist.";
+ mitkThrow() << "Invalid internal state. QmitkMultiLabelTreeModel currentItem is referring to a label that does not exist.";
return QVariant(QString::fromStdString(label->GetName()) + QString(" [%1]").arg(item->GetLabelValue()));
}
}
}
else
{
if (item->HandleAsInstance())
{
auto label = item->GetLabel();
if (TableColumns::LOCKED_COL == index.column())
{
return QVariant(label->GetLocked());
}
else if (TableColumns::COLOR_COL == index.column())
{
return QVariant(QColor(label->GetColor().GetRed() * 255, label->GetColor().GetGreen() * 255, label->GetColor().GetBlue() * 255));
}
else if (TableColumns::VISIBLE_COL == index.column())
{
return QVariant(label->GetVisible());
}
}
}
}
else if (role == Qt::ToolTipRole)
{
if (item->m_ItemType == QmitkMultiLabelSegTreeItem::ItemType::Group)
{
return QVariant(QString("Group %1").arg(item->GetGroupID()));
}
else
{
auto label = item->GetLabel();
if (nullptr == label)
mitkThrow() << "Invalid internal state. QmitkMultiLabelTreeModel currentItem is referring to a label that does not exist.";
QString name = QString::fromStdString("<b>"+label->GetName()+"</b>");
if (!item->HandleAsInstance())
{
name = QString("Label class: %1\nContaining %2 instances").arg(name).arg(item->m_childItems.size());
}
else
{
name = QString::fromStdString(label->GetName()) + QString("\nLabel instance ID: %1\nPixel value: %2").arg(item->GetLabelValue()).arg(item->GetLabelValue());
}
return QVariant(name);
}
}
else if (role == ItemModelRole::LabelDataRole)
{
auto label = item->GetLabel();
if (nullptr!=label) return QVariant::fromValue<void*>(label);
}
else if (role == ItemModelRole::LabelValueRole)
{
auto label = item->GetLabel();
if (nullptr != label) return QVariant(label->GetValue());
}
else if (role == ItemModelRole::LabelInstanceDataRole)
{
if (item->HandleAsInstance())
{
auto label = item->GetLabel();
return QVariant::fromValue<void*>(label);
}
}
else if (role == ItemModelRole::LabelInstanceValueRole)
{
if (item->HandleAsInstance())
{
auto label = item->GetLabel();
return QVariant(label->GetValue());
}
}
else if (role == ItemModelRole::GroupIDRole)
{
QVariant v;
v.setValue(item->GetGroupID());
return v;
}
return QVariant();
}
mitk::Color QtToMitk(const QColor& color)
{
mitk::Color mitkColor;
mitkColor.SetRed(color.red() / 255.0f);
mitkColor.SetGreen(color.green() / 255.0f);
mitkColor.SetBlue(color.blue() / 255.0f);
return mitkColor;
}
bool QmitkMultiLabelTreeModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (!index.isValid())
return false;
auto item = static_cast<QmitkMultiLabelSegTreeItem*>(index.internalPointer());
if (!item)
return false;
if (role == Qt::EditRole)
{
if (TableColumns::NAME_COL != index.column())
{
if (item->HandleAsInstance())
{
auto label = item->GetLabel();
if (TableColumns::LOCKED_COL == index.column())
{
label->SetLocked(value.toBool());
}
else if (TableColumns::COLOR_COL == index.column())
{
label->SetColor(QtToMitk(value.value<QColor>()));
}
else if (TableColumns::VISIBLE_COL == index.column())
{
label->SetVisible(value.toBool());
}
m_Segmentation->UpdateLookupTable(label->GetValue());
m_Segmentation->GetLookupTable()->Modified();
m_Segmentation->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
else
{
}
return true;
}
}
return false;
}
QModelIndex QmitkMultiLabelTreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
auto parentItem = m_RootItem.get();
if (parent.isValid())
parentItem = static_cast<QmitkMultiLabelSegTreeItem *>(parent.internalPointer());
QmitkMultiLabelSegTreeItem *childItem = parentItem->m_childItems[row];
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex QmitkMultiLabelTreeModel::indexOfLabel(mitk::Label::PixelType labelValue) const
{
if (labelValue == mitk::LabelSetImage::UNLABELED_VALUE) return QModelIndex();
auto relevantItem = GetInstanceItem(labelValue, this->m_RootItem.get());
if (nullptr == relevantItem)
return QModelIndex();
auto labelItem = relevantItem->ParentItem();
if (labelItem->m_childItems.size() == 1)
{ //was the only instance of the label, therefor return the label item instat.
relevantItem = labelItem;
}
return GetIndexByItem(relevantItem, this);
}
QModelIndex QmitkMultiLabelTreeModel::indexOfGroup(mitk::LabelSetImage::GroupIndexType groupIndex) const
{
auto relevantItem = GetGroupItem(groupIndex, this->m_RootItem.get());
if (nullptr == relevantItem) QModelIndex();
return GetIndexByItem(relevantItem, this);
}
QModelIndex QmitkMultiLabelTreeModel::parent(const QModelIndex &child) const
{
if (!child.isValid())
return QModelIndex();
QmitkMultiLabelSegTreeItem *childItem = static_cast<QmitkMultiLabelSegTreeItem *>(child.internalPointer());
QmitkMultiLabelSegTreeItem *parentItem = childItem->ParentItem();
if (parentItem == m_RootItem.get())
return QModelIndex();
return createIndex(parentItem->Row(), 0, parentItem);
}
QModelIndex QmitkMultiLabelTreeModel::ClosestLabelInstanceIndex(const QModelIndex& currentIndex) const
{
if (!currentIndex.isValid()) return QModelIndex();
auto currentItem = static_cast<const QmitkMultiLabelSegTreeItem*>(currentIndex.internalPointer());
if (!currentItem) return QModelIndex();
if (currentItem->RootItem() != this->m_RootItem.get()) mitkThrow() << "Invalid call. Passed currentIndex does not seem to be a valid index of this model. It is either outdated or from another model.";
const QmitkMultiLabelSegTreeItem* resultItem = nullptr;
auto searchItem = currentItem;
const auto rootItem = currentItem->RootItem();
while (searchItem != rootItem)
{
const auto* sibling = searchItem;
while (sibling != nullptr)
{
sibling = sibling->NextSibblingItem();
resultItem = GetFirstInstanceLikeItem(sibling);
if (nullptr != resultItem)
break;
}
if (nullptr != resultItem)
break;
// No next closest label instance on this level -> check for closest before
sibling = searchItem;
while (sibling != nullptr)
{
sibling = sibling->PrevSibblingItem();
resultItem = GetFirstInstanceLikeItem(sibling);
if (nullptr != resultItem)
break;
}
if (nullptr != resultItem)
break;
// No closest label instance before current on this level -> moeve one level up
searchItem = searchItem->ParentItem();
}
if (nullptr == resultItem)
return QModelIndex();
return GetIndexByItem(resultItem, this);
}
QModelIndex QmitkMultiLabelTreeModel::FirstLabelInstanceIndex(const QModelIndex& currentIndex) const
{
const QmitkMultiLabelSegTreeItem* currentItem = nullptr;
if (!currentIndex.isValid())
{
currentItem = this->m_RootItem.get();
}
else
{
currentItem = static_cast<const QmitkMultiLabelSegTreeItem*>(currentIndex.internalPointer());
}
if (!currentItem) return QModelIndex();
if (currentItem->RootItem() != this->m_RootItem.get()) mitkThrow() << "Invalid call. Passed currentIndex does not seem to be a valid index of this model. It is either outdated or from another model.";
const QmitkMultiLabelSegTreeItem* resultItem = nullptr;
resultItem = GetFirstInstanceLikeItem(currentItem);
if (nullptr == resultItem)
return QModelIndex();
return GetIndexByItem(resultItem, this);
}
///** Returns the index to the next node in the tree that behaves like an instance (label node with only one instance
//or instance node). If current index is at the end, an invalid index is returned.*/
//QModelIndex QmitkMultiLabelTreeModel::PrevLabelInstanceIndex(const QModelIndex& currentIndex) const;
std::vector <QmitkMultiLabelTreeModel::LabelValueType> QmitkMultiLabelTreeModel::GetLabelsInSubTree(const QModelIndex& currentIndex) const
{
const QmitkMultiLabelSegTreeItem* currentItem = nullptr;
if (!currentIndex.isValid())
{
currentItem = this->m_RootItem.get();
}
else
{
currentItem = static_cast<const QmitkMultiLabelSegTreeItem*>(currentIndex.internalPointer());
}
if (!currentItem) return {};
return currentItem->GetLabelsInSubTree();
}
std::vector <QmitkMultiLabelTreeModel::LabelValueType> QmitkMultiLabelTreeModel::GetLabelInstancesOfSameLabelClass(const QModelIndex& currentIndex) const
{
const QmitkMultiLabelSegTreeItem* currentItem = nullptr;
if (currentIndex.isValid())
{
currentItem = static_cast<const QmitkMultiLabelSegTreeItem*>(currentIndex.internalPointer());
}
if (!currentItem)
return {};
if (QmitkMultiLabelSegTreeItem::ItemType::Group == currentItem->m_ItemType)
return {};
if (QmitkMultiLabelSegTreeItem::ItemType::Instance == currentItem->m_ItemType)
currentItem = currentItem->ParentItem();
return currentItem->GetLabelsInSubTree();
}
Qt::ItemFlags QmitkMultiLabelTreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
if (!index.isValid())
return Qt::NoItemFlags;
auto item = static_cast<QmitkMultiLabelSegTreeItem*>(index.internalPointer());
if (!item)
return Qt::NoItemFlags;
if (TableColumns::NAME_COL != index.column())
{
if (item->HandleAsInstance() &&
((TableColumns::VISIBLE_COL == index.column() && m_AllowVisibilityModification) ||
(TableColumns::COLOR_COL == index.column() && m_AllowVisibilityModification) || //m_AllowVisibilityModification controls visibility and color
(TableColumns::LOCKED_COL == index.column() && m_AllowLockModification)))
{
return Qt::ItemIsEnabled | Qt::ItemIsEditable;
}
else
{
return Qt::ItemIsEnabled;
}
}
else
{
if (item->HandleAsInstance())
{
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
else
{
return Qt::ItemIsEnabled;
}
}
return Qt::NoItemFlags;
}
QVariant QmitkMultiLabelTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if ((Qt::DisplayRole == role) && (Qt::Horizontal == orientation))
{
if (TableColumns::NAME_COL == section)
{
return "Name";
}
else if (TableColumns::LOCKED_COL == section)
{
return "Locked";
}
else if (TableColumns::COLOR_COL == section)
{
return "Color";
}
else if (TableColumns::VISIBLE_COL == section)
{
return "Visibility";
}
}
return QVariant();
}
const mitk::LabelSetImage* QmitkMultiLabelTreeModel::GetSegmentation() const
{
return m_Segmentation;
}
void QmitkMultiLabelTreeModel::SetSegmentation(mitk::LabelSetImage* segmentation)
{
if (m_Segmentation != segmentation)
{
this->m_Segmentation = segmentation;
this->AddObserver();
this->UpdateInternalTree();
}
}
/**Helper function that adds a labek into the item tree. Passes back the new created instance iten*/
QmitkMultiLabelSegTreeItem* AddLabelToGroupTree(mitk::Label* label, QmitkMultiLabelSegTreeItem* groupItem, bool& newLabelItemCreated)
{
if (nullptr == groupItem) return nullptr;
if (nullptr == label) return nullptr;
newLabelItemCreated = false;
std::set<std::string> labelNames;
for (auto labelItem : groupItem->m_childItems)
{
labelNames.emplace(labelItem->GetLabel()->GetName());
}
QmitkMultiLabelSegTreeItem* labelItem = nullptr;
auto finding = labelNames.find(label->GetName());
if (finding != labelNames.end())
{ //other label with same name exists
labelItem = groupItem->m_childItems[std::distance(labelNames.begin(), finding)];
}
else
{
newLabelItemCreated = true;
labelItem = new QmitkMultiLabelSegTreeItem(QmitkMultiLabelSegTreeItem::ItemType::Label, groupItem, nullptr, label->GetName());
auto predicate = [label](const std::string& name) { return name > label->GetName(); };
auto insertFinding = std::find_if(labelNames.begin(), labelNames.end(), predicate);
groupItem->m_childItems.insert(groupItem->m_childItems.begin() + std::distance(labelNames.begin(), insertFinding), labelItem);
}
auto instanceItem = new QmitkMultiLabelSegTreeItem(QmitkMultiLabelSegTreeItem::ItemType::Instance, labelItem, label);
auto predicate = [label](const QmitkMultiLabelSegTreeItem* item) { return item->GetLabelValue() > label->GetValue(); };
auto insertFinding = std::find_if(labelItem->m_childItems.begin(), labelItem->m_childItems.end(), predicate);
labelItem->m_childItems.insert(labelItem->m_childItems.begin() + std::distance(labelItem->m_childItems.begin(), insertFinding), instanceItem);
return instanceItem;
}
void QmitkMultiLabelTreeModel::GenerateInternalGroupTree(unsigned int groupID, QmitkMultiLabelSegTreeItem* groupItem)
{
auto labels = m_Segmentation->GetLabelsByValue(m_Segmentation->GetLabelValuesByGroup(groupID));
for (auto& label : labels)
{
if (label->GetValue()== mitk::LabelSetImage::UNLABELED_VALUE) continue;
bool newItemCreated = false;
AddLabelToGroupTree(label, groupItem, newItemCreated);
}
}
QmitkMultiLabelSegTreeItem* QmitkMultiLabelTreeModel::GenerateInternalTree()
{
auto rootItem = new QmitkMultiLabelSegTreeItem();
if (m_Segmentation.IsNotNull())
{
for (unsigned int groupID = 0; groupID < m_Segmentation->GetNumberOfLayers(); ++groupID)
{
auto groupItem = new QmitkMultiLabelSegTreeItem(QmitkMultiLabelSegTreeItem::ItemType::Group, rootItem);
rootItem->AppendChild(groupItem);
GenerateInternalGroupTree(groupID, groupItem);
}
}
return rootItem;
}
void QmitkMultiLabelTreeModel::UpdateInternalTree()
{
emit beginResetModel();
auto newTree = this->GenerateInternalTree();
this->m_RootItem.reset(newTree);
emit endResetModel();
emit modelChanged();
}
void QmitkMultiLabelTreeModel::ITKEventHandler(const itk::EventObject& e)
{
if (mitk::LabelAddedEvent().CheckEvent(&e))
{
auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&e);
this->OnLabelAdded(labelEvent->GetLabelValue());
}
else if (mitk::LabelModifiedEvent().CheckEvent(&e))
{
auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&e);
this->OnLabelModified(labelEvent->GetLabelValue());
}
else if (mitk::LabelRemovedEvent().CheckEvent(&e))
{
auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&e);
this->OnLabelRemoved(labelEvent->GetLabelValue());
}
else if (mitk::GroupAddedEvent().CheckEvent(&e))
{
auto labelEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&e);
this->OnGroupAdded(labelEvent->GetGroupID());
}
else if (mitk::GroupModifiedEvent().CheckEvent(&e))
{
auto labelEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&e);
this->OnGroupModified(labelEvent->GetGroupID());
}
else if (mitk::GroupRemovedEvent().CheckEvent(&e))
{
auto labelEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&e);
this->OnGroupRemoved(labelEvent->GetGroupID());
}
}
void QmitkMultiLabelTreeModel::AddObserver()
{
m_LabelAddedObserver.Reset();
m_LabelModifiedObserver.Reset();
m_LabelRemovedObserver.Reset();
m_GroupAddedObserver.Reset();
m_GroupModifiedObserver.Reset();
m_GroupRemovedObserver.Reset();
if (this->m_Segmentation.IsNotNull())
{
auto& model = *this;
m_LabelAddedObserver.Reset(m_Segmentation, mitk::LabelAddedEvent(), [&model](const itk::EventObject& event){model.ITKEventHandler(event);});
m_LabelModifiedObserver.Reset(m_Segmentation, mitk::LabelModifiedEvent(), [&model](const itk::EventObject& event) {model.ITKEventHandler(event); });
m_LabelRemovedObserver.Reset(m_Segmentation, mitk::LabelRemovedEvent(), [&model](const itk::EventObject& event) {model.ITKEventHandler(event); });
m_GroupAddedObserver.Reset(m_Segmentation, mitk::GroupAddedEvent(), [&model](const itk::EventObject& event) {
model.ITKEventHandler(event); });
m_GroupModifiedObserver.Reset(m_Segmentation, mitk::GroupModifiedEvent(), [&model](const itk::EventObject& event) {model.ITKEventHandler(event); });
m_GroupRemovedObserver.Reset(m_Segmentation, mitk::GroupRemovedEvent(), [&model](const itk::EventObject& event) {model.ITKEventHandler(event); });
}
}
void QmitkMultiLabelTreeModel::OnLabelAdded(LabelValueType labelValue)
{
GroupIndexType groupIndex = m_Segmentation->GetGroupIndexOfLabel(labelValue);
auto label = m_Segmentation->GetLabel(labelValue);
- if (nullptr == label) mitkThrow() << "Invalid internal state. Segmentation signaled the addition of an label that does not exist in the segmentation. Invalid label value:" << labelValue;
+ if (label.IsNull()) mitkThrow() << "Invalid internal state. Segmentation signaled the addition of a label that does not exist in the segmentation. Invalid label value:" << labelValue;
if (labelValue == mitk::LabelSetImage::UNLABELED_VALUE) return;
auto groupItem = GetGroupItem(groupIndex, this->m_RootItem.get());
bool newLabelCreated = false;
auto instanceItem = AddLabelToGroupTree(label, groupItem, newLabelCreated);
if (newLabelCreated)
{
if (groupItem->m_childItems.size() == 1)
{ //first label added
auto groupIndex = GetIndexByItem(groupItem, this);
emit dataChanged(groupIndex, groupIndex);
this->beginInsertRows(groupIndex, instanceItem->ParentItem()->Row(), instanceItem->ParentItem()->Row());
this->endInsertRows();
}
else
{ //whole new label level added to group item
auto groupIndex = GetIndexByItem(groupItem, this);
this->beginInsertRows(groupIndex, instanceItem->ParentItem()->Row(), instanceItem->ParentItem()->Row());
this->endInsertRows();
}
}
else
{
if (instanceItem->ParentItem()->m_childItems.size() < 3)
{ //second instance item was added, so label item will now able to collapse
// -> the whole label node has to be updated.
auto labelIndex = GetIndexByItem(instanceItem->ParentItem(), this);
emit dataChanged(labelIndex, labelIndex);
this->beginInsertRows(labelIndex, 0, instanceItem->ParentItem()->m_childItems.size()-1);
this->endInsertRows();
}
else
{
// instance item was added to existing label item with multiple instances
//-> just notify the row insertion
auto labelIndex = GetIndexByItem(instanceItem->ParentItem(), this);
this->beginInsertRows(labelIndex, instanceItem->Row(), instanceItem->Row());
this->endInsertRows();
}
}
}
void QmitkMultiLabelTreeModel::OnLabelModified(LabelValueType labelValue)
{
if (labelValue == mitk::LabelSetImage::UNLABELED_VALUE) return;
auto instanceItem = GetInstanceItem(labelValue, this->m_RootItem.get());
if (nullptr == instanceItem)
{
mitkThrow() << "Internal invalid state. QmitkMultiLabelTreeModel received a LabelModified signal for a label that is not represented in the model. Invalid label: " << labelValue;
+ mitkThrow() << "Internal invalid state. QmitkMultiLabelTreeModel received a LabelModified signal for a label that is not represented in the model. Invalid label: " << labelValue;
}
auto labelItem = instanceItem->ParentItem();
if (labelItem->m_ClassName == instanceItem->GetLabel()->GetName())
{ //only the state of the label changed, but not its position in the model tree.
auto index = labelItem->HandleAsInstance() ? GetIndexByItem(labelItem, this) : GetIndexByItem(instanceItem, this);
auto rightIndex = index.sibling(index.row(), this->columnCount() - 1);
emit dataChanged(index, rightIndex);
}
else
{ //the name of the label changed and thus its place in the model tree, delete the current item and add a new one
this->OnLabelRemoved(labelValue);
this->OnLabelAdded(labelValue);
}
}
void QmitkMultiLabelTreeModel::OnLabelRemoved(LabelValueType labelValue)
{
if (labelValue == mitk::LabelSetImage::UNLABELED_VALUE) return;
auto instanceItem = GetInstanceItem(labelValue, this->m_RootItem.get());
if (nullptr == instanceItem)
mitkThrow() << "Internal invalid state. QmitkMultiLabelTreeModel received a LabelRemoved signal for a label that is not represented in the model. Invalid label: " << labelValue;
auto labelItem = instanceItem->ParentItem();
if (labelItem->m_childItems.size() > 2)
{
auto labelIndex = GetIndexByItem(labelItem, this);
this->beginRemoveRows(labelIndex, instanceItem->Row(), instanceItem->Row());
labelItem->RemoveChild(instanceItem->Row());
this->endRemoveRows();
}
else if (labelItem->m_childItems.size() == 2)
{ //After removal only one label is left -> the whole label node is about to be changed (no instances are shown any more).
auto labelIndex = GetIndexByItem(labelItem, this);
this->beginRemoveRows(labelIndex, instanceItem->Row(), instanceItem->Row());
labelItem->RemoveChild(instanceItem->Row());
this->endRemoveRows();
emit dataChanged(labelIndex, labelIndex);
}
else
{ //was the only instance of the label, therefor also remove the label node from the tree.
auto groupItem = labelItem->ParentItem();
auto groupIndex = GetIndexByItem(groupItem, this);
this->beginRemoveRows(groupIndex, labelItem->Row(), labelItem->Row());
groupItem->RemoveChild(labelItem->Row());
this->endRemoveRows();
}
}
void QmitkMultiLabelTreeModel::OnGroupAdded(GroupIndexType groupIndex)
{
if (m_ShowGroups)
{
this->beginInsertRows(QModelIndex(), groupIndex, groupIndex);
auto rootItem = m_RootItem.get();
auto groupItem = new QmitkMultiLabelSegTreeItem(QmitkMultiLabelSegTreeItem::ItemType::Group, rootItem);
rootItem->AppendChild(groupItem);
this->GenerateInternalGroupTree(groupIndex, groupItem);
this->endInsertRows();
}
}
void QmitkMultiLabelTreeModel::OnGroupModified(GroupIndexType /*groupIndex*/)
{
//currently not needed
}
void QmitkMultiLabelTreeModel::OnGroupRemoved(GroupIndexType groupIndex)
{
if (m_ShowGroups)
{
this->beginRemoveRows(QModelIndex(), groupIndex, groupIndex);
auto root = m_RootItem.get();
root->RemoveChild(groupIndex);
this->endRemoveRows();
}
}
void QmitkMultiLabelTreeModel::SetAllowVisibilityModification(bool vmod)
{
m_AllowVisibilityModification = vmod;
}
bool QmitkMultiLabelTreeModel::GetAllowVisibilityModification() const
{
return m_AllowVisibilityModification;
}
void QmitkMultiLabelTreeModel::SetAllowLockModification(bool lmod)
{
m_AllowLockModification = lmod;
}
bool QmitkMultiLabelTreeModel::GetAllowLockModification() const
{
return m_AllowLockModification;
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.h
index a8b4d671d5..33441944ae 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.h
@@ -1,168 +1,168 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitkMultiLabelTreeModel_h
#define QmitkMultiLabelTreeModel_h
#include "mitkLabelSetImage.h"
#include <mitkITKEventObserverGuard.h>
// qt
#include <QAbstractItemModel>
#include "MitkSegmentationUIExports.h"
class QmitkMultiLabelSegTreeItem;
/*!
\class QmitkMultiLabelTreeModel
-The class is used to represent the information of an MITK MultiLabel segmentation instance (labels, spacial groups...).
+The class is used to represent the information of an MITK MultiLabel segmentation instance (labels, spatial groups...).
*/
class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
using LabelValueType = mitk::LabelSetImage::LabelValueType;
using GroupIndexType = mitk::LabelSetImage::GroupIndexType;
QmitkMultiLabelTreeModel(QObject *parent = nullptr);
~QmitkMultiLabelTreeModel() override;
void SetSegmentation(mitk::LabelSetImage* segmentation);
const mitk::LabelSetImage* GetSegmentation() const;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) 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;
/** returns the index of a passed label value (always first column). If label value does not exist in
segmentation or segmentation is not set an invalid index will be returned.*/
QModelIndex indexOfLabel(mitk::Label::PixelType labelValue) const;
QModelIndex indexOfGroup(mitk::LabelSetImage::GroupIndexType groupIndex) const;
/** Returns the index to the next node in the tree that behaves like an instance (label node with only one instance
or instance node). If current index is at the end, an invalid index is returned.*/
QModelIndex ClosestLabelInstanceIndex(const QModelIndex& currentIndex) const;
/** Returns the index to the first child node (or itself) in the tree that behaves like an instance (label node with only one instance
or instance node). If current index is at the end, an invalid index is returned. If an invalid index is passed into the methods,
the search starts at the root; thus the whole tree is search for the first label instance.*/
QModelIndex FirstLabelInstanceIndex(const QModelIndex& currentIndex) const;
///** Returns the index to the next node in the tree that behaves like an instance (label node with only one instance
//or instance node). If current index is at the end, an invalid index is returned.*/
//QModelIndex PrevLabelInstanceIndex(const QModelIndex& currentIndex) const;
/** Returns a vector containing all label values of the passed currentIndex or its child items.*/
std::vector <LabelValueType> GetLabelsInSubTree(const QModelIndex& currentIndex) const;
/** Returns a vector containing all label values of all label instances that belong to the same label
* class like the passed index.
*
* If index points to a group or invalid, nothing will be returned.
* @pre currentIndex must be valid and point to a label (class or instance).
*/
std::vector <LabelValueType> GetLabelInstancesOfSameLabelClass(const QModelIndex& currentIndex) const;
enum TableColumns
{
NAME_COL = 0,
LOCKED_COL,
COLOR_COL,
VISIBLE_COL
};
enum ItemModelRole
{
/**This role returns the label object that is associated with an index.
- On group level it always returns an invalid QVariant
- On label level (with multiple instances) it returns the first label instance).
- On instance level it returns the label instance object.*/
LabelDataRole = 64,
/**This role returns only the label value of the label that would be returned by
LabelDataRole.*/
LabelValueRole = 65,
/**Simelar to LabelDataRole, but only returns a valid QVariant if index points only to
a specific instance (so either instance level or label level with only one instance).
You can use that role if you want to assure that only one specific label instance is
referenced by the index.*/
LabelInstanceDataRole = 66,
/**Simelar to LabelValueRole, but like LabelInstanceDataRole only returns a valid QVariant
if index points only to a specific instance (so either instance level or label
level with only one instance).
You can use that role if you want to assure that only one specific label instance is
referenced by the index.*/
LabelInstanceValueRole = 67,
/**This role returns the group ID the item/index belongs to.*/
GroupIDRole = 68
};
bool GetAllowVisibilityModification() const;
bool GetAllowLockModification() const;
public Q_SLOTS:
void SetAllowVisibilityModification(bool vmod);
void SetAllowLockModification(bool lmod);
Q_SIGNALS:
void dataAvailable();
/** Is emitted whenever the model changes are finished (usually a bit later than dataAvailable()).*/
void modelChanged();
protected:
void ITKEventHandler(const itk::EventObject& e);
void OnLabelAdded(LabelValueType labelValue);
void OnLabelModified(LabelValueType labelValue);
void OnLabelRemoved(LabelValueType labelValue);
void OnGroupAdded(GroupIndexType groupIndex);
void OnGroupModified(GroupIndexType groupIndex);
void OnGroupRemoved(GroupIndexType groupIndex);
private:
void AddObserver();
void UpdateInternalTree();
void GenerateInternalGroupTree(unsigned int layerID, QmitkMultiLabelSegTreeItem* layerItem);
QmitkMultiLabelSegTreeItem* GenerateInternalTree();
mitk::LabelSetImage::Pointer m_Segmentation;
std::mutex m_Mutex;
std::unique_ptr<QmitkMultiLabelSegTreeItem> m_RootItem;
bool m_ShowGroups = true;
bool m_ShowVisibility = true;
bool m_ShowLock = true;
bool m_ShowOther = false;
bool m_AllowVisibilityModification = true;
bool m_AllowLockModification = true;
mitk::ITKEventObserverGuard m_LabelAddedObserver;
mitk::ITKEventObserverGuard m_LabelModifiedObserver;
mitk::ITKEventObserverGuard m_LabelRemovedObserver;
mitk::ITKEventObserverGuard m_GroupAddedObserver;
mitk::ITKEventObserverGuard m_GroupModifiedObserver;
mitk::ITKEventObserverGuard m_GroupRemovedObserver;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.cpp b/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.cpp
index 03b8b4851f..0d1a6864dc 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.cpp
@@ -1,186 +1,186 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkSegWithPreviewToolGUIBase.h"
#include <qcheckbox.h>
#include <qpushbutton.h>
#include <qboxlayout.h>
#include <qlabel.h>
#include <QApplication>
bool DefaultEnableConfirmSegBtnFunction(bool enabled)
{
return enabled;
}
QmitkSegWithPreviewToolGUIBase::QmitkSegWithPreviewToolGUIBase(bool mode2D) : QmitkToolGUI(), m_EnableConfirmSegBtnFnc(DefaultEnableConfirmSegBtnFunction), m_Mode2D(mode2D)
{
connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *)));
}
QmitkSegWithPreviewToolGUIBase::~QmitkSegWithPreviewToolGUIBase()
{
if (m_Tool.IsNotNull())
{
m_Tool->CurrentlyBusy -= mitk::MessageDelegate1<QmitkSegWithPreviewToolGUIBase, bool>(this, &QmitkSegWithPreviewToolGUIBase::BusyStateChanged);
}
}
void QmitkSegWithPreviewToolGUIBase::OnNewToolAssociated(mitk::Tool *tool)
{
if (m_Tool.IsNotNull())
{
this->DisconnectOldTool(m_Tool);
}
m_Tool = dynamic_cast<mitk::SegWithPreviewTool*>(tool);
if (nullptr == m_MainLayout)
{
// create the visible widgets
m_MainLayout = new QVBoxLayout(this);
m_ConfirmSegBtn = new QPushButton("Confirm Segmentation", this);
connect(m_ConfirmSegBtn, SIGNAL(clicked()), this, SLOT(OnAcceptPreview()));
m_CheckIgnoreLocks = new QCheckBox("Ignore label locks", this);
m_CheckIgnoreLocks->setChecked(m_Tool->GetOverwriteStyle() == mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
m_CheckIgnoreLocks->setToolTip("If checked, the lock state of labels will be ignored when the preview segmentation is confermed. Thus also locked label pixels can be changed by the operation.");
m_CheckMerge = new QCheckBox("Merge with existing content", this);
m_CheckMerge->setChecked(m_Tool->GetMergeStyle()==mitk::MultiLabelSegmentation::MergeStyle::Merge);
- m_CheckMerge->setToolTip("If checked, the preview segmantation will be merged with the existing segmantation into a union. If unchecked, the preview content will replace the old segmantation");
+ m_CheckMerge->setToolTip("If checked, the preview segmentation will be merged with the existing segmentation into a union. If unchecked, the preview content will replace the old segmentation");
m_CheckProcessAll = new QCheckBox("Process all time steps", this);
m_CheckProcessAll->setChecked(false);
m_CheckProcessAll->setToolTip("Process all time steps of the dynamic segmentation and not just the currently visible time step.");
m_CheckProcessAll->setVisible(!m_Mode2D);
- //remark: keept m_CheckProcessAll deactivated in 2D because in this refactoring
+ //remark: keep m_CheckProcessAll deactivated in 2D because in this refactoring
//it should be kept to the status quo and it was not clear how interpolation
//would behave. As soon as it is sorted out we can remove that "feature switch"
//or the comment.
this->InitializeUI(m_MainLayout);
m_MainLayout->addWidget(m_ConfirmSegBtn);
m_MainLayout->addWidget(m_CheckIgnoreLocks);
m_MainLayout->addWidget(m_CheckMerge);
m_MainLayout->addWidget(m_CheckProcessAll);
}
if (m_Tool.IsNotNull())
{
this->ConnectNewTool(m_Tool);
}
}
void QmitkSegWithPreviewToolGUIBase::OnAcceptPreview()
{
if (m_Tool.IsNotNull())
{
if (m_CheckIgnoreLocks->isChecked())
{
m_Tool->SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
}
else
{
m_Tool->SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
}
if (m_CheckMerge->isChecked())
{
m_Tool->SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle::Merge);
}
else
{
m_Tool->SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle::Replace);
}
m_Tool->SetCreateAllTimeSteps(m_CheckProcessAll->isChecked());
m_ConfirmSegBtn->setEnabled(false);
m_Tool->ConfirmSegmentation();
}
}
void QmitkSegWithPreviewToolGUIBase::DisconnectOldTool(mitk::SegWithPreviewTool* oldTool)
{
oldTool->CurrentlyBusy -= mitk::MessageDelegate1<QmitkSegWithPreviewToolGUIBase, bool>(this, &QmitkSegWithPreviewToolGUIBase::BusyStateChanged);
}
void QmitkSegWithPreviewToolGUIBase::ConnectNewTool(mitk::SegWithPreviewTool* newTool)
{
newTool->CurrentlyBusy +=
mitk::MessageDelegate1<QmitkSegWithPreviewToolGUIBase, bool>(this, &QmitkSegWithPreviewToolGUIBase::BusyStateChanged);
m_CheckProcessAll->setVisible(newTool->GetTargetSegmentationNode()->GetData()->GetTimeSteps() > 1);
this->EnableWidgets(true);
}
void QmitkSegWithPreviewToolGUIBase::InitializeUI(QBoxLayout* /*mainLayout*/)
{
//default implementation does nothing
}
void QmitkSegWithPreviewToolGUIBase::BusyStateChanged(bool isBusy)
{
if (isBusy)
{
QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
}
else
{
QApplication::restoreOverrideCursor();
}
this->EnableWidgets(!isBusy);
}
void QmitkSegWithPreviewToolGUIBase::EnableWidgets(bool enabled)
{
if (nullptr != m_MainLayout)
{
if (nullptr != m_ConfirmSegBtn)
{
m_ConfirmSegBtn->setEnabled(m_EnableConfirmSegBtnFnc(enabled));
}
if (nullptr != m_CheckIgnoreLocks)
{
m_CheckIgnoreLocks->setEnabled(enabled);
}
if (nullptr != m_CheckMerge)
{
m_CheckMerge->setEnabled(enabled);
}
if (nullptr != m_CheckProcessAll)
{
m_CheckProcessAll->setEnabled(enabled);
}
}
}
void QmitkSegWithPreviewToolGUIBase::SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle mergeStyle)
{
if (nullptr != m_CheckMerge)
{
m_CheckMerge->setChecked(mergeStyle == mitk::MultiLabelSegmentation::MergeStyle::Merge);
}
};
void QmitkSegWithPreviewToolGUIBase::SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle)
{
if (nullptr != m_CheckIgnoreLocks)
{
m_CheckIgnoreLocks->setChecked(overwriteStyle == mitk::MultiLabelSegmentation::OverwriteStyle::IgnoreLocks);
}
};
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.h b/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.h
index 5ce67a274c..3573f88d1f 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkSegWithPreviewToolGUIBase.h
@@ -1,95 +1,95 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitkSegWithPreviewToolGUIBase_h
#define QmitkSegWithPreviewToolGUIBase_h
#include "QmitkToolGUI.h"
#include "mitkSegWithPreviewTool.h"
#include <MitkSegmentationUIExports.h>
class QCheckBox;
class QPushButton;
class QBoxLayout;
/**
\ingroup org_mitk_gui_qt_interactivesegmentation_internal
\brief GUI base clase for tools derived from mitk::SegWithPreviewTool.
*/
class MITKSEGMENTATIONUI_EXPORT QmitkSegWithPreviewToolGUIBase : public QmitkToolGUI
{
Q_OBJECT
public:
mitkClassMacro(QmitkSegWithPreviewToolGUIBase, QmitkToolGUI);
itkCloneMacro(Self);
itkGetConstMacro(Mode2D, bool);
protected slots:
void OnNewToolAssociated(mitk::Tool *);
void OnAcceptPreview();
protected:
QmitkSegWithPreviewToolGUIBase(bool mode2D);
~QmitkSegWithPreviewToolGUIBase() override;
virtual void DisconnectOldTool(mitk::SegWithPreviewTool* oldTool);
virtual void ConnectNewTool(mitk::SegWithPreviewTool* newTool);
/**This method is called by OnNewToolAssociated if the UI is initialized the
first time to allow derived classes to introduce own UI code. Overwrite to change.
The implementation should ensure that alle widgets needed for the tool UI are
properly allocated. If one needs to eecute time (e.g. to connect events between the tool
and the UI) each time the tool changes, override the functions ConnectNewTool() and
DisconnectOldTool().*/
virtual void InitializeUI(QBoxLayout* mainLayout);
void BusyStateChanged(bool isBusy) override;
using EnableConfirmSegBtnFunctionType = std::function<bool(bool)>;
EnableConfirmSegBtnFunctionType m_EnableConfirmSegBtnFnc;
/**This method is used to control/set the enabled state of the tool UI
widgets. It is e.g. used if the busy state is changed (see BusyStateChanged).
- Override the default implmentation, e.g. if a tool adds his own UI elements
+ Override the default implementation, e.g. if a tool adds his own UI elements
(normally by overriding InitializeUI()) and wants to control how the widgets
are enabled/disabled.*/
virtual void EnableWidgets(bool enabled);
template <class TTool>
TTool* GetConnectedToolAs()
{
return dynamic_cast<TTool*>(m_Tool.GetPointer());
};
void SetMergeStyle(mitk::MultiLabelSegmentation::MergeStyle mergeStyle);
void SetOverwriteStyle(mitk::MultiLabelSegmentation::OverwriteStyle overwriteStyle);
private:
QCheckBox* m_CheckIgnoreLocks = nullptr;
QCheckBox* m_CheckMerge = nullptr;
QCheckBox* m_CheckProcessAll = nullptr;
QPushButton* m_ConfirmSegBtn = nullptr;
QBoxLayout* m_MainLayout = nullptr;
/**Indicates if the tool is in 2D or 3D mode.*/
bool m_Mode2D;
mitk::SegWithPreviewTool::Pointer m_Tool;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingGUIControls.ui
index 6f1f495a00..d05b8b79a9 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingGUIControls.ui
+++ b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingGUIControls.ui
@@ -1,169 +1,157 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmitkSegmentAnythingGUIControls</class>
<widget class="QWidget" name="QmitkSegmentAnythingGUIControls">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>699</width>
<height>490</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100000</width>
<height>100000</height>
</size>
</property>
<property name="windowTitle">
<string>QmitkSegmentAnythingToolWidget</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<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>
<layout class="QGridLayout" name="basicSettingsLayout">
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="welcomeNote">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Welcome to Segment Anything Model (SAM) tool in MITK. [Experimental]&lt;/p&gt;&lt;p&gt;Please note that this is only an interface to SAM. MITK does not ship with SAM. Make sure to have a working internet connection to install Segment Anything Model via MITK. &lt;/p&gt;&lt;p&gt;Refer to &lt;a href=&quot;https://segment-anything.com/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://segment-anything.com&lt;/span&gt;&lt;/a&gt; to learn everything about the Segment Anything Model.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="instructLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>
Press SHIFT+Left-click for positive seeds.
Press SHIFT+Right-click for negative seeds.
Press DEL to remove last seed.
+Use level window slider to adjust image contrast.
</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="resetButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100000</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Reset Picks</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="activateButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100000</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Initialize Segment Anything</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="statusLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="samProgressBar">
<property name="maximum">
<number>1</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
- <customwidgets>
- <customwidget>
- <class>ctkComboBox</class>
- <extends>QComboBox</extends>
- <header location="global">ctkComboBox.h</header>
- <container>1</container>
- </customwidget>
- <customwidget>
- <class>ctkCollapsibleGroupBox</class>
- <extends>QWidget</extends>
- <header>ctkCollapsibleGroupBox.h</header>
- </customwidget>
- </customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.cpp
index f3fb059ddf..006f0f9ba2 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.cpp
@@ -1,303 +1,296 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkSegmentAnythingToolGUI.h"
#include <mitkSegmentAnythingTool.h>
#include <mitkProcessExecutor.h>
#include <QApplication>
#include <QDir>
#include <QmitkStyleManager.h>
#include <QDirIterator>
#include <QFileDialog>
#include <mitkCoreServices.h>
#include <mitkIPreferencesService.h>
MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkSegmentAnythingToolGUI, "")
namespace
{
mitk::IPreferences *GetPreferences()
{
auto *preferencesService = mitk::CoreServices::GetPreferencesService();
return preferencesService->GetSystemPreferences()->Node("org.mitk.views.segmentation");
}
}
QmitkSegmentAnythingToolGUI::QmitkSegmentAnythingToolGUI() : QmitkSegWithPreviewToolGUIBase(true)
{
m_EnableConfirmSegBtnFnc = [this](bool enabled)
{
bool result = false;
auto tool = this->GetConnectedToolAs<mitk::SegmentAnythingTool>();
if (nullptr != tool)
{
result = enabled && tool->HasPicks();
}
return result;
};
m_Preferences = GetPreferences();
m_Preferences->OnPropertyChanged +=
mitk::MessageDelegate1<QmitkSegmentAnythingToolGUI, const mitk::IPreferences::ChangeEvent &>(
this, &QmitkSegmentAnythingToolGUI::OnPreferenceChangedEvent);
}
QmitkSegmentAnythingToolGUI::~QmitkSegmentAnythingToolGUI()
{
auto tool = this->GetConnectedToolAs<mitk::SegmentAnythingTool>();
if (nullptr != tool)
{
tool->SAMStatusMessageEvent -= mitk::MessageDelegate1<QmitkSegmentAnythingToolGUI, const std::string&>(
this, &QmitkSegmentAnythingToolGUI::StatusMessageListener);
}
}
void QmitkSegmentAnythingToolGUI::InitializeUI(QBoxLayout *mainLayout)
{
m_Controls.setupUi(this);
m_Controls.statusLabel->setTextFormat(Qt::RichText);
QString welcomeText;
- if (m_GpuLoader.GetGPUCount() != 0)
- {
- welcomeText = "<b>STATUS: </b><i>Welcome to Segment Anything tool. You're in luck: " +
+ welcomeText = "<b>STATUS: </b><i>Welcome to Segment Anything tool. " +
QString::number(m_GpuLoader.GetGPUCount()) + " GPU(s) were detected.</i>";
- }
- else
- {
- welcomeText = "<b>STATUS: </b><i>Welcome to Segment Anything tool. Sorry, " +
- QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected.</i>";
- }
+
connect(m_Controls.activateButton, SIGNAL(clicked()), this, SLOT(OnActivateBtnClicked()));
connect(m_Controls.resetButton, SIGNAL(clicked()), this, SLOT(OnResetPicksClicked()));
QIcon arrowIcon = QmitkStyleManager::ThemeIcon(
QStringLiteral(":/org_mitk_icons/icons/tango/scalable/actions/media-playback-start.svg"));
m_Controls.activateButton->setIcon(arrowIcon);
bool isInstalled = this->ValidatePrefences();
if (isInstalled)
{
QString modelType = QString::fromStdString(m_Preferences->Get("sam modeltype", ""));
welcomeText += " SAM is already found installed. Model type '" + modelType + "' selected in Preferences.";
}
else
{
welcomeText += " SAM tool is not configured correctly. Please go to Preferences (Cntl+P) > Segment Anything to configure and/or install SAM.";
}
this->EnableAll(isInstalled);
this->WriteStatusMessage(welcomeText);
this->ShowProgressBar(false);
m_Controls.samProgressBar->setMaximum(0);
mainLayout->addLayout(m_Controls.verticalLayout);
Superclass::InitializeUI(mainLayout);
}
bool QmitkSegmentAnythingToolGUI::ValidatePrefences()
{
const QString storageDir = QString::fromStdString(m_Preferences->Get("sam python path", ""));
bool isInstalled = QmitkSegmentAnythingToolGUI::IsSAMInstalled(storageDir);
std::string modelType = m_Preferences->Get("sam modeltype", "");
std::string path = m_Preferences->Get("sam parent path", "");
return (isInstalled && !modelType.empty() && !path.empty());
}
void QmitkSegmentAnythingToolGUI::EnableAll(bool isEnable)
{
m_Controls.activateButton->setEnabled(isEnable);
}
void QmitkSegmentAnythingToolGUI::WriteStatusMessage(const QString &message)
{
m_Controls.statusLabel->setText(message);
m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: white");
qApp->processEvents();
}
void QmitkSegmentAnythingToolGUI::WriteErrorMessage(const QString &message)
{
m_Controls.statusLabel->setText(message);
m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: red");
qApp->processEvents();
}
void QmitkSegmentAnythingToolGUI::ShowErrorMessage(const std::string &message, QMessageBox::Icon icon)
{
this->setCursor(Qt::ArrowCursor);
QMessageBox *messageBox = new QMessageBox(icon, nullptr, message.c_str());
messageBox->exec();
delete messageBox;
MITK_WARN << message;
}
void QmitkSegmentAnythingToolGUI::StatusMessageListener(const std::string &message)
{
if (message.rfind("Error", 0) == 0)
{
this->EnableAll(true);
this->WriteErrorMessage(QString::fromStdString(message));
}
else if (message == "TimeOut")
{ // trying to re init the daemon
this->WriteErrorMessage(QString("<b>STATUS: </b><i>Sorry, operation timed out. Reactivating SAM tool...</i>"));
if (this->ActivateSAMDaemon())
{
this->WriteStatusMessage(QString("<b>STATUS: </b><i>Segment Anything tool re-initialized.</i>"));
}
else
{
this->WriteErrorMessage(QString("<b>STATUS: </b><i>Couldn't init tool backend.</i>"));
this->EnableAll(true);
}
}
else
{
this->WriteStatusMessage(QString::fromStdString(message));
}
}
void QmitkSegmentAnythingToolGUI::OnActivateBtnClicked()
{
auto tool = this->GetConnectedToolAs<mitk::SegmentAnythingTool>();
if (nullptr == tool)
{
return;
}
try
{
this->EnableAll(false);
qApp->processEvents();
QString pythonPath = QString::fromStdString(m_Preferences->Get("sam python path", ""));
if (!QmitkSegmentAnythingToolGUI::IsSAMInstalled(pythonPath))
{
throw std::runtime_error(WARNING_SAM_NOT_FOUND);
}
tool->SetPythonPath(pythonPath.toStdString());
tool->SetGpuId(m_Preferences->GetInt("sam gpuid", -1));
const QString modelType = QString::fromStdString(m_Preferences->Get("sam modeltype", ""));
tool->SetModelType(modelType.toStdString());
tool->SetTimeOutLimit(m_Preferences->GetInt("sam timeout", 300));
tool->SetCheckpointPath(m_Preferences->Get("sam parent path", ""));
tool->SetBackend("SAM");
this->WriteStatusMessage(
QString("<b>STATUS: </b><i>Initializing Segment Anything Model...</i>"));
tool->SAMStatusMessageEvent += mitk::MessageDelegate1<QmitkSegmentAnythingToolGUI,const std::string&>(
this, &QmitkSegmentAnythingToolGUI::StatusMessageListener);
if (this->ActivateSAMDaemon())
{
this->WriteStatusMessage(QString("<b>STATUS: </b><i>Segment Anything tool initialized.</i>"));
}
else
{
this->WriteErrorMessage(QString("<b>STATUS: </b><i>Couldn't init tool backend.</i>"));
this->EnableAll(true);
}
}
catch (const std::exception &e)
{
std::stringstream errorMsg;
errorMsg << "<b>STATUS: </b>Error while processing parameters for Segment Anything segmentation. Reason: " << e.what();
this->ShowErrorMessage(errorMsg.str());
this->WriteErrorMessage(QString::fromStdString(errorMsg.str()));
this->EnableAll(true);
return;
}
catch (...)
{
std::string errorMsg = "Unkown error occured while generation Segment Anything segmentation.";
this->ShowErrorMessage(errorMsg);
this->EnableAll(true);
return;
}
}
bool QmitkSegmentAnythingToolGUI::ActivateSAMDaemon()
{
auto tool = this->GetConnectedToolAs<mitk::SegmentAnythingTool>();
if (nullptr == tool)
{
return false;
}
this->ShowProgressBar(true);
qApp->processEvents();
try
{
tool->InitSAMPythonProcess();
while (!tool->IsPythonReady())
{
qApp->processEvents();
}
tool->IsReadyOn();
}
catch (...)
{
tool->IsReadyOff();
}
this->ShowProgressBar(false);
return tool->GetIsReady();
}
void QmitkSegmentAnythingToolGUI::ShowProgressBar(bool enabled)
{
m_Controls.samProgressBar->setEnabled(enabled);
m_Controls.samProgressBar->setVisible(enabled);
}
bool QmitkSegmentAnythingToolGUI::IsSAMInstalled(const QString &pythonPath)
{
QString fullPath = pythonPath;
bool isPythonExists = false;
bool isSamExists = false;
#ifdef _WIN32
isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe"));
if (!(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive)))
{
fullPath += QDir::separator() + QString("Scripts");
isPythonExists =
(!isPythonExists) ? QFile::exists(fullPath + QDir::separator() + QString("python.exe")) : isPythonExists;
}
#else
isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3"));
if (!(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive)))
{
fullPath += QDir::separator() + QString("bin");
isPythonExists =
(!isPythonExists) ? QFile::exists(fullPath + QDir::separator() + QString("python3")) : isPythonExists;
}
#endif
isSamExists = QFile::exists(fullPath + QDir::separator() + QString("run_inference_daemon.py"))
&& QFile::exists(fullPath + QDir::separator() + QString("sam_runner.py"))
&& QFile::exists(fullPath + QDir::separator() + QString("medsam_runner.py"));
bool isExists = isSamExists && isPythonExists;
return isExists;
}
void QmitkSegmentAnythingToolGUI::OnResetPicksClicked()
{
auto tool = this->GetConnectedToolAs<mitk::SegmentAnythingTool>();
if (nullptr != tool)
{
tool->ClearPicks();
}
}
void QmitkSegmentAnythingToolGUI::OnPreferenceChangedEvent(const mitk::IPreferences::ChangeEvent&)
{
this->EnableAll(true);
this->WriteStatusMessage("A Preference change was detected. Please initialize the tool again.");
auto tool = this->GetConnectedToolAs<mitk::SegmentAnythingTool>();
if (nullptr != tool)
{
tool->IsReadyOff();
}
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegmentationTaskListWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkSegmentationTaskListWidget.cpp
index 25f783db43..602b78747c 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSegmentationTaskListWidget.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkSegmentationTaskListWidget.cpp
@@ -1,1012 +1,1012 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkSegmentationTaskListWidget.h"
#include <mitkCoreServices.h>
#include <mitkIPreferencesService.h>
#include <mitkIPreferences.h>
#include <mitkDICOMQIPropertyHelper.h>
#include <mitkIOUtil.h>
#include <mitkMultiLabelIOHelper.h>
#include <mitkLabelSetImageHelper.h>
#include <mitkNodePredicateDataType.h>
#include <mitkNodePredicateFunction.h>
#include <mitkRenderingManager.h>
#include <mitkSegmentationHelper.h>
#include <mitkToolManagerProvider.h>
#include <QmitkFindSegmentationTaskDialog.h>
#include <QmitkStaticDynamicSegmentationDialog.h>
#include <QmitkStyleManager.h>
#include <ui_QmitkSegmentationTaskListWidget.h>
#include <QFileSystemWatcher>
#include <QMessageBox>
#include <QShortcut>
-#include <filesystem>
+#include <mitkFileSystem.h>
namespace
{
mitk::IPreferences* GetSegmentationPreferences()
{
return mitk::CoreServices::GetPreferencesService()->GetSystemPreferences()->Node("/org.mitk.views.segmentation");
}
- std::filesystem::path GetInputLocation(const mitk::BaseData* data)
+ fs::path GetInputLocation(const mitk::BaseData* data)
{
std::string result;
if (data != nullptr)
data->GetPropertyList()->GetStringProperty("MITK.IO.reader.inputlocation", result);
return result;
}
QString ColorString(const QString& string, const QColor& color, const QColor& backgroundColor = QColor::Invalid)
{
if (!color.isValid() && !backgroundColor.isValid())
return string;
auto result = QStringLiteral("<span style=\"");
QStringList strings;
if (color.isValid())
strings << QString("color: %1;").arg(color.name());
if (backgroundColor.isValid())
strings << QString("background-color: %1;").arg(backgroundColor.name());
result += strings.join(' ') + QString("\">%1</span>").arg(string);
return result;
}
mitk::DataStorage::SetOfObjects::ConstPointer GetSubset(const mitk::DataStorage* dataStorage, const mitk::NodePredicateBase* condition, const mitk::DataNode* removedDataNode)
{
auto subset = dataStorage->GetSubset(condition);
if (nullptr != removedDataNode)
{
auto actualSubset = mitk::DataStorage::SetOfObjects::New();
for (auto node : *subset)
{
if (node != removedDataNode)
actualSubset->push_back(node);
}
return actualSubset;
}
return subset;
}
}
/* This constructor has three objectives:
* 1. Do widget initialization that cannot be done in the .ui file
* 2. Connect signals and slots
* 3. Explicitly trigger a reset to a valid initial widget state
*/
QmitkSegmentationTaskListWidget::QmitkSegmentationTaskListWidget(QWidget* parent)
: QWidget(parent),
m_Ui(new Ui::QmitkSegmentationTaskListWidget),
m_FileSystemWatcher(new QFileSystemWatcher(this)),
m_DataStorage(nullptr),
m_UnsavedChanges(false)
{
m_Ui->setupUi(this);
m_Ui->selectionWidget->SetNodePredicate(mitk::TNodePredicateDataType<mitk::SegmentationTaskList>::New());
m_Ui->progressBar->setStyleSheet(QString("QProgressBar::chunk { background-color: %1; }").arg(QmitkStyleManager::GetIconAccentColor()));
m_Ui->findButton->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/icon_find.svg")));
m_Ui->storeButton->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg")));
using Self = QmitkSegmentationTaskListWidget;
connect(m_Ui->selectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &Self::OnSelectionChanged);
connect(m_Ui->previousButton, &QToolButton::clicked, this, &Self::OnPreviousButtonClicked);
connect(m_Ui->nextButton, &QToolButton::clicked, this, &Self::OnNextButtonClicked);
connect(m_Ui->findButton, &QToolButton::clicked, this, &Self::OnFindButtonClicked);
connect(m_Ui->loadButton, &QPushButton::clicked, this, &Self::OnLoadButtonClicked);
connect(m_Ui->storeButton, &QPushButton::clicked, this, &Self::OnStoreButtonClicked);
connect(m_Ui->acceptButton, &QPushButton::clicked, this, &Self::OnAcceptButtonClicked);
connect(m_FileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &Self::OnResultDirectoryChanged);
auto* prevShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_P), this);
connect(prevShortcut, &QShortcut::activated, this, &Self::OnPreviousTaskShortcutActivated);
auto* prevUndoneShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key::Key_P), this);
connect(prevUndoneShortcut, &QShortcut::activated, this, &Self::OnPreviousTaskShortcutActivated);
auto* nextShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_N), this);
connect(nextShortcut, &QShortcut::activated, this, &Self::OnNextTaskShortcutActivated);
auto* nextUndoneShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key::Key_N), this);
connect(nextUndoneShortcut, &QShortcut::activated, this, &Self::OnNextTaskShortcutActivated);
auto *findTaskShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_F), this);
connect(findTaskShortcut, &QShortcut::activated, this, &Self::OnFindTaskShortcutActivated);
auto* loadShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_L), this);
connect(loadShortcut, &QShortcut::activated, this, &Self::OnLoadTaskShortcutActivated);
auto* storeShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_S), parent);
connect(storeShortcut, &QShortcut::activated, this, &Self::OnStoreInterimResultShortcutActivated);
auto* acceptShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_A), parent);
connect(acceptShortcut, &QShortcut::activated, this, &Self::OnAcceptSegmentationShortcutActivated);
this->ResetControls();
this->CheckDataStorage();
}
QmitkSegmentationTaskListWidget::~QmitkSegmentationTaskListWidget()
{
}
void QmitkSegmentationTaskListWidget::SetDataStorage(mitk::DataStorage* dataStorage)
{
m_DataStorage = dataStorage;
m_Ui->selectionWidget->SetDataStorage(dataStorage); // Triggers OnSelectionChanged()
m_Ui->selectionWidget->SetAutoSelectNewNodes(true);
this->CheckDataStorage();
}
void QmitkSegmentationTaskListWidget::CheckDataStorage(const mitk::DataNode* removedNode)
{
QString warning;
if (nullptr == m_DataStorage)
{
warning = QStringLiteral(
"<h3>Developer warning</h3><p>Call <code>SetDataStorage()</code> to fully initialize "
"this instance of <code>QmitkSegmentationTaskListWidget</code>.</p>");
}
else
{
auto isTaskList = mitk::TNodePredicateDataType<mitk::SegmentationTaskList>::New();
auto taskListNodes = GetSubset(m_DataStorage, isTaskList, removedNode);
if (taskListNodes->empty())
{
warning = QStringLiteral(
"<h3>No segmentation task list found</h3><p>Load a segmentation task list to use "
"this plugin.</p>");
}
else if (taskListNodes->Size() > 1)
{
warning = QStringLiteral(
"<h3>More than one segmentation task list found</h3><p>Unload everything but a "
"single segmentation task list to use this plugin.</p>");
}
else
{
const auto* taskListNode = (*taskListNodes)[0].GetPointer();
auto isTaskListNode = mitk::NodePredicateFunction::New([taskListNode](const mitk::DataNode* node) {
return node == taskListNode;
});
auto isChildOfTaskListNode = mitk::NodePredicateFunction::New([this, isTaskListNode](const mitk::DataNode* node) {
return !m_DataStorage->GetSources(node, isTaskListNode, false)->empty();
});
auto isHelperObject = mitk::NodePredicateProperty::New("helper object");
auto isUndesiredNode = mitk::NodePredicateNot::New(mitk::NodePredicateOr::New(
isTaskListNode,
isChildOfTaskListNode,
isHelperObject));
if (!GetSubset(m_DataStorage, isUndesiredNode, removedNode)->empty())
{
warning = QStringLiteral(
"<h3>Unrelated data found</h3><p>Unload everything but a single segmentation task "
"list to use this plugin.</p>");
}
}
}
m_Ui->label->setText("<span style=\"color: " + QmitkStyleManager::GetIconAccentColor() + "\">" + warning + "</span>");
m_Ui->label->setVisible(!warning.isEmpty());
m_Ui->widget->setVisible(warning.isEmpty());
}
void QmitkSegmentationTaskListWidget::OnUnsavedChangesSaved()
{
if (m_UnsavedChanges)
{
m_UnsavedChanges = false;
if (this->ActiveTaskIsShown())
this->UpdateDetailsLabel();
}
}
/* Make sure that the widget transitions into a valid state whenever the
* selection changes.
*/
void QmitkSegmentationTaskListWidget::OnSelectionChanged(const QmitkSingleNodeSelectionWidget::NodeList& nodes)
{
this->UnloadTasks();
this->ResetControls();
if (!nodes.empty())
{
m_TaskListNode = nodes.front();
auto taskList = dynamic_cast<mitk::SegmentationTaskList*>(m_TaskListNode->GetData());
if (taskList != nullptr)
{
this->OnTaskListChanged(taskList);
return;
}
}
this->SetTaskList(nullptr);
m_TaskListNode = nullptr;
}
/* Reset all controls to a default state as a common basis for further
* adjustments.
*/
void QmitkSegmentationTaskListWidget::ResetControls()
{
m_Ui->progressBar->setEnabled(false);
m_Ui->progressBar->setFormat("");
m_Ui->progressBar->setValue(0);
m_Ui->progressBar->setMaximum(1);
m_Ui->previousButton->setEnabled(false);
m_Ui->nextButton->setEnabled(false);
this->UpdateLoadButton();
this->UpdateDetailsLabel();
this->UpdateStoreAndAcceptButtons();
}
/* If the segmentation task changed, reset all member variables to expected
* default values and reset the file system watcher.
*/
void QmitkSegmentationTaskListWidget::SetTaskList(mitk::SegmentationTaskList* taskList)
{
if (m_TaskList != taskList)
{
m_TaskList = taskList;
if (taskList != nullptr)
{
this->SetCurrentTaskIndex(0);
}
else
{
this->SetCurrentTaskIndex(std::nullopt);
}
this->ResetFileSystemWatcher();
}
}
void QmitkSegmentationTaskListWidget::ResetFileSystemWatcher()
{
auto paths = m_FileSystemWatcher->directories();
if (!paths.empty())
m_FileSystemWatcher->removePaths(paths);
if (m_TaskList.IsNotNull())
{
for (const auto& task : *m_TaskList)
{
auto resultPath = m_TaskList->GetAbsolutePath(task.GetResult()).remove_filename();
- if (!std::filesystem::exists(resultPath))
+ if (!fs::exists(resultPath))
{
try
{
- std::filesystem::create_directories(resultPath);
+ fs::create_directories(resultPath);
}
- catch (const std::filesystem::filesystem_error& e)
+ catch (const fs::filesystem_error& e)
{
MITK_ERROR << e.what();
}
}
- if (std::filesystem::exists(resultPath))
+ if (fs::exists(resultPath))
m_FileSystemWatcher->addPath(QString::fromStdString(resultPath.string()));
}
}
}
void QmitkSegmentationTaskListWidget::OnResultDirectoryChanged(const QString&)
{
// TODO: If a segmentation was modified ("Unsaved changes"), saved ("Done"), and then the file is deleted, the status should be "Unsaved changes" instead of "Not done".
this->UpdateProgressBar();
this->UpdateDetailsLabel();
}
void QmitkSegmentationTaskListWidget::UpdateProgressBar()
{
int progress = 0;
for (size_t i = 0; i < m_TaskList->GetNumberOfTasks(); ++i)
{
if (m_TaskList->IsDone(i))
++progress;
}
m_Ui->progressBar->setValue(progress);
}
/* Provided that a valid segmentation task list is currently selected and the
* widget is in its default state, update all controls accordingly.
* TODO: Then, load the first unfinished task, if any.
*/
void QmitkSegmentationTaskListWidget::OnTaskListChanged(mitk::SegmentationTaskList* taskList)
{
this->SetTaskList(taskList);
const auto numTasks = taskList->GetNumberOfTasks();
m_Ui->progressBar->setMaximum(numTasks);
m_Ui->progressBar->setFormat(QStringLiteral("%v/%m Task(s) done"));
m_Ui->progressBar->setEnabled(true);
this->UpdateProgressBar();
m_Ui->loadButton->setEnabled(true);
if (numTasks > 1)
m_Ui->nextButton->setEnabled(true);
// TODO: This line should be enough but it is happening too early even before
// the RenderingManager has any registered render windows, resulting in mismatching
// renderer and data geometries.
// this->LoadNextUnfinishedTask();
}
/* If possible, change the currently displayed task to the previous task.
* Enable/disable navigation buttons according to the task's position.
*/
void QmitkSegmentationTaskListWidget::OnPreviousButtonClicked()
{
auto current = m_CurrentTaskIndex.value();
// If the shift modifier key is pressed, look for the previous undone task.
if (QApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier))
{
if (current > 0)
{
for (decltype(current) i = current; i > 0; --i)
{
if (!m_TaskList->IsDone(i - 1))
{
this->SetCurrentTaskIndex(i - 1);
break;
}
}
}
}
else
{
if (current != 0)
this->SetCurrentTaskIndex(current - 1);
}
this->UpdateNavigationButtons();
}
/* If possible, change the currently displayed task to the next task.
* Enable/disable navigation buttons according to the task's position.
*/
void QmitkSegmentationTaskListWidget::OnNextButtonClicked()
{
const auto numTasks = m_TaskList->GetNumberOfTasks();
auto current = m_CurrentTaskIndex.value();
// If the shift modifier key is pressed, look for the next undone task.
if (QApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier))
{
for (std::remove_const_t<decltype(numTasks)> i = current + 1; i < numTasks; ++i)
{
if (!m_TaskList->IsDone(i))
{
this->SetCurrentTaskIndex(i);
break;
}
}
}
else
{
if (current < numTasks - 1)
this->SetCurrentTaskIndex(current + 1);
}
this->UpdateNavigationButtons();
}
void QmitkSegmentationTaskListWidget::OnFindButtonClicked()
{
if (m_TaskList.IsNull())
return;
QmitkFindSegmentationTaskDialog dialog;
dialog.SetTaskList(m_TaskList);
if (dialog.exec() != QDialog::Accepted)
return;
if (!dialog.GetSelectedTask().has_value())
return;
this->SetCurrentTaskIndex(dialog.GetSelectedTask());
if (dialog.LoadSelectedTask())
{
if (!m_ActiveTaskIndex.has_value() || m_ActiveTaskIndex.value() != dialog.GetSelectedTask().value())
this->OnLoadButtonClicked();
}
}
void QmitkSegmentationTaskListWidget::UpdateNavigationButtons()
{
if (m_TaskList.IsNull() || m_TaskList->GetNumberOfTasks() == 0)
{
m_Ui->previousButton->setEnabled(false);
m_Ui->nextButton->setEnabled(false);
return;
}
const auto maxIndex = m_TaskList->GetNumberOfTasks() - 1;
const auto current = m_CurrentTaskIndex.value();
m_Ui->previousButton->setEnabled(current != 0);
m_Ui->nextButton->setEnabled(current != maxIndex);
}
/* Update affected controls when the currently displayed task changed.
*/
void QmitkSegmentationTaskListWidget::OnCurrentTaskChanged()
{
this->UpdateLoadButton();
this->UpdateNavigationButtons();
this->UpdateDetailsLabel();
this->UpdateStoreAndAcceptButtons();
}
/* Update the load button according to the currently displayed task.
*/
void QmitkSegmentationTaskListWidget::UpdateLoadButton()
{
auto text = !this->ActiveTaskIsShown()
? QStringLiteral("Load task")
: QStringLiteral("Task");
if (m_CurrentTaskIndex.has_value())
{
const auto current = m_CurrentTaskIndex.value();
if (m_TaskList.IsNotNull())
{
text += QString(" %1/%2").arg(current + 1).arg(m_TaskList->GetNumberOfTasks());
if (m_TaskList->HasName(current))
text += QStringLiteral(":\n") + QString::fromStdString(m_TaskList->GetName(current));
}
m_Ui->loadButton->setDisabled(this->ActiveTaskIsShown());
}
else
{
m_Ui->loadButton->setEnabled(false);
}
m_Ui->loadButton->setText(text);
}
/* Update the details label according to the currently display task.
* The text is composed of the status of the task and a variable number
* of text blocks according to the optional values provided by the task.
*/
void QmitkSegmentationTaskListWidget::UpdateDetailsLabel()
{
if (!m_CurrentTaskIndex.has_value())
{
m_Ui->detailsLabel->clear();
return;
}
const auto current = m_CurrentTaskIndex.value();
bool isDone = m_TaskList->IsDone(current);
auto details = QString("<p><b>Status: %1</b> / <b>").arg(this->ActiveTaskIsShown()
? ColorString("Active", Qt::white, QColor(Qt::green).darker())
: ColorString("Inactive", Qt::white, QColor(Qt::red).darker()));
if (m_UnsavedChanges && this->ActiveTaskIsShown())
{
details += QString("%1</b></p>").arg(ColorString("Unsaved changes", Qt::white, QColor(Qt::red).darker()));
}
else
{
details += QString("%1</b></p>").arg(isDone
? ColorString("Done", Qt::white, QColor(Qt::green).darker())
: ColorString("Not done", Qt::white, QColor(Qt::red).darker()));
}
if (m_TaskList->HasDescription(current))
details += QString("<p><b>Description:</b> %1</p>").arg(QString::fromStdString(m_TaskList->GetDescription(current)));
QStringList stringList;
if (m_TaskList->HasImage(current))
stringList << QString::fromStdString("<b>Image:</b> " + m_TaskList->GetImage(current).string());
if (m_TaskList->HasSegmentation(current))
stringList << QString::fromStdString("<b>Segmentation:</b> " + m_TaskList->GetSegmentation(current).string());
if (m_TaskList->HasLabelName(current))
stringList << QString::fromStdString("<b>Label name:</b> " + m_TaskList->GetLabelName(current));
if (m_TaskList->HasLabelNameSuggestions(current))
stringList << QString::fromStdString("<b>Label name suggestions:</b> " + m_TaskList->GetLabelNameSuggestions(current).string());
if (m_TaskList->HasPreset(current))
stringList << QString::fromStdString("<b>Label set preset:</b> " + m_TaskList->GetPreset(current).string());
if (m_TaskList->HasDynamic(current))
stringList << QString("<b>Segmentation type:</b> %1").arg(m_TaskList->GetDynamic(current) ? "Dynamic" : "Static");
if (!stringList.empty())
details += QString("<p>%1</p>").arg(stringList.join(QStringLiteral("<br>")));
m_Ui->detailsLabel->setText(details);
}
void QmitkSegmentationTaskListWidget::UpdateStoreAndAcceptButtons()
{
auto activeTaskIsShown = this->ActiveTaskIsShown();
m_Ui->storeButton->setVisible(activeTaskIsShown);
m_Ui->acceptButton->setEnabled(activeTaskIsShown);
}
/* Load/activate the currently displayed task. Unload all data nodes from
* previously active tasks first, but spare and reuse the image if possible.
*/
void QmitkSegmentationTaskListWidget::OnLoadButtonClicked()
{
if (!this->HandleUnsavedChanges() || m_UnsavedChanges)
return;
m_Ui->loadButton->setEnabled(false);
QApplication::setOverrideCursor(Qt::BusyCursor);
this->LoadTask(this->GetImageDataNode(m_CurrentTaskIndex.value()));
QApplication::restoreOverrideCursor();
}
/* If present, return the image data node for the task with the specified
* index. Otherwise, return nullptr.
*/
mitk::DataNode* QmitkSegmentationTaskListWidget::GetImageDataNode(size_t index) const
{
const auto imagePath = m_TaskList->GetAbsolutePath(m_TaskList->GetImage(index));
auto imageNodes = m_DataStorage->GetDerivations(m_TaskListNode, mitk::NodePredicateFunction::New([imagePath](const mitk::DataNode* node) {
return imagePath == GetInputLocation(node->GetData());
}));
return !imageNodes->empty()
? imageNodes->front()
: nullptr;
}
/* If present, return the segmentation data node for the task with the
* specified index. Otherwise, return nullptr.
*/
mitk::DataNode* QmitkSegmentationTaskListWidget::GetSegmentationDataNode(size_t index) const
{
const auto* imageNode = this->GetImageDataNode(index);
if (imageNode != nullptr)
{
auto segmentations = m_DataStorage->GetDerivations(imageNode, mitk::TNodePredicateDataType<mitk::LabelSetImage>::New());
if (!segmentations->empty())
return segmentations->front();
}
return nullptr;
}
/* Unload all task data nodes but spare the passed image data node.
*/
void QmitkSegmentationTaskListWidget::UnloadTasks(const mitk::DataNode* skip)
{
this->UnsubscribeFromActiveSegmentation();
if (m_TaskListNode.IsNotNull())
{
auto imageNodes = m_DataStorage->GetDerivations(m_TaskListNode, mitk::TNodePredicateDataType<mitk::Image>::New());
for (auto imageNode : *imageNodes)
{
m_DataStorage->Remove(m_DataStorage->GetDerivations(imageNode, nullptr, false));
if (imageNode != skip)
m_DataStorage->Remove(imageNode);
}
}
this->SetActiveTaskIndex(std::nullopt);
}
void QmitkSegmentationTaskListWidget::LoadNextUnfinishedTask()
{
const auto current = m_CurrentTaskIndex.value();
const auto numTasks = m_TaskList->GetNumberOfTasks();
for (size_t unboundNext = current; unboundNext < current + numTasks; ++unboundNext)
{
auto next = unboundNext % numTasks;
if (!m_TaskList->IsDone(next))
{
this->SetCurrentTaskIndex(next);
this->OnLoadButtonClicked();
break;
}
}
}
/* Load/activate the currently displayed task. The task must specify
* an image. The segmentation is either created from scratch with an optional
* name for the first label, possibly based on a label set preset specified by
* the task, or loaded as specified by the task. If a result file does
* exist, it is chosen as segmentation instead.
*/
void QmitkSegmentationTaskListWidget::LoadTask(mitk::DataNode::Pointer imageNode)
{
this->UnloadTasks(imageNode);
const auto current = m_CurrentTaskIndex.value();
mitk::Image::Pointer image;
mitk::LabelSetImage::Pointer segmentation;
try
{
if (imageNode.IsNull())
{
const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetImage(current));
image = mitk::IOUtil::Load<mitk::Image>(path.string());
}
const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetResult(current));
const auto interimPath = m_TaskList->GetInterimPath(path);
- if (std::filesystem::exists(path))
+ if (fs::exists(path))
{
segmentation = mitk::IOUtil::Load<mitk::LabelSetImage>(path.string());
}
- else if (std::filesystem::exists(interimPath))
+ else if (fs::exists(interimPath))
{
segmentation = mitk::IOUtil::Load<mitk::LabelSetImage>(interimPath.string());
}
else if (m_TaskList->HasSegmentation(current))
{
const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetSegmentation(current));
segmentation = mitk::IOUtil::Load<mitk::LabelSetImage>(path.string());
}
}
catch (const mitk::Exception&)
{
return;
}
if (imageNode.IsNull())
{
imageNode = mitk::DataNode::New();
imageNode->SetData(image);
m_DataStorage->Add(imageNode, m_TaskListNode);
mitk::RenderingManager::GetInstance()->InitializeViews(image->GetTimeGeometry());
}
else
{
image = static_cast<mitk::Image*>(imageNode->GetData());
}
auto name = "Task " + std::to_string(current + 1);
imageNode->SetName(name);
if (segmentation.IsNull())
{
mitk::Image::ConstPointer templateImage = image;
if (templateImage->GetDimension() > 3)
{
if (m_TaskList->HasDynamic(current))
{
if (!m_TaskList->GetDynamic(current))
templateImage = mitk::SegmentationHelper::GetStaticSegmentationTemplate(image);
}
else
{
QmitkStaticDynamicSegmentationDialog dialog(this);
dialog.SetReferenceImage(templateImage);
dialog.exec();
templateImage = dialog.GetSegmentationTemplate();
}
}
auto segmentationNode = mitk::LabelSetImageHelper::CreateNewSegmentationNode(imageNode, templateImage, name);
segmentation = static_cast<mitk::LabelSetImage*>(segmentationNode->GetData());
if (m_TaskList->HasPreset(current))
{
const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetPreset(current));
mitk::MultiLabelIOHelper::LoadLabelSetImagePreset(path.string(), segmentation);
}
else
{
auto label = mitk::LabelSetImageHelper::CreateNewLabel(segmentation);
if (m_TaskList->HasLabelName(current))
label->SetName(m_TaskList->GetLabelName(current));
segmentation->AddLabel(label, segmentation->GetActiveLayer());
}
m_DataStorage->Add(segmentationNode, imageNode);
}
else
{
auto segmentationNode = mitk::DataNode::New();
segmentationNode->SetName(name);
segmentationNode->SetData(segmentation);
m_DataStorage->Add(segmentationNode, imageNode);
}
// Workaround for T29431. Remove when T26953 is fixed.
mitk::DICOMQIPropertyHelper::DeriveDICOMSourceProperties(image, segmentation);
auto prefs = GetSegmentationPreferences();
if (prefs != nullptr)
{
if (m_TaskList->HasLabelNameSuggestions(current))
{
auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetLabelNameSuggestions(current));
prefs->PutBool("default label naming", false);
prefs->Put("label suggestions", path.string());
prefs->PutBool("replace standard suggestions", true);
prefs->PutBool("suggest once", true);
}
else
{
prefs->PutBool("default label naming", true);
prefs->Put("label suggestions", "");
}
}
m_UnsavedChanges = false;
this->SetActiveTaskIndex(current);
this->SubscribeToActiveSegmentation();
this->OnCurrentTaskChanged();
}
void QmitkSegmentationTaskListWidget::SubscribeToActiveSegmentation()
{
if (m_ActiveTaskIndex.has_value())
{
auto segmentationNode = this->GetSegmentationDataNode(m_ActiveTaskIndex.value());
if (segmentationNode != nullptr)
{
auto segmentation = static_cast<mitk::LabelSetImage*>(segmentationNode->GetData());
auto command = itk::SimpleMemberCommand<QmitkSegmentationTaskListWidget>::New();
command->SetCallbackFunction(this, &QmitkSegmentationTaskListWidget::OnSegmentationModified);
m_SegmentationModifiedObserverTag = segmentation->AddObserver(itk::ModifiedEvent(), command);
}
}
}
void QmitkSegmentationTaskListWidget::UnsubscribeFromActiveSegmentation()
{
if (m_ActiveTaskIndex.has_value() && m_SegmentationModifiedObserverTag.has_value())
{
auto segmentationNode = this->GetSegmentationDataNode(m_ActiveTaskIndex.value());
if (segmentationNode != nullptr)
{
auto segmentation = static_cast<mitk::LabelSetImage*>(segmentationNode->GetData());
segmentation->RemoveObserver(m_SegmentationModifiedObserverTag.value());
}
m_SegmentationModifiedObserverTag.reset();
}
}
void QmitkSegmentationTaskListWidget::OnSegmentationModified()
{
if (!m_UnsavedChanges)
{
m_UnsavedChanges = true;
if (m_ActiveTaskIndex.value() == m_CurrentTaskIndex)
this->UpdateDetailsLabel();
}
}
void QmitkSegmentationTaskListWidget::SetActiveTaskIndex(const std::optional<size_t>& index)
{
if (m_ActiveTaskIndex != index)
{
m_ActiveTaskIndex = index;
this->UpdateStoreAndAcceptButtons();
}
}
void QmitkSegmentationTaskListWidget::SetCurrentTaskIndex(const std::optional<size_t>& index)
{
if (m_CurrentTaskIndex != index)
{
m_CurrentTaskIndex = index;
this->OnCurrentTaskChanged();
}
}
bool QmitkSegmentationTaskListWidget::ActiveTaskIsShown() const
{
return m_ActiveTaskIndex.has_value() && m_CurrentTaskIndex.has_value() && m_ActiveTaskIndex == m_CurrentTaskIndex;
}
bool QmitkSegmentationTaskListWidget::HandleUnsavedChanges(const QString& alternativeTitle)
{
if (m_UnsavedChanges)
{
const auto active = m_ActiveTaskIndex.value();
const auto current = m_CurrentTaskIndex.value();
QString title;
if (alternativeTitle.isEmpty())
{
title = QString("Load task %1").arg(current + 1);
if (m_TaskList->HasName(current))
title += ": " + QString::fromStdString(m_TaskList->GetName(current));
}
else
{
title = alternativeTitle;
}
auto text = QString("The currently active task %1 ").arg(active + 1);
if (m_TaskList->HasName(active))
text += "(" + QString::fromStdString(m_TaskList->GetName(active)) + ") ";
text += "has unsaved changes.";
auto reply = QMessageBox::question(this, title, text, QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel);
switch (reply)
{
case QMessageBox::Save:
- this->SaveActiveTask(!std::filesystem::exists(m_TaskList->GetResult(active)));
+ this->SaveActiveTask(!fs::exists(m_TaskList->GetResult(active)));
break;
case QMessageBox::Discard:
m_UnsavedChanges = false;
break;
default:
return false;
}
}
return true;
}
void QmitkSegmentationTaskListWidget::SaveActiveTask(bool saveAsIntermediateResult)
{
if (!m_ActiveTaskIndex.has_value())
return;
QApplication::setOverrideCursor(Qt::BusyCursor);
try
{
const auto active = m_ActiveTaskIndex.value();
m_TaskList->SaveTask(active, this->GetSegmentationDataNode(active)->GetData(), saveAsIntermediateResult);
this->OnUnsavedChangesSaved();
}
catch (const mitk::Exception& e)
{
MITK_ERROR << e;
}
QApplication::restoreOverrideCursor();
}
bool QmitkSegmentationTaskListWidget::OnPreShutdown()
{
return this->HandleUnsavedChanges(QStringLiteral("Application shutdown"));
}
void QmitkSegmentationTaskListWidget::OnPreviousTaskShortcutActivated()
{
m_Ui->previousButton->click();
}
void QmitkSegmentationTaskListWidget::OnNextTaskShortcutActivated()
{
m_Ui->nextButton->click();
}
void QmitkSegmentationTaskListWidget::OnFindTaskShortcutActivated()
{
m_Ui->findButton->click();
}
void QmitkSegmentationTaskListWidget::OnLoadTaskShortcutActivated()
{
m_Ui->loadButton->click();
}
void QmitkSegmentationTaskListWidget::OnStoreInterimResultShortcutActivated()
{
m_Ui->storeButton->click();
}
void QmitkSegmentationTaskListWidget::OnAcceptSegmentationShortcutActivated()
{
m_Ui->acceptButton->click();
}
void QmitkSegmentationTaskListWidget::OnStoreButtonClicked()
{
this->SaveActiveTask(true);
}
void QmitkSegmentationTaskListWidget::OnAcceptButtonClicked()
{
auto* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager();
int activeToolId = -1;
if (toolManager != nullptr)
activeToolId = toolManager->GetActiveToolID();
this->SaveActiveTask();
this->LoadNextUnfinishedTask();
if (toolManager != nullptr)
toolManager->ActivateTool(activeToolId);
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.cpp b/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.cpp
index da6e9261fc..a907216f77 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.cpp
@@ -1,238 +1,282 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.
Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.s
============================================================================*/
#include "QmitkSetupVirtualEnvUtil.h"
#include "mitkLog.h"
#include <QStandardPaths>
#include <itkCommand.h>
#include <regex>
#include <QDir>
#include <QApplication>
#include <QProcess>
#include <QStringDecoder>
QmitkSetupVirtualEnvUtil::QmitkSetupVirtualEnvUtil()
{
m_BaseDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() +
qApp->organizationName() + QDir::separator();
}
QmitkSetupVirtualEnvUtil::QmitkSetupVirtualEnvUtil(const QString &baseDir)
{
m_BaseDir = baseDir;
}
QString& QmitkSetupVirtualEnvUtil::GetBaseDir()
{
return m_BaseDir;
}
QString QmitkSetupVirtualEnvUtil::GetVirtualEnvPath()
{
return m_venvPath;
}
QString& QmitkSetupVirtualEnvUtil::GetSystemPythonPath()
{
return m_SysPythonPath;
}
QString& QmitkSetupVirtualEnvUtil::GetPythonPath()
{
return m_PythonPath;
}
QString& QmitkSetupVirtualEnvUtil::GetPipPath()
{
return m_PipPath;
}
void QmitkSetupVirtualEnvUtil::SetVirtualEnvPath(const QString &path)
{
m_venvPath = path;
}
void QmitkSetupVirtualEnvUtil::SetPipPath(const QString &path)
{
m_PipPath = path;
}
void QmitkSetupVirtualEnvUtil::SetPythonPath(const QString &path)
{
if (this->IsPythonPath(path))
{
m_PythonPath = path;
}
else
{
MITK_INFO << "Python was not detected in " + path.toStdString();
}
}
void QmitkSetupVirtualEnvUtil::SetSystemPythonPath(const QString &path)
{
if (this->IsPythonPath(path))
{
m_SysPythonPath = path;
}
else
{
MITK_INFO << "Python was not detected in " + path.toStdString();
}
}
void QmitkSetupVirtualEnvUtil::PrintProcessEvent(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;
}
}
void QmitkSetupVirtualEnvUtil::InstallPytorch(const std::string &workingDir,
void (*callback)(itk::Object *, const itk::EventObject &, void *))
{
mitk::ProcessExecutor::ArgumentListType args;
auto spExec = mitk::ProcessExecutor::New();
auto spCommand = itk::CStyleCommand::New();
spCommand->SetCallback(callback);
spExec->AddObserver(mitk::ExternalProcessOutputEvent(), spCommand);
args.push_back("-m");
args.push_back("pip");
args.push_back("install");
args.push_back("light-the-torch==0.7.5");
spExec->Execute(workingDir, "python", args);
PipInstall("torch>=2.0.0", workingDir, callback, "ltt");
PipInstall("torchvision>=0.15.0", workingDir, callback, "ltt");
}
void QmitkSetupVirtualEnvUtil::InstallPytorch()
{
this->InstallPytorch(GetPythonPath().toStdString(), &PrintProcessEvent);
}
+bool QmitkSetupVirtualEnvUtil::IsVenvInstalled(const QString &pythonPath)
+{
+ bool isVenvInstalled = false;
+ QProcess pyProcess;
+#ifdef _WIN32
+ const QString PYTHON_EXE = "python.exe";
+#else
+ const QString PYTHON_EXE = "python3";
+#endif
+ pyProcess.start(pythonPath + QDir::separator() + PYTHON_EXE,
+ QStringList() << "-m" << "venv", QProcess::ReadOnly); //insuffient args to provoke stderr out
+ if (pyProcess.waitForFinished())
+ {
+ auto venvCaptured = QString(QStringDecoder(QStringDecoder::Utf8)(pyProcess.readAllStandardError()));
+ isVenvInstalled = venvCaptured.startsWith(QString("usage")); // if venv found, shows correct usage instructions
+ }
+ return isVenvInstalled;
+}
+
void QmitkSetupVirtualEnvUtil::PipInstall(const std::string &library,
const std::string &workingDir,
void (*callback)(itk::Object *, const itk::EventObject &, void *),
const std::string &command)
{
mitk::ProcessExecutor::ArgumentListType args;
auto spExec = mitk::ProcessExecutor::New();
auto spCommand = itk::CStyleCommand::New();
spCommand->SetCallback(callback);
spExec->AddObserver(mitk::ExternalProcessOutputEvent(), spCommand);
args.push_back("install");
args.push_back(library);
spExec->Execute(workingDir, command, args);
}
void QmitkSetupVirtualEnvUtil::PipInstall(const std::string &library,
void (*callback)(itk::Object*, const itk::EventObject&, void*),
const std::string& command)
{
this->PipInstall(library, this->GetPipPath().toStdString(), callback, command);
}
void QmitkSetupVirtualEnvUtil::ExecutePython(const std::string &pythonCode,
const std::string &workingDir,
void (*callback)(itk::Object *, const itk::EventObject &, void *),
const std::string &command)
{
mitk::ProcessExecutor::ArgumentListType args;
auto spExec = mitk::ProcessExecutor::New();
auto spCommand = itk::CStyleCommand::New();
spCommand->SetCallback(callback);
spExec->AddObserver(mitk::ExternalProcessOutputEvent(), spCommand);
args.push_back("-c");
args.push_back(pythonCode);
spExec->Execute(workingDir, command, args);
}
void QmitkSetupVirtualEnvUtil::ExecutePython(const std::string &args,
void (*callback)(itk::Object *, const itk::EventObject &, void *),
const std::string &command)
{
this->ExecutePython(args, this->GetPythonPath().toStdString(), callback, command);
}
bool QmitkSetupVirtualEnvUtil::IsPythonPath(const QString &pythonPath)
{
QString fullPath = pythonPath;
bool isExists =
#ifdef _WIN32
QFile::exists(fullPath + QDir::separator() + QString("python.exe"));
#else
QFile::exists(fullPath + QDir::separator() + QString("python3"));
#endif
return isExists;
}
std::pair<QString, QString> QmitkSetupVirtualEnvUtil::GetExactPythonPath(const QString &pyEnv)
{
QString fullPath = pyEnv;
bool pythonDoesExist = false;
bool isSupportedVersion = false;
#ifdef _WIN32
const std::string PYTHON_EXE = "python.exe";
// check if python exist in given folder.
pythonDoesExist = QFile::exists(fullPath + QDir::separator() + QString::fromStdString(PYTHON_EXE));
if (!pythonDoesExist && // check if in Scripts already, if not go there
!(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive)))
{
fullPath += QDir::separator() + QString("Scripts");
pythonDoesExist = QFile::exists(fullPath + QDir::separator() + QString("python.exe"));
}
#else
const std::string PYTHON_EXE = "python3";
pythonDoesExist = QFile::exists(fullPath + QDir::separator() + QString::fromStdString(PYTHON_EXE));
if (!pythonDoesExist &&
!(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive)))
{
fullPath += QDir::separator() + QString("bin");
pythonDoesExist = QFile::exists(fullPath + QDir::separator() + QString("python3"));
}
#endif
std::pair<QString, QString> pythonPath;
if (pythonDoesExist)
{
std::regex sanitizer(R"(3\.(\d+))");
QProcess pyProcess;
pyProcess.start(fullPath + QDir::separator() + QString::fromStdString(PYTHON_EXE),
QStringList() << "--version",
QProcess::ReadOnly);
if (pyProcess.waitForFinished())
{
auto pyVersionCaptured = QString(QStringDecoder(QStringDecoder::Utf8)(pyProcess.readAllStandardOutput())).toStdString();
std::smatch match; // Expecting "Python 3.xx.xx" or "Python 3.xx"
if (std::regex_search(pyVersionCaptured, match, sanitizer) && !match.empty())
{
std::string pyVersionNumber = match[0];
int pySubVersion = std::stoi(match[1]);
isSupportedVersion = (pySubVersion > 8) ? (pySubVersion < 13) : false;
pythonPath.second = QString::fromStdString(pyVersionNumber);
}
}
}
pythonPath.first = pythonDoesExist &&isSupportedVersion ? fullPath : "";
return pythonPath;
}
+
+QString QmitkSetupVirtualEnvUtil::GetPipPackageVersion(const QString &pythonPath, const QString &packageName)
+{
+#ifdef _WIN32
+ const QString PIP_EXE = "pip.exe";
+#else
+ const QString PIP_EXE = "pip3";
+#endif
+ QString version;
+ QProcess pipProcess;
+ pipProcess.start(pythonPath + QDir::separator() + PIP_EXE, QStringList() << "show" << packageName, QProcess::ReadOnly);
+ if (pipProcess.waitForFinished())
+ {
+ while (pipProcess.canReadLine())
+ {
+ QString line = pipProcess.readLine();
+ if (line.startsWith("Version"))
+ {
+ version = (line.split(":")[1]).trimmed();
+ break;
+ }
+ }
+ }
+ return version;
+}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.h b/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.h
index 518fc5addc..db12623ad7 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.h
@@ -1,200 +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.s
============================================================================*/
#ifndef QmitkSetupVirtualEnvUtil_h_Included
#define QmitkSetupVirtualEnvUtil_h_Included
#include "mitkLog.h"
#include "mitkProcessExecutor.h"
#include <MitkSegmentationUIExports.h>
#include <QString>
/**
* @brief Abstract Class to Setup a python virtual environment and pip install required packages.
* Derive this class for creating installer for the respective tool.
*/
class MITKSEGMENTATIONUI_EXPORT QmitkSetupVirtualEnvUtil
{
public:
QmitkSetupVirtualEnvUtil(const QString& baseDir);
QmitkSetupVirtualEnvUtil();
/**
* @brief Implement the method in child class
* to setup the virtual environment.
*/
virtual bool SetupVirtualEnv(const QString& venvName) = 0;
/**
* @brief Get the Virtual Env Path object. Override this method in the respective
* tool installer class.
*
* @return QString
*/
virtual QString GetVirtualEnvPath();
/**
* @brief Function to Pip install a library package given the location of
* pip3 executable.
* Any callback function can be passed to process the output.
*
* @param library
* @param workingDir
* @param callback
* @param command
*/
void PipInstall(const std::string &library,
const std::string &workingDir,
void (*callback)(itk::Object *, const itk::EventObject &, void *),
const std::string &command = "pip3");
/**
* @brief Overloaded function to Pip install a library function.
*
* @param library
* @param callback
* @param command
*/
void PipInstall(const std::string &library,
void (*callback)(itk::Object *, const itk::EventObject &, void *),
const std::string &command = "pip3");
/**
* @brief Function to execute any python code given a python path.
* Any callback function can be passed to process the output.
*
* @param args
* @param pythonPath
* @param callback
* @param command
*/
void ExecutePython(const std::string &args,
const std::string &pythonPath,
void (*callback)(itk::Object *, const itk::EventObject &, void *),
const std::string &command = "python");
/**
* @brief Overloaded function to Execute Python code.
* Any callback function can be passed to process the output.
*
* @param args
* @param callback
* @param command
*/
void ExecutePython(const std::string &args,
void (*callback)(itk::Object *, const itk::EventObject &, void *),
const std::string &command = "python");
/**
* @brief Installs pytorch using light-the-torch package, correctly identifying cuda version.
* Requires location of pip3 executable.
* Any callback function can be passed to process the output.
*
* @param workingDir
* @param callback
*/
void InstallPytorch(const std::string &workingDir, void (*callback)(itk::Object *, const itk::EventObject &, void *));
/**
* @brief Overloaded function to install pytorch using light-the-torch package, correctly
* identifying cuda version.
*/
void InstallPytorch();
/**
* @brief Get the Base Dir object
*
* @return QString&
*/
QString& GetBaseDir();
/**
* @brief Get the System Python Path object
*
* @return QString&
*/
QString& GetSystemPythonPath();
/**
* @brief Get the Python Path object
*
* @return QString&
*/
QString& GetPythonPath();
/**
* @brief Get the Pip Path object
*
* @return QString&
*/
QString& GetPipPath();
/**
* @brief Set the System Python Path object
*
* @param path
*/
void SetSystemPythonPath(const QString& path);
/**
* @brief Set the Python Path object
*
* @param path
*/
void SetPythonPath(const QString& path);
/**
* @brief Set the Pip Path object
*
* @param path
*/
void SetPipPath(const QString& path);
/**
* @brief Set the Virtual Env Path object
*
* @param path
*/
void SetVirtualEnvPath(const QString &path);
/**
* @brief Check if the path provide has python executable or not.
*
* @param pythonPath
* @return true
* @return false
*/
bool IsPythonPath(const QString &pythonPath);
+ /**
+ * @brief Checks if venv module is available for the python.
+ *
+ */
+ static bool IsVenvInstalled(const QString &pythonPath);
+
+ /**
+ * @brief Returns version of the pip installed package
+ *
+ */
+ static QString GetPipPackageVersion(const QString &pythonPath, const QString &packageName);
+
/**
* @brief Function can be used as callback to simply print out all the process execution output
* parsed out from itk::EventObject.
*
*/
static void PrintProcessEvent(itk::Object *, const itk::EventObject &e, void *);
/**
* @brief Get the exact Python path and version for any OS from the virtual environment path.
* @return A pair of the exact python path and its Python version or empty, if an supported
* version of Python could not be found.
*/
static std::pair<QString, QString> GetExactPythonPath(const QString &pyEnv);
private:
QString m_PythonPath;
QString m_PipPath;
QString m_BaseDir;
QString m_venvPath;
QString m_SysPythonPath;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp
index 4881c9cfac..a8be7459c7 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp
@@ -1,1497 +1,1497 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkRenderWindow.h"
#include "QmitkRenderWindowWidget.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 <mitkTimeNavigationController.h>
#include "mitkToolManager.h"
#include "mitkUndoController.h"
#include <mitkExtractSliceFilter.h>
#include <mitkPlanarCircle.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 <mitkImageToContourFilter.h>
#include <mitkImagePixelReadAccessor.h>
// Includes for the merge operation
#include "mitkImageToContourFilter.h"
#include <mitkLabelSetImage.h>
#include <mitkLabelSetImageConverter.h>
#include <QCheckBox>
#include <QCursor>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <vtkDoubleArray.h>
#include <vtkFieldData.h>
#include <vtkPolyVertex.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPolyData.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 QmitkSlicesInterpolator::ActionToSliceDimensionMapType QmitkSlicesInterpolator::CreateActionToSlicer(const QList<QmitkRenderWindow*>& windows)
{
std::map<QAction *, mitk::SliceNavigationController *> actionToSliceDimension;
for (auto* window : windows)
{
std::string windowName;
auto renderWindowWidget = dynamic_cast<QmitkRenderWindowWidget*>(window->parentWidget());
if (renderWindowWidget)
{
windowName = renderWindowWidget->GetCornerAnnotationText();
}
else
{
windowName = window->GetRenderer()->GetName();
}
auto slicer = window->GetSliceNavigationController();
actionToSliceDimension[new QAction(QString::fromStdString(windowName), nullptr)] = slicer;
}
return actionToSliceDimension;
}
mitk::Image::Pointer ExtractSliceFromImage(mitk::Image* image,
const mitk::PlaneGeometry * contourPlane,
unsigned int timeStep)
{
vtkSmartPointer<mitkVtkImageOverwrite> reslice = vtkSmartPointer<mitkVtkImageOverwrite>::New();
// set to false to extract a slice
reslice->SetOverwriteMode(false);
reslice->Modified();
mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice);
extractor->SetInput(image);
extractor->SetTimeStep(timeStep);
extractor->SetWorldGeometry(contourPlane);
extractor->SetVtkOutputRequest(false);
extractor->SetResliceTransformByGeometry(image->GetTimeGeometry()->GetGeometryForTimeStep(timeStep));
extractor->Update();
mitk::Image::Pointer slice = extractor->GetOutput();
return slice;
}
QmitkSlicesInterpolator::QmitkSlicesInterpolator(QWidget *parent, const char * /*name*/)
: QWidget(parent),
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_CurrentActiveLabelValue(0),
m_FirstRun(true)
{
m_GroupBoxEnableExclusiveInterpolationMode = new QGroupBox("Interpolation", this);
QVBoxLayout *vboxLayout = new QVBoxLayout(m_GroupBoxEnableExclusiveInterpolationMode);
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);
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()));
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);
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)
);
}
}
void QmitkSlicesInterpolator::SetActiveLabelValue(mitk::LabelSetImage::LabelValueType labelValue)
{
bool changedValue = labelValue != this->m_CurrentActiveLabelValue;
this->m_CurrentActiveLabelValue = labelValue;
if (changedValue) this->OnActiveLabelChanged(labelValue);
};
mitk::DataStorage *QmitkSlicesInterpolator::GetDataStorage()
{
if (m_DataStorage.IsNotNull())
{
return m_DataStorage;
}
else
{
return nullptr;
}
}
void QmitkSlicesInterpolator::InitializeWindow(QmitkRenderWindow* window)
{
auto slicer = window->GetSliceNavigationController();
if (slicer == nullptr)
{
MITK_WARN << "Tried setting up interpolation for a render window that does not have a slice navigation controller set";
return;
}
// Has to be initialized
m_LastSNC = slicer;
itk::MemberCommand<QmitkSlicesInterpolator>::Pointer deleteCommand =
itk::MemberCommand<QmitkSlicesInterpolator>::New();
deleteCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted);
m_ControllerToDeleteObserverTag[slicer] = slicer->AddObserver(itk::DeleteEvent(), deleteCommand);
itk::MemberCommand<QmitkSlicesInterpolator>::Pointer sliceChangedCommand =
itk::MemberCommand<QmitkSlicesInterpolator>::New();
sliceChangedCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSliceChanged);
m_ControllerToSliceObserverTag[slicer] = slicer->AddObserver(mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0), sliceChangedCommand);
}
void QmitkSlicesInterpolator::Initialize(mitk::ToolManager *toolManager,
const QList<QmitkRenderWindow*>& windows)
{
Q_ASSERT(!windows.empty());
if (m_Initialized)
{
// remove old observers
this->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);
auto* timeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController();
itk::MemberCommand<QmitkSlicesInterpolator>::Pointer timeChangedCommand =
itk::MemberCommand<QmitkSlicesInterpolator>::New();
timeChangedCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnTimeChanged);
m_ControllerToTimeObserverTag =
timeNavigationController->AddObserver(mitk::TimeNavigationController::TimeEvent(0), timeChangedCommand);
m_TimePoint = timeNavigationController->GetSelectedTimePoint();
// connect to the slice navigation controller. after each change, call the interpolator
for (auto* window : windows)
{
this->InitializeWindow(window);
}
m_ActionToSlicerMap = CreateActionToSlicer(windows);
}
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);
}
auto* timeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController();
timeNavigationController->RemoveObserver(m_ControllerToTimeObserverTag);
for (auto* slicer : m_ControllerToSliceObserverTag.keys())
{
slicer->RemoveObserver(m_ControllerToDeleteObserverTag.take(slicer));
slicer->RemoveObserver(m_ControllerToSliceObserverTag.take(slicer));
}
m_ActionToSlicerMap.clear();
m_ToolManager = nullptr;
m_Initialized = false;
}
QmitkSlicesInterpolator::~QmitkSlicesInterpolator()
{
if (m_Initialized)
{
// remove old observers
this->Uninitialize();
}
WaitForFutures();
if (m_DataStorage.IsNotNull())
{
m_DataStorage->RemoveNodeEvent.RemoveListener(
mitk::MessageDelegate1<QmitkSlicesInterpolator, const mitk::DataNode*>(this, &QmitkSlicesInterpolator::NodeRemoved)
);
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);
m_SurfaceInterpolator->SetCurrentInterpolationSession(nullptr);
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);
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);
this->Show3DInterpolationResult(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_InterpolatedSurfaceNode);
m_InterpolatedSurfaceNode->SetData(nullptr);
m_BtnReinit3DInterpolation->setEnabled(false);
m_CmbInterpolation->setCurrentIndex(0);
return;
}
// Updating the current selected segmentation for the 3D interpolation
this->SetCurrentContourListID();
if (m_2DInterpolationEnabled)
{
OnInterpolationActivated(true); // re-initialize if needed
}
}
void QmitkSlicesInterpolator::OnToolManagerReferenceDataModified()
{
}
void QmitkSlicesInterpolator::OnTimeChanged(itk::Object *sender, const itk::EventObject &e)
{
if (!dynamic_cast<const mitk::TimeNavigationController::TimeEvent*>(&e))
{
return;
}
const auto* timeNavigationController = dynamic_cast<mitk::TimeNavigationController*>(sender);
if (nullptr == timeNavigationController)
{
return;
}
bool timeChanged = m_TimePoint != timeNavigationController->GetSelectedTimePoint();
m_TimePoint = timeNavigationController->GetSelectedTimePoint();
if (m_Watcher.isRunning())
m_Watcher.waitForFinished();
if (timeChanged)
{
if (m_3DInterpolationEnabled)
{
m_InterpolatedSurfaceNode->SetData(nullptr);
}
m_SurfaceInterpolator->Modified();
}
if (nullptr == m_LastSNC)
{
return;
}
if (TranslateAndInterpolateChangedSlice(m_LastSNC->GetCreatedWorldGeometry()))
{
m_LastSNC->GetRenderer()->RequestUpdate();
}
}
void QmitkSlicesInterpolator::OnSliceChanged(itk::Object *sender, const itk::EventObject &e)
{
if (!dynamic_cast<const mitk::SliceNavigationController::GeometrySliceEvent*>(&e))
{
return;
}
auto sliceNavigationController = dynamic_cast<mitk::SliceNavigationController*>(sender);
if (nullptr == sliceNavigationController)
{
return;
}
if(m_2DInterpolationEnabled)
{
this->On2DInterpolationEnabled(m_2DInterpolationEnabled);
}
if (TranslateAndInterpolateChangedSlice(e, sliceNavigationController))
{
sliceNavigationController->GetRenderer()->RequestUpdate();
}
}
bool QmitkSlicesInterpolator::TranslateAndInterpolateChangedSlice(const itk::EventObject& e,
mitk::SliceNavigationController* sliceNavigationController)
{
const mitk::SliceNavigationController::GeometrySliceEvent* event =
dynamic_cast<const mitk::SliceNavigationController::GeometrySliceEvent*>(&e);
mitk::TimeGeometry* timeGeometry = event->GetTimeGeometry();
m_LastSNC = sliceNavigationController;
return this->TranslateAndInterpolateChangedSlice(timeGeometry);
}
bool QmitkSlicesInterpolator::TranslateAndInterpolateChangedSlice(const mitk::TimeGeometry* timeGeometry)
{
if (!m_2DInterpolationEnabled)
{
return false;
}
if (nullptr == timeGeometry)
{
return false;
}
if (!timeGeometry->IsValidTimePoint(m_TimePoint))
{
return false;
}
mitk::SlicedGeometry3D* slicedGeometry =
dynamic_cast<mitk::SlicedGeometry3D*>(timeGeometry->GetGeometryForTimePoint(m_TimePoint).GetPointer());
if (nullptr == slicedGeometry)
{
return false;
}
mitk::PlaneGeometry* plane = dynamic_cast<mitk::PlaneGeometry*>(slicedGeometry->GetPlaneGeometry(m_LastSNC->GetStepper()->GetPos()));
if (nullptr == plane)
{
return false;
}
this->Interpolate(plane);
return true;
}
void QmitkSlicesInterpolator::Interpolate(mitk::PlaneGeometry *plane)
{
if (nullptr == m_ToolManager)
{
return;
}
mitk::DataNode* node = m_ToolManager->GetWorkingData(0);
if (nullptr == node)
{
return;
}
m_Segmentation = dynamic_cast<mitk::Image*>(node->GetData());
if (nullptr == m_Segmentation)
{
return;
}
if (!m_Segmentation->GetTimeGeometry()->IsValidTimePoint(m_TimePoint))
{
MITK_WARN << "Cannot interpolate WorkingImage. Passed time point is not within the time bounds of WorkingImage. "
"Time point: "
<< m_TimePoint;
return;
}
const auto timeStep = m_Segmentation->GetTimeGeometry()->TimePointToTimeStep(m_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);
// maybe just have a variable that stores the active label color.
if (m_ToolManager)
{
auto* workingNode = m_ToolManager->GetWorkingData(0);
if (workingNode != nullptr)
{
auto* activeLabel = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData())->GetActiveLabel();
if (nullptr != activeLabel)
{
auto activeColor = activeLabel->GetColor();
m_FeedbackNode->SetProperty("color", mitk::ColorProperty::New(activeColor));
}
}
}
m_LastSliceIndex = clickedSliceIndex;
}
void QmitkSlicesInterpolator::OnSurfaceInterpolationFinished()
{
mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0);
if (workingNode && workingNode->GetData())
{
const auto segmentation = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
if (segmentation == nullptr)
{
MITK_ERROR << "Run3DInterpolation triggered with no MultiLabelSegmentation as working data.";
return;
}
mitk::Surface::Pointer interpolatedSurface = m_SurfaceInterpolator->GetInterpolationResult(segmentation, m_CurrentActiveLabelValue, segmentation->GetTimeGeometry()->TimePointToTimeStep(m_TimePoint));
if (interpolatedSurface.IsNotNull())
{
m_BtnApply3D->setEnabled(true);;
m_InterpolatedSurfaceNode->SetData(interpolatedSurface);
this->Show3DInterpolationResult(true);
if (!m_DataStorage->Exists(m_InterpolatedSurfaceNode))
{
m_DataStorage->Add(m_InterpolatedSurfaceNode);
}
}
else
{
m_BtnApply3D->setEnabled(false);
if (m_DataStorage->Exists(m_InterpolatedSurfaceNode))
{
this->Show3DInterpolationResult(false);
}
}
}
m_BtnReinit3DInterpolation->setEnabled(true);
for (auto* slicer : m_ControllerToSliceObserverTag.keys())
{
slicer->GetRenderer()->RequestUpdate();
}
}
void QmitkSlicesInterpolator::OnAcceptInterpolationClicked()
{
auto* workingNode = m_ToolManager->GetWorkingData(0);
auto* planeGeometry = m_LastSNC->GetCurrentPlaneGeometry();
auto* interpolatedPreview = dynamic_cast<mitk::Image*>(m_FeedbackNode->GetData());
if (nullptr == workingNode || nullptr == interpolatedPreview)
return;
auto* segmentationImage = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
if (nullptr == segmentationImage)
return;
if (!segmentationImage->GetTimeGeometry()->IsValidTimePoint(m_TimePoint))
{
MITK_WARN << "Cannot accept interpolation. Time point selected by SliceNavigationController is not within the "
"time bounds of segmentation. Time point: "
<< m_TimePoint;
return;
}
const auto timeStep = segmentationImage->GetTimeGeometry()->TimePointToTimeStep(m_TimePoint);
auto interpolatedSlice = mitk::SegTool2D::GetAffectedImageSliceAs2DImage(planeGeometry, segmentationImage, timeStep)->Clone();
auto activeValue = segmentationImage->GetActiveLabel()->GetValue();
mitk::TransferLabelContentAtTimeStep(
interpolatedPreview,
interpolatedSlice,
segmentationImage->GetConstLabelsByValue(segmentationImage->GetLabelValuesByGroup(segmentationImage->GetActiveLayer())),
timeStep,
0,
mitk::LabelSetImage::UNLABELED_VALUE,
false,
{ {0, mitk::LabelSetImage::UNLABELED_VALUE}, {1, activeValue} }
);
mitk::SegTool2D::WriteBackSegmentationResult(workingNode, planeGeometry, interpolatedSlice, timeStep);
m_FeedbackNode->SetData(nullptr);
}
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;
if (4 == m_Segmentation->GetDimension())
{
const auto* geometry = m_Segmentation->GetTimeGeometry();
if (!geometry->IsValidTimePoint(m_TimePoint))
{
MITK_WARN << "Cannot accept all interpolations. Time point selected by passed SliceNavigationController is not "
"within the time bounds of segmentation. Time point: "
<< m_TimePoint;
return;
}
mitk::Image::Pointer activeLabelImage;
try
{
auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(m_Segmentation);
activeLabelImage = mitk::CreateLabelMask(labelSetImage, labelSetImage->GetActiveLabel()->GetValue());
}
catch (const std::exception& e)
{
MITK_ERROR << e.what() << " | NO LABELSETIMAGE IN WORKING NODE\n";
}
m_Interpolator->SetSegmentationVolume(activeLabelImage);
timeStep = geometry->TimePointToTimeStep(m_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::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();
// Go through the sliced indices
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();
// Do the interpolation here.
for (size_t threadIndex = 0; threadIndex < numThreads; ++threadIndex)
{
interpolate(threadIndex);
}
m_Interpolator->DisableSliceImageCache();
const mitk::Label::PixelType newDestinationLabel = dynamic_cast<mitk::LabelSetImage *>(m_Segmentation)->GetActiveLabel()->GetValue();
// Do and Undo Operations
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);
mitk::DiffImageApplier::GetInstanceForUndo()->SetDestinationLabel(newDestinationLabel);
// 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);
for (auto it = m_ActionToSlicerMap.begin(); it != m_ActionToSlicerMap.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 labelSetImage = dynamic_cast<mitk::LabelSetImage*>(segmentationDataNode->GetData());
auto activeLabelColor = labelSetImage->GetActiveLabel()->GetColor();
std::string activeLabelName = labelSetImage->GetActiveLabel()->GetName();
auto segmentation = GetData<mitk::Image>(segmentationDataNode);
if (referenceImage.IsNull() || segmentation.IsNull())
return;
const auto* segmentationGeometry = segmentation->GetTimeGeometry();
if (!referenceImage->GetTimeGeometry()->IsValidTimePoint(m_TimePoint) ||
!segmentationGeometry->IsValidTimePoint(m_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::IOComponentEnum::USHORT == segmentation->GetPixelType().GetComponentType());
surfaceToImageFilter->SetInput(interpolatedSurface);
surfaceToImageFilter->Update();
mitk::Image::Pointer interpolatedSegmentation = surfaceToImageFilter->GetOutput();
auto timeStep = segmentationGeometry->TimePointToTimeStep(m_TimePoint);
const mitk::Label::PixelType newDestinationLabel = labelSetImage->GetActiveLabel()->GetValue();
TransferLabelContentAtTimeStep(
interpolatedSegmentation,
labelSetImage,
labelSetImage->GetConstLabelsByValue(labelSetImage->GetLabelValuesByGroup(labelSetImage->GetActiveLayer())),
timeStep,
0,
0,
false,
{{1, newDestinationLabel}},
mitk::MultiLabelSegmentation::MergeStyle::Merge,
mitk::MultiLabelSegmentation::OverwriteStyle::RegardLocks);
this->Show3DInterpolationResult(false);
std::string name = segmentationDataNode->GetName() + " 3D-interpolation - " + activeLabelName;
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.
+ // possibility to serialize 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);
interpolatedSurfaceDataNode->SetColor(activeLabelColor);
m_DataStorage->Add(interpolatedSurfaceDataNode, segmentationDataNode);
}
void QmitkSlicesInterpolator::OnReinit3DInterpolation()
{
// Step 1. Load from the isContourPlaneGeometry nodes the contourNodes.
mitk::NodePredicateProperty::Pointer pred =
mitk::NodePredicateProperty::New("isContourPlaneGeometry", mitk::BoolProperty::New(true));
mitk::DataStorage::SetOfObjects::ConstPointer contourNodes =
m_DataStorage->GetDerivations(m_ToolManager->GetWorkingData(0), pred);
if (contourNodes->Size() != 0)
{
if (m_ToolManager->GetWorkingData(0) != nullptr)
{
try
{
auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(m_ToolManager->GetWorkingData(0)->GetData());
if (!labelSetImage->GetTimeGeometry()->IsValidTimePoint(m_TimePoint))
{
MITK_ERROR << "Invalid time point requested for interpolation pipeline.";
return;
}
mitk::SurfaceInterpolationController::CPIVector newCPIs;
// Adding label and timeStep information for the contourNodes.
for (auto it = contourNodes->Begin(); it != contourNodes->End(); ++it)
{
auto contourNode = it->Value();
auto labelID = dynamic_cast<mitk::UShortProperty *>(contourNode->GetProperty("labelID"))->GetValue();
auto timeStep = dynamic_cast<mitk::IntProperty *>(contourNode->GetProperty("timeStep"))->GetValue();
auto planeGeometry = dynamic_cast<mitk::PlanarFigure *>(contourNode->GetData())->GetPlaneGeometry();
auto groupID = labelSetImage->GetGroupIndexOfLabel(labelID);
auto sliceImage = ExtractSliceFromImage(labelSetImage->GetGroupImage(groupID), planeGeometry, timeStep);
mitk::ImageToContourFilter::Pointer contourExtractor = mitk::ImageToContourFilter::New();
contourExtractor->SetInput(sliceImage);
contourExtractor->SetContourValue(labelID);
contourExtractor->Update();
mitk::Surface::Pointer contour = contourExtractor->GetOutput();
if (contour->GetVtkPolyData()->GetNumberOfPoints() == 0)
continue;
contour->DisconnectPipeline();
newCPIs.emplace_back(contour, planeGeometry->Clone(),labelID,timeStep);
}
m_SurfaceInterpolator->CompleteReinitialization(newCPIs);
}
catch(const std::exception& e)
{
MITK_ERROR << "Exception thrown casting toolmanager working data to labelsetImage";
}
}
}
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
{
auto iter = m_ActionToSlicerMap.find(action);
if (iter != m_ActionToSlicerMap.end())
{
mitk::SliceNavigationController *slicer = iter->second;
this->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();
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)
{
auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(workingNode->GetData());
if (nullptr == labelSetImage)
{
MITK_ERROR << "NO LABELSETIMAGE IN WORKING NODE\n";
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
return;
}
const auto* activeLabel = labelSetImage->GetActiveLabel();
const auto* segmentation = dynamic_cast<mitk::Image*>(workingNode->GetData());
if (nullptr != activeLabel && nullptr != segmentation)
{
auto activeLabelImage = mitk::CreateLabelMask(labelSetImage, activeLabel->GetValue());
m_Interpolator->SetSegmentationVolume(activeLabelImage);
if (referenceNode)
{
mitk::Image *referenceImage = dynamic_cast<mitk::Image *>(referenceNode->GetData());
m_Interpolator->SetReferenceVolume(referenceImage); // may be nullptr
}
}
}
}
this->UpdateVisibleSuggestion();
}
void QmitkSlicesInterpolator::Run3DInterpolation()
{
auto workingNode = m_ToolManager->GetWorkingData(0);
if (workingNode == nullptr)
{
MITK_ERROR << "Run3DInterpolation triggered with no working data set.";
return;
}
const auto segmentation = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
if (segmentation == nullptr)
{
MITK_ERROR << "Run3DInterpolation triggered with no MultiLabelSegmentation as working data.";
return;
}
if (!segmentation->ExistLabel(m_CurrentActiveLabelValue))
{
MITK_ERROR << "Run3DInterpolation triggered with no valid label selected. Currently selected invalid label: "<<m_CurrentActiveLabelValue;
return;
}
m_SurfaceInterpolator->Interpolate(segmentation,m_CurrentActiveLabelValue,segmentation->GetTimeGeometry()->TimePointToTimeStep(m_TimePoint));
}
void QmitkSlicesInterpolator::StartUpdateInterpolationTimer()
{
m_Timer->start(500);
}
void QmitkSlicesInterpolator::StopUpdateInterpolationTimer()
{
if(m_ToolManager)
{
const auto* workingNode = m_ToolManager->GetWorkingData(0);
const auto activeColor = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData())->GetActiveLabel()->GetColor();
m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(activeColor));
}
m_Timer->stop();
}
void QmitkSlicesInterpolator::ChangeSurfaceColor()
{
float currentColor[3];
m_InterpolatedSurfaceNode->GetColor(currentColor);
m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB));
m_InterpolatedSurfaceNode->Update();
mitk::RenderingManager::GetInstance()->RequestUpdateAll(mitk::RenderingManager::REQUEST_UPDATE_3DWINDOWS);
}
void QmitkSlicesInterpolator::On3DInterpolationActivated(bool on)
{
m_3DInterpolationEnabled = on;
try
{
// this->PrepareInputsFor3DInterpolation();
m_SurfaceInterpolator->Modified();
}
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
this->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
this->UpdateVisibleSuggestion();
}
void QmitkSlicesInterpolator::OnInterpolationAborted(const itk::EventObject& /*e*/)
{
m_CmbInterpolation->setCurrentIndex(0);
m_FeedbackNode->SetData(nullptr);
}
void QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged(const itk::EventObject & /*e*/)
{
auto workingNode = m_ToolManager->GetWorkingData(0);
if (workingNode == nullptr)
{
MITK_DEBUG << "OnSurfaceInterpolationInfoChanged triggered with no working data set.";
return;
}
const auto segmentation = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
if (segmentation == nullptr)
{
MITK_DEBUG << "OnSurfaceInterpolationInfoChanged triggered with no MultiLabelSegmentation as working data.";
return;
}
if (!segmentation->ExistLabel(m_CurrentActiveLabelValue))
{
MITK_DEBUG << "OnSurfaceInterpolationInfoChanged triggered with no valid label selected. Currently selected invalid label: " << m_CurrentActiveLabelValue;
return;
}
if (m_Watcher.isRunning())
m_Watcher.waitForFinished();
if (m_3DInterpolationEnabled)
{
m_InterpolatedSurfaceNode->SetData(nullptr);
m_Future = QtConcurrent::run(&QmitkSlicesInterpolator::Run3DInterpolation, this);
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);
if (!workingNode->GetData()->GetTimeGeometry()->IsValidTimePoint(m_TimePoint))
{
MITK_WARN << "Cannot accept interpolation. Time point selected by SliceNavigationController is not within the time bounds of WorkingImage. Time point: " << m_TimePoint;
return;
}
m_SurfaceInterpolator->SetDistanceImageVolume(50000);
auto segmentationImage = dynamic_cast<mitk::LabelSetImage *>(workingNode->GetData());
m_SurfaceInterpolator->SetCurrentInterpolationSession(segmentationImage);
}
else
{
QWidget::setEnabled(false);
}
}
}
void QmitkSlicesInterpolator::Show3DInterpolationResult(bool status)
{
if (m_InterpolatedSurfaceNode.IsNotNull())
m_InterpolatedSurfaceNode->SetVisibility(status);
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSlicesInterpolator::OnActiveLabelChanged(mitk::Label::PixelType)
{
m_FeedbackNode->SetData(nullptr);
m_InterpolatedSurfaceNode->SetData(nullptr);
if (m_Watcher.isRunning())
m_Watcher.waitForFinished();
if (m_3DInterpolationEnabled)
{
m_SurfaceInterpolator->Modified();
}
if (m_2DInterpolationEnabled)
{
m_FeedbackNode->SetData(nullptr);
this->OnInterpolationActivated(true);
m_LastSNC->SendSlice();
}
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
this->UpdateVisibleSuggestion();
}
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) || (m_Segmentation->GetDimension() != 4)) )
{
QMessageBox info;
info.setWindowTitle("3D Interpolation Process");
info.setIcon(QMessageBox::Information);
info.setText("3D Interpolation is only supported for 3D/4D 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_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_FeedbackNode ||
node == m_InterpolatedSurfaceNode)
{
WaitForFutures();
}
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorGUIControls.ui
index b4a1b287b6..05aae03ebb 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorGUIControls.ui
+++ b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorGUIControls.ui
@@ -1,357 +1,357 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmitkTotalSegmentatorToolGUIControls</class>
<widget class="QWidget" name="QmitkTotalSegmentatorToolGUIControls">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>699</width>
<height>352</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100000</width>
<height>100000</height>
</size>
</property>
<property name="windowTitle">
<string>QmitkTotalSegmentatorToolWidget</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<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>
<layout class="QGridLayout" name="basicSettingsLayout">
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="welcomeNote">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Welcome to TotalSegmentator v2 tool in MITK. [Experimental]&lt;/p&gt;&lt;p&gt;Please note that this is only an interface to TotalSegmentator. MITK does not ship with TotalSegmentator. Make sure to have a working internet connection to install TotalSegmentator. Or, choose an python environment with TotalSegmentator in it before inferencing.
- Currently, the tool is pretrained on CT images only. &lt;/p&gt;&lt;p&gt;Refer to &lt;a href=&quot;https://github.com/wasserth/TotalSegmentator&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/wasserth/TotalSegmentator&lt;/span&gt;&lt;/a&gt; to learn everything about the TotalSegmentator.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Welcome to TotalSegmentator v2 tool in MITK. [Experimental]&lt;/p&gt;&lt;p&gt;Please note that this is only an interface to TotalSegmentator. MITK does not ship with TotalSegmentator. Make sure to have a working internet connection to install TotalSegmentator. Or, choose an python environment with TotalSegmentator in it before inferencing.
+ The tool is predominantly for CT images only. For MR image choose &amp;quot;total_mr&amp;quot; task specifically. &lt;/p&gt;&lt;p&gt;Refer to &lt;a href=&quot;https://github.com/wasserth/TotalSegmentator&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://github.com/wasserth/TotalSegmentator&lt;/span&gt;&lt;/a&gt; to learn everything about the TotalSegmentator.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row ="1" colspan="4">
<widget class="QPushButton" name="installButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100000</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Install TotalSegmentator</string>
</property>
</widget>
</item>
<item row="2" colspan="4">
<widget class="ctkCollapsibleGroupBox" name="installGroupBox" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title" stdset="0">
<string>Install Options</string>
</property>
<property name="flat" stdset="0">
<bool>true</bool>
</property>
<property name="collapsedHeight" stdset="0">
<number>5</number>
</property>
<property name="collapsed" stdset="0">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_0">
<item>
<layout class="QGridLayout" name="_1">
<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>
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="OverrideLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Use Custom Installation:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="ctkCheckBox" name="overrideBox" native="true">
<property name="checked" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="clearButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100000</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Clear Install</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="pythonEnvLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Custom Env. Path:</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="ctkComboBox" name="pythonEnvComboBox"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="sysPythonLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>System Python (>=3.9):</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<widget class="ctkComboBox" name="sysPythonComboBox"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="3" colspan="4">
<widget class="QLabel" name="subtaskLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Task:</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="3">
<widget class="ctkComboBox" name="subtaskComboBox"/>
</item>
</layout>
</item>
<item>
<widget class="ctkCollapsibleGroupBox" name="advancedGroupBox" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title" stdset="0">
<string>Advanced</string>
</property>
<property name="flat" stdset="0">
<bool>true</bool>
</property>
<property name="collapsedHeight" stdset="0">
<number>5</number>
</property>
<property name="collapsed" stdset="0">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QGridLayout" name="_2">
<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>
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="fastLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Fast:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="ctkCheckBox" name="fastBox" native="true">
<property name="checked" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="gpuSpinLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>GPU Id:</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<widget class="ctkComboBox" name="gpuComboBox"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="previewButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100000</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Run TotalSegmentator</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="statusLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
- <customwidgets>
+ <customwidgets>
<customwidget>
<class>ctkComboBox</class>
<extends>QComboBox</extends>
<header location="global">ctkComboBox.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ctkCollapsibleGroupBox</class>
<extends>QWidget</extends>
<header>ctkCollapsibleGroupBox.h</header>
</customwidget>
<customwidget>
<class>ctkCheckBox</class>
<extends>QWidget</extends>
<header>ctkCheckBox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp
index c792a6249c..8505970dd0 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp
@@ -1,501 +1,516 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkTotalSegmentatorToolGUI.h"
#include "mitkProcessExecutor.h"
#include "mitkTotalSegmentatorTool.h"
#include <QApplication>
#include <QDir>
#include <QDirIterator>
#include <QFileDialog>
#include <QIcon>
#include <QmitkStyleManager.h>
#include <QMessageBox>
MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkTotalSegmentatorToolGUI, "")
QmitkTotalSegmentatorToolGUI::QmitkTotalSegmentatorToolGUI()
: QmitkMultiLabelSegWithPreviewToolGUIBase(), m_SuperclassEnableConfirmSegBtnFnc(m_EnableConfirmSegBtnFnc)
{
// Nvidia-smi command returning zero doesn't always imply lack of GPUs.
// Pytorch uses its own libraries to communicate to the GPUs. Hence, only a warning can be given.
if (m_GpuLoader.GetGPUCount() == 0)
{
std::string warning = "WARNING: No GPUs were detected on your machine. The TotalSegmentator tool can be very slow.";
this->ShowErrorMessage(warning);
}
m_EnableConfirmSegBtnFnc = [this](bool enabled)
{ return !m_FirstPreviewComputation ? m_SuperclassEnableConfirmSegBtnFnc(enabled) : false; };
}
void QmitkTotalSegmentatorToolGUI::ConnectNewTool(mitk::SegWithPreviewTool *newTool)
{
Superclass::ConnectNewTool(newTool);
m_FirstPreviewComputation = true;
}
void QmitkTotalSegmentatorToolGUI::InitializeUI(QBoxLayout *mainLayout)
{
m_Controls.setupUi(this);
#ifndef _WIN32
m_Controls.sysPythonComboBox->addItem("/usr/bin");
#endif
this->AutoParsePythonPaths();
m_Controls.sysPythonComboBox->addItem("Select");
m_Controls.sysPythonComboBox->setCurrentIndex(0);
m_Controls.pythonEnvComboBox->addItem("Select");
m_Controls.pythonEnvComboBox->setDuplicatesEnabled(false);
m_Controls.pythonEnvComboBox->setDisabled(true);
m_Controls.previewButton->setDisabled(true);
m_Controls.statusLabel->setTextFormat(Qt::RichText);
m_Controls.subtaskComboBox->addItems(VALID_TASKS);
QString welcomeText;
this->SetGPUInfo();
if (m_GpuLoader.GetGPUCount() != 0)
{
welcomeText = "<b>STATUS: </b><i>Welcome to TotalSegmentator tool. You're in luck: " +
QString::number(m_GpuLoader.GetGPUCount()) + " GPU(s) were detected.</i>";
}
else
{
welcomeText = "<b>STATUS: </b><i>Welcome to TotalSegmentator tool. Sorry, " +
QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected.</i>";
}
connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewBtnClicked()));
connect(m_Controls.clearButton, SIGNAL(clicked()), this, SLOT(OnClearInstall()));
connect(m_Controls.installButton, SIGNAL(clicked()), this, SLOT(OnInstallBtnClicked()));
connect(m_Controls.overrideBox, SIGNAL(stateChanged(int)), this, SLOT(OnOverrideChecked(int)));
connect(m_Controls.pythonEnvComboBox,
QOverload<int>::of(&QComboBox::activated),
[=](int index) { OnPythonPathChanged(m_Controls.pythonEnvComboBox->itemText(index)); });
connect(m_Controls.sysPythonComboBox,
QOverload<int>::of(&QComboBox::activated),
[=](int index) { OnSystemPythonChanged(m_Controls.sysPythonComboBox->itemText(index)); });
QString lastSelectedPyEnv = m_Settings.value("TotalSeg/LastCustomPythonPath").toString();
if (!lastSelectedPyEnv.isEmpty() && lastSelectedPyEnv!= "Select")
{
m_Controls.pythonEnvComboBox->insertItem(0, lastSelectedPyEnv);
}
m_Controls.fastBox->setChecked(m_Settings.value("TotalSeg/LastFast").toBool());
const QString storageDir = m_Installer.GetVirtualEnvPath();
m_IsInstalled = this->IsTotalSegmentatorInstalled(storageDir);
if (m_IsInstalled)
{
m_PythonPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(storageDir).first;
m_Installer.SetVirtualEnvPath(m_PythonPath);
this->EnableAll(m_IsInstalled);
welcomeText += " TotalSegmentator is already found installed.";
}
else
{
welcomeText += " TotalSegmentator is not installed. Please click on \"Install TotalSegmentator\" above.";
}
this->WriteStatusMessage(welcomeText);
QIcon deleteIcon =
QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/edit-delete.svg"));
QIcon arrowIcon =
QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/tango/scalable/actions/media-playback-start.svg"));
m_Controls.clearButton->setIcon(deleteIcon);
m_Controls.previewButton->setIcon(arrowIcon);
mainLayout->addLayout(m_Controls.verticalLayout);
Superclass::InitializeUI(mainLayout);
}
void QmitkTotalSegmentatorToolGUI::SetGPUInfo()
{
std::vector<QmitkGPUSpec> specs = m_GpuLoader.GetAllGPUSpecs();
for (const QmitkGPUSpec &gpuSpec : specs)
{
m_Controls.gpuComboBox->addItem(QString::number(gpuSpec.id) + ": " + gpuSpec.name + " (" + gpuSpec.memory + ")");
}
if (specs.empty())
{
m_Controls.gpuComboBox->setEditable(true);
m_Controls.gpuComboBox->addItem(QString::number(0));
m_Controls.gpuComboBox->setValidator(new QIntValidator(0, 999, this));
}
}
unsigned int QmitkTotalSegmentatorToolGUI::FetchSelectedGPUFromUI() const
{
QString gpuInfo = m_Controls.gpuComboBox->currentText();
if (m_GpuLoader.GetGPUCount() == 0)
{
return static_cast<unsigned int>(gpuInfo.toInt());
}
else
{
QString gpuId = gpuInfo.split(":", Qt::SkipEmptyParts).first();
return static_cast<unsigned int>(gpuId.toInt());
}
}
void QmitkTotalSegmentatorToolGUI::EnableAll(bool isEnable)
{
m_Controls.previewButton->setEnabled(isEnable);
m_Controls.subtaskComboBox->setEnabled(isEnable);
m_Controls.installButton->setEnabled((!isEnable));
}
void QmitkTotalSegmentatorToolGUI::OnInstallBtnClicked()
{
bool isInstalled = false;
const auto [path, version] = OnSystemPythonChanged(m_Controls.sysPythonComboBox->currentText());
if (path.isEmpty())
{
this->WriteErrorMessage("<b>ERROR: </b>Couldn't find compatible Python.");
return;
}
+ if (!QmitkSetupVirtualEnvUtil::IsVenvInstalled(path))
+ {
+ this->WriteErrorMessage("venv module not found for the selected python to create a new virtual "
+ "environment. Please install venv or select another compatibile python");
+ return;
+ }
// check if python 3.13 and ask for confirmation
if (version.startsWith("3.13") &&
QMessageBox::No == QMessageBox::question(
nullptr,
"Installing TotalSegmentator",
QString("WARNING: This is an unsupported version of Python that may not work. "
"We recommend using a supported Python version between 3.9 and 3.12.\n\n"
"Continue anyway?"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No))
{
return;
}
this->WriteStatusMessage("<b>STATUS: </b>Installing TotalSegmentator...");
m_Installer.SetSystemPythonPath(path);
isInstalled = m_Installer.SetupVirtualEnv(m_Installer.VENV_NAME);
if (isInstalled)
{
m_PythonPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(m_Installer.GetVirtualEnvPath()).first;
this->WriteStatusMessage("<b>STATUS: </b>Successfully installed TotalSegmentator.");
}
else
{
this->WriteErrorMessage("<b>ERROR: </b>Couldn't install TotalSegmentator.");
}
this->EnableAll(isInstalled);
}
void QmitkTotalSegmentatorToolGUI::OnPreviewBtnClicked()
{
auto tool = this->GetConnectedToolAs<mitk::TotalSegmentatorTool>();
if (nullptr == tool)
{
return;
}
try
{
m_Controls.previewButton->setEnabled(false);
qApp->processEvents();
if (!this->IsTotalSegmentatorInstalled(m_PythonPath))
{
throw std::runtime_error(WARNING_TOTALSEG_NOT_FOUND);
}
bool isFast = m_Controls.fastBox->isChecked();
QString subTask = m_Controls.subtaskComboBox->currentText();
- if (subTask != VALID_TASKS[0])
+ if (subTask != VALID_TASKS[0] && subTask != VALID_TASKS[1])
{
isFast = true;
}
tool->SetPythonPath(m_PythonPath.toStdString());
tool->SetGpuId(FetchSelectedGPUFromUI());
tool->SetFast(isFast);
tool->SetSubTask(subTask.toStdString());
this->WriteStatusMessage(QString("<b>STATUS: </b><i>Starting Segmentation task... This might take a while.</i>"));
m_FirstPreviewComputation = false;
tool->UpdatePreview();
m_Controls.previewButton->setEnabled(true);
}
catch (const std::exception &e)
{
std::stringstream errorMsg;
errorMsg << "<b>STATUS: </b>Error while processing parameters for TotalSegmentator segmentation. Reason: "
<< e.what();
this->ShowErrorMessage(errorMsg.str());
this->WriteErrorMessage(QString::fromStdString(errorMsg.str()));
m_Controls.previewButton->setEnabled(true);
m_FirstPreviewComputation = true;
return;
}
catch (...)
{
- std::string errorMsg = "Unkown error occured while generation TotalSegmentator segmentation.";
+ std::string errorMsg = "Unknown error occurred while generation TotalSegmentator segmentation.";
this->ShowErrorMessage(errorMsg);
m_Controls.previewButton->setEnabled(true);
m_FirstPreviewComputation = true;
return;
}
this->SetLabelSetPreview(tool->GetPreviewSegmentation());
this->ActualizePreviewLabelVisibility();
this->WriteStatusMessage("<b>STATUS: </b><i>Segmentation task finished successfully.</i>");
QString pythonPathTextItem = m_Controls.pythonEnvComboBox->currentText();
if (!pythonPathTextItem.isEmpty() && pythonPathTextItem != "Select") // only cache if the prediction ended without errors.
{
QString lastSelectedPyEnv = m_Settings.value("TotalSeg/LastCustomPythonPath").toString();
if (lastSelectedPyEnv != pythonPathTextItem)
{
m_Settings.setValue("TotalSeg/LastCustomPythonPath", pythonPathTextItem);
}
}
m_Settings.setValue("TotalSeg/LastFast", m_Controls.fastBox->isChecked());
}
void QmitkTotalSegmentatorToolGUI::ShowErrorMessage(const std::string &message, QMessageBox::Icon icon)
{
this->setCursor(Qt::ArrowCursor);
QMessageBox *messageBox = new QMessageBox(icon, nullptr, message.c_str());
messageBox->exec();
delete messageBox;
MITK_WARN << message;
}
void QmitkTotalSegmentatorToolGUI::WriteStatusMessage(const QString &message)
{
m_Controls.statusLabel->setText(message);
m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: white");
qApp->processEvents();
}
void QmitkTotalSegmentatorToolGUI::WriteErrorMessage(const QString &message)
{
m_Controls.statusLabel->setText(message);
m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: red");
qApp->processEvents();
}
bool QmitkTotalSegmentatorToolGUI::IsTotalSegmentatorInstalled(const QString &pythonPath)
{
QString fullPath = pythonPath;
bool isPythonExists = false, isExists = false;
#ifdef _WIN32
isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe"));
if (!(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive)))
{
fullPath += QDir::separator() + QString("Scripts");
isPythonExists =
(!isPythonExists) ? QFile::exists(fullPath + QDir::separator() + QString("python.exe")) : isPythonExists;
}
isExists = QFile::exists(fullPath + QDir::separator() + QString("TotalSegmentator.exe")) && isPythonExists;
#else
isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3"));
if (!(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive)))
{
fullPath += QDir::separator() + QString("bin");
isPythonExists =
(!isPythonExists) ? QFile::exists(fullPath + QDir::separator() + QString("python3")) : isPythonExists;
}
isExists = QFile::exists(fullPath + QDir::separator() + QString("TotalSegmentator")) && isPythonExists;
#endif
+ if (isExists && m_Installer.TOTALSEGMENTATOR_VERSION !=
+ QmitkSetupVirtualEnvUtil::GetPipPackageVersion(fullPath, "TotalSegmentator"))
+ {
+ isExists = false;
+ }
return isExists;
}
void QmitkTotalSegmentatorToolGUI::AutoParsePythonPaths()
{
QString homeDir = QDir::homePath();
std::vector<QString> searchDirs;
#ifdef _WIN32
searchDirs.push_back(QString("C:") + QDir::separator() + QString("ProgramData") + QDir::separator() +
QString("anaconda3"));
#else
// Add search locations for possible standard python paths here
searchDirs.push_back(homeDir + QDir::separator() + "environments");
searchDirs.push_back(homeDir + QDir::separator() + "anaconda3");
searchDirs.push_back(homeDir + QDir::separator() + "miniconda3");
searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "miniconda3");
searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "anaconda3");
#endif
for (QString searchDir : searchDirs)
{
if (searchDir.endsWith("anaconda3", Qt::CaseInsensitive))
{
if (QDir(searchDir).exists())
{
m_Controls.sysPythonComboBox->addItem("(base): " + searchDir);
searchDir.append((QDir::separator() + QString("envs")));
}
}
for (QDirIterator subIt(searchDir, QDir::AllDirs, QDirIterator::NoIteratorFlags); subIt.hasNext();)
{
subIt.next();
QString envName = subIt.fileName();
- if (!envName.startsWith('.')) // Filter out irrelevent hidden folders, if any.
+ if (!envName.startsWith('.')) // Filter out irrelevant hidden folders, if any.
{
m_Controls.pythonEnvComboBox->addItem("(" + envName + "): " + subIt.filePath());
}
}
}
}
std::pair<QString, QString> QmitkTotalSegmentatorToolGUI::OnSystemPythonChanged(const QString &pyEnv)
{
std::pair<QString, QString> pyPath;
if (pyEnv == QString("Select"))
{
m_Controls.previewButton->setDisabled(true);
QString path =
QFileDialog::getExistingDirectory(m_Controls.sysPythonComboBox->parentWidget(), "Python Path", "dir");
if (!path.isEmpty())
{
this->OnSystemPythonChanged(path); // recall same function for new path validation
bool oldState = m_Controls.sysPythonComboBox->blockSignals(true); // block signal firing while inserting item
m_Controls.sysPythonComboBox->insertItem(0, path);
m_Controls.sysPythonComboBox->setCurrentIndex(0);
m_Controls.sysPythonComboBox->blockSignals(
oldState); // unblock signal firing after inserting item. Remove this after Qt6 migration
}
}
else
{
QString uiPyPath = this->GetPythonPathFromUI(pyEnv);
pyPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(uiPyPath);
}
return pyPath;
}
void QmitkTotalSegmentatorToolGUI::OnPythonPathChanged(const QString &pyEnv)
{
if (pyEnv == QString("Select"))
{
m_Controls.previewButton->setDisabled(true);
QString path =
QFileDialog::getExistingDirectory(m_Controls.pythonEnvComboBox->parentWidget(), "Python Path", "dir");
if (!path.isEmpty())
{
this->OnPythonPathChanged(path); // recall same function for new path validation
bool oldState = m_Controls.pythonEnvComboBox->blockSignals(true); // block signal firing while inserting item
m_Controls.pythonEnvComboBox->insertItem(0, path);
m_Controls.pythonEnvComboBox->setCurrentIndex(0);
m_Controls.pythonEnvComboBox->blockSignals(
oldState); // unblock signal firing after inserting item. Remove this after Qt6 migration
}
}
else if (!this->IsTotalSegmentatorInstalled(this->GetPythonPathFromUI(pyEnv)))
{
this->ShowErrorMessage(WARNING_TOTALSEG_NOT_FOUND);
m_Controls.previewButton->setDisabled(true);
}
else
{// Show positive status meeage
m_Controls.previewButton->setDisabled(false);
QString uiPyPath = this->GetPythonPathFromUI(pyEnv);
m_PythonPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(uiPyPath).first;
}
}
QString QmitkTotalSegmentatorToolGUI::GetPythonPathFromUI(const QString &pyUI) const
{
QString fullPath = pyUI;
if (-1 != fullPath.indexOf(")"))
{
fullPath = fullPath.mid(fullPath.indexOf(")") + 2);
}
return fullPath.simplified();
}
void QmitkTotalSegmentatorToolGUI::OnOverrideChecked(int state)
{
bool isEnabled = false;
if (state == Qt::Checked)
{
isEnabled = true;
m_Controls.previewButton->setDisabled(true);
m_PythonPath.clear();
}
else
{
m_PythonPath.clear();
m_Controls.previewButton->setDisabled(true);
if (m_IsInstalled)
{
const QString pythonPath = m_Installer.GetVirtualEnvPath();
auto pathObject = QmitkSetupVirtualEnvUtil::GetExactPythonPath(pythonPath);
m_PythonPath = pathObject.first;
this->EnableAll(m_IsInstalled);
}
}
m_Controls.pythonEnvComboBox->setEnabled(isEnabled);
}
void QmitkTotalSegmentatorToolGUI::OnClearInstall()
{
QDir folderPath(m_Installer.GetVirtualEnvPath());
if (folderPath.removeRecursively())
{
m_Controls.installButton->setEnabled(true);
m_IsInstalled = false;
if (!m_Controls.overrideBox->isChecked())
{
m_Controls.previewButton->setEnabled(false);
}
}
else
{
MITK_ERROR
<< "The virtual environment couldn't be removed. Please check if you have the required access privileges or, some other process is accessing the folders.";
}
}
bool QmitkTotalSegmentatorToolInstaller::SetupVirtualEnv(const QString& venvName)
{
if (GetSystemPythonPath().isEmpty())
{
return false;
}
+ if (!QmitkSetupVirtualEnvUtil::IsVenvInstalled(GetSystemPythonPath()))
+ {
+ return false;
+ }
QDir folderPath(GetBaseDir());
folderPath.mkdir(venvName);
if (!folderPath.cd(venvName))
{
return false; // Check if directory creation was successful.
}
mitk::ProcessExecutor::ArgumentListType args;
auto spExec = mitk::ProcessExecutor::New();
auto spCommand = itk::CStyleCommand::New();
spCommand->SetCallback(&PrintProcessEvent);
spExec->AddObserver(mitk::ExternalProcessOutputEvent(), spCommand);
args.push_back("-m");
args.push_back("venv");
args.push_back(venvName.toStdString());
#ifdef _WIN32
QString pythonFile = GetSystemPythonPath() + QDir::separator() + "python.exe";
QString pythonExeFolder = "Scripts";
#else
QString pythonFile = GetSystemPythonPath() + QDir::separator() + "python3";
QString pythonExeFolder = "bin";
#endif
spExec->Execute(GetBaseDir().toStdString(), pythonFile.toStdString(), args); // Setup local virtual environment
if (folderPath.cd(pythonExeFolder))
{
this->SetPythonPath(folderPath.absolutePath());
this->SetPipPath(folderPath.absolutePath());
this->InstallPytorch();
for (auto &package : PACKAGES)
{
this->PipInstall(package.toStdString(), &PrintProcessEvent);
}
std::string pythonCode; // python syntax to check if torch is installed with CUDA.
pythonCode.append("import torch;");
pythonCode.append("print('Pytorch was installed with CUDA') if torch.cuda.is_available() else print('PyTorch was "
"installed WITHOUT CUDA');");
this->ExecutePython(pythonCode, &PrintProcessEvent);
return true;
}
return false;
}
QString QmitkTotalSegmentatorToolInstaller::GetVirtualEnvPath()
{
return STORAGE_DIR + VENV_NAME;
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h
index de8e2df0bc..4beb2a2e45 100644
--- a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h
+++ b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h
@@ -1,182 +1,183 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitkTotalSegmentatorToolGUI_h_Included
#define QmitkTotalSegmentatorToolGUI_h_Included
#include "QmitkMultiLabelSegWithPreviewToolGUIBase.h"
#include "QmitkSetupVirtualEnvUtil.h"
#include "QmitknnUNetGPU.h"
#include "ui_QmitkTotalSegmentatorGUIControls.h"
#include <MitkSegmentationUIExports.h>
#include <QMessageBox>
#include <QSettings>
#include <QStandardPaths>
#include <QDir>
/**
* @brief Installer class for TotalSegmentator Tool.
* Class specifies the virtual environment name, install version, packages required to pip install
* and implements SetupVirtualEnv method.
*
*/
class QmitkTotalSegmentatorToolInstaller : public QmitkSetupVirtualEnvUtil
{
public:
const QString VENV_NAME = ".totalsegmentator_v2";
- const QString TOTALSEGMENTATOR_VERSION = "2.0.5";
+ const QString TOTALSEGMENTATOR_VERSION = "2.2.1";
const std::vector<QString> PACKAGES = {QString("Totalsegmentator==") + TOTALSEGMENTATOR_VERSION,
QString("setuptools")}; /* just in case */
const QString STORAGE_DIR;
inline QmitkTotalSegmentatorToolInstaller(
const QString baseDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator()
+ qApp->organizationName() + QDir::separator())
: QmitkSetupVirtualEnvUtil(baseDir), STORAGE_DIR(baseDir){};
bool SetupVirtualEnv(const QString &) override;
QString GetVirtualEnvPath() override;
};
/**
\ingroup org_mitk_gui_qt_interactivesegmentation_internal
\brief GUI for mitk::TotalSegmentatorTool.
\sa mitk::
*/
class MITKSEGMENTATIONUI_EXPORT QmitkTotalSegmentatorToolGUI : public QmitkMultiLabelSegWithPreviewToolGUIBase
{
Q_OBJECT
public:
mitkClassMacro(QmitkTotalSegmentatorToolGUI, QmitkMultiLabelSegWithPreviewToolGUIBase);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
protected slots:
/**
* @brief Qt Slot
*/
void OnPreviewBtnClicked();
/**
* @brief Qt Slot
*/
void OnPythonPathChanged(const QString &);
/**
* @brief Qt Slot
*/
std::pair<QString, QString> OnSystemPythonChanged(const QString &);
/**
* @brief Qt Slot
*/
void OnInstallBtnClicked();
/**
* @brief Qt Slot
*/
void OnOverrideChecked(int);
/**
* @brief Qt Slot
*/
void OnClearInstall();
protected:
QmitkTotalSegmentatorToolGUI();
~QmitkTotalSegmentatorToolGUI() = default;
void ConnectNewTool(mitk::SegWithPreviewTool *newTool) override;
void InitializeUI(QBoxLayout *mainLayout) override;
/**
* @brief Enable (or Disable) GUI elements.
*/
void EnableAll(bool);
/**
- * @brief Searches and parses paths of python virtual enviroments
+ * @brief Searches and parses paths of python virtual environments
* from predefined lookout locations
*/
void AutoParsePythonPaths();
/**
* @brief Checks if TotalSegmentator command is valid in the selected python virtual environment.
*
* @return bool
*/
bool IsTotalSegmentatorInstalled(const QString &);
/**
* @brief Creates a QMessage object and shows on screen.
*/
void ShowErrorMessage(const std::string &, QMessageBox::Icon = QMessageBox::Critical);
/**
* @brief Writes any message in white on the tool pane.
*/
void WriteStatusMessage(const QString &);
/**
* @brief Writes any message in red on the tool pane.
*/
void WriteErrorMessage(const QString &);
/**
* @brief Adds GPU information to the gpu combo box.
- * In case, there aren't any GPUs avaialble, the combo box will be
+ * In case, there aren't any GPUs available, the combo box will be
* rendered editable.
*/
void SetGPUInfo();
/**
* @brief Returns GPU id of the selected GPU from the Combo box.
*
* @return unsigned int
*/
unsigned int FetchSelectedGPUFromUI() const;
/**
* @brief Get the virtual env path from UI combobox removing any
* extra special characters.
*
* @return QString
*/
QString GetPythonPathFromUI(const QString &) const;
/**
* @brief For storing values like Python path across sessions.
*/
QSettings m_Settings;
QString m_PythonPath;
QmitkGPULoader m_GpuLoader;
Ui_QmitkTotalSegmentatorToolGUIControls m_Controls;
bool m_FirstPreviewComputation = true;
bool m_IsInstalled = false;
EnableConfirmSegBtnFunctionType m_SuperclassEnableConfirmSegBtnFnc;
const std::string WARNING_TOTALSEG_NOT_FOUND =
"TotalSegmentator is not detected in the selected python environment.Please select a valid "
"python environment or install TotalSegmentator.";
const QStringList VALID_TASKS = {
"total",
+ "total_mr",
"cerebral_bleed",
"hip_implant",
"coronary_arteries",
"body",
"lung_vessels",
"pleural_pericard_effusion"
};
QmitkTotalSegmentatorToolInstaller m_Installer;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h
index 0cf1cc66bb..e071c03009 100644
--- a/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h
+++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h
@@ -1,258 +1,258 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitknnUNetFolderParser_h
#define QmitknnUNetFolderParser_h
#include <QDirIterator>
#include <memory>
#include <functional>
#include <QString>
#include <vector>
/**
* @brief Struct to store each (Folder) Node of the hierarchy tree structure.
*
*/
struct FolderNode
{
QString name;
QString path; // parent
std::vector<std::shared_ptr<FolderNode>> subFolders;
};
/**
- * @brief Class to store and retreive folder hierarchy information
+ * @brief Class to store and retrieve folder hierarchy information
* of RESULTS_FOLDER. Only Root node is explicitly stored in m_RootNode.
- * No. of sub levels in the hierachry is defined in the LEVEL constant.
+ * No. of sub levels in the hierarchry is defined in the LEVEL constant.
*
*/
class QmitknnUNetFolderParser
{
public:
/**
* @brief Construct a new QmitknnUNetFolderParser object
* Initializes root folder node object pointer calls
* @param parentFolder
*/
QmitknnUNetFolderParser(const QString parentFolder);
/**
* @brief Destroy the QmitknnUNetFolderParser object
*
*/
~QmitknnUNetFolderParser() = default;
/**
* @brief Returns the "Results Folder" string which is parent path of the root node.
*
* @return QString
*/
QString getResultsFolder();
/**
* @brief Returns the Model Names from root node. Template function,
* type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T getModelNames()
{
auto models = GetSubFolderNamesFromNode<T>(m_RootNode);
return models;
}
/**
* @brief Returns the task names for a given model. Template function,
* type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @param modelName
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T getTasksForModel(const QString &modelName)
{
std::shared_ptr<FolderNode> modelNode = GetSubNodeMatchingNameCrietria(modelName, m_RootNode);
auto tasks = GetSubFolderNamesFromNode<T>(modelNode);
return tasks;
}
/**
* @brief Returns the models names for a given task. Template function,
* type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @param taskName
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T getModelsForTask(const QString &taskName)
{
T modelsForTask;
auto models = GetSubFolderNamesFromNode<T>(m_RootNode);
foreach (QString model, models)
{
QStringList taskList = getTasksForModel<QStringList>(model);
if (taskList.contains(taskName, Qt::CaseInsensitive))
{
modelsForTask << model;
}
}
return modelsForTask;
}
/**
* @brief Returns all the task names present in the root node with possible duplicates.
* Template function, type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T getAllTasks()
{
T allTasks;
auto models = GetSubFolderNamesFromNode<T>(m_RootNode);
foreach (QString model, models)
{
allTasks << getTasksForModel<QStringList>(model);
}
return allTasks;
}
/**
* @brief Returns the trainer / planner names for a given task & model. Template function,
* type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @param taskName
* @param modelName
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T getTrainerPlannersForTask(const QString &taskName, const QString &modelName)
{
std::shared_ptr<FolderNode> modelNode = GetSubNodeMatchingNameCrietria(modelName, m_RootNode);
std::shared_ptr<FolderNode> taskNode = GetSubNodeMatchingNameCrietria(taskName, modelNode);
auto tps = GetSubFolderNamesFromNode<T>(taskNode);
return tps;
}
/**
* @brief Returns the Folds names for a given trainer,planner,task & model name. Template function,
* type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @param trainer
* @param planner
* @param taskName
* @param modelName
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T getFoldsForTrainerPlanner(const QString &trainer,
const QString &planner,
const QString &taskName,
const QString &modelName)
{
std::shared_ptr<FolderNode> modelNode = GetSubNodeMatchingNameCrietria(modelName, m_RootNode);
std::shared_ptr<FolderNode> taskNode = GetSubNodeMatchingNameCrietria(taskName, modelNode);
QString trainerPlanner = trainer + QString("__") + planner;
std::shared_ptr<FolderNode> tpNode = GetSubNodeMatchingNameCrietria(trainerPlanner, taskNode);
auto folds = GetSubFolderNamesFromNode<T>(tpNode);
return folds;
}
private:
const int m_LEVEL = 4;
std::shared_ptr<FolderNode> m_RootNode;
/**
* @brief Returns rule function wrapper to check for specific files at given Result_Folder hierarchy level.
*
* @param level
* @return std::function<bool(QString)>
*/
std::function<bool(QString)> RuleEngine(int level);
/**
* @brief Iterates through the root node and returns the sub FolderNode object Matching Name Crietria
*
* @param queryName
* @param parentNode
* @return std::shared_ptr<FolderNode>
*/
std::shared_ptr<FolderNode> GetSubNodeMatchingNameCrietria(const QString &queryName, std::shared_ptr<FolderNode> parentNode);
/**
* @brief Returns the sub folder names for a folder node object. Template function,
* type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @param std::shared_ptr<FolderNode>
* @return T (any of stl or Qt containers which supports push_back call)
*/
template <typename T>
T GetSubFolderNamesFromNode(const std::shared_ptr<FolderNode> parent)
{
T folders;
std::vector<std::shared_ptr<FolderNode>> subNodes = parent->subFolders;
for (std::shared_ptr<FolderNode> folder : subNodes)
{
folders.push_back(folder->name);
}
return folders;
}
/**
* @brief Iterates through the sub folder hierarchy upto a level provided
* and create a tree structure.
*
* @param parent
* @param level
*/
void InitDirs(std::shared_ptr<FolderNode> parent, int level);
/**
* @brief Iterates through the sub folder hierarchy upto a level provided
* and clears the sub folder std::vector from each node.
*
* @param parent
* @param level
*/
void DeleteDirs(std::shared_ptr<FolderNode> parent, int level);
/**
* @brief Template function to fetch all folders inside a given path.
* The type can be any of stl or Qt containers which supports push_back call.
*
* @tparam T
* @param path
* @return T
*/
template <typename T>
T FetchFoldersFromDir(const QString &path, std::function<bool(QString)> callback)
{
T folders;
for (QDirIterator it(path, QDir::AllDirs, QDirIterator::NoIteratorFlags); it.hasNext();)
{
it.next();
if (!it.fileName().startsWith('.') && callback(it.filePath()))
{
folders.push_back(it.fileName());
}
}
return folders;
}
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp
index d69d23064b..794e1de862 100644
--- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp
+++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp
@@ -1,1200 +1,1200 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitknnUNetToolGUI.h"
#include "mitkProcessExecutor.h"
#include "mitknnUnetTool.h"
#include <mitkTimeNavigationController.h>
#include <QApplication>
#include <QDir>
#include <QDirIterator>
#include <QIcon>
#include <QmitkStyleManager.h>
#include <QmitknnUNetEnsembleLayout.h>
#include <QtGlobal>
#include <algorithm>
#include <ctkCollapsibleGroupBox.h>
#include <itksys/SystemTools.hxx>
#include <nlohmann/json.hpp>
#include <mitkIOUtil.h>
MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitknnUNetToolGUI, "")
QmitknnUNetToolGUI::QmitknnUNetToolGUI() : QmitkMultiLabelSegWithPreviewToolGUIBase(), m_SuperclassEnableConfirmSegBtnFnc(m_EnableConfirmSegBtnFnc)
{
// Nvidia-smi command returning zero doesn't always imply lack of GPUs.
// Pytorch uses its own libraries to communicate to the GPUs. Hence, only a warning can be given.
if (m_GpuLoader.GetGPUCount() == 0)
{
std::string warning = "WARNING: No GPUs were detected on your machine. The nnUNet tool might not work.";
this->ShowErrorMessage(warning);
}
// define predicates for multi modal data selection combobox
auto imageType = mitk::TNodePredicateDataType<mitk::Image>::New();
auto labelSetImageType = mitk::NodePredicateNot::New(mitk::TNodePredicateDataType<mitk::LabelSetImage>::New());
m_MultiModalPredicate = mitk::NodePredicateAnd::New(imageType, labelSetImageType).GetPointer();
m_nnUNetThread = new QThread(this);
m_Worker = new nnUNetDownloadWorker;
m_Worker->moveToThread(m_nnUNetThread);
m_EnableConfirmSegBtnFnc = [this](bool enabled)
{
return !m_FirstPreviewComputation ? m_SuperclassEnableConfirmSegBtnFnc(enabled) : false;
};
}
QmitknnUNetToolGUI::~QmitknnUNetToolGUI()
{
m_nnUNetThread->quit();
m_nnUNetThread->wait();
}
void QmitknnUNetToolGUI::ConnectNewTool(mitk::SegWithPreviewTool *newTool)
{
Superclass::ConnectNewTool(newTool);
newTool->IsTimePointChangeAwareOff();
m_FirstPreviewComputation = true;
}
void QmitknnUNetToolGUI::InitializeUI(QBoxLayout *mainLayout)
{
m_Controls.setupUi(this);
#ifndef _WIN32
m_Controls.pythonEnvComboBox->addItem("/usr/bin");
#endif
m_Controls.pythonEnvComboBox->addItem("Select");
AutoParsePythonPaths();
SetGPUInfo();
connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewRequested()));
connect(m_Controls.modeldirectoryBox,
SIGNAL(directoryChanged(const QString &)),
this,
SLOT(OnDirectoryChanged(const QString &)));
connect(
m_Controls.modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &)));
connect(m_Controls.taskBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTaskChanged(const QString &)));
connect(
m_Controls.plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &)));
connect(m_Controls.multiModalBox, SIGNAL(stateChanged(int)), this, SLOT(OnCheckBoxChanged(int)));
connect(m_Controls.pythonEnvComboBox,
#if QT_VERSION >= 0x050F00 // 5.15
SIGNAL(textActivated(const QString &)),
#elif QT_VERSION >= 0x050C00 // 5.12
SIGNAL(currentTextChanged(const QString &)),
#endif
this,
SLOT(OnPythonPathChanged(const QString &)));
connect(m_Controls.refreshdirectoryBox, SIGNAL(clicked()), this, SLOT(OnRefreshPresssed()));
connect(m_Controls.clearCacheButton, SIGNAL(clicked()), this, SLOT(OnClearCachePressed()));
connect(m_Controls.startDownloadButton, SIGNAL(clicked()), this, SLOT(OnDownloadModel()));
connect(m_Controls.stopDownloadButton, SIGNAL(clicked()), this, SLOT(OnStopDownload()));
// Qthreads
qRegisterMetaType<mitk::ProcessExecutor::Pointer>();
qRegisterMetaType<mitk::ProcessExecutor::ArgumentListType>();
connect(this, &QmitknnUNetToolGUI::Operate, m_Worker, &nnUNetDownloadWorker::DoWork);
connect(m_Worker, &nnUNetDownloadWorker::Exit, this, &QmitknnUNetToolGUI::OnDownloadWorkerExit);
connect(m_nnUNetThread, &QThread::finished, m_Worker, &QObject::deleteLater);
m_Controls.multiModalValueLabel->setStyleSheet("font-weight: bold; color: white");
m_Controls.multiModalValueLabel->setVisible(false);
m_Controls.requiredModalitiesLabel->setVisible(false);
m_Controls.stopDownloadButton->setVisible(false);
m_Controls.previewButton->setEnabled(false);
QIcon refreshIcon =
QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/view-refresh.svg"));
m_Controls.refreshdirectoryBox->setIcon(refreshIcon);
QIcon dirIcon =
QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg"));
m_Controls.modeldirectoryBox->setIcon(dirIcon);
m_Controls.refreshdirectoryBox->setEnabled(true);
QIcon stopIcon =
QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/status/dialog-error.svg"));
m_Controls.stopDownloadButton->setIcon(stopIcon);
m_Controls.statusLabel->setTextFormat(Qt::RichText);
if (m_GpuLoader.GetGPUCount() != 0)
{
WriteStatusMessage(QString("<b>STATUS: </b><i>Welcome to nnUNet. " + QString::number(m_GpuLoader.GetGPUCount()) +
" GPUs were detected.</i>"));
}
else
{
WriteErrorMessage(QString("<b>STATUS: </b><i>Welcome to nnUNet. " + QString::number(m_GpuLoader.GetGPUCount()) +
" GPUs were detected.</i>"));
}
mainLayout->addLayout(m_Controls.verticalLayout);
Superclass::InitializeUI(mainLayout);
m_UI_ROWS = m_Controls.advancedSettingsLayout->rowCount(); // Must do. Row count is correct only here.
this->DisableEverything();
QString lastSelectedPyEnv = m_Settings.value("nnUNet/LastPythonPath").toString();
m_Controls.pythonEnvComboBox->setCurrentText(lastSelectedPyEnv);
}
void QmitknnUNetToolGUI::EnableWidgets(bool enabled)
{
Superclass::EnableWidgets(enabled);
}
void QmitknnUNetToolGUI::ClearAllModalities()
{
m_Controls.multiModalBox->setChecked(false);
this->ClearAllModalLabels();
}
void QmitknnUNetToolGUI::ClearAllModalLabels()
{
for (auto modalLabel : m_ModalLabels)
{
delete modalLabel; // delete the layout item
m_ModalLabels.pop_back();
}
m_Controls.advancedSettingsLayout->update();
}
void QmitknnUNetToolGUI::DisableEverything()
{
m_Controls.modeldirectoryBox->setEnabled(false);
m_Controls.refreshdirectoryBox->setEnabled(false);
m_Controls.previewButton->setEnabled(false);
m_Controls.multiModalValueLabel->setVisible(false);
m_Controls.multiModalBox->setEnabled(false);
this->ClearAllComboBoxes();
this->ClearAllModalities();
}
void QmitknnUNetToolGUI::ClearAllComboBoxes()
{
m_Controls.modelBox->clear();
m_Controls.taskBox->clear();
m_Controls.foldBox->clear();
m_Controls.trainerBox->clear();
m_Controls.plannerBox->clear();
for (auto &layout : m_EnsembleParams)
{
layout->modelBox->clear();
layout->trainerBox->clear();
layout->plannerBox->clear();
layout->foldBox->clear();
}
}
std::vector<mitk::Image::ConstPointer> QmitknnUNetToolGUI::FetchMultiModalImagesFromUI()
{
std::vector<mitk::Image::ConstPointer> modals;
if (m_Controls.multiModalBox->isChecked() && !m_Modalities.empty())
{
std::set<std::string> nodeNames; // set container for keeping names of all nodes to check if they are added twice.
for (QmitkSingleNodeSelectionWidget *modality : m_Modalities)
{
mitk::DataNode::Pointer node = modality->GetSelectedNode();
if (nodeNames.find(node->GetName()) == nodeNames.end())
{
modals.push_back(dynamic_cast<const mitk::Image *>(node->GetData()));
nodeNames.insert(node->GetName());
}
else
{
throw std::runtime_error("Same modality is selected more than once. Please change your selection.");
break;
}
}
}
return modals;
}
bool QmitknnUNetToolGUI::IsNNUNetInstalled(const QString &pythonPath)
{
QString fullPath = pythonPath;
#ifdef _WIN32
if (!(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive)))
{
fullPath += QDir::separator() + QString("Scripts");
}
#else
if (!(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive)))
{
fullPath += QDir::separator() + QString("bin");
}
#endif
fullPath = fullPath.mid(fullPath.indexOf(" ") + 1);
bool isExists = QFile::exists(fullPath + QDir::separator() + QString("nnUNet_predict")) &&
QFile::exists(fullPath + QDir::separator() + QString("python3"));
return isExists;
}
void QmitknnUNetToolGUI::ShowErrorMessage(const std::string &message, QMessageBox::Icon icon)
{
this->setCursor(Qt::ArrowCursor);
QMessageBox *messageBox = new QMessageBox(icon, nullptr, message.c_str());
messageBox->exec();
delete messageBox;
MITK_WARN << message;
}
void QmitknnUNetToolGUI::WriteStatusMessage(const QString &message)
{
m_Controls.statusLabel->setText(message);
m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: white");
}
void QmitknnUNetToolGUI::WriteErrorMessage(const QString &message)
{
m_Controls.statusLabel->setText(message);
m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: red");
}
void QmitknnUNetToolGUI::ProcessEnsembleModelsParams(mitk::nnUNetTool::Pointer tool)
{
if (m_EnsembleParams[0]->modelBox->currentText() == m_EnsembleParams[1]->modelBox->currentText())
{
throw std::runtime_error("Both models you have selected for ensembling are the same.");
}
QString taskName = m_Controls.taskBox->currentText();
bool isPPJson = m_Controls.postProcessingCheckBox->isChecked();
std::vector<mitk::ModelParams> requestQ;
QString ppDirFolderNamePart1 = "ensemble_";
QStringList ppDirFolderNameParts;
for (auto &layout : m_EnsembleParams)
{
QStringList ppDirFolderName;
QString modelName = layout->modelBox->currentText();
ppDirFolderName << modelName;
ppDirFolderName << "__";
QString trainer = layout->trainerBox->currentText();
ppDirFolderName << trainer;
ppDirFolderName << "__";
QString planId = layout->plannerBox->currentText();
ppDirFolderName << planId;
if (!this->IsModelExists(modelName, taskName, QString(trainer + "__" + planId)))
{
std::string errorMsg = "The configuration " + modelName.toStdString() +
" you have selected doesn't exist. Check your Results Folder again.";
throw std::runtime_error(errorMsg);
}
std::vector<std::string> testfold = FetchSelectedFoldsFromUI(layout->foldBox);
mitk::ModelParams modelObject = MapToRequest(modelName, taskName, trainer, planId, testfold);
requestQ.push_back(modelObject);
ppDirFolderNameParts << ppDirFolderName.join(QString(""));
}
tool->EnsembleOn();
if (isPPJson)
{
QString ppJsonFilePossibility1 = QDir::cleanPath(
m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + "ensembles" +
QDir::separator() + taskName + QDir::separator() + ppDirFolderNamePart1 + ppDirFolderNameParts.first() + "--" +
ppDirFolderNameParts.last() + QDir::separator() + "postprocessing.json");
QString ppJsonFilePossibility2 = QDir::cleanPath(
m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + "ensembles" +
QDir::separator() + taskName + QDir::separator() + ppDirFolderNamePart1 + ppDirFolderNameParts.last() + "--" +
ppDirFolderNameParts.first() + QDir::separator() + "postprocessing.json");
if (QFile(ppJsonFilePossibility1).exists())
{
tool->SetPostProcessingJsonDirectory(ppJsonFilePossibility1.toStdString());
const QString statusMsg = "<i>Post Processing JSON file found: </i>" + ppJsonFilePossibility1;
this->WriteStatusMessage(statusMsg);
}
else if (QFile(ppJsonFilePossibility2).exists())
{
tool->SetPostProcessingJsonDirectory(ppJsonFilePossibility2.toStdString());
const QString statusMsg = "<i>Post Processing JSON file found:</i>" + ppJsonFilePossibility2;
this->WriteStatusMessage(statusMsg);
}
else
{
std::string errorMsg =
"No post processing file was found for the selected ensemble combination. Continuing anyway...";
this->ShowErrorMessage(errorMsg);
}
}
tool->m_ParamQ.clear();
tool->m_ParamQ = requestQ;
}
void QmitknnUNetToolGUI::ProcessModelParams(mitk::nnUNetTool::Pointer tool)
{
tool->EnsembleOff();
std::vector<mitk::ModelParams> requestQ;
QString modelName = m_Controls.modelBox->currentText();
QString taskName = m_Controls.taskBox->currentText();
QString trainer = m_Controls.trainerBox->currentText();
QString planId = m_Controls.plannerBox->currentText();
std::vector<std::string> fetchedFolds = this->FetchSelectedFoldsFromUI(m_Controls.foldBox);
mitk::ModelParams modelObject = MapToRequest(modelName, taskName, trainer, planId, fetchedFolds);
requestQ.push_back(modelObject);
tool->m_ParamQ.clear();
tool->m_ParamQ = requestQ;
}
bool QmitknnUNetToolGUI::IsModelExists(const QString &modelName, const QString &taskName, const QString &trainerPlanner)
{
QString modelSearchPath =
QDir::cleanPath(m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + modelName +
QDir::separator() + taskName + QDir::separator() + trainerPlanner);
if (QDir(modelSearchPath).exists())
{
return true;
}
return false;
}
void QmitknnUNetToolGUI::CheckAllInCheckableComboBox(ctkCheckableComboBox *foldBox)
{
// Recalling all added items to check-mark it.
const QAbstractItemModel *qaim = foldBox->checkableModel();
auto rows = qaim->rowCount();
for (std::remove_const_t<decltype(rows)> i = 0; i < rows; ++i)
{
const QModelIndex mi = qaim->index(i, 0);
foldBox->setCheckState(mi, Qt::Checked);
}
}
std::pair<QStringList, QStringList> QmitknnUNetToolGUI::ExtractTrainerPlannerFromString(QStringList trainerPlanners)
{
QString splitterString = "__";
QStringList trainers, planners;
for (const auto &trainerPlanner : trainerPlanners)
{
trainers << trainerPlanner.split(splitterString, Qt::SkipEmptyParts).first();
planners << trainerPlanner.split(splitterString, Qt::SkipEmptyParts).last();
}
trainers.removeDuplicates();
planners.removeDuplicates();
return std::make_pair(trainers, planners);
}
std::vector<std::string> QmitknnUNetToolGUI::FetchSelectedFoldsFromUI(ctkCheckableComboBox *foldBox)
{
std::vector<std::string> folds;
if (foldBox->noneChecked())
{
this->CheckAllInCheckableComboBox(foldBox);
}
QModelIndexList foldList = foldBox->checkedIndexes();
for (const auto &index : foldList)
{
QString foldQString = foldBox->itemText(index.row());
if(foldQString != "dummy_element_that_nobody_can_see")
{
foldQString = foldQString.split("_", Qt::SkipEmptyParts).last();
folds.push_back(foldQString.toStdString());
}
else
{
throw std::runtime_error("Folds are not recognized. Please check if your nnUNet results folder structure is legitimate");
}
}
return folds;
}
void QmitknnUNetToolGUI::UpdateCacheCountOnUI()
{
QString cacheText = m_CACHE_COUNT_BASE_LABEL + QString::number(m_Cache.size());
m_Controls.cacheCountLabel->setText(cacheText);
}
void QmitknnUNetToolGUI::AddToCache(size_t &hashKey, mitk::LabelSetImage::ConstPointer mlPreview)
{
nnUNetCache *newCacheObj = new nnUNetCache;
newCacheObj->m_SegCache = mlPreview;
m_Cache.insert(hashKey, newCacheObj);
MITK_INFO << "New hash: " << hashKey << " " << newCacheObj->m_SegCache.GetPointer();
this->UpdateCacheCountOnUI();
}
void QmitknnUNetToolGUI::SetGPUInfo()
{
std::vector<QmitkGPUSpec> specs = m_GpuLoader.GetAllGPUSpecs();
for (const QmitkGPUSpec &gpuSpec : specs)
{
m_Controls.gpuComboBox->addItem(QString::number(gpuSpec.id) + ": " + gpuSpec.name + " (" + gpuSpec.memory + ")");
}
if (specs.empty())
{
m_Controls.gpuComboBox->setEditable(true);
m_Controls.gpuComboBox->addItem(QString::number(0));
m_Controls.gpuComboBox->setValidator(new QIntValidator(0, 999, this));
}
}
unsigned int QmitknnUNetToolGUI::FetchSelectedGPUFromUI()
{
QString gpuInfo = m_Controls.gpuComboBox->currentText();
if (m_GpuLoader.GetGPUCount() == 0)
{
return static_cast<unsigned int>(gpuInfo.toInt());
}
else
{
QString gpuId = gpuInfo.split(":", Qt::SkipEmptyParts).first();
return static_cast<unsigned int>(gpuId.toInt());
}
}
QString QmitknnUNetToolGUI::FetchResultsFolderFromEnv()
{
const char *pathVal = itksys::SystemTools::GetEnv("RESULTS_FOLDER");
QString retVal;
if (pathVal)
{
retVal = QString::fromUtf8(pathVal);
}
else
{
retVal = m_Settings.value("nnUNet/LastRESULTS_FOLDERPath").toString();
}
return retVal;
}
void QmitknnUNetToolGUI::DumpJSONfromPickle(const QString &picklePath)
{
const QString pickleFile = picklePath + QDir::separator() + m_PICKLE_FILENAME;
const QString jsonFile = picklePath + QDir::separator() + m_MITK_EXPORT_JSON_FILENAME;
if (!QFile::exists(jsonFile))
{
mitk::ProcessExecutor::Pointer spExec = mitk::ProcessExecutor::New();
mitk::ProcessExecutor::ArgumentListType args;
args.push_back("-c");
std::string pythonCode; // python syntax to parse plans.pkl file and export as Json file.
pythonCode.append("import pickle;");
pythonCode.append("import json;");
pythonCode.append("loaded_pickle = pickle.load(open('");
pythonCode.append(pickleFile.toStdString());
pythonCode.append("','rb'));");
pythonCode.append("modal_dict = {key: loaded_pickle[key] for key in loaded_pickle.keys() if key in "
"['modalities','num_modalities']};");
pythonCode.append("json.dump(modal_dict, open('");
pythonCode.append(jsonFile.toStdString());
pythonCode.append("', 'w'))");
args.push_back(pythonCode);
try
{
spExec->Execute(m_PythonPath.toStdString(), "python3", args);
}
catch (const mitk::Exception &e)
{
MITK_ERROR << "Pickle parsing FAILED!" << e.GetDescription();
this->WriteStatusMessage(
"Parsing failed in backend. Multiple Modalities will now have to be manually entered by the user.");
}
}
}
void QmitknnUNetToolGUI::ExportAvailableModelsAsJSON(const QString &resultsFolder)
{
const QString jsonPath = resultsFolder + QDir::separator() + m_AVAILABLE_MODELS_JSON_FILENAME;
if (!QFile::exists(jsonPath))
{
auto spExec = mitk::ProcessExecutor::New();
mitk::ProcessExecutor::ArgumentListType args;
args.push_back("--export");
args.push_back(resultsFolder.toStdString());
try
{
spExec->Execute(m_PythonPath.toStdString(), "nnUNet_print_available_pretrained_models", args);
}
catch (const mitk::Exception &e)
{
MITK_ERROR << "Exporting information FAILED." << e.GetDescription();
this->WriteStatusMessage("Exporting information FAILED.");
}
}
}
void QmitknnUNetToolGUI::DisplayMultiModalInfoFromJSON(const QString &jsonPath)
{
std::ifstream file(jsonPath.toStdString());
if (file.is_open())
{
auto jsonObj = nlohmann::json::parse(file, nullptr, false);
if (jsonObj.is_discarded() || !jsonObj.is_object())
{
MITK_ERROR << "Could not parse \"" << jsonPath.toStdString() << "\" as JSON object!";
return;
}
auto num_mods = jsonObj["num_modalities"].get<int>();
this->ClearAllModalLabels();
if (num_mods > 1)
{
m_Controls.multiModalBox->setChecked(true);
m_Controls.multiModalBox->setEnabled(false);
m_Controls.multiModalValueLabel->setText(QString::number(num_mods));
OnModalitiesNumberChanged(num_mods);
m_Controls.advancedSettingsLayout->update();
auto obj = jsonObj["modalities"];
int count = 0;
for (const auto &value : obj)
{
QLabel *label = new QLabel(QString::fromStdString("<i>" + value.get<std::string>() + "</i>"), this);
m_ModalLabels.push_back(label);
m_Controls.advancedSettingsLayout->addWidget(label, m_UI_ROWS + 1 + count, 0);
count++;
}
m_Controls.advancedSettingsLayout->update();
}
else
{
m_Controls.multiModalBox->setChecked(false);
}
}
}
void QmitknnUNetToolGUI::FillAvailableModelsInfoFromJSON(const QString &jsonPath)
{
std::ifstream file(jsonPath.toStdString());
if (file.is_open() && m_Controls.availableBox->count() < 1)
{
auto jsonObj = nlohmann::json::parse(file, nullptr, false);
if (jsonObj.is_discarded() || !jsonObj.is_object())
{
MITK_ERROR << "Could not parse \"" << jsonPath.toStdString() << "\" as JSON object!";
return;
}
for (const auto &obj : jsonObj.items())
{
m_Controls.availableBox->addItem(QString::fromStdString(obj.key()));
}
}
}
mitk::ModelParams QmitknnUNetToolGUI::MapToRequest(const QString &modelName,
const QString &taskName,
const QString &trainer,
const QString &planId,
const std::vector<std::string> &folds)
{
mitk::ModelParams requestObject;
requestObject.model = modelName.toStdString();
requestObject.trainer = trainer.toStdString();
requestObject.planId = planId.toStdString();
requestObject.task = taskName.toStdString();
requestObject.folds = folds;
mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs<mitk::nnUNetTool>();
requestObject.inputName = tool->GetRefNode()->GetName();
requestObject.timeStamp =
std::to_string(mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint());
return requestObject;
}
void QmitknnUNetToolGUI::SetComboBoxToNone(ctkCheckableComboBox* comboBox)
{
comboBox->clear();
comboBox->addItem("dummy_element_that_nobody_can_see");
qobject_cast<QListView *>(comboBox->view())->setRowHidden(0, true); // For the cosmetic purpose of showing "None" on the combobox.
}
/* ---------------------SLOTS---------------------------------------*/
void QmitknnUNetToolGUI::OnPreviewRequested()
{
mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs<mitk::nnUNetTool>();
if (nullptr != tool)
{
QString pythonPathTextItem = "";
try
{
size_t hashKey(0);
m_Controls.previewButton->setEnabled(false); // To prevent misclicked back2back prediction.
qApp->processEvents();
tool->PredictOn(); // purposefully placed to make tool->GetMTime different than before.
QString modelName = m_Controls.modelBox->currentText();
if (modelName.startsWith("ensemble", Qt::CaseInsensitive))
{
this->ProcessEnsembleModelsParams(tool);
}
else
{
this->ProcessModelParams(tool);
}
pythonPathTextItem = m_Controls.pythonEnvComboBox->currentText();
QString pythonPath = m_PythonPath;
if (!this->IsNNUNetInstalled(pythonPath))
{
throw std::runtime_error("nnUNet is not detected in the selected python environment. Please select a valid "
"python environment or install nnUNet.");
}
tool->SetPythonPath(pythonPath.toStdString());
tool->SetModelDirectory(m_ParentFolder->getResultsFolder().toStdString());
// checkboxes
tool->SetMirror(m_Controls.mirrorBox->isChecked());
tool->SetMixedPrecision(m_Controls.mixedPrecisionBox->isChecked());
tool->SetNoPip(false);
bool doCache = m_Controls.enableCachingCheckBox->isChecked();
// Spinboxes
tool->SetGpuId(FetchSelectedGPUFromUI());
// Multi-Modal
tool->MultiModalOff();
if (m_Controls.multiModalBox->isChecked())
{
tool->m_OtherModalPaths.clear();
tool->m_OtherModalPaths = FetchMultiModalImagesFromUI();
tool->MultiModalOn();
}
if (doCache)
{
hashKey = nnUNetCache::GetUniqueHash(tool->m_ParamQ);
if (m_Cache.contains(hashKey))
{
tool->PredictOff(); // purposefully placed to make tool->GetMTime different than before.
}
}
if (tool->GetPredict())
{
tool->m_InputBuffer = nullptr;
this->WriteStatusMessage(
QString("<b>STATUS: </b><i>Starting Segmentation task... This might take a while.</i>"));
m_FirstPreviewComputation = false;
tool->UpdatePreview();
if (nullptr == tool->GetOutputBuffer())
{
this->SegmentationProcessFailed();
}
else
{
this->SegmentationResultHandler(tool);
if (doCache)
{
this->AddToCache(hashKey, tool->GetOutputBuffer());
}
tool->ClearOutputBuffer();
}
tool->PredictOff(); // purposefully placed to make tool->GetMTime different than before.
}
else
{
MITK_INFO << "won't do segmentation. Key found: " << QString::number(hashKey).toStdString();
if (m_Cache.contains(hashKey))
{
nnUNetCache *cacheObject = m_Cache[hashKey];
MITK_INFO << "fetched pointer " << cacheObject->m_SegCache.GetPointer();
tool->SetOutputBuffer(const_cast<mitk::LabelSetImage *>(cacheObject->m_SegCache.GetPointer()));
this->SegmentationResultHandler(tool, true);
}
}
m_Controls.previewButton->setEnabled(true);
}
catch (const std::exception &e)
{
std::stringstream errorMsg;
errorMsg << "<b>STATUS: </b>Error while processing parameters for nnUNet segmentation. Reason: " << e.what();
this->ShowErrorMessage(errorMsg.str());
this->WriteErrorMessage(QString::fromStdString(errorMsg.str()));
m_Controls.previewButton->setEnabled(true);
tool->PredictOff();
m_FirstPreviewComputation = true;
return;
}
catch (...)
{
- std::string errorMsg = "Unkown error occured while generation nnUNet segmentation.";
+ std::string errorMsg = "Unknown error occurred while generation nnUNet segmentation.";
this->ShowErrorMessage(errorMsg);
m_Controls.previewButton->setEnabled(true);
tool->PredictOff();
m_FirstPreviewComputation = true;
return;
}
if (!pythonPathTextItem.isEmpty())
{ // only cache if the prediction ended without errors.
m_Settings.setValue("nnUNet/LastPythonPath", pythonPathTextItem);
}
}
}
void QmitknnUNetToolGUI::OnRefreshPresssed()
{
const QString resultsFolder = m_Controls.modeldirectoryBox->directory();
this->OnDirectoryChanged(resultsFolder);
}
void QmitknnUNetToolGUI::OnDirectoryChanged(const QString &resultsFolder)
{
m_IsResultsFolderValid = false;
m_Controls.previewButton->setEnabled(false);
this->ClearAllComboBoxes();
this->ClearAllModalities();
m_ParentFolder = std::make_shared<QmitknnUNetFolderParser>(resultsFolder);
auto tasks = m_ParentFolder->getAllTasks<QStringList>();
tasks.removeDuplicates();
std::for_each(tasks.begin(), tasks.end(), [this](QString task) { m_Controls.taskBox->addItem(task); });
m_Settings.setValue("nnUNet/LastRESULTS_FOLDERPath", resultsFolder);
}
void QmitknnUNetToolGUI::OnModelChanged(const QString &model)
{
if (model.isEmpty())
{
return;
}
this->ClearAllModalities();
auto selectedTask = m_Controls.taskBox->currentText();
ctkComboBox *box = qobject_cast<ctkComboBox *>(sender());
if (box == m_Controls.modelBox)
{
if (model == m_VALID_MODELS.last())
{
m_Controls.trainerBox->setVisible(false);
m_Controls.trainerLabel->setVisible(false);
m_Controls.plannerBox->setVisible(false);
m_Controls.plannerLabel->setVisible(false);
m_Controls.foldBox->setVisible(false);
m_Controls.foldLabel->setVisible(false);
m_Controls.previewButton->setEnabled(false);
this->ShowEnsembleLayout(true);
auto models = m_ParentFolder->getModelsForTask<QStringList>(m_Controls.taskBox->currentText());
models.removeDuplicates();
models.removeOne(m_VALID_MODELS.last());
for (auto &layout : m_EnsembleParams)
{
layout->modelBox->clear();
layout->trainerBox->clear();
layout->plannerBox->clear();
std::for_each(models.begin(),
models.end(),
[&layout, this](QString model)
{
if (m_VALID_MODELS.contains(model, Qt::CaseInsensitive))
layout->modelBox->addItem(model);
});
}
}
else
{
m_Controls.trainerBox->setVisible(true);
m_Controls.trainerLabel->setVisible(true);
m_Controls.plannerBox->setVisible(true);
m_Controls.plannerLabel->setVisible(true);
m_Controls.foldBox->setVisible(true);
m_Controls.foldLabel->setVisible(true);
m_Controls.previewButton->setEnabled(false);
this->ShowEnsembleLayout(false);
m_Controls.trainerBox->clear();
m_Controls.plannerBox->clear();
auto trainerPlanners = m_ParentFolder->getTrainerPlannersForTask<QStringList>(selectedTask, model);
if(trainerPlanners.isEmpty())
{
this->ShowErrorMessage("No plans.pkl found for "+model.toStdString()+". Check your directory or download the task again.");
this->SetComboBoxToNone(m_Controls.foldBox);
return;
}
QStringList trainers, planners;
std::tie(trainers, planners) = ExtractTrainerPlannerFromString(trainerPlanners);
std::for_each(
trainers.begin(), trainers.end(), [this](QString trainer) { m_Controls.trainerBox->addItem(trainer); });
std::for_each(
planners.begin(), planners.end(), [this](QString planner) { m_Controls.plannerBox->addItem(planner); });
}
}
else if (!m_EnsembleParams.empty())
{
m_Controls.previewButton->setEnabled(false);
for (auto &layout : m_EnsembleParams)
{
if (box == layout->modelBox)
{
layout->trainerBox->clear();
layout->plannerBox->clear();
auto trainerPlanners = m_ParentFolder->getTrainerPlannersForTask<QStringList>(selectedTask, model);
if(trainerPlanners.isEmpty())
{
this->ShowErrorMessage("No plans.pkl found for "+model.toStdString()+". Check your directory or download the task again.");
this->SetComboBoxToNone(layout->foldBox);
return;
}
QStringList trainers, planners;
std::tie(trainers, planners) = ExtractTrainerPlannerFromString(trainerPlanners);
std::for_each(trainers.begin(),
trainers.end(),
[&layout](const QString &trainer) { layout->trainerBox->addItem(trainer); });
std::for_each(planners.begin(),
planners.end(),
[&layout](const QString &planner) { layout->plannerBox->addItem(planner); });
break;
}
}
}
}
void QmitknnUNetToolGUI::OnTaskChanged(const QString &task)
{
if (task.isEmpty())
{
return;
}
m_Controls.modelBox->clear();
auto models = m_ParentFolder->getModelsForTask<QStringList>(task);
models.removeDuplicates();
if (!models.contains(m_VALID_MODELS.last(), Qt::CaseInsensitive))
{
models << m_VALID_MODELS.last(); // add ensemble even if folder doesn't exist
}
std::for_each(models.begin(),
models.end(),
[this](QString model)
{
if (m_VALID_MODELS.contains(model, Qt::CaseInsensitive))
m_Controls.modelBox->addItem(model);
});
}
void QmitknnUNetToolGUI::OnTrainerChanged(const QString &plannerSelected)
{
if (plannerSelected.isEmpty())
{
return;
}
m_IsResultsFolderValid = false;
QString parentPath;
auto *box = qobject_cast<ctkComboBox *>(sender());
if (box == m_Controls.plannerBox)
{
m_Controls.foldBox->clear();
auto selectedTrainer = m_Controls.trainerBox->currentText();
auto selectedTask = m_Controls.taskBox->currentText();
auto selectedModel = m_Controls.modelBox->currentText();
auto folds = m_ParentFolder->getFoldsForTrainerPlanner<QStringList>(
selectedTrainer, plannerSelected, selectedTask, selectedModel);
if(folds.isEmpty())
{
this->ShowErrorMessage("No valid folds found. Check your directory or download the task again.");
this->SetComboBoxToNone(m_Controls.foldBox);
return;
}
std::for_each(folds.begin(),
folds.end(),
[this](QString fold)
{
if (fold.startsWith("fold_", Qt::CaseInsensitive)) // imposed by nnUNet
m_Controls.foldBox->addItem(fold);
});
if (m_Controls.foldBox->count() != 0)
{
m_IsResultsFolderValid = true;
this->CheckAllInCheckableComboBox(m_Controls.foldBox);
auto tempPath = QStringList() << m_ParentFolder->getResultsFolder() << "nnUNet" << selectedModel << selectedTask
<< QString("%1__%2").arg(selectedTrainer, plannerSelected);
parentPath = QDir::cleanPath(tempPath.join(QDir::separator()));
}
}
else if (!m_EnsembleParams.empty())
{
for (auto &layout : m_EnsembleParams)
{
if (box == layout->plannerBox)
{
layout->foldBox->clear();
auto selectedTrainer = layout->trainerBox->currentText();
auto selectedTask = m_Controls.taskBox->currentText();
auto selectedModel = layout->modelBox->currentText();
auto folds = m_ParentFolder->getFoldsForTrainerPlanner<QStringList>(
selectedTrainer, plannerSelected, selectedTask, selectedModel);
if(folds.isEmpty())
{
this->ShowErrorMessage("No valid folds found. Check your directory.");
this->SetComboBoxToNone(layout->foldBox);
return;
}
std::for_each(folds.begin(),
folds.end(),
[&layout](const QString &fold)
{
if (fold.startsWith("fold_", Qt::CaseInsensitive)) // imposed by nnUNet
layout->foldBox->addItem(fold);
});
if (layout->foldBox->count() != 0)
{
this->CheckAllInCheckableComboBox(layout->foldBox);
m_IsResultsFolderValid = true;
auto tempPath = QStringList() << m_ParentFolder->getResultsFolder() << "nnUNet" << selectedModel
<< selectedTask << QString("%1__%2").arg(selectedTrainer, plannerSelected);
parentPath = QDir::cleanPath(tempPath.join(QDir::separator()));
}
break;
}
}
}
if (m_IsResultsFolderValid)
{
m_Controls.previewButton->setEnabled(true);
const QString mitkJsonFile = parentPath + QDir::separator() + m_MITK_EXPORT_JSON_FILENAME;
this->DumpJSONfromPickle(parentPath);
if (QFile::exists(mitkJsonFile))
{
this->DisplayMultiModalInfoFromJSON(mitkJsonFile);
}
}
}
void QmitknnUNetToolGUI::OnPythonPathChanged(const QString &pyEnv)
{
if (pyEnv == QString("Select"))
{
QString path =
QFileDialog::getExistingDirectory(m_Controls.pythonEnvComboBox->parentWidget(), "Python Path", "dir");
if (!path.isEmpty())
{
this->OnPythonPathChanged(path); // recall same function for new path validation
m_Controls.pythonEnvComboBox->insertItem(0, path);
m_Controls.pythonEnvComboBox->setCurrentIndex(0);
}
}
else if (!this->IsNNUNetInstalled(pyEnv))
{
std::string warning =
"WARNING: nnUNet is not detected on the Python environment you selected. Please select another "
"environment or create one. For more info refer https://github.com/MIC-DKFZ/nnUNet";
this->ShowErrorMessage(warning);
this->DisableEverything();
m_Controls.availableBox->clear();
}
else
{
m_Controls.modeldirectoryBox->setEnabled(true);
m_Controls.refreshdirectoryBox->setEnabled(true);
m_Controls.multiModalBox->setEnabled(true);
QString setVal = this->FetchResultsFolderFromEnv();
if (!setVal.isEmpty())
{
m_Controls.modeldirectoryBox->setDirectory(setVal);
}
this->OnRefreshPresssed();
m_PythonPath = pyEnv.mid(pyEnv.indexOf(" ") + 1);
#ifdef _WIN32
if (!(m_PythonPath.endsWith("Scripts", Qt::CaseInsensitive) || m_PythonPath.endsWith("Scripts/", Qt::CaseInsensitive)))
{
m_PythonPath += QDir::separator() + QString("Scripts");
}
#else
if (!(m_PythonPath.endsWith("bin", Qt::CaseInsensitive) || m_PythonPath.endsWith("bin/", Qt::CaseInsensitive)))
{
m_PythonPath += QDir::separator() + QString("bin");
}
#endif
// Export available model info as json and fill them for Download
QString tempPath = QString::fromStdString(mitk::IOUtil::GetTempPath());
this->ExportAvailableModelsAsJSON(tempPath);
const QString jsonPath = tempPath + QDir::separator() + m_AVAILABLE_MODELS_JSON_FILENAME;
if (QFile::exists(jsonPath))
{
this->FillAvailableModelsInfoFromJSON(jsonPath);
}
}
}
void QmitknnUNetToolGUI::OnCheckBoxChanged(int state)
{
bool visibility = false;
if (state == Qt::Checked)
{
visibility = true;
}
ctkCheckBox *box = qobject_cast<ctkCheckBox *>(sender());
if (box != nullptr)
{
if (box->objectName() == QString("multiModalBox"))
{
m_Controls.requiredModalitiesLabel->setVisible(visibility);
m_Controls.multiModalValueLabel->setVisible(visibility);
if (!visibility)
{
this->OnModalitiesNumberChanged(0);
m_Controls.multiModalValueLabel->setText("0");
this->ClearAllModalLabels();
}
}
}
}
void QmitknnUNetToolGUI::OnModalitiesNumberChanged(int num)
{
while (num > static_cast<int>(m_Modalities.size()))
{
QmitkSingleNodeSelectionWidget *multiModalBox = new QmitkSingleNodeSelectionWidget(this);
mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs<mitk::nnUNetTool>();
multiModalBox->SetDataStorage(tool->GetDataStorage());
multiModalBox->SetInvalidInfo("Select corresponding modalities");
multiModalBox->SetNodePredicate(m_MultiModalPredicate);
multiModalBox->setObjectName(QString("multiModal_" + QString::number(m_Modalities.size() + 1)));
m_Controls.advancedSettingsLayout->addWidget(multiModalBox, m_UI_ROWS + m_Modalities.size() + 1, 1, 1, 3);
m_Modalities.push_back(multiModalBox);
}
while (num < static_cast<int>(m_Modalities.size()) && !m_Modalities.empty())
{
QmitkSingleNodeSelectionWidget *child = m_Modalities.back();
delete child; // delete the layout item
m_Modalities.pop_back();
}
m_Controls.advancedSettingsLayout->update();
}
void QmitknnUNetToolGUI::AutoParsePythonPaths()
{
QString homeDir = QDir::homePath();
std::vector<QString> searchDirs;
#ifdef _WIN32
searchDirs.push_back(QString("C:") + QDir::separator() + QString("ProgramData") + QDir::separator() +
QString("anaconda3"));
#else
// Add search locations for possible standard python paths here
searchDirs.push_back(homeDir + QDir::separator() + "environments");
searchDirs.push_back(homeDir + QDir::separator() + "anaconda3");
searchDirs.push_back(homeDir + QDir::separator() + "miniconda3");
searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "miniconda3");
searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "anaconda3");
#endif
for (QString searchDir : searchDirs)
{
if (searchDir.endsWith("anaconda3", Qt::CaseInsensitive))
{
if (QDir(searchDir).exists())
{
m_Controls.pythonEnvComboBox->insertItem(0, "(base): " + searchDir);
searchDir.append((QDir::separator() + QString("envs")));
}
}
for (QDirIterator subIt(searchDir, QDir::AllDirs, QDirIterator::NoIteratorFlags); subIt.hasNext();)
{
subIt.next();
QString envName = subIt.fileName();
- if (!envName.startsWith('.')) // Filter out irrelevent hidden folders, if any.
+ if (!envName.startsWith('.')) // Filter out irrelevant hidden folders, if any.
{
m_Controls.pythonEnvComboBox->insertItem(0, "(" + envName + "): " + subIt.filePath());
}
}
}
m_Controls.pythonEnvComboBox->setCurrentIndex(-1);
}
void QmitknnUNetToolGUI::SegmentationProcessFailed()
{
m_FirstPreviewComputation = true;
this->WriteErrorMessage(
"<b>STATUS: </b><i>Error in the segmentation process. <br>No resulting segmentation can be loaded.</i>");
this->setCursor(Qt::ArrowCursor);
std::stringstream stream;
stream << "Error in the segmentation process. No resulting segmentation can be loaded.";
this->ShowErrorMessage(stream.str());
}
void QmitknnUNetToolGUI::SegmentationResultHandler(mitk::nnUNetTool *tool, bool forceRender)
{
if (forceRender)
{
tool->RenderOutputBuffer();
}
this->SetLabelSetPreview(tool->GetPreviewSegmentation());
this->WriteStatusMessage("<b>STATUS: </b><i>Segmentation task finished successfully.</i>");
this->ActualizePreviewLabelVisibility();
}
void QmitknnUNetToolGUI::ShowEnsembleLayout(bool visible)
{
if (m_EnsembleParams.empty())
{
ctkCollapsibleGroupBox *groupBoxModel1 = new ctkCollapsibleGroupBox(this);
auto lay1 = std::make_unique<QmitknnUNetTaskParamsUITemplate>(groupBoxModel1);
groupBoxModel1->setObjectName(QString::fromUtf8("model_1_Box"));
groupBoxModel1->setTitle(QString::fromUtf8("Model 1"));
groupBoxModel1->setMinimumSize(QSize(0, 0));
groupBoxModel1->setCollapsedHeight(5);
groupBoxModel1->setCollapsed(false);
groupBoxModel1->setFlat(true);
groupBoxModel1->setAlignment(Qt::AlignRight);
m_Controls.advancedSettingsLayout->addWidget(groupBoxModel1, 5, 0, 1, 2);
connect(lay1->modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &)));
connect(
lay1->plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &)));
m_EnsembleParams.push_back(std::move(lay1));
ctkCollapsibleGroupBox *groupBoxModel2 = new ctkCollapsibleGroupBox(this);
auto lay2 = std::make_unique<QmitknnUNetTaskParamsUITemplate>(groupBoxModel2);
groupBoxModel2->setObjectName(QString::fromUtf8("model_2_Box"));
groupBoxModel2->setTitle(QString::fromUtf8("Model 2"));
groupBoxModel2->setMinimumSize(QSize(0, 0));
groupBoxModel2->setCollapsedHeight(5);
groupBoxModel2->setCollapsed(false);
groupBoxModel2->setFlat(true);
groupBoxModel2->setAlignment(Qt::AlignLeft);
m_Controls.advancedSettingsLayout->addWidget(groupBoxModel2, 5, 2, 1, 2);
connect(lay2->modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &)));
connect(
lay2->plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &)));
m_EnsembleParams.push_back(std::move(lay2));
}
for (auto &layout : m_EnsembleParams)
{
layout->setVisible(visible);
}
}
void QmitknnUNetToolGUI::OnDownloadModel()
{
auto selectedTask = m_Controls.availableBox->currentText();
if(!selectedTask.isEmpty())
{
auto spExec = mitk::ProcessExecutor::New();
mitk::ProcessExecutor::ArgumentListType args;
args.push_back(selectedTask.toStdString());
this->WriteStatusMessage(
"Downloading the requested task in to the selected Results Folder. This might take some time "
"depending on your internet connection...");
m_Processes["DOWNLOAD"] = spExec;
if (!m_nnUNetThread->isRunning())
{
MITK_DEBUG << "Starting thread...";
m_nnUNetThread->start();
}
QString resultsFolder = m_ParentFolder->getResultsFolder();
emit Operate(resultsFolder, m_PythonPath, spExec, args);
m_Controls.stopDownloadButton->setVisible(true);
m_Controls.startDownloadButton->setVisible(false);
}
}
void QmitknnUNetToolGUI::OnDownloadWorkerExit(const bool isSuccess, const QString message)
{
if (isSuccess)
{
this->WriteStatusMessage(message + QString(" Click Refresh Results Folder to use the new Task."));
}
else
{
MITK_ERROR << "Download FAILED! " << message.toStdString();
this->WriteStatusMessage(QString("Download failed. Check your internet connection. " + message));
}
m_Controls.stopDownloadButton->setVisible(false);
m_Controls.startDownloadButton->setVisible(true);
}
void QmitknnUNetToolGUI::OnStopDownload()
{
mitk::ProcessExecutor::Pointer spExec = m_Processes["DOWNLOAD"];
spExec->KillProcess();
this->WriteStatusMessage("Download Killed by the user.");
m_Controls.stopDownloadButton->setVisible(false);
m_Controls.startDownloadButton->setVisible(true);
}
void QmitknnUNetToolGUI::OnClearCachePressed()
{
m_Cache.clear();
this->UpdateCacheCountOnUI();
}
diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h
index 0d4f016f35..1af7ce8032 100644
--- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h
+++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h
@@ -1,405 +1,405 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitknnUNetToolGUI_h
#define QmitknnUNetToolGUI_h
#include "QmitkMultiLabelSegWithPreviewToolGUIBase.h"
#include "QmitknnUNetFolderParser.h"
#include "QmitknnUNetGPU.h"
#include "QmitknnUNetWorker.h"
#include "mitkProcessExecutor.h"
#include "mitknnUnetTool.h"
#include "ui_QmitknnUNetToolGUIControls.h"
#include <MitkSegmentationUIExports.h>
#include <QCache>
#include <QMessageBox>
#include <QSettings>
#include <QThread>
#include <QmitkSingleNodeSelectionWidget.h>
#include <QmitknnUNetEnsembleLayout.h>
#include <boost/functional/hash.hpp>
#include <unordered_map>
class nnUNetCache
{
public:
mitk::LabelSetImage::ConstPointer m_SegCache;
static size_t GetUniqueHash(std::vector<mitk::ModelParams> &requestQ)
{
size_t hashCode = 0;
for (mitk::ModelParams &request : requestQ)
{
boost::hash_combine(hashCode, request.generateHash());
}
return hashCode;
}
};
class MITKSEGMENTATIONUI_EXPORT QmitknnUNetToolGUI : public QmitkMultiLabelSegWithPreviewToolGUIBase
{
Q_OBJECT
public:
mitkClassMacro(QmitknnUNetToolGUI, QmitkMultiLabelSegWithPreviewToolGUIBase);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
QCache<size_t, nnUNetCache> m_Cache;
/**
* @brief The hash map stores all bifurcating processes' ID.
*
*/
std::unordered_map<std::string, mitk::ProcessExecutor::Pointer> m_Processes;
protected slots:
/**
* @brief Qt slot
*
*/
void OnPreviewRequested();
/**
* @brief Qt slot
*
*/
void OnDirectoryChanged(const QString&);
/**
* @brief Qt slot
*
*/
void OnModelChanged(const QString&);
/**
* @brief Qt slot
*
*/
void OnTaskChanged(const QString &);
/**
* @brief Qt slot
*
*/
void OnTrainerChanged(const QString&);
/**
* @brief Qt slot
*
*/
void OnCheckBoxChanged(int);
/**
* @brief Qthread slot to capture failures from thread worker and
* shows error message
*
*/
void SegmentationProcessFailed();
/**
- * @brief Qthread to capture sucessfull nnUNet segmentation.
+ * @brief Qthread to capture successful nnUNet segmentation.
* Further, renders the LabelSet image
*/
void SegmentationResultHandler(mitk::nnUNetTool *, bool forceRender = false);
/**
* @brief Qt Slot
*
*/
void OnModalitiesNumberChanged(int);
/**
* @brief Qt Slot
*
*/
void OnPythonPathChanged(const QString&);
/**
* @brief Qt slot
*
*/
void OnRefreshPresssed();
/**
* @brief Qt slot
*
*/
void OnClearCachePressed();
/**
* @brief Qt slot
*
*/
void OnDownloadModel();
/**
* @brief Qt slot
*
*/
void OnDownloadWorkerExit(bool, const QString);
/**
* @brief Qt slot
*
*/
void OnStopDownload();
signals:
/**
* @brief signal for starting the segmentation which is caught by a worker thread.
*/
void Operate(QString, QString, mitk::ProcessExecutor::Pointer, mitk::ProcessExecutor::ArgumentListType);
protected:
QmitknnUNetToolGUI();
~QmitknnUNetToolGUI();
void ConnectNewTool(mitk::SegWithPreviewTool* newTool) override;
void InitializeUI(QBoxLayout* mainLayout) override;
void EnableWidgets(bool enabled) override;
private:
/**
* @brief Adds an element and hides it so that the ctkCheckableComboBox shows "None".
*/
void SetComboBoxToNone(ctkCheckableComboBox*);
/**
* @brief Parses the available_models.json file from RESULTS_FOLDER and loads
* the task names to the Download combobox in Advanced.
*/
void FillAvailableModelsInfoFromJSON(const QString&);
/**
* @brief Exports available models to download from nnUNet_print_available_pretrained_models
* output.
*/
void ExportAvailableModelsAsJSON(const QString&);
/**
* @brief Clears all displayed modal labels and widgets from GUI.
*
*/
void ClearAllModalities();
/**
* @brief Parses Json file containing modality info and populates
* labels and selection widgets accordingly on the GUI.
*/
void DisplayMultiModalInfoFromJSON(const QString&);
/**
* @brief Clears all modality labels previously populated from GUI.
*
*/
void ClearAllModalLabels();
/**
* @brief Runs a set of python commands to read "plans.pkl" and extract
* modality information required for inferencing. This information is exported
* as json file : "mitk_export.json".
*
* @return QString
*/
void DumpJSONfromPickle(const QString&);
/**
* @brief Searches RESULTS_FOLDER environment variable. If not found,
* returns from the QSettings stored last used path value.
* @return QString
*/
QString FetchResultsFolderFromEnv();
/**
* @brief Returns GPU id of the selected GPU from the Combo box.
*
* @return unsigned int
*/
unsigned int FetchSelectedGPUFromUI();
/**
* @brief Adds GPU information to the gpu combo box.
- * In case, there aren't any GPUs avaialble, the combo box will be
+ * In case, there aren't any GPUs available, the combo box will be
* rendered editable.
*/
void SetGPUInfo();
/**
* @brief Inserts the hash and segmentation into cache and
* updates count on UI.
*/
void AddToCache(size_t&, mitk::LabelSetImage::ConstPointer);
/**
* @brief Checks all the entries of the ctkCheckableComboBox ui widget.
* This feature is not present in ctkCheckableComboBox API.
*/
void CheckAllInCheckableComboBox(ctkCheckableComboBox*);
/**
* @brief Parses the folder names containing trainer and planner together and,
* returns it as separate lists.
* @return std::pair<QStringList, QStringList>
*/
std::pair<QStringList, QStringList> ExtractTrainerPlannerFromString(QStringList);
/**
* @brief Parses the ensemble UI elements and sets to nnUNetTool object pointer.
*
*/
void ProcessEnsembleModelsParams(mitk::nnUNetTool::Pointer);
/**
* @brief Parses the UI elements and sets to nnUNetTool object pointer.
*
*/
void ProcessModelParams(mitk::nnUNetTool::Pointer);
/**
* @brief Creates and renders QmitknnUNetTaskParamsUITemplate layout for ensemble input.
*/
void ShowEnsembleLayout(bool visible = true);
/**
* @brief Creates a QMessage object and shows on screen.
*/
void ShowErrorMessage(const std::string&, QMessageBox::Icon = QMessageBox::Critical);
/**
* @brief Writes any message in white on the tool pane.
*/
void WriteStatusMessage(const QString&);
/**
* @brief Writes any message in red on the tool pane.
*/
void WriteErrorMessage(const QString&);
/**
- * @brief Searches and parses paths of python virtual enviroments
+ * @brief Searches and parses paths of python virtual environments
* from predefined lookout locations
*/
void AutoParsePythonPaths();
/**
* @brief Check if pretrained model sub folder inside RESULTS FOLDER exist.
*/
bool IsModelExists(const QString&, const QString&, const QString&);
/**
* @brief Clears all combo boxes
* Any new combo box added in the future can be featured here for clearance.
*
*/
void ClearAllComboBoxes();
/**
* @brief Disable/deactivates the nnUNet GUI.
* Clears any multi modal labels and selection widgets, as well.
*/
void DisableEverything();
/**
* @brief Checks if nnUNet_predict command is valid in the selected python virtual environment.
*
* @return bool
*/
bool IsNNUNetInstalled(const QString&);
/**
* @brief Mapper function to map QString entries from UI to ModelParam attributes.
*
* @return mitk::ModelParams
*/
mitk::ModelParams MapToRequest(
const QString&, const QString&, const QString&, const QString&, const std::vector<std::string>&);
/**
* @brief Returns checked fold names from the ctk-Checkable-ComboBox.
*
* @return std::vector<std::string>
*/
std::vector<std::string> FetchSelectedFoldsFromUI(ctkCheckableComboBox*);
/**
* @brief Returns all paths from the dynamically generated ctk-path-line-edit boxes.
*
* @return std::vector<std::string>
*/
std::vector<mitk::Image::ConstPointer> FetchMultiModalImagesFromUI();
/**
* @brief Updates cache count on UI.
*
*/
void UpdateCacheCountOnUI();
Ui_QmitknnUNetToolGUIControls m_Controls;
QmitkGPULoader m_GpuLoader;
/**
* @brief Stores all dynamically added ctk-path-line-edit UI elements.
*
*/
std::vector<QmitkSingleNodeSelectionWidget*> m_Modalities;
std::vector<QLabel*> m_ModalLabels;
std::vector<std::unique_ptr<QmitknnUNetTaskParamsUITemplate>> m_EnsembleParams;
mitk::NodePredicateBase::Pointer m_MultiModalPredicate;
QString m_PythonPath;
/**
* @brief Stores row count of the "advancedSettingsLayout" layout element. This value helps dynamically add
* ctk-path-line-edit UI elements at the right place. Forced to initialize in the InitializeUI method since there is
* no guarantee of retrieving exact row count anywhere else.
*
*/
int m_UI_ROWS;
/**
* @brief Stores path of the model director (RESULTS_FOLDER appended by "nnUNet").
*
*/
std::shared_ptr<QmitknnUNetFolderParser> m_ParentFolder = nullptr;
/**
* @brief Valid list of models supported by nnUNet
*
*/
const QStringList m_VALID_MODELS = {"2d", "3d_lowres", "3d_fullres", "3d_cascade_fullres", "ensembles"};
const QString m_CACHE_COUNT_BASE_LABEL = "Cached Items: ";
const QString m_MITK_EXPORT_JSON_FILENAME = "mitk_export.json";
const QString m_AVAILABLE_MODELS_JSON_FILENAME = "available_models.json";
const QString m_PICKLE_FILENAME = "plans.pkl";
/**
* @brief For storing values across sessions. Currently, RESULTS_FOLDER value is cached using this.
*/
QSettings m_Settings;
bool m_IsResultsFolderValid = false;
QThread* m_nnUNetThread;
nnUNetDownloadWorker* m_Worker;
bool m_FirstPreviewComputation = true;
EnableConfirmSegBtnFunctionType m_SuperclassEnableConfirmSegBtnFnc;
};
#endif
diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetWorker.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetWorker.h
index c99aeb7a4c..f64b865380 100644
--- a/Modules/SegmentationUI/Qmitk/QmitknnUNetWorker.h
+++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetWorker.h
@@ -1,51 +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 QmitknnUNetWorker_h
#define QmitknnUNetWorker_h
#include "mitkProcessExecutor.h"
#include <QMutex>
#include <QObject>
Q_DECLARE_METATYPE(mitk::ProcessExecutor::Pointer);
Q_DECLARE_METATYPE(mitk::ProcessExecutor::ArgumentListType);
/**
- * @brief Class to execute some functions from the Segmentation Plugin in a seperate thread
+ * @brief Class to execute some functions from the Segmentation Plugin in a separate thread
*/
class nnUNetDownloadWorker : public QObject
{
Q_OBJECT
public slots:
/**
* @brief Starts the download process worker thread.
*
*/
void DoWork(QString, QString, mitk::ProcessExecutor::Pointer, mitk::ProcessExecutor::ArgumentListType);
signals:
/**
- * @brief the signal emitted when a download process is finshed; success or failed
+ * @brief the signal emitted when a download process is finished; success or failed
*
* @param exitCode
* @param message
*/
void Exit(bool exitCode, const QString message);
private:
QMutex mutex;
};
#endif
diff --git a/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidget.cpp b/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidget.cpp
index ae6843eac7..a1d2d23974 100644
--- a/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidget.cpp
+++ b/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidget.cpp
@@ -1,259 +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 "QmitkBooleanOperationsWidget.h"
#include <ui_QmitkBooleanOperationsWidgetControls.h>
#include <mitkDataStorage.h>
#include <mitkException.h>
#include <mitkRenderingManager.h>
#include <mitkMultiLabelPredicateHelper.h>
#include <mitkBooleanOperation.h>
#include <mitkProgressBar.h>
#include <mitkLabelSetImageHelper.h>
QmitkBooleanOperationsWidget::QmitkBooleanOperationsWidget(mitk::DataStorage* dataStorage, QWidget* parent)
: QWidget(parent)
{
m_Controls = new Ui::QmitkBooleanOperationsWidgetControls;
m_Controls->setupUi(this);
m_Controls->label1st->setText("<img width=16 height=16 src=\":/Qmitk/BooleanLabelA_32x32.png\"/>");
m_Controls->label1st->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
m_Controls->label2nd->setText("<img width=16 height=16 src=\":/Qmitk/BooleanLabelB_32x32.png\"/>");
m_Controls->label2nd->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
m_Controls->segNodeSelector->SetDataStorage(dataStorage);
m_Controls->segNodeSelector->SetNodePredicate(mitk::GetMultiLabelSegmentationPredicate());
m_Controls->segNodeSelector->SetSelectionIsOptional(false);
- m_Controls->segNodeSelector->SetInvalidInfo(QStringLiteral("Please select segmentation for extraction."));
+ m_Controls->segNodeSelector->SetInvalidInfo(QStringLiteral("Please select segmentation for boolean operations."));
m_Controls->segNodeSelector->SetPopUpTitel(QStringLiteral("Select segmentation"));
- m_Controls->segNodeSelector->SetPopUpHint(QStringLiteral("Select the segmentation that should be used as source for extraction."));
+ m_Controls->segNodeSelector->SetPopUpHint(QStringLiteral("Select the segmentation that should be used as source for boolean operations."));
m_Controls->labelInspector->SetMultiSelectionMode(true);
connect(m_Controls->segNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged,
this, &QmitkBooleanOperationsWidget::OnSegSelectionChanged);
connect(m_Controls->labelInspector, &QmitkMultiLabelInspector::CurrentSelectionChanged,
this, &QmitkBooleanOperationsWidget::OnLabelSelectionChanged);
connect(m_Controls->differenceButton, &QToolButton::clicked, this, &QmitkBooleanOperationsWidget::OnDifferenceButtonClicked);
connect(m_Controls->intersectionButton, &QToolButton::clicked, this, &QmitkBooleanOperationsWidget::OnIntersectionButtonClicked);
connect(m_Controls->unionButton, &QToolButton::clicked, this, &QmitkBooleanOperationsWidget::OnUnionButtonClicked);
m_Controls->segNodeSelector->SetAutoSelectNewNodes(true);
this->ConfigureWidgets();
}
QmitkBooleanOperationsWidget::~QmitkBooleanOperationsWidget()
{
m_Controls->labelInspector->SetMultiLabelNode(nullptr);
}
void QmitkBooleanOperationsWidget::OnSegSelectionChanged(QmitkAbstractNodeSelectionWidget::NodeList /*nodes*/)
{
auto node = m_Controls->segNodeSelector->GetSelectedNode();
m_Controls->labelInspector->SetMultiLabelNode(node);
this->ConfigureWidgets();
}
void QmitkBooleanOperationsWidget::OnLabelSelectionChanged(mitk::LabelSetImage::LabelValueVectorType /*labels*/)
{
this->ConfigureWidgets();
}
namespace
{
- std::string GenerateLabelHTML(const mitk::Label* label)
+ std::string GenerateLabelHTML(const mitk::Label* label, const mitk::LabelSetImage* segmentation)
{
std::stringstream stream;
auto color = label->GetColor();
stream << "<span style='color: #" << std::hex << std::setfill('0')
<< std::setw(2) << static_cast<int>(color.GetRed()*255)
<< std::setw(2) << static_cast<int>(color.GetGreen()*255)
<< std::setw(2) << static_cast<int>(color.GetBlue()*255)
<< "; font-size: 20px '>&#x25A0;</span>" << std::dec;
- stream << "<font class=\"normal\"> " << label->GetName()<< "</font>";
+ stream << "<font class=\"normal\"> " << label->GetName();
+ if (segmentation->GetLabelValuesByName(segmentation->GetGroupIndexOfLabel(label->GetValue()), label->GetName()).size() > 1)
+ {
+ stream << " [" << label->GetValue() << "]";
+ }
+ stream << "</font>";
return stream.str();
}
}
void QmitkBooleanOperationsWidget::ConfigureWidgets()
{
auto selectedLabelValues = m_Controls->labelInspector->GetSelectedLabels();
auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation();
auto styleSheet = qApp->styleSheet();
m_Controls->line1stLabel->document()->setDefaultStyleSheet(styleSheet);
m_Controls->lineOtherLabels->document()->setDefaultStyleSheet(styleSheet);
if (selectedLabelValues.empty())
{
m_Controls->line1stLabel->setHtml(QStringLiteral("<font class=\"warning\">Select 1st label to proceed.</font>"));
}
else
{
- auto label = seg->GetLabel(selectedLabelValues.front());
- m_Controls->line1stLabel->setText(QString::fromStdString(GenerateLabelHTML(label)));
+ mitk::Label::ConstPointer label = seg->GetLabel(selectedLabelValues.front());
+ if (label.IsNotNull())
+ m_Controls->line1stLabel->setText(QString::fromStdString(GenerateLabelHTML(label,seg)));
+ else
+ m_Controls->line1stLabel->setText(QString("Unknown label ID ")+QString::number(selectedLabelValues.front()));
}
if (selectedLabelValues.size() < 2)
{
m_Controls->lineOtherLabels->setHtml(QStringLiteral("<font class=\"warning\">Select secondary label(s) to proceed.</font>"));
}
else
{
+ decltype(selectedLabelValues) otherLabelValues(selectedLabelValues.begin() + 1, selectedLabelValues.end());
+
std::stringstream stream;
- for (auto iter = selectedLabelValues.cbegin() + 1; iter != selectedLabelValues.cend(); ++iter)
+ for (const auto& labelValue : otherLabelValues)
{
- auto label = seg->GetLabel(*iter);
+ mitk::Label::ConstPointer label = seg->GetLabel(labelValue);
if (stream.rdbuf()->in_avail() != 0) stream << "; ";
- stream << GenerateLabelHTML(label);
+ if (label.IsNotNull())
+ stream << GenerateLabelHTML(label,seg);
+ else
+ stream << "Unknown label ID " << labelValue;
}
m_Controls->lineOtherLabels->setText(QString::fromStdString(stream.str()));
}
m_Controls->differenceButton->setEnabled(selectedLabelValues.size()>1);
m_Controls->intersectionButton->setEnabled(selectedLabelValues.size() > 1);
m_Controls->unionButton->setEnabled(selectedLabelValues.size() > 1);
}
void QmitkBooleanOperationsWidget::OnDifferenceButtonClicked()
{
QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
mitk::ProgressBar::GetInstance()->Reset();
mitk::ProgressBar::GetInstance()->AddStepsToDo(110);
unsigned int currentProgress = 0;
auto progressCallback = [&currentProgress](float filterProgress)
{
auto delta = (filterProgress * 100) - currentProgress;
if (delta > 0)
{
currentProgress += delta;
mitk::ProgressBar::GetInstance()->Progress(delta);
}
};
auto selectedLabelValues = m_Controls->labelInspector->GetSelectedLabels();
auto minuend = selectedLabelValues.front();
auto subtrahends = mitk::LabelSetImage::LabelValueVectorType(selectedLabelValues.begin() + 1, selectedLabelValues.end());
auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation();
auto resultMask = mitk::BooleanOperation::GenerateDifference(seg, minuend, subtrahends, progressCallback);
std::stringstream name;
name << "Difference " << seg->GetLabel(minuend)->GetName() << " -";
for (auto label : subtrahends)
{
name << " " << seg->GetLabel(label)->GetName();
}
this->SaveResultLabelMask(resultMask, name.str());
mitk::ProgressBar::GetInstance()->Reset();
QApplication::restoreOverrideCursor();
}
void QmitkBooleanOperationsWidget::OnIntersectionButtonClicked()
{
QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
mitk::ProgressBar::GetInstance()->Reset();
mitk::ProgressBar::GetInstance()->AddStepsToDo(110);
unsigned int currentProgress = 0;
auto progressCallback = [&currentProgress](float filterProgress)
{
auto delta = (filterProgress * 100) - currentProgress;
if (delta > 0)
{
currentProgress += delta;
mitk::ProgressBar::GetInstance()->Progress(delta);
}
};
auto selectedLabelValues = m_Controls->labelInspector->GetSelectedLabels();
auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation();
auto resultMask = mitk::BooleanOperation::GenerateIntersection(seg, selectedLabelValues, progressCallback);
std::stringstream name;
name << "Intersection";
for (auto label : selectedLabelValues)
{
name << " " << seg->GetLabel(label)->GetName();
}
this->SaveResultLabelMask(resultMask, name.str());
mitk::ProgressBar::GetInstance()->Reset();
QApplication::restoreOverrideCursor();
}
void QmitkBooleanOperationsWidget::OnUnionButtonClicked()
{
QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
mitk::ProgressBar::GetInstance()->Reset();
mitk::ProgressBar::GetInstance()->AddStepsToDo(110);
unsigned int currentProgress = 0;
auto progressCallback = [&currentProgress](float filterProgress)
{
auto delta = (filterProgress * 100) - currentProgress;
if (delta > 0)
{
currentProgress += delta;
mitk::ProgressBar::GetInstance()->Progress(delta);
}
};
auto selectedLabelValues = m_Controls->labelInspector->GetSelectedLabels();
auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation();
auto resultMask = mitk::BooleanOperation::GenerateUnion(seg, selectedLabelValues, progressCallback);
std::stringstream name;
name << "Union";
for (auto label : selectedLabelValues)
{
name << " " << seg->GetLabel(label)->GetName();
}
this->SaveResultLabelMask(resultMask, name.str());
mitk::ProgressBar::GetInstance()->Reset();
QApplication::restoreOverrideCursor();
}
void QmitkBooleanOperationsWidget::SaveResultLabelMask(const mitk::Image* resultMask, const std::string& labelName) const
{
auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation();
if (seg == nullptr) mitkThrow() << "Widget is in invalid state. Processing was triggered with no segmentation selected.";
auto labels = m_Controls->labelInspector->GetSelectedLabels();
if (labels.empty()) mitkThrow() << "Widget is in invalid state. Processing was triggered with no label selected.";
auto groupID = seg->AddLayer();
auto newLabel = mitk::LabelSetImageHelper::CreateNewLabel(seg, labelName, true);
seg->AddLabelWithContent(newLabel, resultMask, groupID, 1);
m_Controls->labelInspector->GetMultiLabelSegmentation()->Modified();
m_Controls->labelInspector->GetMultiLabelNode()->Modified();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
diff --git a/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidgetControls.ui b/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidgetControls.ui
index b9c623ea2b..4d950d6aa7 100644
--- a/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidgetControls.ui
+++ b/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidgetControls.ui
@@ -1,291 +1,291 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmitkBooleanOperationsWidgetControls</class>
<widget class="QWidget" name="QmitkBooleanOperationsWidgetControls">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>249</width>
<height>293</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QmitkSingleNodeSelectionWidget" name="segNodeSelector" native="true"/>
</item>
<item>
<widget class="QmitkMultiLabelInspector" name="labelInspector" native="true"/>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Selected operands:</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item row="4" column="0">
<widget class="QLabel" name="label2nd">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QTextEdit" name="line1stLabel">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>26</height>
</size>
</property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="lineWrapMode">
<enum>QTextEdit::NoWrap</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
hr { height: 1px; border-width: 0; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Test text&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label1st">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QTextEdit" name="lineOtherLabels">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>26</height>
</size>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="lineWrapMode">
<enum>QTextEdit::NoWrap</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QToolButton" name="differenceButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
- <string>Subtracts first segmentation from the second one</string>
+ <string>Subtracts second segmentation from the first one</string>
</property>
<property name="text">
<string>Difference</string>
</property>
<property name="icon">
<iconset resource="../resources/SegmentationUI.qrc">
<normaloff>:/Qmitk/BooleanDifference_48x48.png</normaloff>:/Qmitk/BooleanDifference_48x48.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
<property name="arrowType">
<enum>Qt::NoArrow</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="intersectionButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
- <string>Keeps just the overlapping areas of two the segmentations</string>
+ <string>Keeps just the overlapping areas of the two segmentations</string>
</property>
<property name="text">
<string>Intersection</string>
</property>
<property name="icon">
<iconset resource="../resources/SegmentationUI.qrc">
<normaloff>:/Qmitk/BooleanIntersection_48x48.png</normaloff>:/Qmitk/BooleanIntersection_48x48.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
<property name="arrowType">
<enum>Qt::NoArrow</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="unionButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Combines the two segmentations</string>
</property>
<property name="text">
<string>Union</string>
</property>
<property name="icon">
<iconset resource="../resources/SegmentationUI.qrc">
<normaloff>:/Qmitk/BooleanUnion_48x48.png</normaloff>:/Qmitk/BooleanUnion_48x48.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
<property name="arrowType">
<enum>Qt::NoArrow</enum>
</property>
</widget>
</item>
</layout>
</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>
<customwidgets>
<customwidget>
<class>QmitkSingleNodeSelectionWidget</class>
<extends>QWidget</extends>
<header location="global">QmitkSingleNodeSelectionWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QmitkMultiLabelInspector</class>
<extends>QWidget</extends>
<header location="global">QmitkMultiLabelInspector.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../resources/SegmentationUI.qrc"/>
</resources>
<connections/>
</ui>
diff --git a/Modules/SegmentationUI/SegmentationUtilities/QmitkConvertToMultiLabelSegmentationWidgetControls.ui b/Modules/SegmentationUI/SegmentationUtilities/QmitkConvertToMultiLabelSegmentationWidgetControls.ui
index 91296e98e4..814b27ebe1 100644
--- a/Modules/SegmentationUI/SegmentationUtilities/QmitkConvertToMultiLabelSegmentationWidgetControls.ui
+++ b/Modules/SegmentationUI/SegmentationUtilities/QmitkConvertToMultiLabelSegmentationWidgetControls.ui
@@ -1,157 +1,157 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmitkConvertToMultiLabelSegmentationWidgetControls</class>
<widget class="QWidget" name="QmitkConvertToMultiLabelSegmentationWidgetControls">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>408</width>
<height>316</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Inputs;</string>
</property>
</widget>
</item>
<item>
<widget class="QmitkMultiNodeSelectionWidget" name="inputNodesSelector" native="true"/>
</item>
<item>
<widget class="QGroupBox" name="groupOutput">
<property name="title">
<string>Output:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="radioNewSeg">
<property name="text">
<string>Convert to new segmentation</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>20</number>
</property>
<item>
<widget class="QmitkSingleNodeSelectionWidget" name="refNodeSelector" native="true"/>
</item>
<item>
<widget class="QCheckBox" name="checkMultipleOutputs">
<property name="toolTip">
<string>If activated, the conversion will generate separate multi label segmentations for each input instead of joining all inputs in one multi label segmentation.</string>
</property>
<property name="text">
- <string>Convert inputs separatly</string>
+ <string>Convert inputs separately</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QRadioButton" name="radioAddToSeg">
<property name="text">
<string>Add to existing segmentation</string>
</property>
</widget>
</item>
<item>
<widget class="QmitkSingleNodeSelectionWidget" name="outputSegSelector" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupGrouping">
<property name="title">
<string>Grouping:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QRadioButton" name="radioMergeGroup">
<property name="text">
<string>Merge all inputs in new group</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioSingleGroup">
<property name="text">
<string>New group per input</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnConvert">
<property name="text">
<string>Convert</string>
</property>
</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>label</zorder>
<zorder>inputNodesSelector</zorder>
<zorder>groupGrouping</zorder>
<zorder>btnConvert</zorder>
<zorder>groupOutput</zorder>
</widget>
<customwidgets>
<customwidget>
<class>QmitkMultiNodeSelectionWidget</class>
<extends>QWidget</extends>
<header location="global">QmitkMultiNodeSelectionWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QmitkSingleNodeSelectionWidget</class>
<extends>QWidget</extends>
<header location="global">QmitkSingleNodeSelectionWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/Modules/SegmentationUI/files.cmake b/Modules/SegmentationUI/files.cmake
index c17c063bd9..6b02e44ba8 100644
--- a/Modules/SegmentationUI/files.cmake
+++ b/Modules/SegmentationUI/files.cmake
@@ -1,126 +1,133 @@
set(CPP_FILES
Qmitk/QmitkSegWithPreviewToolGUIBase.cpp
Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp
Qmitk/QmitkBinaryThresholdToolGUIBase.cpp
Qmitk/QmitkBinaryThresholdToolGUI.cpp
Qmitk/QmitkBinaryThresholdULToolGUI.cpp
Qmitk/QmitkConfirmSegmentationDialog.cpp
Qmitk/QmitkCopyToClipBoardDialog.cpp
Qmitk/QmitkDrawPaintbrushToolGUI.cpp
Qmitk/QmitkErasePaintbrushToolGUI.cpp
Qmitk/QmitkEditableContourToolGUIBase.cpp
Qmitk/QmitkGrowCutToolGUI.cpp
Qmitk/QmitkLiveWireTool2DGUI.cpp
Qmitk/QmitkLassoToolGUI.cpp
Qmitk/QmitkOtsuTool3DGUI.cpp
Qmitk/QmitkPaintbrushToolGUI.cpp
Qmitk/QmitkPickingToolGUI.cpp
Qmitk/QmitkSlicesInterpolator.cpp
Qmitk/QmitkToolGUI.cpp
Qmitk/QmitkToolGUIArea.cpp
Qmitk/QmitkToolSelectionBox.cpp
Qmitk/QmitknnUNetFolderParser.cpp
Qmitk/QmitknnUNetToolGUI.cpp
Qmitk/QmitknnUNetWorker.cpp
Qmitk/QmitknnUNetGPU.cpp
Qmitk/QmitkSurfaceStampWidget.cpp
Qmitk/QmitkMaskStampWidget.cpp
Qmitk/QmitkStaticDynamicSegmentationDialog.cpp
Qmitk/QmitkSimpleLabelSetListWidget.cpp
Qmitk/QmitkSegmentationTaskListWidget.cpp
Qmitk/QmitkTotalSegmentatorToolGUI.cpp
Qmitk/QmitkSetupVirtualEnvUtil.cpp
Qmitk/QmitkMultiLabelInspector.cpp
Qmitk/QmitkMultiLabelManager.cpp
Qmitk/QmitkMultiLabelTreeModel.cpp
Qmitk/QmitkMultiLabelTreeView.cpp
Qmitk/QmitkMultiLabelPresetHelper.cpp
Qmitk/QmitkLabelColorItemDelegate.cpp
Qmitk/QmitkLabelToggleItemDelegate.cpp
Qmitk/QmitkFindSegmentationTaskDialog.cpp
Qmitk/QmitkSegmentAnythingToolGUI.cpp
Qmitk/QmitkMedSAMToolGUI.cpp
+ Qmitk/QmitkMonaiLabelToolGUI.cpp
+ Qmitk/QmitkMonaiLabel2DToolGUI.cpp
+ Qmitk/QmitkMonaiLabel3DToolGUI.cpp
SegmentationUtilities/QmitkBooleanOperationsWidget.cpp
SegmentationUtilities/QmitkImageMaskingWidget.cpp
SegmentationUtilities/QmitkMorphologicalOperationsWidget.cpp
SegmentationUtilities/QmitkConvertToMultiLabelSegmentationWidget.cpp
SegmentationUtilities/QmitkExtractFromMultiLabelSegmentationWidget.cpp
)
set(H_FILES
Qmitk/QmitkMultiLabelPresetHelper.h
)
set(MOC_H_FILES
Qmitk/QmitkSegWithPreviewToolGUIBase.h
Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h
Qmitk/QmitkBinaryThresholdToolGUIBase.h
Qmitk/QmitkBinaryThresholdToolGUI.h
Qmitk/QmitkBinaryThresholdULToolGUI.h
Qmitk/QmitkConfirmSegmentationDialog.h
Qmitk/QmitkCopyToClipBoardDialog.h
Qmitk/QmitkDrawPaintbrushToolGUI.h
Qmitk/QmitkErasePaintbrushToolGUI.h
Qmitk/QmitkEditableContourToolGUIBase.h
Qmitk/QmitkGrowCutToolGUI.h
Qmitk/QmitkLiveWireTool2DGUI.h
Qmitk/QmitkLassoToolGUI.h
Qmitk/QmitkOtsuTool3DGUI.h
Qmitk/QmitkPaintbrushToolGUI.h
Qmitk/QmitkPickingToolGUI.h
Qmitk/QmitkSlicesInterpolator.h
Qmitk/QmitkToolGUI.h
Qmitk/QmitkToolGUIArea.h
Qmitk/QmitkToolSelectionBox.h
Qmitk/QmitknnUNetFolderParser.h
Qmitk/QmitknnUNetToolGUI.h
Qmitk/QmitknnUNetGPU.h
Qmitk/QmitknnUNetWorker.h
Qmitk/QmitknnUNetEnsembleLayout.h
Qmitk/QmitkSurfaceStampWidget.h
Qmitk/QmitkMaskStampWidget.h
Qmitk/QmitkStaticDynamicSegmentationDialog.h
Qmitk/QmitkSimpleLabelSetListWidget.h
Qmitk/QmitkSegmentationTaskListWidget.h
Qmitk/QmitkTotalSegmentatorToolGUI.h
Qmitk/QmitkSetupVirtualEnvUtil.h
Qmitk/QmitkMultiLabelInspector.h
Qmitk/QmitkMultiLabelManager.h
Qmitk/QmitkMultiLabelTreeModel.h
Qmitk/QmitkMultiLabelTreeView.h
Qmitk/QmitkLabelColorItemDelegate.h
Qmitk/QmitkLabelToggleItemDelegate.h
Qmitk/QmitkFindSegmentationTaskDialog.h
Qmitk/QmitkSegmentAnythingToolGUI.h
Qmitk/QmitkMedSAMToolGUI.h
+ Qmitk/QmitkMonaiLabelToolGUI.h
+ Qmitk/QmitkMonaiLabel2DToolGUI.h
+ Qmitk/QmitkMonaiLabel3DToolGUI.h
SegmentationUtilities/QmitkBooleanOperationsWidget.h
SegmentationUtilities/QmitkImageMaskingWidget.h
SegmentationUtilities/QmitkMorphologicalOperationsWidget.h
SegmentationUtilities/QmitkConvertToMultiLabelSegmentationWidget.h
SegmentationUtilities/QmitkExtractFromMultiLabelSegmentationWidget.h
)
set(UI_FILES
Qmitk/QmitkConfirmSegmentationDialog.ui
Qmitk/QmitkGrowCutToolWidgetControls.ui
Qmitk/QmitkOtsuToolWidgetControls.ui
Qmitk/QmitkSurfaceStampWidgetGUIControls.ui
Qmitk/QmitkMaskStampWidgetGUIControls.ui
Qmitk/QmitknnUNetToolGUIControls.ui
Qmitk/QmitkEditableContourToolGUIControls.ui
Qmitk/QmitkSegmentationTaskListWidget.ui
Qmitk/QmitkTotalSegmentatorGUIControls.ui
Qmitk/QmitkMultiLabelInspectorControls.ui
Qmitk/QmitkMultiLabelManagerControls.ui
Qmitk/QmitkFindSegmentationTaskDialog.ui
Qmitk/QmitkSegmentAnythingGUIControls.ui
Qmitk/QmitkMedSAMGUIControls.ui
+ Qmitk/QmitkMonaiLabelToolGUIControls.ui
SegmentationUtilities/QmitkBooleanOperationsWidgetControls.ui
SegmentationUtilities/QmitkImageMaskingWidgetControls.ui
SegmentationUtilities/QmitkMorphologicalOperationsWidgetControls.ui
SegmentationUtilities/QmitkConvertToMultiLabelSegmentationWidgetControls.ui
SegmentationUtilities/QmitkExtractFromMultiLabelSegmentationWidgetControls.ui
)
set(QRC_FILES
resources/SegmentationUI.qrc
)
diff --git a/Modules/SurfaceInterpolation/CMakeLists.txt b/Modules/SurfaceInterpolation/CMakeLists.txt
index de0af4ff06..2fc8040ea3 100644
--- a/Modules/SurfaceInterpolation/CMakeLists.txt
+++ b/Modules/SurfaceInterpolation/CMakeLists.txt
@@ -1,6 +1,6 @@
MITK_CREATE_MODULE(
DEPENDS MitkImageExtraction MitkContourModel MitkAlgorithmsExt MitkImageStatistics
- PACKAGE_DEPENDS PUBLIC Eigen
+ PACKAGE_DEPENDS PUBLIC ITK|ITKEigen3
)
add_subdirectory(Testing)
diff --git a/Modules/SurfaceInterpolation/Testing/mitkImageToPointCloudFilterTest.cpp b/Modules/SurfaceInterpolation/Testing/mitkImageToPointCloudFilterTest.cpp
index d8b13f8ecf..38af470166 100644
--- a/Modules/SurfaceInterpolation/Testing/mitkImageToPointCloudFilterTest.cpp
+++ b/Modules/SurfaceInterpolation/Testing/mitkImageToPointCloudFilterTest.cpp
@@ -1,78 +1,78 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkImageToPointCloudFilter.h>
#include <mitkTestFixture.h>
#include <mitkIOUtil.h>
class mitkImageToPointCloudFilterTestSuite : public mitk::TestFixture
{
CPPUNIT_TEST_SUITE(mitkImageToPointCloudFilterTestSuite);
MITK_TEST(testImageToPointCloudFilterInitialization);
MITK_TEST(testInput);
MITK_TEST(testUnstructuredGridGeneration);
MITK_TEST(testThreshold);
CPPUNIT_TEST_SUITE_END();
private:
/** Members used inside the different test methods. All members are initialized via setUp().*/
mitk::Image::Pointer m_BallImage;
public:
/**
- * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used
+ * @brief Setup Always call this method before each Test-case to ensure correct and new initialization 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_BallImage = mitk::IOUtil::Load<mitk::Image>(GetTestDataFilePath("BallBinary30x30x30.nrrd")); }
void testImageToPointCloudFilterInitialization()
{
mitk::ImageToUnstructuredGridFilter::Pointer testFilter = mitk::ImageToUnstructuredGridFilter::New();
CPPUNIT_ASSERT_MESSAGE("Testing instantiation of test object", testFilter.IsNotNull());
}
void testInput()
{
mitk::ImageToPointCloudFilter::Pointer testFilter = mitk::ImageToPointCloudFilter::New();
testFilter->SetInput(m_BallImage);
CPPUNIT_ASSERT_MESSAGE("Testing set / get input!", testFilter->GetInput() == m_BallImage);
}
void testUnstructuredGridGeneration()
{
mitk::ImageToPointCloudFilter::Pointer testFilter = mitk::ImageToPointCloudFilter::New();
testFilter->SetInput(m_BallImage);
testFilter->Update();
CPPUNIT_ASSERT_MESSAGE("Testing UnstructuredGrid generation!", testFilter->GetOutput() != nullptr);
}
void testThreshold()
{
mitk::ImageToPointCloudFilter::Pointer testFilter1 = mitk::ImageToPointCloudFilter::New();
testFilter1->SetInput(m_BallImage);
testFilter1->Update();
int numberOfPoints1 = testFilter1->GetNumberOfExtractedPoints();
mitk::ImageToPointCloudFilter::Pointer testFilter2 = mitk::ImageToPointCloudFilter::New();
testFilter2->SetInput(m_BallImage);
testFilter2->SetMethod(mitk::ImageToPointCloudFilter::LAPLACIAN_STD_DEV3);
testFilter2->Update();
int numberOfPoints2 = testFilter2->GetNumberOfExtractedPoints();
CPPUNIT_ASSERT_MESSAGE("Testing Threshold", numberOfPoints1 > numberOfPoints2);
}
};
MITK_TEST_SUITE_REGISTRATION(mitkImageToPointCloudFilter)
diff --git a/Modules/SurfaceInterpolation/mitkCreateDistanceImageFromSurfaceFilter.h b/Modules/SurfaceInterpolation/mitkCreateDistanceImageFromSurfaceFilter.h
index 843013952c..9e9f047138 100644
--- a/Modules/SurfaceInterpolation/mitkCreateDistanceImageFromSurfaceFilter.h
+++ b/Modules/SurfaceInterpolation/mitkCreateDistanceImageFromSurfaceFilter.h
@@ -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.
============================================================================*/
#ifndef mitkCreateDistanceImageFromSurfaceFilter_h
#define mitkCreateDistanceImageFromSurfaceFilter_h
#include <MitkSurfaceInterpolationExports.h>
#include "mitkImageSource.h"
#include "mitkProgressBar.h"
#include "mitkSurface.h"
#include "vnl/vnl_vector_fixed.h"
#include "itkImageBase.h"
-#include <Eigen/Dense>
+#include <itkeigen/Eigen/Dense>
namespace mitk
{
/**
\brief This filter interpolates the 3D surface for a segmented area. The basis for the interpolation
are the edge-points of contours that are drawn into an image.
The interpolation itself is performed via Radial Basis Function Interpolation.
ATTENTION:
This filter needs beside the edge points of the delineated contours additionally the normals for each
edge point.
\sa mitkSurfaceInterpolationController
Based on the contour edge points and their normal this filter calculates a distance function with the following
properties:
- Putting a point into the distance function that lies inside the considered surface gives a negativ scalar
value
- Putting a point into the distance function that lies outside the considered surface gives a positive scalar
value
- Putting a point into the distance function that lies exactly on the considered surface gives the value zero
With this interpolated distance function a distance image will be created. The desired surface can then be
extract e.g.
with the marching cubes algorithm. (Within the distance image the surface goes exactly where the pixelvalues
are zero)
Note that the obtained distance image has always an isotropig spacing. The size (in this case volume) of the
image can be
adjusted by calling SetDistanceImageVolume(unsigned int volume) which specifies the number ob pixels enclosed
by the image.
\ingroup Process
$Author: fetzer$
*/
class MITKSURFACEINTERPOLATION_EXPORT CreateDistanceImageFromSurfaceFilter : public ImageSource
{
public:
typedef vnl_vector_fixed<double, 3> PointType;
typedef itk::Image<double, 3> DistanceImageType;
typedef DistanceImageType::IndexType IndexType;
typedef std::vector<PointType> NormalList;
typedef std::vector<PointType> CenterList;
typedef std::vector<Surface::Pointer> SurfaceList;
mitkClassMacro(CreateDistanceImageFromSurfaceFilter, ImageSource);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
itkGetMacro(DistanceImageSpacing, double);
using Superclass::SetInput;
// Methods copied from mitkSurfaceToSurfaceFilter
virtual void SetInput(const mitk::Surface *surface);
virtual void SetInput(unsigned int idx, const mitk::Surface *surface);
virtual const mitk::Surface *GetInput();
virtual const mitk::Surface *GetInput(unsigned int idx);
virtual void RemoveInputs(mitk::Surface *input);
/**
\brief Set the size of the output distance image. The size is specified by the image's volume
(i.e. in this case how many pixels are enclosed by the image)
If non is set, the volume will be 500000 pixels.
*/
itkSetMacro(DistanceImageVolume, unsigned int);
void PrintEquationSystem();
// Resets the filter, i.e. removes all inputs and outputs
void Reset();
/**
\brief Set whether the mitkProgressBar should be used
\a Parameter true for using the progress bar, false otherwise
*/
void SetUseProgressBar(bool);
/**
\brief Set the stepsize which the progress bar should proceed
\a Parameter The stepsize for progressing
*/
void SetProgressStepSize(unsigned int stepSize);
void SetReferenceImage(itk::ImageBase<3>::Pointer referenceImage);
protected:
CreateDistanceImageFromSurfaceFilter();
~CreateDistanceImageFromSurfaceFilter() override;
void GenerateData() override;
void GenerateOutputInformation() override;
private:
void CreateSolutionMatrixAndFunctionValues();
double CalculateDistanceValue(PointType p);
void FillDistanceImage();
/**
* \brief This method fills the given variables with the minimum and
* maximum coordinates that contain all input-points in index- and
* world-coordinates.
*
* This method iterates over all input-points and transforms them from
* world-coordinates to index-coordinates using the transform of the
* reference-Image.
* Next, the minimal and maximal index-coordinates are determined that
* span an area that contains all given input-points.
* These index-coordinates are then transformed back to world-coordinates.
*
* These minimal and maximal points are then set to the given variables.
*/
void DetermineBounds(DistanceImageType::PointType &minPointInWorldCoordinates,
DistanceImageType::PointType &maxPointInWorldCoordinates,
DistanceImageType::IndexType &minPointInIndexCoordinates,
DistanceImageType::IndexType &maxPointInIndexCoordinates);
void PreprocessContourPoints();
void CreateEmptyDistanceImage();
// Datastructures for the interpolation
CenterList m_Centers;
NormalList m_Normals;
Eigen::MatrixXd m_SolutionMatrix;
Eigen::VectorXd m_FunctionValues;
Eigen::VectorXd m_Weights;
DistanceImageType::Pointer m_DistanceImageITK;
itk::ImageBase<3>::Pointer m_ReferenceImage;
double m_DistanceImageSpacing;
double m_DistanceImageDefaultBufferValue;
unsigned int m_DistanceImageVolume;
bool m_UseProgressBar;
unsigned int m_ProgressStepSize;
};
} // namespace
#endif
diff --git a/Modules/SurfaceInterpolation/mitkPlaneProposer.h b/Modules/SurfaceInterpolation/mitkPlaneProposer.h
index 3d823f68a4..e498f0dc83 100644
--- a/Modules/SurfaceInterpolation/mitkPlaneProposer.h
+++ b/Modules/SurfaceInterpolation/mitkPlaneProposer.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 mitkPlaneProposer_h
#define mitkPlaneProposer_h
#include <MitkSurfaceInterpolationExports.h>
#include <mitkPoint.h>
#include <mitkVector.h>
#include <itkSmartPointer.h>
#include <array>
#include <vector>
#include <mitkDataStorage.h>
namespace mitk
{
class SliceNavigationController;
class UnstructuredGrid;
/**
* @brief The PlaneProposer creates a new plane based on an input point cloud
*
* The proposal is either created by using the lease squares in order to fit
* a plane to the provided point cloud or by using the centroid of three clusters
*
* If less than three clusters are provided, least squares is chosen automatically.
* If the centroid method is chosed either the three biggest clusters are chosen by
* default. If the users sets PlaneProposer::SetUseDistances(true) the three clusters
* with the biggerst mean distance of all points are chosen. The latter requires the
* distances to be set as PointData scalar to the underlying VTK object.
*
* The user can either take the plane information or he can set a mitk::SliceNavigationController
* which will be used to automatically rotate to the suggested position.
*/
class MITKSURFACEINTERPOLATION_EXPORT PlaneProposer
{
public:
/**
* @brief Encapsulates the geometrical information needed to descripe a PlaneInfo
*
* normal = the normal of the plane
* x,y = the axes of the PlaneInfo
*/
struct PlaneInfo
{
mitk::Vector3D normal;
mitk::Vector3D x;
mitk::Vector3D y;
mitk::Point3D pointOnPlane;
};
PlaneProposer();
~PlaneProposer();
void SetUnstructuredGrids(std::vector<itk::SmartPointer<mitk::UnstructuredGrid>> &grids);
/**
* @brief If true, the three clusters with the biggest mean distances are used for plane proposal
* Required the distance for each point to be set in PointData scalars
*/
void SetUseDistances(bool);
/**
* @brief Tells the proposer to use least squares method for plane creating
*
* This will eb chosen automatically if less than three point clusters are provided
*/
void SetUseLeastSquares(bool);
/**
* @brief Sets the number of the clusters to be used for plane creation (default=3)
*/
void SetNumberOfClustersToUse(unsigned int);
void SetSliceNavigationController(itk::SmartPointer<mitk::SliceNavigationController> &snc);
/**
* @brief Creates the actual plane proposal
*
* Is less than three clusters are provide the least squares method will be chosen automatically
* The result will either be executed on a mitk::SliceNavigationController if provided or can be
* retrieved by calling mitk::PlaneProposer::GetProposedPlaneInfo()
*/
void CreatePlaneInfo();
PlaneInfo GetProposedPlaneInfo();
std::array<std::array<double, 3>, 3> GetCentroids();
private:
PlaneProposer(const PlaneProposer &); // not implemented on purpose
PlaneProposer &operator=(const PlaneProposer &); // not implemented on purpose
/**
- * @brief Creates a plane suggestion based on the cluster centriods
+ * @brief Creates a plane suggestion based on the cluster centroids
*/
PlaneInfo CreatePlaneByCentroids(const std::vector<std::pair<int, int>> &sizeIDs,
const std::vector<std::pair<double, int>> &avgDistances);
/**
* @brief Creates a plane suggestion based on the least squares
*/
PlaneInfo CreatePlaneByLeastSquares(const std::vector<std::pair<int, int>> &sizeIDs,
const std::vector<std::pair<double, int>> &avgDistances);
std::vector<itk::SmartPointer<mitk::UnstructuredGrid>> m_Grids;
bool m_UseDistances;
bool m_UseLeastSquares;
unsigned int m_NumberOfClustersToUse;
std::array<std::array<double, 3>, 3> m_Centroids;
itk::SmartPointer<mitk::SliceNavigationController> m_SNC;
PlaneInfo m_ProposedPlaneInfo;
};
} // namespace
#endif
diff --git a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h
index c683d416af..f6d4c1c083 100644
--- a/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h
+++ b/Modules/SurfaceInterpolation/mitkSurfaceInterpolationController.h
@@ -1,259 +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.
============================================================================*/
#ifndef mitkSurfaceInterpolationController_h
#define mitkSurfaceInterpolationController_h
#include <mitkDataStorage.h>
#include <mitkLabelSetImage.h>
#include <mitkLabel.h>
#include <mitkSurface.h>
#include <MitkSurfaceInterpolationExports.h>
namespace mitk
{
class ComputeContourSetNormalsFilter;
class CreateDistanceImageFromSurfaceFilter;
class LabelSetImage;
class ReduceContourSetFilter;
class MITKSURFACEINTERPOLATION_EXPORT SurfaceInterpolationController : public itk::Object
{
public:
mitkClassMacroItkParent(SurfaceInterpolationController, itk::Object);
itkFactorylessNewMacro(Self);
itkCloneMacro(Self);
struct MITKSURFACEINTERPOLATION_EXPORT ContourPositionInformation
{
Surface::ConstPointer Contour;
PlaneGeometry::ConstPointer Plane;
Label::PixelType LabelValue;
TimeStepType TimeStep;
ContourPositionInformation()
: Plane(nullptr),
LabelValue(std::numeric_limits<Label::PixelType>::max()),
TimeStep(std::numeric_limits<TimeStepType>::max())
{
}
ContourPositionInformation(Surface::ConstPointer contour,
PlaneGeometry::ConstPointer plane,
Label::PixelType labelValue,
TimeStepType timeStep)
:
Contour(contour),
Plane(plane),
LabelValue(labelValue),
TimeStep(timeStep)
{
}
bool IsPlaceHolder() const
{
return Contour.IsNull();
}
};
typedef std::vector<ContourPositionInformation> CPIVector;
static SurfaceInterpolationController *GetInstance();
/**
* @brief Adds new extracted contours to the list. If one or more contours at a given position
* already exist they will be updated respectively
*/
void AddNewContours(const std::vector<ContourPositionInformation>& newCPIs, bool reinitializeAction = false, bool silent = false);
/**
* @brief Removes the contour for a given plane for the current selected segmentation
* @param contourInfo the contour which should be removed
* @param keepPlaceholderForUndo
* @return true if a contour was found and removed, false if no contour was found
*/
bool RemoveContour(ContourPositionInformation contourInfo, bool keepPlaceholderForUndo = false);
void RemoveObservers();
/**
* @brief Performs the interpolation.
*
*/
void Interpolate(const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep);
/**
* @brief Get the Result of the interpolation operation.
*
* @return mitk::Surface::Pointer
*/
mitk::Surface::Pointer GetInterpolationResult(const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep);
/**
* @brief Sets the minimum spacing of the current selected segmentation
* This is needed since the contour points we reduced before they are used to interpolate the surface.
*
* @param minSpacing Parameter to set
+ * @param minSpacing Parameter to set
*/
void SetMinSpacing(double minSpacing);
/**
* @brief Sets the minimum spacing of the current selected segmentation
* This is needed since the contour points we reduced before they are used to interpolate the surface
* @param maxSpacing Set the max Spacing for interpolation
*/
void SetMaxSpacing(double maxSpacing);
/**
* Sets the volume i.e. the number of pixels that the distance image should have
* By evaluation we found out that 50.000 pixel delivers a good result
*/
void SetDistanceImageVolume(unsigned int distImageVolume);
/**
* @brief Get the current selected segmentation for which the interpolation is performed
* @return the current segmentation image
*/
mitk::LabelSetImage* GetCurrentSegmentation();
void SetDataStorage(DataStorage::Pointer ds);
/**
* Sets the current list of contourpoints which is used for the surface interpolation
* @param currentSegmentationImage The current selected segmentation
*/
void SetCurrentInterpolationSession(LabelSetImage* currentSegmentationImage);
/**
* @brief Remove interpolation session
* @param segmentationImage the session to be removed
*/
void RemoveInterpolationSession(const LabelSetImage* segmentationImage);
/**
* @brief Removes all sessions
*/
void RemoveAllInterpolationSessions();
/**
* @brief Get the Contours at a certain timeStep and layerID.
*
* @param timeStep Time Step from which to get the contours.
* @param labelValue label from which to get the contours.
* @return std::vector<ContourPositionInformation> Returns contours.
*/
CPIVector* GetContours(LabelSetImage::LabelValueType labelValue, TimeStepType timeStep);
std::vector<LabelSetImage::LabelValueType> GetAffectedLabels(const LabelSetImage* seg, TimeStepType timeStep, const PlaneGeometry* plane) const;
/**
+ * @brief Triggered with the "Reinit Interpolation" action. The contours are used to repopulate the
* @brief Triggered with the "Reinit Interpolation" action. The contours are used to repopulate the
* surfaceInterpolator data structures so that interpolation can be performed after reloading data.
*/
void CompleteReinitialization(const std::vector<ContourPositionInformation>& newCPIs);
/**
* @brief Removes contours of a particular label and at a given time step for the current session/segmentation.
*
* @param segmentationImage
* @param label Label of contour to remove.
* @param timeStep Time step in which to remove the contours.
* @remark if the label or time step does not exist, nothing happens.
*/
void RemoveContours(const LabelSetImage* segmentationImage, mitk::Label::PixelType label, TimeStepType timeStep);
/**
* @brief Removes contours of a particular label and at a given time step for the current session/segmentation.
*
* @param segmentationImage
* @param label Label of contour to remove.
* @remark if the label or time step does not exist, nothing happens.
*/
void RemoveContours(const LabelSetImage* segmentationImage, mitk::Label::PixelType label);
unsigned int GetNumberOfInterpolationSessions();
/**
* @brief Get the Segmentation Image Node object
*
* @return DataNode* returns the DataNode containing the segmentation image.
*/
mitk::DataNode* GetSegmentationImageNode() const;
protected:
SurfaceInterpolationController();
~SurfaceInterpolationController() override;
template <typename TPixel, unsigned int VImageDimension>
void GetImageBase(itk::Image<TPixel, VImageDimension> *input, itk::ImageBase<3>::Pointer &result);
private:
/**
* @brief
*
* @param caller
* @param event
*/
void OnSegmentationDeleted(const itk::Object *caller, const itk::EventObject &event);
/**
+ * @brief Function that removes contours of a particular label when the "Remove Label" event is triggered in the labelSetImage.
* @brief Function that removes contours of a particular label when the "Remove Label" event is triggered in the labelSetImage.
*
*/
void OnRemoveLabel(const itk::Object* caller, const itk::EventObject& event);
/**
* @brief When a new contour is added to the pipeline or an existing contour is replaced,
* the plane geometry information of that contour is added as a child node to the
* current node of the segmentation image. This is useful in the retrieval of contour information
* when data is reloaded after saving.
*
* @param contourInfo contourInfo struct to add to data storage.
*/
void AddPlaneGeometryNodeToDataStorage(const ContourPositionInformation& contourInfo) const;
DataStorage::SetOfObjects::ConstPointer GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode) const;
DataStorage::SetOfObjects::ConstPointer GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode, LabelSetImage::LabelValueType labelValue) const;
DataStorage::SetOfObjects::ConstPointer GetPlaneGeometryNodeFromDataStorage(const DataNode* segNode, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep) const;
/**
* Adds Contours from the active Label to the interpolation pipeline
*/
void AddActiveLabelContoursForInterpolation(ReduceContourSetFilter* reduceFilter, const LabelSetImage* segmentationImage, LabelSetImage::LabelValueType labelValue, TimeStepType timeStep);
/**
* @brief Clears the interpolation data structures. Called from CompleteReinitialization().
*
*/
void ClearInterpolationSession();
void RemoveObserversInternal(const mitk::LabelSetImage* segmentationImage);
/**
* @brief Add contour to the interpolation pipeline
*
* @param contourInfo Contour information to be added
* @param reinitializationAction If the contour is coming from a reinitialization process or not
*/
void AddToCPIMap(ContourPositionInformation& contourInfo, bool reinitializationAction = false);
unsigned int m_DistanceImageVolume;
mitk::DataStorage::Pointer m_DataStorage;
WeakPointer<LabelSetImage> m_SelectedSegmentation;
};
}
#endif
diff --git a/Modules/XNAT/src/QmitkHttpStatusCodeHandler.cpp b/Modules/XNAT/src/QmitkHttpStatusCodeHandler.cpp
index e1c1978719..038c2bcc9e 100644
--- a/Modules/XNAT/src/QmitkHttpStatusCodeHandler.cpp
+++ b/Modules/XNAT/src/QmitkHttpStatusCodeHandler.cpp
@@ -1,246 +1,246 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkHttpStatusCodeHandler.h"
#include "QMessageBox"
#include <mitkCommon.h>
#include <sstream>
QmitkHttpStatusCodeHandler::QmitkHttpStatusCodeHandler()
{
}
QmitkHttpStatusCodeHandler::~QmitkHttpStatusCodeHandler()
{
}
static void CreateMessageBox(int statusCode, std::string errorMessage)
{
std::stringstream ss;
// Qt uses different type of status code numbers.
switch (statusCode)
{
case 1:
QMessageBox::warning(nullptr,
"ConnectionRefusedError - 1",
"The remote server refused the connection (the server is not accepting requests).");
break;
case 2:
QMessageBox::warning(
nullptr,
"RemoteHostClosedError - 2",
"The remote server closed the connection prematurely, before the entire reply was received and processed.");
break;
case 3:
QMessageBox::warning(nullptr, "HostNotFoundError - 3", "The remote host name was not found (invalid hostname).");
break;
case 4:
QMessageBox::warning(nullptr, "TimeoutError - 4", "The connection to the remote server timed out.");
break;
case 5:
QMessageBox::warning(nullptr,
"OperationCanceledError - 5",
"The operation was canceled via calls to abort() or close() before it was finished.");
break;
case 6:
QMessageBox::warning(nullptr, "SslHandshakeFailedError - 6", "The SSL/TLS handshake failed and the encrypted "
"channel could not be established. The sslErrors() "
"signal should have been emitted.");
break;
case 7:
QMessageBox::warning(nullptr,
"TemporaryNetworkFailureError - 7",
"The connection was broken due to disconnection from the network, however the system has "
"initiated roaming to another access point. The request should be resubmitted and will be "
"processed as soon as the connection is re-established.");
break;
case 8:
QMessageBox::warning(
nullptr,
"NetworkSessionFailedError - 8",
"The connection was broken due to disconnection from the network or failure to start the network.");
break;
case 9:
QMessageBox::warning(nullptr,
"BackgroundRequestNotAllowedError - 9",
"The background request is not currently allowed due to platform policy.");
break;
case 10:
QMessageBox::warning(nullptr, "TooManyRedirectsError - 10 ", "While following redirects, the maximum limit was "
"reached. The limit is by default set to 50 or as "
"set by QNetworkRequest::setMaxRedirectsAllowed().");
break;
case 11:
QMessageBox::warning(nullptr, "InsecureRedirectError - 11 ", "While following redirects, the network access API "
"detected a redirect from a encrypted protocol "
"(https) to an unencrypted one (http).");
break;
case 101:
QMessageBox::warning(
nullptr,
"ProxyConnectionRefusedError - 101",
"The connection to the proxy server was refused (the proxy server is not accepting requests).");
break;
case 102:
QMessageBox::warning(
nullptr,
"ProxyConnectionClosedError - 102",
"The proxy server closed the connection prematurely, before the entire reply was received and processed.");
break;
case 103:
QMessageBox::warning(
nullptr, "ProxyNotFoundError - 103", "The proxy host name was not found (invalid proxy hostname).");
break;
case 104:
QMessageBox::warning(
nullptr,
"ProxyTimeoutError - 104",
"The connection to the proxy timed out or the proxy did not reply in time to the request sent.");
break;
case 105:
QMessageBox::warning(nullptr, "ProxyAuthenticationRequiredError - 105", "The proxy requires authentication in "
"order to honour the request but did not "
"accept any credentials offered (if "
"any).");
break;
case 201:
QMessageBox::warning(nullptr,
"ContentAccessDenied - 201",
"The access to the remote content was denied (similar to HTTP error 401).");
break;
case 202:
QMessageBox::warning(nullptr,
"ContentOperationNotPermittedError - 202",
"The operation requested on the remote content is not permitted.");
break;
case 203:
QMessageBox::warning(nullptr,
"ContentNotFoundError - 203",
"The remote content was not found at the server (similar to HTTP error 404).");
break;
case 204:
QMessageBox::warning(nullptr, "AuthenticationRequiredError - 204", "The remote server requires authentication to "
"serve the content but the credentials "
"provided were not accepted (if any).");
break;
case 205:
QMessageBox::warning(nullptr, "ContentReSendError - 205", "The request needed to be sent again, but this failed "
"for example because the upload data could not be read "
"a second time.");
break;
case 206:
QMessageBox::warning(
nullptr,
"ContentConflictError - 206",
"The request could not be completed due to a conflict with the current state of the resource.");
break;
case 207:
QMessageBox::warning(
nullptr, "ContentGoneError - 207", "The requested resource is no longer available at the server.");
break;
case 401:
QMessageBox::warning(
nullptr,
"InternalServerError - 401",
"The server encountered an unexpected condition which prevented it from fulfilling the request.");
break;
case 402:
QMessageBox::warning(nullptr,
"OperationNotImplementedError - 402",
"The server does not support the functionality required to fulfill the request.");
break;
case 403:
QMessageBox::warning(
nullptr, "ServiceUnavailableError - 403", "The server is unable to handle the request at this time.");
break;
case 301:
QMessageBox::warning(nullptr,
"ProtocolUnknownError - 301",
"The Network Access API cannot honor the request because the protocol is not known.");
break;
case 302:
QMessageBox::warning(
nullptr, "ProtocolInvalidOperationError - 302", "The requested operation is invalid for this protocol.");
break;
case 99:
QMessageBox::warning(nullptr, "UnknownNetworkError - 99", "An unknown network-related error was detected.");
break;
case 199:
QMessageBox::warning(nullptr, "UnknownProxyError - 199", "An unknown proxy-related error was detected.");
break;
case 299:
QMessageBox::warning(
nullptr, "UnknownContentError - 299", "An unknown error related to the remote content was detected.");
break;
case 399:
QMessageBox::warning(
nullptr,
"ProtocolFailure - 399",
"A breakdown in protocol was detected (parsing error, invalid or unexpected responses, etc.).");
break;
case 499:
QMessageBox::warning(
nullptr, "UnknownServerError - 499", "An unknown error related to the server response was detected.");
break;
default:
- ss << "An Http Error occured with error code " << statusCode << " and server message: " << errorMessage;
+ ss << "An Http Error occurred with error code " << statusCode << " and server message: " << errorMessage;
QMessageBox::warning(nullptr, "HTTP ERROR", ss.str().c_str());
break;
}
}
bool QmitkHttpStatusCodeHandler::HandleErrorMessage(const char *_errorMsg)
{
static int lastCode = 0;
std::string errorMsg(_errorMsg, strnlen(_errorMsg, strlen(_errorMsg)));
bool success = true;
/*
* sample error response:
* ERROR: An error occurred: ctkRuntimeException: Syncing with http request failed.
* {d55ec279-8a65-46d6-80d3-cec079066109}: 202: Error downloading
* https:... - server replied: Forbidden
*/
if (errorMsg.find("request failed.") == std::string::npos)
success = false;
std::string::size_type indexOfErrorCode = errorMsg.find(": Error") - 3;
std::string::size_type indexOfServerResponse =
errorMsg.rfind("server replied: ") + 16; // Length of "server replied : " is 16
if (indexOfErrorCode == std::string::npos || indexOfServerResponse == std::string::npos)
success = false;
std::string statusCodeString = errorMsg.substr(indexOfErrorCode, 3);
std::stringstream str;
str << statusCodeString;
int statusCode;
str >> statusCode;
std::string serverResponse = errorMsg.substr(indexOfServerResponse);
if (lastCode != statusCode)
::CreateMessageBox(statusCode, serverResponse);
if (!success && lastCode != statusCode)
{
QMessageBox::warning(nullptr, "General Error", errorMsg.c_str());
}
if (lastCode != statusCode)
lastCode = statusCode;
else
lastCode = 0;
return success;
}
diff --git a/Plugins/org.blueberry.core.commands/src/common/berryNamedHandleObject.h b/Plugins/org.blueberry.core.commands/src/common/berryNamedHandleObject.h
index 549e017b07..3e0c48e9c7 100644
--- a/Plugins/org.blueberry.core.commands/src/common/berryNamedHandleObject.h
+++ b/Plugins/org.blueberry.core.commands/src/common/berryNamedHandleObject.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 BERRYNAMEDHANDLEOBJECT_H_
#define BERRYNAMEDHANDLEOBJECT_H_
#include "berryHandleObject.h"
namespace berry {
/**
* A handle object that carries with it a name and a description. This type of
* handle object is quite common across the commands code base. For example,
* <code>Command</code>, <code>Context</code> and <code>Scheme</code>.
*
* @since 3.1
*/
class BERRY_COMMANDS NamedHandleObject : public HandleObject {
public:
berryObjectMacro(NamedHandleObject);
protected:
/**
* The description for this handle. This value may be <code>null</code> if
* the handle is undefined or has no description.
*/
QString description;
/**
- * The name of this handle. This valud should not be <code>null</code>
+ * The name of this handle. This value should not be <code>null</code>
* unless the handle is undefined.
*/
QString name;
/**
* Constructs a new instance of <code>NamedHandleObject</code>.
*
* @param id
* The identifier for this handle; must not be <code>null</code>.
*/
NamedHandleObject(const QString& id);
public:
/**
* Returns the description for this handle.
*
* @return The description; may be <code>null</code> if there is no
* description.
* @throws NotDefinedException
* If the handle is not currently defined.
*/
virtual QString GetDescription() const;
/**
* Returns the name for this handle.
*
* @return The name for this handle; never <code>null</code>.
* @throws NotDefinedException
* If the handle is not currently defined.
*/
virtual QString GetName() const;
};
}
#endif /*BERRYNAMEDHANDLEOBJECT_H_*/
diff --git a/Plugins/org.blueberry.core.runtime/src/internal/berryExtensionRegistry.cpp b/Plugins/org.blueberry.core.runtime/src/internal/berryExtensionRegistry.cpp
index 360ee8568c..6e6cf25925 100644
--- a/Plugins/org.blueberry.core.runtime/src/internal/berryExtensionRegistry.cpp
+++ b/Plugins/org.blueberry.core.runtime/src/internal/berryExtensionRegistry.cpp
@@ -1,1340 +1,1340 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "berryExtensionRegistry.h"
#include "berryCombinedEventDelta.h"
#include "berryConfigurationElement.h"
#include "berryConfigurationElementAttribute.h"
#include "berryConfigurationElementDescription.h"
#include "berryExtension.h"
#include "berryExtensionHandle.h"
#include "berryExtensionPoint.h"
#include "berryExtensionPointHandle.h"
#include "berryExtensionsParser.h"
#include "berryIConfigurationElement.h"
#include "berryIExtension.h"
#include "berryIExtensionPoint.h"
#include "berrySimpleExtensionPointFilter.h"
#include "berryMultiStatus.h"
#include "berryRegistryConstants.h"
#include "berryRegistryContribution.h"
#include "berryRegistryContributor.h"
#include "berryRegistryMessages.h"
#include "berryRegistryObjectFactory.h"
#include "berryRegistryObjectManager.h"
#include "berryRegistryProperties.h"
#include "berryRegistryStrategy.h"
#include "berryStatus.h"
#include <QThread>
#include <QElapsedTimer>
namespace berry {
struct ExtensionRegistry::ListenerInfo {
IExtensionPointFilter filter;
IRegistryEventListener* listener;
ListenerInfo(IRegistryEventListener* listener, const IExtensionPointFilter& filter)
: filter(filter), listener(listener)
{
}
/**
* Used by ListenerList to ensure uniqueness.
*/
bool operator==(const ListenerInfo& another) const
{
return another.listener == this->listener;
}
};
void ExtensionRegistry::Add(const SmartPointer<RegistryContribution> &element)
{
QWriteLocker l(&access);
eventDelta = CombinedEventDelta::RecordAddition();
BasicAdd(element, true);
FireRegistryChangeEvent();
eventDelta.Reset();
}
QString ExtensionRegistry::AddExtension(int extension)
{
Extension::Pointer addedExtension = registryObjects->GetObject(extension, RegistryObjectManager::EXTENSION).Cast<Extension>();
QString extensionPointToAddTo = addedExtension->GetExtensionPointIdentifier();
ExtensionPoint::Pointer extPoint = registryObjects->GetExtensionPointObject(extensionPointToAddTo);
//orphan extension
if (extPoint.IsNull())
{
registryObjects->AddOrphan(extensionPointToAddTo, extension);
return QString();
}
// otherwise, link them
QList<int> newExtensions = extPoint->GetRawChildren();
newExtensions.push_back(extension);
Link(extPoint, newExtensions);
if (!eventDelta.IsNull())
eventDelta.RememberExtension(extPoint, extension);
return extPoint->GetNamespace();
//return RecordChange(extPoint, extension, ExtensionDelta::ADDED);
}
QString ExtensionRegistry::AddExtensionPoint(int extPoint)
{
ExtensionPoint::Pointer extensionPoint = registryObjects->GetObject(extPoint, RegistryObjectManager::EXTENSION_POINT).Cast<ExtensionPoint>();
if (!eventDelta.IsNull())
eventDelta.RememberExtensionPoint(extensionPoint);
QList<int> orphans = registryObjects->RemoveOrphans(extensionPoint->GetUniqueIdentifier());
if (orphans.empty())
return QString();
Link(extensionPoint, orphans);
if (!eventDelta.IsNull())
eventDelta.RememberExtensions(extensionPoint, orphans);
return extensionPoint->GetNamespace();
//return RecordChange(extensionPoint, orphans, ExtensionDelta::ADDED);
}
QSet<QString> ExtensionRegistry::AddExtensionsAndExtensionPoints(const SmartPointer<RegistryContribution>& element)
{
// now add and resolve extensions and extension points
QSet<QString> affectedNamespaces;
QList<int> extPoints = element->GetExtensionPoints();
for (int i = 0; i < extPoints.size(); i++)
{
QString namespaze = this->AddExtensionPoint(extPoints[i]);
if (!namespaze.isEmpty())
affectedNamespaces.insert(namespaze);
}
QList<int> extensions = element->GetExtensions();
for (int i = 0; i < extensions.size(); i++)
{
QString namespaze = this->AddExtension(extensions[i]);
if (!namespaze.isEmpty())
affectedNamespaces.insert(namespaze);
}
return affectedNamespaces;
}
void ExtensionRegistry::AddListenerInternal(IRegistryEventListener* listener, const IExtensionPointFilter& filter)
{
listeners.Add(ListenerInfo(listener, filter));
}
void ExtensionRegistry::BasicAdd(const SmartPointer<RegistryContribution>& element, bool link)
{
registryObjects->AddContribution(element);
if (!link)
return;
AddExtensionsAndExtensionPoints(element);
SetObjectManagers(registryObjects->CreateDelegatingObjectManager(
registryObjects->GetAssociatedObjects(element->GetContributorId())));
}
void ExtensionRegistry::SetObjectManagers(const SmartPointer<IObjectManager>& manager)
{
if (!eventDelta.IsNull())
eventDelta.SetObjectManager(manager);
}
void ExtensionRegistry::BasicRemove(const QString& contributorId)
{
// ignore anonymous namespaces
RemoveExtensionsAndExtensionPoints(contributorId);
QHash<int, RegistryObject::Pointer> associatedObjects = registryObjects->GetAssociatedObjects(contributorId);
registryObjects->RemoveObjects(associatedObjects);
registryObjects->AddNavigableObjects(associatedObjects); // put the complete set of navigable objects
SetObjectManagers(registryObjects->CreateDelegatingObjectManager(associatedObjects));
registryObjects->RemoveContribution(contributorId);
registryObjects->RemoveContributor(contributorId);
}
void ExtensionRegistry::FireRegistryChangeEvent()
{
// if there is nothing to say, just bail out
if (listeners.IsEmpty())
{
return;
}
// for thread safety, create tmp collections
QList<ListenerInfo> tmpListeners = listeners.GetListeners();
// do the notification asynchronously
//strategy->ScheduleChangeEvent(tmpListeners, tmpDeltas, this);
this->ScheduleChangeEvent(tmpListeners, eventDelta);
}
//RegistryDelta ExtensionRegistry::GetDelta(const QString& namespaze) const
//{
// // is there a delta for the plug-in?
// RegistryDelta existingDelta = deltas.value(namespaze);
// if (existingDelta != null)
// return existingDelta;
// //if not, create one
// RegistryDelta delta = new RegistryDelta();
// deltas.put(namespace, delta);
// return delta;
//}
void ExtensionRegistry::Link(const SmartPointer<ExtensionPoint>& extPoint, const QList<int>& extensions)
{
extPoint->SetRawChildren(extensions);
registryObjects->Add(extPoint, true);
}
//QString ExtensionRegistry::RecordChange(const SmartPointer<ExtensionPoint>& extPoint, int extension, int kind)
//{
// // avoid computing deltas when there are no listeners
// if (listeners.isEmpty())
// return QString();
// ExtensionDelta extensionDelta = new ExtensionDelta();
// extensionDelta.setExtension(extension);
// extensionDelta.setExtensionPoint(extPoint.getObjectId());
// extensionDelta.setKind(kind);
// getDelta(extPoint.getNamespace()).addExtensionDelta(extensionDelta);
// return extPoint.getNamespace();
//}
//QString ExtensionRegistry::RecordChange(const SmartPointer<ExtensionPoint>& extPoint, const QList<int>& extensions, int kind)
//{
// if (listeners.isEmpty())
// return null;
// QString namespace = extPoint.getNamespace();
// if (extensions == null || extensions.length == 0)
// return namespace;
// RegistryDelta pluginDelta = getDelta(extPoint.getNamespace());
// for (int i = 0; i < extensions.length; i++) {
// ExtensionDelta extensionDelta = new ExtensionDelta();
// extensionDelta.setExtension(extensions[i]);
// extensionDelta.setExtensionPoint(extPoint.getObjectId());
// extensionDelta.setKind(kind);
// pluginDelta.addExtensionDelta(extensionDelta);
// }
// return namespace;
//}
QString ExtensionRegistry::RemoveExtension(int extensionId)
{
Extension::Pointer extension = registryObjects->GetObject(extensionId, RegistryObjectManager::EXTENSION).Cast<Extension>();
registryObjects->RemoveExtensionFromNamespaceIndex(extensionId, extension->GetNamespaceIdentifier());
QString xptName = extension->GetExtensionPointIdentifier();
ExtensionPoint::Pointer extPoint = registryObjects->GetExtensionPointObject(xptName);
if (extPoint.IsNull())
{
registryObjects->RemoveOrphan(xptName, extensionId);
return QString();
}
// otherwise, unlink the extension from the extension point
QList<int> existingExtensions = extPoint->GetRawChildren();
QList<int> newExtensions;
if (existingExtensions.size() > 1)
{
for (int i = 0; i < existingExtensions.size(); ++i)
if (existingExtensions[i] != extension->GetObjectId())
newExtensions.push_back(existingExtensions[i]);
}
Link(extPoint, newExtensions);
if (!eventDelta.IsNull())
eventDelta.RememberExtension(extPoint, extensionId);
return extPoint->GetNamespace();
//return recordChange(extPoint, extension.getObjectId(), IExtensionDelta.REMOVED);
}
QString ExtensionRegistry::RemoveExtensionPoint(int extPoint)
{
ExtensionPoint::Pointer extensionPoint = registryObjects->GetObject(
extPoint, RegistryObjectManager::EXTENSION_POINT).Cast<ExtensionPoint>();
registryObjects->RemoveExtensionPointFromNamespaceIndex(extPoint, extensionPoint->GetNamespace());
QList<int> existingExtensions = extensionPoint->GetRawChildren();
if (!existingExtensions.empty())
{
registryObjects->AddOrphans(extensionPoint->GetUniqueIdentifier(), existingExtensions);
Link(extensionPoint, QList<int>());
}
if (!eventDelta.IsNull())
{
eventDelta.RememberExtensionPoint(extensionPoint);
eventDelta.RememberExtensions(extensionPoint, existingExtensions);
}
return extensionPoint->GetNamespace();
//return recordChange(extensionPoint, existingExtensions, IExtensionDelta.REMOVED);
}
QSet<QString> ExtensionRegistry::RemoveExtensionsAndExtensionPoints(const QString& contributorId)
{
QSet<QString> affectedNamespaces;
QList<int> extensions = registryObjects->GetExtensionsFrom(contributorId);
for (int i = 0; i < extensions.size(); i++)
{
QString namespaze = this->RemoveExtension(extensions[i]);
if (!namespaze.isEmpty())
affectedNamespaces.insert(namespaze);
}
// remove extension points
QList<int> extPoints = registryObjects->GetExtensionPointsFrom(contributorId);
for (int i = 0; i < extPoints.size(); i++)
{
QString namespaze = this->RemoveExtensionPoint(extPoints[i]);
if (!namespaze.isEmpty())
affectedNamespaces.insert(namespaze);
}
return affectedNamespaces;
}
struct ExtensionRegistry::QueueElement
{
QList<ListenerInfo> listenerInfos;
CombinedEventDelta scheduledDelta;
QueueElement()
{
}
QueueElement(const QList<ListenerInfo>& infos, const CombinedEventDelta& delta)
: listenerInfos(infos), scheduledDelta(delta)
{
}
};
class ExtensionRegistry::RegistryEventThread : public QThread
{
private:
QAtomicInt stop;
ExtensionRegistry* registry;
Queue& queue;
public:
RegistryEventThread(ExtensionRegistry* registry, Queue& queue)
: stop(0), registry(registry), queue(queue)
{
this->setObjectName("Extension Registry Event Dispatcher");
}
void interrupt()
{
stop.fetchAndStoreOrdered(1);
}
void run() override
{
while (!stop.fetchAndAddOrdered(0))
{
QueueElement element;
{
Queue::Locker l(&queue);
while (queue.empty())
queue.wait();
element = queue.takeFirst();
}
registry->ProcessChangeEvent(element.listenerInfos, element.scheduledDelta);
}
}
};
bool ExtensionRegistry::CheckReadWriteAccess(QObject* key, bool persist) const
{
if (masterToken == key)
return true;
if (userToken == key && !persist)
return true;
return false;
}
void ExtensionRegistry::LogError(const QString& owner, const QString& contributionName, const ctkException& e)
{
QString message = QString("Could not parse XML contribution for \"%1\". Any contributed extensions "
"and extension points will be ignored.").arg(QString(owner) + "/" + contributionName);
IStatus::Pointer status(new Status(IStatus::ERROR_TYPE, RegistryMessages::OWNER_NAME, 0, message, e, BERRY_STATUS_LOC));
Log(status);
}
void ExtensionRegistry::CreateExtensionData(const QString& contributorId,
const ConfigurationElementDescription& description,
const SmartPointer<RegistryObject>& parent, bool persist)
{
ConfigurationElement::Pointer currentConfigurationElement = GetElementFactory()->CreateConfigurationElement(persist);
currentConfigurationElement->SetContributorId(contributorId);
currentConfigurationElement->SetName(description.GetName());
QList<ConfigurationElementAttribute> descriptionProperties = description.GetAttributes();
QList<QString> properties;
if (!descriptionProperties.empty())
{
for (int i = 0; i < descriptionProperties.size(); i++)
{
properties.push_back(descriptionProperties[i].GetName());
properties.push_back(Translate(descriptionProperties[i].GetValue(), nullptr));
}
}
currentConfigurationElement->SetProperties(properties);
QString value = description.GetValue();
if (!value.isEmpty())
currentConfigurationElement->SetValue(value);
GetObjectManager()->Add(currentConfigurationElement, true);
// process children
QList<ConfigurationElementDescription> children = description.GetChildren();
if (!children.empty())
{
for (int i = 0; i < children.size(); i++)
{
CreateExtensionData(contributorId, children[i], currentConfigurationElement, persist);
}
}
QList<int> newValues = parent->GetRawChildren();
newValues.push_back(currentConfigurationElement->GetObjectId());
parent->SetRawChildren(newValues);
currentConfigurationElement->SetParentId(parent->GetObjectId());
currentConfigurationElement->SetParentType(parent.Cast<ConfigurationElement>() ?
RegistryObjectManager::CONFIGURATION_ELEMENT :
RegistryObjectManager::EXTENSION);
}
bool ExtensionRegistry::RemoveObject(const SmartPointer<RegistryObject>& registryObject,
bool isExtensionPoint, QObject* token)
{
if (!CheckReadWriteAccess(token, registryObject->ShouldPersist()))
throw ctkInvalidArgumentException("Unauthorized access to the ExtensionRegistry.removeExtension() method. Check if proper access token is supplied.");
int id = registryObject->GetObjectId();
QWriteLocker l(&access);
eventDelta = CombinedEventDelta::RecordRemoval();
if (isExtensionPoint)
{
RemoveExtensionPoint(id);
}
else
{
RemoveExtension(id);
}
QHash<int, RegistryObject::Pointer> removed;
removed.insert(id, registryObject);
// There is some asymmetry between extension and extension point removal. Removing extension point makes
// extensions "orphans" but does not remove them. As a result, only extensions needs to be processed.
if (!isExtensionPoint)
{
registryObjects->AddAssociatedObjects(removed, registryObject);
}
registryObjects->RemoveObjects(removed);
registryObjects->AddNavigableObjects(removed);
IObjectManager::Pointer manager = registryObjects->CreateDelegatingObjectManager(removed);
//GetDelta(namespaze)->SetObjectManager(manager);
//eventDelta->SetObjectManager(manager);
registryObjects->UnlinkChildFromContributions(id);
FireRegistryChangeEvent();
eventDelta.Reset();
return true;
}
void ExtensionRegistry::SetFileManager(const QString& /*cacheBase*/, bool /*isCacheReadOnly*/)
{
// if (cacheStorageManager != nullptr)
// cacheStorageManager->Close(); // close existing file manager first
// if (cacheBase != null) {
// cacheStorageManager = new StorageManager(cacheBase, isCacheReadOnly ? "none" : null, isCacheReadOnly); //$NON-NLS-1$
// try {
// cacheStorageManager.open(!isCacheReadOnly);
// } catch (IOException e) {
// // Ignore the exception. The registry will be rebuilt from source.
// }
// }
}
void ExtensionRegistry::EnterRead()
{
access.lockForRead();
}
void ExtensionRegistry::ExitRead()
{
access.unlock();
}
void ExtensionRegistry::SetElementFactory()
{
if (isMultiLanguage)
{
throw ctkRuntimeException("Multi-language registry not supported yet.");
//theRegistryObjectFactory = new RegistryObjectFactoryMulti(this);
}
else
{
theRegistryObjectFactory.reset(new RegistryObjectFactory(this));
}
}
//TableReader ExtensionRegistry::getTableReader() const
//{
// return theTableReader;
//}
bool ExtensionRegistry::CheckCache()
{
// for (int index = 0; index < strategy.getLocationsLength(); index++) {
// File possibleCacheLocation = strategy.getStorage(index);
// if (possibleCacheLocation == null)
// break; // bail out on the first null
// setFileManager(possibleCacheLocation, strategy.isCacheReadOnly(index));
// if (cacheStorageManager != null) {
// // check this new location:
// File cacheFile = null;
// try {
// cacheFile = cacheStorageManager.lookup(TableReader.getTestFileName(), false);
// } catch (IOException e) {
// //Ignore the exception. The registry will be rebuilt from the xml files.
// }
// if (cacheFile != null && cacheFile.isFile())
// return true; // found the appropriate location
// }
// }
return false;
}
void ExtensionRegistry::StopChangeEventScheduler()
{
if (!eventThread.isNull())
{
Queue::Locker l(&queue);
eventThread->interrupt();
eventThread->wait();
eventThread.reset();
}
}
SmartPointer<RegistryObjectManager> ExtensionRegistry::GetObjectManager() const
{
return registryObjects;
}
void ExtensionRegistry::AddListener(IRegistryEventListener* listener, const QString& extensionPointId)
{
AddListenerInternal(listener, extensionPointId.isEmpty() ? IExtensionPointFilter(nullptr)
: IExtensionPointFilter(new SimpleExtensionPointFilter(extensionPointId)));
}
void ExtensionRegistry::AddListener(IRegistryEventListener* listener, const IExtensionPointFilter& filter)
{
this->AddListenerInternal(listener, filter);
}
QList<SmartPointer<IConfigurationElement> > ExtensionRegistry::GetConfigurationElementsFor(const QString& extensionPointId) const
{
// this is just a convenience API - no need to do any sync'ing here
int lastdot = extensionPointId.lastIndexOf('.');
if (lastdot == -1)
{
QList<IConfigurationElement::Pointer>();
}
return GetConfigurationElementsFor(extensionPointId.left(lastdot), extensionPointId.mid(lastdot + 1));
}
QList<SmartPointer<IConfigurationElement> > ExtensionRegistry::GetConfigurationElementsFor(const QString& pluginId,
const QString& extensionPointSimpleId) const
{
// this is just a convenience API - no need to do any sync'ing here
IExtensionPoint::Pointer extPoint = this->GetExtensionPoint(pluginId, extensionPointSimpleId);
if (extPoint.IsNull())
return QList<IConfigurationElement::Pointer>();
return extPoint->GetConfigurationElements();
}
QList<SmartPointer<IConfigurationElement> > ExtensionRegistry::GetConfigurationElementsFor(const QString& pluginId,
const QString& extensionPointName,
const QString& extensionId) const
{
// this is just a convenience API - no need to do any sync'ing here
IExtension::Pointer extension = this->GetExtension(pluginId, extensionPointName, extensionId);
if (extension.IsNull())
return QList<IConfigurationElement::Pointer>();
return extension->GetConfigurationElements();
}
SmartPointer<IExtension> ExtensionRegistry::GetExtension(const QString& extensionId) const
{
if (extensionId.isEmpty())
return IExtension::Pointer();
int lastdot = extensionId.lastIndexOf('.');
if (lastdot == -1)
return IExtension::Pointer();
QString namespaze = extensionId.left(lastdot);
QList<ExtensionHandle::Pointer> extensions;
{
QReadLocker l(&access);
extensions = registryObjects->GetExtensionsFromNamespace(namespaze);
}
for (int i = 0; i < extensions.size(); i++)
{
ExtensionHandle::Pointer suspect = extensions[i];
if (extensionId == suspect->GetUniqueIdentifier())
return suspect;
}
return IExtension::Pointer();
}
SmartPointer<IExtension> ExtensionRegistry::GetExtension(const QString& extensionPointId, const QString& extensionId) const
{
// this is just a convenience API - no need to do any sync'ing here
int lastdot = extensionPointId.lastIndexOf('.');
if (lastdot == -1)
return IExtension::Pointer();
return GetExtension(extensionPointId.left(lastdot), extensionPointId.mid(lastdot + 1), extensionId);
}
SmartPointer<IExtension> ExtensionRegistry::GetExtension(const QString& pluginId,
const QString& extensionPointName,
const QString& extensionId) const
{
// this is just a convenience API - no need to do any sync'ing here
IExtensionPoint::Pointer extPoint = GetExtensionPoint(pluginId, extensionPointName);
if (extPoint.IsNotNull())
return extPoint->GetExtension(extensionId);
return IExtension::Pointer();
}
SmartPointer<IExtensionPoint> ExtensionRegistry::GetExtensionPoint(const QString& xptUniqueId) const
{
QReadLocker l(&access);
return registryObjects->GetExtensionPointHandle(xptUniqueId);
}
SmartPointer<IExtensionPoint> ExtensionRegistry::GetExtensionPoint(const QString& elementName, const QString& xpt) const
{
QReadLocker l(&access);
return registryObjects->GetExtensionPointHandle(elementName + '.' + xpt);
}
QList<SmartPointer<IExtensionPoint> > ExtensionRegistry::GetExtensionPoints() const
{
QList<ExtensionPointHandle::Pointer> handles;
{
QReadLocker l(&access);
handles = registryObjects->GetExtensionPointsHandles();
}
QList<IExtensionPoint::Pointer> result;
foreach(ExtensionPointHandle::Pointer handle, handles)
{
result.push_back(handle);
}
return result;
}
QList<SmartPointer<IExtensionPoint> > ExtensionRegistry::GetExtensionPoints(const QString& namespaceName) const
{
QList<ExtensionPointHandle::Pointer> handles;
{
QReadLocker l(&access);
handles = registryObjects->GetExtensionPointsFromNamespace(namespaceName);
}
QList<IExtensionPoint::Pointer> result;
foreach(ExtensionPointHandle::Pointer handle, handles)
{
result.push_back(handle);
}
return result;
}
QList<SmartPointer<IExtension> > ExtensionRegistry::GetExtensions(const QString& namespaceName) const
{
QList<ExtensionHandle::Pointer> handles;
{
QReadLocker l(&access);
handles = registryObjects->GetExtensionsFromNamespace(namespaceName);
}
QList<IExtension::Pointer> result;
foreach (ExtensionHandle::Pointer handle, handles)
{
result.push_back(handle);
}
return result;
}
QList<SmartPointer<IExtension> > ExtensionRegistry::GetExtensions(const SmartPointer<IContributor>& contributor) const
{
RegistryContributor::Pointer regContributor = contributor.Cast<RegistryContributor>();
if (regContributor.IsNull())
throw ctkInvalidArgumentException("Contributor must be a RegistryContributor."); // should never happen
QString contributorId = regContributor->GetActualId();
QList<ExtensionHandle::Pointer> handles;
{
QReadLocker l(&access);
handles = registryObjects->GetExtensionsFromContributor(contributorId);
}
QList<IExtension::Pointer> result;
foreach (ExtensionHandle::Pointer handle, handles)
{
result.push_back(handle);
}
return result;
}
QList<SmartPointer<IExtensionPoint> > ExtensionRegistry::GetExtensionPoints(const SmartPointer<IContributor>& contributor) const
{
RegistryContributor::Pointer regContributor = contributor.Cast<RegistryContributor>();
if (regContributor.IsNull())
throw ctkInvalidArgumentException("Contributor must be a RegistryContributor."); // should never happen
QString contributorId = regContributor->GetActualId();
QList<ExtensionPointHandle::Pointer> handles;
{
QReadLocker l(&access);
handles = registryObjects->GetExtensionPointsFromContributor(contributorId);
}
QList<IExtensionPoint::Pointer> result;
foreach (ExtensionPointHandle::Pointer handle, handles)
{
result.push_back(handle);
}
return result;
}
QList<QString> ExtensionRegistry::GetNamespaces() const
{
QReadLocker l(&access);
QList<KeyedElement::Pointer> namespaceElements = registryObjects->GetNamespacesIndex().Elements();
QList<QString> namespaceNames;
for (int i = 0; i < namespaceElements.size(); i++)
{
namespaceNames.push_back(namespaceElements[i]->GetKey());
}
return namespaceNames;
}
bool ExtensionRegistry::HasContributor(const SmartPointer<IContributor>& contributor) const
{
RegistryContributor::Pointer regContributor = contributor.Cast<RegistryContributor>();
if (regContributor.IsNull())
throw ctkInvalidArgumentException("Contributor must be a RegistryContributor."); // should never happen
QString contributorId = regContributor->GetActualId();
return HasContributor(contributorId);
}
bool ExtensionRegistry::HasContributor(const QString& contributorId) const
{
QReadLocker l(&access);
return registryObjects->HasContribution(contributorId);
}
void ExtensionRegistry::Remove(const QString& removedContributorId, long timestamp)
{
Remove(removedContributorId);
if (timestamp != 0)
aggregatedTimestamp.Remove(timestamp);
}
void ExtensionRegistry::RemoveContributor(const SmartPointer<IContributor>& contributor, QObject* key)
{
RegistryContributor::Pointer regContributor = contributor.Cast<RegistryContributor>();
if (regContributor.IsNull())
throw ctkInvalidArgumentException("Contributor must be a RegistryContributor."); // should never happen
if (!CheckReadWriteAccess(key, true))
throw ctkInvalidArgumentException("Unauthorized access to the ExtensionRegistry.removeContributor() method. Check if proper access token is supplied.");
QString contributorId = regContributor->GetActualId();
Remove(contributorId);
}
void ExtensionRegistry::Remove(const QString& removedContributorId)
{
QWriteLocker l(&access);
eventDelta = CombinedEventDelta::RecordRemoval();
BasicRemove(removedContributorId);
FireRegistryChangeEvent();
eventDelta.Reset();
}
void ExtensionRegistry::RemoveListener(IRegistryEventListener* listener)
{
listeners.Remove(ListenerInfo(listener, IExtensionPointFilter(nullptr)));
}
ExtensionRegistry::ExtensionRegistry(RegistryStrategy* registryStrategy, QObject* masterToken, QObject* userToken)
: registryObjects(nullptr), isMultiLanguage(false), mlErrorLogged(false), eventThread(nullptr)
{
isMultiLanguage = RegistryProperties::GetProperty(RegistryConstants::PROP_REGISTRY_MULTI_LANGUAGE) == "true";
if (registryStrategy != nullptr)
strategy.reset(registryStrategy);
else
strategy.reset(new RegistryStrategy(QList<QString>(), QList<bool>(), nullptr));
this->masterToken = masterToken;
this->userToken = userToken;
registryObjects = new RegistryObjectManager(this);
bool isRegistryFilledFromCache = false; // indicates if registry was able to use cache to populate it's content
if (strategy->CacheUse())
{
// Try to read the registry from the cache first. If that fails, create a new registry
QElapsedTimer timer;
if (Debug())
timer.start();
//The cache is made of several files, find the real names of these other files. If all files are found, try to initialize the objectManager
if (CheckCache())
{
// TODO Registry Cache
// try {
// theTableReader.setTableFile(cacheStorageManager.lookup(TableReader.TABLE, false));
// theTableReader.setExtraDataFile(cacheStorageManager.lookup(TableReader.EXTRA, false));
// theTableReader.setMainDataFile(cacheStorageManager.lookup(TableReader.MAIN, false));
// theTableReader.setContributionsFile(cacheStorageManager.lookup(TableReader.CONTRIBUTIONS, false));
// theTableReader.setContributorsFile(cacheStorageManager.lookup(TableReader.CONTRIBUTORS, false));
// theTableReader.setNamespacesFile(cacheStorageManager.lookup(TableReader.NAMESPACES, false));
// theTableReader.setOrphansFile(cacheStorageManager.lookup(TableReader.ORPHANS, false));
// long timestamp = strategy.getContributionsTimestamp();
// isRegistryFilledFromCache = registryObjects.init(timestamp);
// if (isRegistryFilledFromCache)
// aggregatedTimestamp.set(timestamp);
// } catch (IOException e) {
// // The registry will be rebuilt from the xml files. Make sure to clear anything filled
// // from cache so that we won't have partially filled items.
// isRegistryFilledFromCache = false;
// clearRegistryCache();
// log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, 0, RegistryMessages.registry_bad_cache, e));
// }
}
// if (!isRegistryFilledFromCache)
// {
// // set cache storage manager to a first writable location
// for (int index = 0; index < strategy.getLocationsLength(); index++) {
// if (!strategy.isCacheReadOnly(index)) {
// setFileManager(strategy.getStorage(index), false);
// break;
// }
// }
// }
if (Debug() && isRegistryFilledFromCache)
BERRY_INFO << "Reading registry cache: " << timer.elapsed() << "ms";
if (Debug())
{
if (!isRegistryFilledFromCache)
BERRY_INFO << "Reloading registry from manifest files...";
else
BERRY_INFO << "Using registry cache...";
}
}
if (DebugEvents())
{
struct DebugRegistryListener : public IRegistryEventListener
{
void Added(const QList<IExtension::Pointer>& extensions) override
{
BERRY_INFO << "Registry extensions ADDED:";
foreach(IExtension::Pointer extension, extensions)
{
BERRY_INFO << "\t" << extension->GetExtensionPointUniqueIdentifier() << " - "
<< extension->GetNamespaceIdentifier() << "." << extension->GetSimpleIdentifier();
}
}
void Removed(const QList<IExtension::Pointer>& extensions) override
{
BERRY_INFO << "Registry extensions REMOVED:";
foreach(IExtension::Pointer extension, extensions)
{
BERRY_INFO << "\t" << extension->GetExtensionPointUniqueIdentifier() << " - "
<< extension->GetNamespaceIdentifier() << "." << extension->GetSimpleIdentifier();
}
}
void Added(const QList<IExtensionPoint::Pointer>& extensionPoints) override
{
BERRY_INFO << "Registry extension-points ADDED:";
foreach(IExtensionPoint::Pointer extensionPoint, extensionPoints)
{
BERRY_INFO << "\t" << extensionPoint->GetUniqueIdentifier();
}
}
void Removed(const QList<IExtensionPoint::Pointer>& extensionPoints) override
{
BERRY_INFO << "Registry extension-points REMOVED:";
foreach(IExtensionPoint::Pointer extensionPoint, extensionPoints)
{
BERRY_INFO << "\t" << extensionPoint->GetUniqueIdentifier();
}
}
};
debugRegistryListener.reset(new DebugRegistryListener());
AddListener(debugRegistryListener.data());
}
// Do extra start processing if specified in the registry strategy
strategy->OnStart(this, isRegistryFilledFromCache);
}
ExtensionRegistry::~ExtensionRegistry()
{
}
void ExtensionRegistry::Stop(QObject* /*key*/)
{
- // If the registry creator specified a key token, check that the key mathches it
+ // If the registry creator specified a key token, check that the key matches it
// (it is assumed that registry owner keeps the key to prevent unautorized access).
if (masterToken != nullptr && masterToken != nullptr)
{
throw ctkInvalidArgumentException("Unauthorized access to the ExtensionRegistry.stop() method. Check if proper access token is supplied."); //$NON-NLS-1$
}
// Do extra stop processing if specified in the registry strategy
strategy->OnStop(this);
StopChangeEventScheduler();
// if (cacheStorageManager == nullptr)
// return;
// if (!registryObjects.isDirty() || cacheStorageManager.isReadOnly()) {
// cacheStorageManager.close();
// theTableReader.close();
// return;
// }
// File tableFile = null;
// File mainFile = null;
// File extraFile = null;
// File contributionsFile = null;
// File contributorsFile = null;
// File namespacesFile = null;
// File orphansFile = null;
// TableWriter theTableWriter = new TableWriter(this);
// try {
// cacheStorageManager.lookup(TableReader.TABLE, true);
// cacheStorageManager.lookup(TableReader.MAIN, true);
// cacheStorageManager.lookup(TableReader.EXTRA, true);
// cacheStorageManager.lookup(TableReader.CONTRIBUTIONS, true);
// cacheStorageManager.lookup(TableReader.CONTRIBUTORS, true);
// cacheStorageManager.lookup(TableReader.NAMESPACES, true);
// cacheStorageManager.lookup(TableReader.ORPHANS, true);
// tableFile = File.createTempFile(TableReader.TABLE, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$
// mainFile = File.createTempFile(TableReader.MAIN, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$
// extraFile = File.createTempFile(TableReader.EXTRA, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$
// contributionsFile = File.createTempFile(TableReader.CONTRIBUTIONS, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$
// contributorsFile = File.createTempFile(TableReader.CONTRIBUTORS, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$
// namespacesFile = File.createTempFile(TableReader.NAMESPACES, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$
// orphansFile = File.createTempFile(TableReader.ORPHANS, ".new", cacheStorageManager.getBase()); //$NON-NLS-1$
// theTableWriter.setTableFile(tableFile);
// theTableWriter.setExtraDataFile(extraFile);
// theTableWriter.setMainDataFile(mainFile);
// theTableWriter.setContributionsFile(contributionsFile);
// theTableWriter.setContributorsFile(contributorsFile);
// theTableWriter.setNamespacesFile(namespacesFile);
// theTableWriter.setOrphansFile(orphansFile);
// } catch (IOException e) {
// cacheStorageManager.close();
// return; //Ignore the exception since we can recompute the cache
// }
// try {
// long timestamp;
// // A bit of backward compatibility: if registry was modified, but timestamp was not,
// // it means that the new timestamp tracking mechanism was not used. In this case
// // explicitly obtain timestamps for all contributions. Note that this logic
// // maintains a problem described in the bug 104267 for contributions that
// // don't use the timestamp tracking mechanism.
// if (aggregatedTimestamp.isModifed())
// timestamp = aggregatedTimestamp.getContentsTimestamp(); // use timestamp tracking
// else
// timestamp = strategy.getContributionsTimestamp(); // use legacy approach
// if (theTableWriter.saveCache(registryObjects, timestamp))
// cacheStorageManager.update(new QString[] {TableReader.TABLE, TableReader.MAIN, TableReader.EXTRA, TableReader.CONTRIBUTIONS, TableReader.CONTRIBUTORS, TableReader.NAMESPACES, TableReader.ORPHANS}, new QString[] {tableFile.getName(), mainFile.getName(), extraFile.getName(), contributionsFile.getName(), contributorsFile.getName(), namespacesFile.getName(), orphansFile.getName()});
// } catch (IOException e) {
// //Ignore the exception since we can recompute the cache
// }
// theTableReader.close();
// cacheStorageManager.close();
}
void ExtensionRegistry::ClearRegistryCache()
{
// QString[] keys = new QString[] {TableReader.TABLE, TableReader.MAIN, TableReader.EXTRA, TableReader.CONTRIBUTIONS, TableReader.ORPHANS};
// for (int i = 0; i < keys.length; i++)
// try {
// cacheStorageManager.remove(keys[i]);
// } catch (IOException e) {
// log(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, IStatus.ERROR, RegistryMessages.meta_registryCacheReadProblems, e));
// }
aggregatedTimestamp.Reset();
}
RegistryObjectFactory* ExtensionRegistry::GetElementFactory()
{
if (theRegistryObjectFactory.isNull())
SetElementFactory();
return theRegistryObjectFactory.data();
}
void ExtensionRegistry::Log(const SmartPointer<IStatus>& status) const
{
strategy->Log(status);
}
QString ExtensionRegistry::Translate(const QString& key, QTranslator* resources) const
{
if (isMultiLanguage)
return key;
return strategy->Translate(key, resources);
}
bool ExtensionRegistry::Debug() const
{
return strategy->Debug();
}
bool ExtensionRegistry::DebugEvents() const
{
return strategy->DebugRegistryEvents();
}
bool ExtensionRegistry::UseLazyCacheLoading() const
{
return strategy->CacheLazyLoading();
}
long ExtensionRegistry::ComputeState() const
{
return strategy->GetContainerTimestamp();
}
QObject* ExtensionRegistry::CreateExecutableExtension(const SmartPointer<RegistryContributor>& defaultContributor,
const QString& className, const QString& requestedContributorName)
{
return strategy->CreateExecutableExtension(defaultContributor, className, requestedContributorName);
}
void ExtensionRegistry::ProcessChangeEvent(
const QList<ListenerInfo>& listenerInfos, const CombinedEventDelta& scheduledDelta)
{
for (int i = 0; i < listenerInfos.size(); i++)
{
const ListenerInfo& listenerInfo = listenerInfos[i];
IRegistryEventListener* extensionListener = listenerInfo.listener;
QList<IExtension::Pointer> extensions = scheduledDelta.GetExtensions(listenerInfo.filter);
QList<IExtensionPoint::Pointer> extensionPoints = scheduledDelta.GetExtensionPoints(listenerInfo.filter);
// notification order - on addition: extension points; then extensions
if (scheduledDelta.IsAddition())
{
if (!extensionPoints.empty())
extensionListener->Added(extensionPoints);
if (!extensions.empty())
extensionListener->Added(extensions);
}
else
{ // on removal: extensions; then extension points
if (!extensions.empty())
extensionListener->Removed(extensions);
if (!extensionPoints.empty())
extensionListener->Removed(extensionPoints);
}
}
IObjectManager::Pointer manager = scheduledDelta.GetObjectManager();
if (manager.IsNotNull())
manager->Close();
}
void ExtensionRegistry::ScheduleChangeEvent(const QList<ListenerInfo>& listenerInfos,
const CombinedEventDelta& scheduledDelta)
{
QueueElement newElement(listenerInfos, scheduledDelta);
if (eventThread.isNull())
{
eventThread.reset(new RegistryEventThread(this, queue));
eventThread->start();
}
{
Queue::Locker l(&queue);
queue.push_back(newElement);
queue.notify();
}
}
bool ExtensionRegistry::AddContribution(QIODevice* is, const SmartPointer<IContributor>& contributor,
bool persist, const QString& contributionName,
QTranslator* translationBundle, QObject* key, long timestamp)
{
bool result = AddContribution(is, contributor, persist, contributionName, translationBundle, key);
if (timestamp != 0)
aggregatedTimestamp.Add(timestamp);
return result;
}
bool ExtensionRegistry::AddContribution(QIODevice* is, const SmartPointer<IContributor>& contributor, bool persist,
const QString& contributionName, QTranslator* translationBundle, QObject* key)
{
if (!CheckReadWriteAccess(key, persist))
throw ctkInvalidArgumentException("Unauthorized access to the ExtensionRegistry::AddContribution() method. Check if proper access token is supplied.");
RegistryContributor::Pointer internalContributor = contributor.Cast<RegistryContributor>();
registryObjects->AddContributor(internalContributor); // only adds a contributor if it is not already present
QString ownerName = internalContributor->GetActualName();
QString message = QString("Problems parsing plug-in manifest for: \"%1\".").arg(ownerName);
MultiStatus::Pointer problems(new MultiStatus(RegistryMessages::OWNER_NAME,
ExtensionsParser::PARSE_PROBLEM, message, BERRY_STATUS_LOC));
ExtensionsParser parser(problems, this);
RegistryContribution::Pointer contribution =
GetElementFactory()->CreateContribution(internalContributor->GetActualId(), persist);
try
{
QXmlInputSource xmlInput(is);
bool success = parser.parseManifest(strategy->GetXMLParser(), &xmlInput, contributionName,
GetObjectManager().GetPointer(), contribution, translationBundle);
int status = problems->GetSeverity();
if (status != IStatus::OK_TYPE || !success)
{
Log(problems);
if (status == IStatus::ERROR_TYPE || status == IStatus::CANCEL_TYPE || !success)
return false;
}
}
catch (const ctkException& e)
{
LogError(ownerName, contributionName, e);
return false;
}
Add(contribution); // the add() method does synchronization
return true;
}
bool ExtensionRegistry::AddExtensionPoint(const QString& identifier, const SmartPointer<IContributor>& contributor,
bool persist, const QString& label, const QString& schemaReference, QObject* token)
{
if (!CheckReadWriteAccess(token, persist))
throw ctkInvalidArgumentException("Unauthorized access to the ExtensionRegistry::AddExtensionPoint() method. Check if proper access token is supplied.");
RegistryContributor::Pointer internalContributor = contributor.Cast<RegistryContributor>();
registryObjects->AddContributor(internalContributor); // only adds a contributor if it is not already present
QString contributorId = internalContributor->GetActualId();
// Extension point Id might not be null
if (identifier.isEmpty())
{
QString message = QString("Missing ID for the extension point \"%1\". Element ignored.").arg(label);
IStatus::Pointer status(new Status(IStatus::ERROR_TYPE, RegistryMessages::OWNER_NAME, 0, message, BERRY_STATUS_LOC));
Log(status);
}
// addition wraps in a contribution
RegistryContribution::Pointer contribution = GetElementFactory()->CreateContribution(contributorId, persist);
ExtensionPoint::Pointer currentExtPoint = GetElementFactory()->CreateExtensionPoint(persist);
QString uniqueId;
QString namespaceName;
int simpleIdStart = identifier.lastIndexOf('.');
if (simpleIdStart == -1)
{
namespaceName = contribution->GetDefaultNamespace();
uniqueId = namespaceName + '.' + identifier;
}
else
{
namespaceName = identifier.left(simpleIdStart);
uniqueId = identifier;
}
currentExtPoint->SetUniqueIdentifier(uniqueId);
currentExtPoint->SetNamespace(namespaceName);
QString labelNLS = Translate(label, nullptr);
currentExtPoint->SetLabel(labelNLS);
currentExtPoint->SetSchema(schemaReference);
if (!GetObjectManager()->AddExtensionPoint(currentExtPoint, true))
{
if (Debug())
{
QString msg = QString("Ignored duplicate extension point \"%1\" supplied by \"%2\".").arg(uniqueId).arg(contribution->GetDefaultNamespace());
IStatus::Pointer status(new Status(IStatus::ERROR_TYPE, RegistryMessages::OWNER_NAME, 0, msg, BERRY_STATUS_LOC));
Log(status);
}
return false;
}
currentExtPoint->SetContributorId(contributorId);
// array format: {Number of extension points, Number of extensions, Extension Id}
QList<int> contributionChildren;
// Put the extension points into this namespace
contributionChildren.push_back(1);
contributionChildren.push_back(0);
contributionChildren.push_back(currentExtPoint->GetObjectId());
contribution->SetRawChildren(contributionChildren);
Add(contribution);
return true;
}
bool ExtensionRegistry::AddExtension(const QString& identifier, const SmartPointer<IContributor>& contributor,
bool persist, const QString& label, const QString& extensionPointId,
const ConfigurationElementDescription& configurationElements, QObject* token)
{
if (!CheckReadWriteAccess(token, persist))
throw ctkInvalidArgumentException("Unauthorized access to the ExtensionRegistry::AddExtensionPoint() method. Check if proper access token is supplied.");
// prepare namespace information
RegistryContributor::Pointer internalContributor = contributor.Cast<RegistryContributor>();
registryObjects->AddContributor(internalContributor); // only adds a contributor if it is not already present
QString contributorId = internalContributor->GetActualId();
// addition wraps in a contribution
RegistryContribution::Pointer contribution = GetElementFactory()->CreateContribution(contributorId, persist);
Extension::Pointer currentExtension = GetElementFactory()->CreateExtension(persist);
QString simpleId;
QString namespaceName;
int simpleIdStart = identifier.lastIndexOf('.');
if (simpleIdStart != -1)
{
simpleId = identifier.mid(simpleIdStart + 1);
namespaceName = identifier.left(simpleIdStart);
}
else
{
simpleId = identifier;
namespaceName = contribution->GetDefaultNamespace();
}
currentExtension->SetSimpleIdentifier(simpleId);
currentExtension->SetNamespaceIdentifier(namespaceName);
QString extensionLabelNLS = Translate(label, nullptr);
currentExtension->SetLabel(extensionLabelNLS);
QString targetExtensionPointId;
if (extensionPointId.indexOf('.') == -1) // No dots -> namespace name added at the start
targetExtensionPointId = contribution->GetDefaultNamespace() + '.' + extensionPointId;
else
targetExtensionPointId = extensionPointId;
currentExtension->SetExtensionPointIdentifier(targetExtensionPointId);
// if we have an Id specified, check for duplicates. Only issue warning if duplicate found
// as it might still work fine - depending on the access pattern.
if (!simpleId.isNull() && Debug())
{
QString uniqueId = namespaceName + '.' + simpleId;
IExtension::Pointer existingExtension = GetExtension(uniqueId);
if (existingExtension.IsNotNull())
{
QString currentSupplier = contribution->GetDefaultNamespace();
QString existingSupplier = existingExtension->GetContributor()->GetName();
QString msg = QString("Extensions supplied by \"%1\" and \"%2\" have the same Id: \"%3\".")
.arg(currentSupplier).arg(existingSupplier).arg(uniqueId);
IStatus::Pointer status(new Status(IStatus::WARNING_TYPE, RegistryMessages::OWNER_NAME, 0, msg, BERRY_STATUS_LOC));
Log(status);
return false;
}
}
GetObjectManager()->Add(currentExtension, true);
CreateExtensionData(contributorId, configurationElements, currentExtension, persist);
currentExtension->SetContributorId(contributorId);
QList<int> contributionChildren;
contributionChildren.push_back(0);
contributionChildren.push_back(1);
contributionChildren.push_back(currentExtension->GetObjectId());
contribution->SetRawChildren(contributionChildren);
Add(contribution);
return true;
}
bool ExtensionRegistry::RemoveExtension(const SmartPointer<IExtension>& extension, QObject* token)
{
ExtensionHandle::Pointer handle = extension.Cast<ExtensionHandle>();
if (handle.IsNull())
return false;
return RemoveObject(handle->GetObject(), false, token);
}
bool ExtensionRegistry::RemoveExtensionPoint(const SmartPointer<IExtensionPoint>& extensionPoint, QObject* token)
{
ExtensionPointHandle::Pointer handle = extensionPoint.Cast<ExtensionPointHandle>();
if (handle.IsNull())
return false;
return RemoveObject(handle->GetObject(), true, token);
}
QList<SmartPointer<IContributor> > ExtensionRegistry::GetAllContributors() const
{
QList<IContributor::Pointer> result;
QReadLocker l(&access);
foreach(RegistryContributor::Pointer contributor, registryObjects->GetContributors().values())
{
result.push_back(contributor);
}
return result;
}
bool ExtensionRegistry::IsMultiLanguage() const
{
return isMultiLanguage;
}
QList<QString> ExtensionRegistry::Translate(const QList<QString>& nonTranslated, const SmartPointer<IContributor>& contributor,
const QLocale& locale) const
{
return strategy->Translate(nonTranslated, contributor, locale);
}
QLocale ExtensionRegistry::GetLocale() const
{
return strategy->GetLocale();
}
void ExtensionRegistry::LogMultiLangError() const
{
if (mlErrorLogged) // only log this error ones
return;
IStatus::Pointer status(new Status(IStatus::ERROR_TYPE, RegistryMessages::OWNER_NAME, 0,
QString("The requested multi-language operation is not enabled. See runtime option \"")
+ RegistryConstants::PROP_REGISTRY_MULTI_LANGUAGE + "\".", ctkInvalidArgumentException(""), BERRY_STATUS_LOC));
Log(status);
mlErrorLogged = true;
}
}
diff --git a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPerspective.cpp b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPerspective.cpp
index 5dfb2ac26a..2a7cdb76c2 100644
--- a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPerspective.cpp
+++ b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPerspective.cpp
@@ -1,45 +1,45 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "berryHelpPerspective.h"
namespace berry {
const QString HelpPerspective::ID = "org.blueberry.perspectives.help";
HelpPerspective::HelpPerspective()
{
}
void HelpPerspective::CreateInitialLayout(berry::IPageLayout::Pointer layout)
{
QString editorArea = layout->GetEditorArea();
layout->AddView("org.blueberry.views.helpsearch",
berry::IPageLayout::LEFT, 0.3f, editorArea);
berry::IFolderLayout::Pointer leftFolder =
layout->CreateFolder("lefttop", berry::IPageLayout::TOP, 0.65f, "org.blueberry.views.helpsearch");
- leftFolder->AddView("org.blueberry.views.helpcontents");
leftFolder->AddView("org.blueberry.views.helpindex");
+ leftFolder->AddView("org.blueberry.views.helpcontents");
// Make every help related view unclosable
IViewLayout::Pointer lo = layout->GetViewLayout("org.blueberry.views.helpsearch");
lo->SetCloseable(false);
lo = layout->GetViewLayout("org.blueberry.views.helpcontents");
lo->SetCloseable(false);
lo = layout->GetViewLayout("org.blueberry.views.helpindex");
lo->SetCloseable(false);
}
}
diff --git a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPluginActivator.cpp b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPluginActivator.cpp
index 38acff3ef9..f849a5dbd2 100644
--- a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPluginActivator.cpp
+++ b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPluginActivator.cpp
@@ -1,475 +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.
============================================================================*/
#include "berryHelpPluginActivator.h"
#include "berryHelpContentView.h"
#include "berryHelpIndexView.h"
#include "berryHelpSearchView.h"
#include "berryHelpEditor.h"
#include "berryHelpEditorInput.h"
#include "berryHelpEditorInputFactory.h"
#include "berryHelpPerspective.h"
+#include "berryHelpWebView.h"
#include "berryQHelpEngineConfiguration.h"
#include "berryQHelpEngineWrapper.h"
#include <berryPlatformUI.h>
#include <service/event/ctkEventConstants.h>
#include <QDir>
#include <QDateTime>
+#include <QTimer>
+#include <QWebEngineProfile>
+#include <QWebEngineUrlRequestJob>
+#include <QWebEngineUrlSchemeHandler>
+
+namespace
+{
+ class HelpDeviceReply final : public QIODevice
+ {
+ public:
+ HelpDeviceReply(const QUrl& request, const QByteArray& fileData);
+ ~HelpDeviceReply() override;
+
+ qint64 bytesAvailable() const override;
+ void close() override;
+
+ private:
+ qint64 readData(char* data, qint64 maxlen) override;
+ qint64 writeData(const char* data, qint64 maxlen) override;
+
+ QByteArray m_Data;
+ const qint64 m_OrigLen;
+ };
+
+ HelpDeviceReply::HelpDeviceReply(const QUrl&, const QByteArray& fileData)
+ : m_Data(fileData),
+ m_OrigLen(fileData.length())
+ {
+ this->setOpenMode(QIODevice::ReadOnly);
+
+ QTimer::singleShot(0, this, &QIODevice::readyRead);
+ QTimer::singleShot(0, this, &QIODevice::readChannelFinished);
+ }
+
+ HelpDeviceReply::~HelpDeviceReply()
+ {
+ }
+
+ qint64 HelpDeviceReply::bytesAvailable() const
+ {
+ return m_Data.length() + QIODevice::bytesAvailable();
+ }
+
+ void HelpDeviceReply::close()
+ {
+ QIODevice::close();
+ this->deleteLater();
+ }
+
+ qint64 HelpDeviceReply::readData(char* data, qint64 maxlen)
+ {
+ qint64 len = qMin(qint64(m_Data.length()), maxlen);
+
+ if (len)
+ {
+ memcpy(data, m_Data.constData(), len);
+ m_Data.remove(0, len);
+ }
+
+ return len;
+ }
+
+ qint64 HelpDeviceReply::writeData(const char*, qint64)
+ {
+ return 0;
+ }
+
+
+ class HelpUrlSchemeHandler final : public QWebEngineUrlSchemeHandler
+ {
+ public:
+ explicit HelpUrlSchemeHandler(QObject* parent = nullptr);
+ ~HelpUrlSchemeHandler() override;
+
+ void requestStarted(QWebEngineUrlRequestJob* job) override;
+ };
+
+ HelpUrlSchemeHandler::HelpUrlSchemeHandler(QObject* parent)
+ : QWebEngineUrlSchemeHandler(parent)
+ {
+ }
+
+ HelpUrlSchemeHandler::~HelpUrlSchemeHandler()
+ {
+ }
+
+ enum class ResolveUrlResult
+ {
+ Error,
+ Redirect,
+ Data
+ };
+
+ ResolveUrlResult ResolveUrl(const QUrl& url, QUrl& redirectedUrl, QByteArray& data)
+ {
+ auto& helpEngine = berry::HelpPluginActivator::getInstance()->getQHelpEngine();
+
+ const auto targetUrl = helpEngine.findFile(url);
+
+ if (!targetUrl.isValid())
+ return ResolveUrlResult::Error;
+
+ if (targetUrl != url)
+ {
+ redirectedUrl = targetUrl;
+ return ResolveUrlResult::Redirect;
+ }
+
+ data = helpEngine.fileData(targetUrl);
+ return ResolveUrlResult::Data;
+ }
+
+
+ void HelpUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob* job)
+ {
+ QUrl url = job->requestUrl();
+ QUrl redirectedUrl;
+ QByteArray data;
+
+ switch (ResolveUrl(url, redirectedUrl, data))
+ {
+ case ResolveUrlResult::Data:
+ job->reply(
+ berry::HelpWebView::mimeFromUrl(url).toLatin1(),
+ new HelpDeviceReply(url, data));
+ break;
+
+ case ResolveUrlResult::Redirect:
+ job->redirect(redirectedUrl);
+ break;
+
+ case ResolveUrlResult::Error:
+ job->reply(
+ QByteArrayLiteral("text/html"),
+ new HelpDeviceReply(url, berry::HelpWebView::m_PageNotFoundMessage.arg(url.toString()).toUtf8()));
+ break;
+ }
+ }
+}
namespace berry {
class HelpPerspectiveListener : public IPerspectiveListener
{
public:
Events::Types GetPerspectiveEventTypes() const override;
using IPerspectiveListener::PerspectiveChanged;
void PerspectiveOpened(const SmartPointer<IWorkbenchPage>& page, const IPerspectiveDescriptor::Pointer& perspective) override;
void PerspectiveChanged(const SmartPointer<IWorkbenchPage>& page, const IPerspectiveDescriptor::Pointer& perspective, const QString &changeId) override;
};
class HelpWindowListener : public IWindowListener
{
public:
HelpWindowListener();
~HelpWindowListener() override;
void WindowClosed(const IWorkbenchWindow::Pointer& window) override;
void WindowOpened(const IWorkbenchWindow::Pointer& window) override;
private:
// We use the same perspective listener for every window
QScopedPointer<IPerspectiveListener> perspListener;
};
HelpPluginActivator* HelpPluginActivator::instance = nullptr;
HelpPluginActivator::HelpPluginActivator()
- : pluginListener(nullptr)
+ : helpSchemeHandler(const_cast<QWebEngineUrlSchemeHandler*>(QWebEngineProfile::defaultProfile()->urlSchemeHandler("qthelp"))),
+ pluginListener(nullptr)
{
this->instance = this;
+
+ if (helpSchemeHandler == nullptr)
+ {
+ helpSchemeHandler = new HelpUrlSchemeHandler(this);
+ QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("qthelp", helpSchemeHandler);
+ }
}
HelpPluginActivator::~HelpPluginActivator()
{
instance = nullptr;
}
void
HelpPluginActivator::start(ctkPluginContext* context)
{
BERRY_REGISTER_EXTENSION_CLASS(berry::HelpContentView, context)
BERRY_REGISTER_EXTENSION_CLASS(berry::HelpIndexView, context)
BERRY_REGISTER_EXTENSION_CLASS(berry::HelpSearchView, context)
BERRY_REGISTER_EXTENSION_CLASS(berry::HelpEditor, context)
BERRY_REGISTER_EXTENSION_CLASS(berry::HelpEditorInputFactory, context)
BERRY_REGISTER_EXTENSION_CLASS(berry::HelpPerspective, context)
QFileInfo qhcInfo = context->getDataFile("qthelpcollection.qhc");
helpEngine.reset(new QHelpEngineWrapper(qhcInfo.absoluteFilePath()));
helpEngine->setReadOnly(false);
if (!helpEngine->setupData())
{
BERRY_ERROR << "QHelpEngine set-up failed: " << helpEngine->error().toStdString();
return;
}
helpEngineConfiguration.reset(new QHelpEngineConfiguration(context, *helpEngine.data()));
delete pluginListener;
pluginListener = new QCHPluginListener(context, helpEngine.data());
context->connectPluginListener(pluginListener, SLOT(pluginChanged(ctkPluginEvent)));
// register all QCH files from all the currently installed plugins
pluginListener->processPlugins();
helpEngine->initialDocSetupDone();
// Register a wnd listener which registers a perspective listener for each
// new window. The perspective listener opens the help home page in the window
// if no other help page is opened yet.
wndListener.reset(new HelpWindowListener());
PlatformUI::GetWorkbench()->AddWindowListener(wndListener.data());
// Register an event handler for CONTEXTHELP_REQUESTED events
helpContextHandler.reset(new HelpContextHandler);
ctkDictionary helpHandlerProps;
helpHandlerProps.insert(ctkEventConstants::EVENT_TOPIC, "org/blueberry/ui/help/CONTEXTHELP_REQUESTED");
context->registerService<ctkEventHandler>(helpContextHandler.data(), helpHandlerProps);
}
void
HelpPluginActivator::stop(ctkPluginContext* /*context*/)
{
delete pluginListener;
pluginListener = nullptr;
if (PlatformUI::IsWorkbenchRunning())
{
PlatformUI::GetWorkbench()->RemoveWindowListener(wndListener.data());
}
wndListener.reset();
helpEngineConfiguration.reset();
helpEngine.reset();
}
HelpPluginActivator *HelpPluginActivator::getInstance()
{
return instance;
}
QHelpEngineWrapper& HelpPluginActivator::getQHelpEngine()
{
return *helpEngine;
}
void HelpPluginActivator::linkActivated(IWorkbenchPage::Pointer page, const QUrl &link)
{
IEditorInput::Pointer input(new HelpEditorInput(link));
// see if an editor with the same input is already open
IEditorPart::Pointer reuseEditor = page->FindEditor(input);
if (reuseEditor)
{
// just activate it
page->Activate(reuseEditor);
}
else
{
// reuse the currently active editor, if it is a HelpEditor
reuseEditor = page->GetActiveEditor();
if (reuseEditor.IsNotNull() && page->GetReference(reuseEditor)->GetId() == HelpEditor::EDITOR_ID)
{
page->ReuseEditor(reuseEditor.Cast<IReusableEditor>(), input);
page->Activate(reuseEditor);
}
else
{
// get the last used HelpEditor instance
QList<IEditorReference::Pointer> editors =
page->FindEditors(IEditorInput::Pointer(nullptr), HelpEditor::EDITOR_ID, IWorkbenchPage::MATCH_ID);
if (editors.empty())
{
// no HelpEditor is currently open, create a new one
page->OpenEditor(input, HelpEditor::EDITOR_ID);
}
else
{
// reuse an existing editor
reuseEditor = editors.front()->GetEditor(false);
page->ReuseEditor(reuseEditor.Cast<IReusableEditor>(), input);
page->Activate(reuseEditor);
}
}
}
}
QCHPluginListener::QCHPluginListener(ctkPluginContext* context, QHelpEngine* helpEngine)
: delayRegistration(true), context(context), helpEngine(helpEngine)
{}
void QCHPluginListener::processPlugins()
{
QMutexLocker lock(&mutex);
processPlugins_unlocked();
}
void QCHPluginListener::pluginChanged(const ctkPluginEvent& event)
{
QMutexLocker lock(&mutex);
if (delayRegistration)
{
this->processPlugins_unlocked();
return;
}
/* Only should listen for RESOLVED and UNRESOLVED events.
*
* When a plugin is updated the Framework will publish an UNRESOLVED and
* then a RESOLVED event which should cause the plugin to be removed
* and then added back into the registry.
*
* When a plugin is uninstalled the Framework should publish an UNRESOLVED
* event and then an UNINSTALLED event so the plugin will have been removed
* by the UNRESOLVED event before the UNINSTALLED event is published.
*/
QSharedPointer<ctkPlugin> plugin = event.getPlugin();
switch (event.getType())
{
case ctkPluginEvent::RESOLVED :
addPlugin(plugin);
break;
case ctkPluginEvent::UNRESOLVED :
removePlugin(plugin);
break;
default:
break;
}
}
void QCHPluginListener::processPlugins_unlocked()
{
if (!delayRegistration) return;
foreach (QSharedPointer<ctkPlugin> plugin, context->getPlugins())
{
if (isPluginResolved(plugin))
addPlugin(plugin);
else
removePlugin(plugin);
}
delayRegistration = false;
}
bool QCHPluginListener::isPluginResolved(QSharedPointer<ctkPlugin> plugin)
{
return (plugin->getState() & (ctkPlugin::RESOLVED | ctkPlugin::ACTIVE | ctkPlugin::STARTING | ctkPlugin::STOPPING)) != 0;
}
void QCHPluginListener::removePlugin(QSharedPointer<ctkPlugin> plugin)
{
// bail out if system plugin
if (plugin->getPluginId() == 0) return;
QFileInfo qchDirInfo = context->getDataFile("qch_files/" + QString::number(plugin->getPluginId()));
if (qchDirInfo.exists())
{
QDir qchDir(qchDirInfo.absoluteFilePath());
QStringList qchEntries = qchDir.entryList(QStringList("*.qch"));
QStringList qchFiles;
foreach(QString qchEntry, qchEntries)
{
qchFiles << qchDir.absoluteFilePath(qchEntry);
}
// unregister the cached qch files
foreach(QString qchFile, qchFiles)
{
QString namespaceName = QHelpEngineCore::namespaceName(qchFile);
if (namespaceName.isEmpty())
{
BERRY_ERROR << "Could not get the namespace for qch file " << qchFile.toStdString();
continue;
}
else
{
if (!helpEngine->unregisterDocumentation(namespaceName))
{
BERRY_ERROR << "Unregistering qch namespace " << namespaceName.toStdString() << " failed: " << helpEngine->error().toStdString();
}
}
}
// clean the directory
foreach(QString qchEntry, qchEntries)
{
qchDir.remove(qchEntry);
}
}
}
void QCHPluginListener::addPlugin(QSharedPointer<ctkPlugin> plugin)
{
// bail out if system plugin
if (plugin->getPluginId() == 0) return;
QFileInfo qchDirInfo = context->getDataFile("qch_files/" + QString::number(plugin->getPluginId()));
QUrl location(plugin->getLocation());
QFileInfo pluginFileInfo(location.toLocalFile());
if (!qchDirInfo.exists() || qchDirInfo.lastModified() < pluginFileInfo.lastModified())
{
removePlugin(plugin);
if (!qchDirInfo.exists())
{
QDir().mkpath(qchDirInfo.absoluteFilePath());
}
QStringList localQCHFiles;
QStringList resourceList = plugin->findResources("/", "*.qch", true);
foreach(QString resource, resourceList)
{
QByteArray content = plugin->getResource(resource);
QFile localFile(qchDirInfo.absoluteFilePath() + "/" + resource.section('/', -1));
localFile.open(QIODevice::WriteOnly);
localFile.write(content);
localFile.close();
if (localFile.error() != QFile::NoError)
{
BERRY_WARN << "Error writing " << localFile.fileName().toStdString()
<< ": " << localFile.errorString().toStdString();
}
else
{
localQCHFiles << localFile.fileName();
}
}
foreach(QString qchFile, localQCHFiles)
{
if (!helpEngine->registerDocumentation(qchFile))
{
BERRY_ERROR << "Registering qch file " << qchFile.toStdString() << " failed: " << helpEngine->error().toStdString();
}
}
}
}
IPerspectiveListener::Events::Types HelpPerspectiveListener::GetPerspectiveEventTypes() const
{
return Events::OPENED | Events::CHANGED;
}
void HelpPerspectiveListener::PerspectiveOpened(const SmartPointer<IWorkbenchPage>& page, const IPerspectiveDescriptor::Pointer& perspective)
{
// if no help editor is opened, open one showing the home page
if (perspective->GetId() == HelpPerspective::ID &&
page->FindEditors(IEditorInput::Pointer(nullptr), HelpEditor::EDITOR_ID, IWorkbenchPage::MATCH_ID).empty())
{
IEditorInput::Pointer input(new HelpEditorInput());
page->OpenEditor(input, HelpEditor::EDITOR_ID);
}
}
void HelpPerspectiveListener::PerspectiveChanged(const SmartPointer<IWorkbenchPage>& page, const IPerspectiveDescriptor::Pointer& perspective, const QString &changeId)
{
if (perspective->GetId() == HelpPerspective::ID && changeId == IWorkbenchPage::CHANGE_RESET)
{
PerspectiveOpened(page, perspective);
}
}
HelpWindowListener::HelpWindowListener()
: perspListener(new HelpPerspectiveListener())
{
// Register perspective listener for already opened windows
typedef QList<IWorkbenchWindow::Pointer> WndVec;
WndVec windows = PlatformUI::GetWorkbench()->GetWorkbenchWindows();
for (WndVec::iterator i = windows.begin(); i != windows.end(); ++i)
{
(*i)->AddPerspectiveListener(perspListener.data());
}
}
HelpWindowListener::~HelpWindowListener()
{
if (!PlatformUI::IsWorkbenchRunning()) return;
typedef QList<IWorkbenchWindow::Pointer> WndVec;
WndVec windows = PlatformUI::GetWorkbench()->GetWorkbenchWindows();
for (WndVec::iterator i = windows.begin(); i != windows.end(); ++i)
{
(*i)->RemovePerspectiveListener(perspListener.data());
}
}
void HelpWindowListener::WindowClosed(const IWorkbenchWindow::Pointer& window)
{
window->RemovePerspectiveListener(perspListener.data());
}
void HelpWindowListener::WindowOpened(const IWorkbenchWindow::Pointer& window)
{
window->AddPerspectiveListener(perspListener.data());
}
void HelpContextHandler::handleEvent(const ctkEvent &event)
{
struct _runner : public Poco::Runnable
{
_runner(const ctkEvent& ev) : ev(ev) {}
void run() override
{
QUrl helpUrl;
if (ev.containsProperty("url"))
{
helpUrl = QUrl(ev.getProperty("url").toString());
}
else
{
helpUrl = contextUrl();
}
HelpPluginActivator::linkActivated(PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage(),
helpUrl);
delete this;
}
QUrl contextUrl() const
{
berry::IWorkbench* currentWorkbench = berry::PlatformUI::GetWorkbench();
if (currentWorkbench)
{
berry::IWorkbenchWindow::Pointer currentWorkbenchWindow = currentWorkbench->GetActiveWorkbenchWindow();
if (currentWorkbenchWindow)
{
berry::IWorkbenchPage::Pointer currentPage = currentWorkbenchWindow->GetActivePage();
if (currentPage)
{
berry::IWorkbenchPart::Pointer currentPart = currentPage->GetActivePart();
if (currentPart)
{
QString pluginID = currentPart->GetSite()->GetPluginId();
QString viewID = currentPart->GetSite()->GetId();
QString loc = "qthelp://" + pluginID + "/bundle/%1.html";
QHelpEngineWrapper& helpEngine = HelpPluginActivator::getInstance()->getQHelpEngine();
// Get view help page if available
QUrl contextUrl(loc.arg(viewID.replace(".", "_")));
QUrl url = helpEngine.findFile(contextUrl);
if (url.isValid()) return url;
else
{
BERRY_INFO << "Context help url invalid: " << contextUrl.toString().toStdString();
}
// If no view help exists get plugin help if available
QUrl pluginContextUrl(loc.arg(pluginID.replace(".", "_")));
url = helpEngine.findFile(pluginContextUrl);
if (url.isValid()) return url;
// Try to get the index.html file of the plug-in contributing the
// currently active part.
QUrl pluginIndexUrl(loc.arg("index"));
url = helpEngine.findFile(pluginIndexUrl);
if (url != pluginIndexUrl)
{
// Use the default page instead of another index.html
// (merged via the virtual folder property).
url = QUrl();
}
return url;
}
}
}
}
return QUrl();
}
ctkEvent ev;
};
// sync with GUI thread
Display::GetDefault()->AsyncExec(new _runner(event));
}
}
diff --git a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPluginActivator.h b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPluginActivator.h
index 5464fde6f7..da1ee7a7e9 100644
--- a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPluginActivator.h
+++ b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpPluginActivator.h
@@ -1,114 +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 BERRYLOGPLUGIN_H_
#define BERRYLOGPLUGIN_H_
#include <ctkPluginActivator.h>
#include <service/event/ctkEvent.h>
#include <service/event/ctkEventHandler.h>
#include <QScopedPointer>
#include <QMutex>
#include <berryIWorkbenchPage.h>
#include <berryIWindowListener.h>
class QHelpEngine;
+class QWebEngineUrlSchemeHandler;
namespace berry {
class QHelpEngineConfiguration;
class QHelpEngineWrapper;
class QCHPluginListener;
class HelpContextHandler : public QObject, public ctkEventHandler
{
Q_OBJECT
Q_INTERFACES(ctkEventHandler)
public:
void handleEvent(const ctkEvent& event) override;
};
class HelpPluginActivator : public QObject, public ctkPluginActivator
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org_blueberry_ui_qt_help")
Q_INTERFACES(ctkPluginActivator)
public:
HelpPluginActivator();
~HelpPluginActivator() override;
void start(ctkPluginContext* context) override;
void stop(ctkPluginContext* context) override;
static HelpPluginActivator* getInstance();
static void linkActivated(IWorkbenchPage::Pointer page, const QUrl &link);
QHelpEngineWrapper& getQHelpEngine();
private:
Q_DISABLE_COPY(HelpPluginActivator)
static HelpPluginActivator* instance;
QScopedPointer<QHelpEngineWrapper, QScopedPointerDeleteLater > helpEngine;
QScopedPointer<QHelpEngineConfiguration> helpEngineConfiguration;
QScopedPointer<HelpContextHandler> helpContextHandler;
+ QWebEngineUrlSchemeHandler* helpSchemeHandler;
QCHPluginListener* pluginListener;
QScopedPointer<IWindowListener> wndListener;
};
/**
* A listener for CTK plugin events. When plugins come and go we look to see
* if there are any qch files and update the QHelpEngine accordingly.
*/
class QCHPluginListener : public QObject {
Q_OBJECT
public:
QCHPluginListener(ctkPluginContext* context, QHelpEngine* helpEngine);
void processPlugins();
public Q_SLOTS:
void pluginChanged(const ctkPluginEvent& event);
private:
void processPlugins_unlocked();
bool isPluginResolved(QSharedPointer<ctkPlugin> plugin);
void removePlugin(QSharedPointer<ctkPlugin> plugin);
void addPlugin(QSharedPointer<ctkPlugin> plugin);
QMutex mutex;
bool delayRegistration;
ctkPluginContext* context;
QHelpEngine* helpEngine;
};
}
#endif /*BERRYLOGPLUGIN_H_*/
diff --git a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpWebView.cpp b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpWebView.cpp
index 1e81da25a5..9c9e2eea2f 100644
--- a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpWebView.cpp
+++ b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpWebView.cpp
@@ -1,450 +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 "berryHelpWebView.h"
#include "berryHelpPluginActivator.h"
#include "berryHelpEditor.h"
#include "berryHelpEditorInput.h"
#include "berryQHelpEngineWrapper.h"
#include <berryIWorkbenchPage.h>
#include <QCoreApplication>
-#include <QTimer>
#include <QStringBuilder>
#include <QTemporaryFile>
#include <QDesktopServices>
#include <QWheelEvent>
#include <QWebEngineSettings>
-#include <QWebEngineUrlSchemeHandler>
-#include <QWebEngineUrlRequestJob>
-#include <QWebEngineProfile>
namespace berry {
struct ExtensionMap {
const char *extension;
const char *mimeType;
} extensionMap[] = {
{ ".bmp", "image/bmp" },
{ ".css", "text/css" },
{ ".gif", "image/gif" },
{ ".html", "text/html" },
{ ".htm", "text/html" },
{ ".ico", "image/x-icon" },
{ ".jpeg", "image/jpeg" },
{ ".jpg", "image/jpeg" },
{ ".js", "application/x-javascript" },
{ ".mng", "video/x-mng" },
{ ".pbm", "image/x-portable-bitmap" },
{ ".pgm", "image/x-portable-graymap" },
{ ".pdf", "application/pdf" },
{ ".png", "image/png" },
{ ".ppm", "image/x-portable-pixmap" },
{ ".rss", "application/rss+xml" },
{ ".svg", "image/svg+xml" },
{ ".svgz", "image/svg+xml" },
{ ".text", "text/plain" },
{ ".tif", "image/tiff" },
{ ".tiff", "image/tiff" },
{ ".txt", "text/plain" },
{ ".xbm", "image/x-xbitmap" },
{ ".xml", "text/xml" },
{ ".xpm", "image/x-xpm" },
{ ".xsl", "text/xsl" },
{ ".xhtml", "application/xhtml+xml" },
{ ".wml", "text/vnd.wap.wml" },
{ ".wmlc", "application/vnd.wap.wmlc" },
{ "about:blank", nullptr },
{ nullptr, nullptr }
};
-class HelpDeviceReply final : public QIODevice
-{
-public:
- HelpDeviceReply(const QUrl& request, const QByteArray& fileData);
- ~HelpDeviceReply() override;
-
- qint64 bytesAvailable() const override;
- void close() override;
-
-private:
- qint64 readData(char* data, qint64 maxlen) override;
- qint64 writeData(const char* data, qint64 maxlen) override;
-
- QByteArray m_Data;
- const qint64 m_OrigLen;
-};
-
-HelpDeviceReply::HelpDeviceReply(const QUrl&, const QByteArray& fileData)
- : m_Data(fileData),
- m_OrigLen(fileData.length())
-{
- this->setOpenMode(QIODevice::ReadOnly);
-
- QTimer::singleShot(0, this, &QIODevice::readyRead);
- QTimer::singleShot(0, this, &QIODevice::readChannelFinished);
-}
-
-HelpDeviceReply::~HelpDeviceReply()
-{
-}
-
-qint64 HelpDeviceReply::bytesAvailable() const
-{
- return m_Data.length() + QIODevice::bytesAvailable();
-}
-
-void HelpDeviceReply::close()
-{
- QIODevice::close();
- this->deleteLater();
-}
-
-qint64 HelpDeviceReply::readData(char* data, qint64 maxlen)
-{
- qint64 len = qMin(qint64(m_Data.length()), maxlen);
-
- if (len)
- {
- memcpy(data, m_Data.constData(), len);
- m_Data.remove(0, len);
- }
-
- return len;
-}
-
-qint64 HelpDeviceReply::writeData(const char*, qint64)
-{
- return 0;
-}
-
-
-class HelpUrlSchemeHandler final : public QWebEngineUrlSchemeHandler
-{
-public:
- explicit HelpUrlSchemeHandler(QObject* parent = nullptr);
- ~HelpUrlSchemeHandler() override;
-
- void requestStarted(QWebEngineUrlRequestJob* job) override;
-};
-
-HelpUrlSchemeHandler::HelpUrlSchemeHandler(QObject* parent)
- : QWebEngineUrlSchemeHandler(parent)
-{
-}
-
-HelpUrlSchemeHandler::~HelpUrlSchemeHandler()
-{
-}
-
-enum class ResolveUrlResult
-{
- Error,
- Redirect,
- Data
-};
-
-ResolveUrlResult ResolveUrl(const QUrl& url, QUrl& redirectedUrl, QByteArray& data)
-{
- auto& helpEngine = HelpPluginActivator::getInstance()->getQHelpEngine();
-
- const auto targetUrl = helpEngine.findFile(url);
-
- if (!targetUrl.isValid())
- return ResolveUrlResult::Error;
-
- if (targetUrl != url)
- {
- redirectedUrl = targetUrl;
- return ResolveUrlResult::Redirect;
- }
-
- data = helpEngine.fileData(targetUrl);
- return ResolveUrlResult::Data;
-}
-
-
-void HelpUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob* job)
-{
- QUrl url = job->requestUrl();
- QUrl redirectedUrl;
- QByteArray data;
-
- switch (ResolveUrl(url, redirectedUrl, data))
- {
- case ResolveUrlResult::Data:
- job->reply(
- HelpWebView::mimeFromUrl(url).toLatin1(),
- new HelpDeviceReply(url, data));
- break;
-
- case ResolveUrlResult::Redirect:
- job->redirect(redirectedUrl);
- break;
-
- case ResolveUrlResult::Error:
- job->reply(
- QByteArrayLiteral("text/html"),
- new HelpDeviceReply(url, HelpWebView::m_PageNotFoundMessage.arg(url.toString()).toUtf8()));
- break;
- }
-}
-
const QString HelpWebView::m_PageNotFoundMessage =
QCoreApplication::translate("org.blueberry.ui.qt.help", "<title>Context Help</title><div "
"align=\"center\"><br><br><h1>No help page found for identifier</h1><br><h3>'%1'"
"</h3></div>");
const QString HelpWebView::m_MissingContextMessage =
QCoreApplication::translate("org.blueberry.ui.qt.help", "<title>Context Help</title><div "
"align=\"center\"><br><br><h1>Unknown context..</h1><h1>&nbsp;</h1><h1>Please click inside a view and hit F1 again!</h1></div>");
class HelpPage final : public QWebEnginePage
{
public:
explicit HelpPage(QObject* parent = nullptr);
~HelpPage() override;
private:
bool acceptNavigationRequest(const QUrl& url, NavigationType type, bool isMainFrame) override;
};
HelpPage::HelpPage(QObject* parent)
: QWebEnginePage(parent)
{
}
HelpPage::~HelpPage()
{
}
bool HelpPage::acceptNavigationRequest(const QUrl& url, NavigationType, bool)
{
if (url.scheme().contains("http"))
{
QDesktopServices::openUrl(url);
return false;
}
return true;
}
HelpWebView::HelpWebView(IEditorSite::Pointer, QWidget *parent, qreal zoom)
: QWebEngineView(parent),
m_LoadFinished(false),
- m_HelpEngine(HelpPluginActivator::getInstance()->getQHelpEngine()),
- m_HelpSchemeHandler(new HelpUrlSchemeHandler(this))
+ m_HelpEngine(HelpPluginActivator::getInstance()->getQHelpEngine())
{
- QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("qthelp", m_HelpSchemeHandler);
-
auto helpPage = new HelpPage(this);
this->setPage(helpPage);
this->setAcceptDrops(false);
auto action = pageAction(QWebEnginePage::OpenLinkInNewWindow);
action->setText("Open Link in New Tab");
if (parent == nullptr)
action->setVisible(false);
this->pageAction(QWebEnginePage::DownloadLinkToDisk)->setVisible(false);
this->pageAction(QWebEnginePage::DownloadImageToDisk)->setVisible(false);
connect(pageAction(QWebEnginePage::Copy), SIGNAL(changed()), this, SLOT(actionChanged()));
connect(pageAction(QWebEnginePage::Back), SIGNAL(changed()), this, SLOT(actionChanged()));
connect(pageAction(QWebEnginePage::Forward), SIGNAL(changed()), this, SLOT(actionChanged()));
connect(page(), SIGNAL(linkHovered(QString)), this, SIGNAL(highlighted(QString)));
connect(this, SIGNAL(urlChanged(QUrl)), this, SIGNAL(sourceChanged(QUrl)));
connect(this, SIGNAL(loadStarted()), this, SLOT(setLoadStarted()));
connect(this, SIGNAL(loadFinished(bool)), this, SLOT(setLoadFinished(bool)));
this->setFont(this->viewerFont());
this->setZoomFactor(zoom == 0.0 ? 1.0 : zoom);
}
HelpWebView::~HelpWebView()
{
}
QFont HelpWebView::viewerFont() const
{
QWebEngineSettings* webSettings = settings();
return QFont(webSettings->fontFamily(QWebEngineSettings::StandardFont),
webSettings->fontSize(QWebEngineSettings::DefaultFontSize));
}
void HelpWebView::setViewerFont(const QFont &font)
{
QWebEngineSettings *webSettings = settings();
webSettings->setFontFamily(QWebEngineSettings::StandardFont, font.family());
webSettings->setFontSize(QWebEngineSettings::DefaultFontSize, font.pointSize());
}
void HelpWebView::scaleUp()
{
setZoomFactor(zoomFactor() + 0.1);
}
void HelpWebView::scaleDown()
{
setZoomFactor(qMax(0.0, zoomFactor() - 0.1));
}
void HelpWebView::resetScale()
{
setZoomFactor(1.0);
}
bool HelpWebView::handleForwardBackwardMouseButtons(QMouseEvent *e)
{
if (e->button() == Qt::XButton1)
{
triggerPageAction(QWebEnginePage::Back);
return true;
}
if (e->button() == Qt::XButton2)
{
triggerPageAction(QWebEnginePage::Forward);
return true;
}
return false;
}
void HelpWebView::setSource(const QUrl &url)
{
if (url.toString().trimmed().isEmpty()) {
setHtml(m_MissingContextMessage);
}
else if (m_HelpEngine.findFile(url).isValid())
{
load(url);
}
else
{
setHtml(m_PageNotFoundMessage.arg(url.toString()));
}
}
void HelpWebView::wheelEvent(QWheelEvent *e)
{
if (e->modifiers()& Qt::ControlModifier)
{
e->accept();
e->angleDelta().y() > 0 ? scaleUp() : scaleDown();
}
else
{
QWebEngineView::wheelEvent(e);
}
}
void HelpWebView::actionChanged()
{
QAction *a = qobject_cast<QAction *>(sender());
if (a == pageAction(QWebEnginePage::Copy))
emit copyAvailable(a->isEnabled());
else if (a == pageAction(QWebEnginePage::Back))
emit backwardAvailable(a->isEnabled());
else if (a == pageAction(QWebEnginePage::Forward))
emit forwardAvailable(a->isEnabled());
}
void HelpWebView::setLoadStarted()
{
m_LoadFinished = false;
}
void HelpWebView::setLoadFinished(bool ok)
{
m_LoadFinished = ok;
emit sourceChanged(url());
}
QString HelpWebView::mimeFromUrl(const QUrl &url)
{
const QString &path = url.path();
const int index = path.lastIndexOf(QLatin1Char('.'));
const QByteArray &ext = path.mid(index).toUtf8().toLower();
const ExtensionMap *e = extensionMap;
while (e->extension)
{
if (ext == e->extension)
return QLatin1String(e->mimeType);
++e;
}
return QLatin1String("");
}
bool HelpWebView::canOpenPage(const QString &url)
{
return !mimeFromUrl(url).isEmpty();
}
bool HelpWebView::isLocalUrl(const QUrl &url)
{
const QString &scheme = url.scheme();
return scheme.isEmpty()
|| scheme == QLatin1String("file")
|| scheme == QLatin1String("qrc")
|| scheme == QLatin1String("data")
|| scheme == QLatin1String("qthelp")
|| scheme == QLatin1String("about");
}
bool HelpWebView::launchWithExternalApp(const QUrl &url)
{
if (isLocalUrl(url))
{
const QHelpEngine& helpEngine = HelpPluginActivator::getInstance()->getQHelpEngine();
const QUrl &resolvedUrl = helpEngine.findFile(url);
if (!resolvedUrl.isValid())
return false;
const QString& path = resolvedUrl.path();
if (!canOpenPage(path))
{
QTemporaryFile tmpTmpFile;
if (!tmpTmpFile.open())
return false;
const QString &extension = QFileInfo(path).completeSuffix();
QFile actualTmpFile(tmpTmpFile.fileName() % QLatin1String(".")
% extension);
if (!actualTmpFile.open(QIODevice::ReadWrite | QIODevice::Truncate))
return false;
actualTmpFile.write(helpEngine.fileData(resolvedUrl));
actualTmpFile.close();
return QDesktopServices::openUrl(QUrl(actualTmpFile.fileName()));
}
}
else if (url.scheme() == QLatin1String("http"))
{
return QDesktopServices::openUrl(url);
}
return false;
}
void HelpWebView::home()
{
setSource(m_HelpEngine.homePage());
}
}
diff --git a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpWebView.h b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpWebView.h
index b7d8029759..f6f21548bb 100644
--- a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpWebView.h
+++ b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpWebView.h
@@ -1,104 +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 BERRYHELPWEBVIEW_H
#define BERRYHELPWEBVIEW_H
#include <QFont>
#include <QAction>
#include <QWebEnginePage>
#include <QWebEngineView>
#include <berryIEditorSite.h>
-class QWebEngineUrlSchemeHandler;
-
namespace berry {
class QHelpEngineWrapper;
class HelpWebView : public QWebEngineView
{
Q_OBJECT
public:
explicit HelpWebView(IEditorSite::Pointer editorSite, QWidget *parent, qreal zoom = 0.0);
~HelpWebView() override;
QFont viewerFont() const;
void setViewerFont(const QFont &font);
qreal scale() const { return this->zoomFactor(); }
bool handleForwardBackwardMouseButtons(QMouseEvent *e);
void setSource(const QUrl &url);
inline QString documentTitle() const
{ return title(); }
inline bool hasSelection() const
{ return !selectedText().isEmpty(); } // ### this is suboptimal
inline void copy()
{ return triggerPageAction(QWebEnginePage::Copy); }
inline bool isForwardAvailable() const
{ return pageAction(QWebEnginePage::Forward)->isEnabled(); }
inline bool isBackwardAvailable() const
{ return pageAction(QWebEnginePage::Back)->isEnabled(); }
inline bool hasLoadFinished() const
{ return m_LoadFinished; }
static QString mimeFromUrl(const QUrl &url);
static bool canOpenPage(const QString &url);
static bool isLocalUrl(const QUrl &url);
static bool launchWithExternalApp(const QUrl &url);
static const QString m_MissingContextMessage;
static const QString m_PageNotFoundMessage;
public Q_SLOTS:
void backward() { back(); }
void home();
void scaleUp();
void scaleDown();
void resetScale();
Q_SIGNALS:
void copyAvailable(bool enabled);
void forwardAvailable(bool enabled);
void backwardAvailable(bool enabled);
void highlighted(const QString &);
void sourceChanged(const QUrl &);
protected:
void wheelEvent(QWheelEvent *) override;
private Q_SLOTS:
void actionChanged();
void setLoadStarted();
void setLoadFinished(bool ok);
private:
bool m_LoadFinished;
QHelpEngineWrapper& m_HelpEngine;
- QWebEngineUrlSchemeHandler* m_HelpSchemeHandler;
};
}
#endif // BERRYHELPWEBVIEW_H
diff --git a/Plugins/org.blueberry.ui.qt.help/src/internal/berryQHelpEngineWrapper.cpp b/Plugins/org.blueberry.ui.qt.help/src/internal/berryQHelpEngineWrapper.cpp
index 45ca5566a3..8b0ee076a7 100644
--- a/Plugins/org.blueberry.ui.qt.help/src/internal/berryQHelpEngineWrapper.cpp
+++ b/Plugins/org.blueberry.ui.qt.help/src/internal/berryQHelpEngineWrapper.cpp
@@ -1,60 +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.
============================================================================*/
#include "berryQHelpEngineWrapper.h"
#include <QHelpSearchEngine>
namespace berry {
QHelpEngineWrapper::QHelpEngineWrapper(const QString &collectionFile)
: QHelpEngine(collectionFile)
{
/*
* Otherwise we will waste time if several new docs are found,
* because we will start to index them, only to be interrupted
* by the next request. Also, there is a nasty SQLITE bug that will
* cause the application to hang for minutes in that case.
- * This call is reverted by initalDocSetupDone(), which must be
+ * This call is reverted by initialDocSetupDone(), which must be
* called after the new docs have been installed.
*/
disconnect(this, SIGNAL(setupFinished()),
searchEngine(), SLOT(indexDocumentation()));
}
QHelpEngineWrapper::~QHelpEngineWrapper()
{
}
void QHelpEngineWrapper::initialDocSetupDone()
{
connect(this, SIGNAL(setupFinished()),
searchEngine(), SLOT(indexDocumentation()));
setupData();
}
const QString QHelpEngineWrapper::homePage() const
{
return m_HomePage;
}
void QHelpEngineWrapper::setHomePage(const QString &page)
{
if (m_HomePage != page)
{
m_HomePage = page;
emit homePageChanged(page);
}
}
} // end namespace berry
diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryParameterValueConverterProxy.h b/Plugins/org.blueberry.ui.qt/src/internal/berryParameterValueConverterProxy.h
index 843c223234..c3e33981af 100644
--- a/Plugins/org.blueberry.ui.qt/src/internal/berryParameterValueConverterProxy.h
+++ b/Plugins/org.blueberry.ui.qt/src/internal/berryParameterValueConverterProxy.h
@@ -1,78 +1,78 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 BERRYPARAMETERVALUECONVERTERPROXY_H
#define BERRYPARAMETERVALUECONVERTERPROXY_H
#include "berryIParameterValueConverter.h"
#include <QSharedPointer>
namespace berry {
struct IConfigurationElement;
/**
- * A proxy for a parameter value converter that has been defined in the regisry.
+ * A proxy for a parameter value converter that has been defined in the registry.
* This delays the class loading until the converter is really asked to do
* string/object conversions.
*/
class ParameterValueConverterProxy : public IParameterValueConverter
{
private:
/**
* The configuration element providing the executable extension that will
* extend <code>AbstractParameterValueConverter</code>. This value will
* not be <code>null</code>.
*/
const SmartPointer<IConfigurationElement> converterConfigurationElement;
/**
* The real parameter value converter instance. This will be
* <code>null</code> until one of the conversion methods are used.
*/
mutable QSharedPointer<IParameterValueConverter> parameterValueConverter;
/**
* Returns the real parameter value converter for this proxy or throws an
* exception indicating the converter could not be obtained.
*
* @return the real converter for this proxy; never <code>null</code>.
* @throws ParameterValueConversionException
* if the converter could not be obtained
*/
IParameterValueConverter* GetConverter() const;
public:
/**
* Constructs a <code>ParameterValueConverterProxy</code> to represent the
* real converter until it is needed.
*
* @param converterConfigurationElement
* The configuration element from which the real converter can be
* loaded.
*/
ParameterValueConverterProxy(
const SmartPointer<IConfigurationElement>& converterConfigurationElement);
Object::Pointer ConvertToObject(const QString& parameterValue) override;
QString ConvertToString(const Object::Pointer& parameterValue) override;
};
}
#endif // BERRYPARAMETERVALUECONVERTERPROXY_H
diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryRegistryReader.h b/Plugins/org.blueberry.ui.qt/src/internal/berryRegistryReader.h
index 205579322d..991ffdb7e2 100644
--- a/Plugins/org.blueberry.ui.qt/src/internal/berryRegistryReader.h
+++ b/Plugins/org.blueberry.ui.qt/src/internal/berryRegistryReader.h
@@ -1,165 +1,165 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 BERRYREGISTRYREADER_H_
#define BERRYREGISTRYREADER_H_
#include <berrySmartPointer.h>
#include <QList>
namespace berry {
struct IExtension;
struct IExtensionRegistry;
struct IConfigurationElement;
/**
* \ingroup org_blueberry_ui_internal
*
* Template implementation of a registry reader that creates objects
* representing registry contents. Typically, an extension
* contains one element, but this reader handles multiple
* elements per extension.
*
* To start reading the extensions from the registry for an
* extension point, call the method <code>readRegistry</code>.
*
* To read children of an IConfigurationElement, call the
* method <code>readElementChildren</code> from your implementation
* of the method <code>readElement</code>, as it will not be
* done by default.
*/
class RegistryReader {
// for dynamic UI - remove this cache to avoid inconsistency
//protected static Hashtable extensionPoints = new Hashtable();
/**
* The constructor.
*/
protected:
RegistryReader();
virtual ~RegistryReader();
/**
* Logs the error in the workbench log using the provided
* text and the information in the configuration element.
*/
static void LogError(const SmartPointer<IConfigurationElement>& element,
const QString& text);
/**
* Logs a very common registry error when a required attribute is missing.
*/
static void LogMissingAttribute(const SmartPointer<IConfigurationElement>& element,
const QString& attributeName);
/**
* Logs a very common registry error when a required child is missing.
*/
static void LogMissingElement(const SmartPointer<IConfigurationElement>& element,
const QString& elementName);
/**
* Logs a registry error when the configuration element is unknown.
*/
static void LogUnknownElement(const SmartPointer<IConfigurationElement>& element);
public:
/**
* Apply a reproducible order to the list of extensions
* provided, such that the order will not change as
* extensions are added or removed.
* @param extensions the extensions to order
* @return ordered extensions
*/
static QList<SmartPointer<IExtension> > OrderExtensions(const QList<SmartPointer<IExtension> >& extensions);
protected:
/**
* Implement this method to read element's attributes.
* If children should also be read, then implementor
* is responsible for calling <code>readElementChildren</code>.
* Implementor is also responsible for logging missing
* attributes.
*
* @return true if element was recognized, false if not.
*/
virtual bool ReadElement(const SmartPointer<IConfigurationElement>& element) = 0;
/**
* Read the element's children. This is called by
* the subclass' readElement method when it wants
* to read the children of the element.
*/
virtual void ReadElementChildren(const SmartPointer<IConfigurationElement>& element);
/**
* Read each element one at a time by calling the
* subclass implementation of <code>readElement</code>.
*
* Logs an error if the element was not recognized.
*/
virtual void ReadElements(const QList<SmartPointer<IConfigurationElement> >& elements);
/**
* Read one extension by looping through its
* configuration elements.
*/
virtual void ReadExtension(const SmartPointer<IExtension>& extension);
public:
/**
* Start the registry reading process using the
* supplied plugin ID and extension point.
*
* @param registry the registry to read from
- * @param pluginId the plugin id of the extenion point
+ * @param pluginId the plugin id of the extension point
* @param extensionPoint the extension point id
*/
virtual void ReadRegistry(IExtensionRegistry* registry,
const QString& pluginId,
const QString& extensionPoint);
/**
* Utility for extracting the description child of an element.
*
* @param configElement the element
* @return the description
*/
static QString GetDescription(const SmartPointer<IConfigurationElement>& configElement);
/**
* Utility for extracting the value of a class attribute or a nested class
* element that follows the pattern set forth by
* {@link org.blueberry.core.runtime.IExecutableExtension}.
*
* @param configElement
* the element
* @param classAttributeName
* the name of the class attribute to check
* @return the value of the attribute or nested class element
*/
static QString GetClassValue(const SmartPointer<IConfigurationElement>& configElement,
const QString& classAttributeName);
};
}
#endif /*BERRYREGISTRYREADER_H_*/
diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbench.cpp b/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbench.cpp
index 2118c1b969..6429efefaa 100644
--- a/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbench.cpp
+++ b/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbench.cpp
@@ -1,1976 +1,1976 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "berryLog.h"
#include "tweaklets/berryWorkbenchTweaklet.h"
#include "berryWorkbench.h"
#include <berrySafeRunner.h>
#include "berryPolicy.h"
#include "berrySaveablesList.h"
#include "berryViewRegistry.h"
#include "berryEditorRegistry.h"
#include "berryEditorHistory.h"
#include "berryEditorHistoryItem.h"
#include "berryServiceLocatorCreator.h"
#include "berryWorkbenchPage.h"
#include "berryPerspective.h"
#include "berryPreferenceConstants.h"
#include "berryUIExtensionTracker.h"
#include "berryWorkbenchWindow.h"
#include "berryDisplay.h"
#include "services/berryIServiceFactory.h"
#include "util/berrySafeRunnable.h"
#include "berryWorkbenchServiceRegistry.h"
#include "berryWorkbenchPlugin.h"
#include "berryWorkbenchConstants.h"
#include "berryWorkbenchMenuService.h"
#include "berryEvaluationService.h"
#include "berryCommandService.h"
#include "berryCommandManager.h"
#include "berryMenuManager.h"
#include "berryParameterType.h"
#include "berryQActionProperties.h"
#include "berrySourceProviderService.h"
#include "berryWorkbenchLocationService.h"
#include <berryCommand.h>
#include <berryCommandCategory.h>
#include <berryIElementFactory.h>
#include <berryIHandler.h>
#include <berryIHandlerService.h>
#include <berryIProduct.h>
#include <berryISourceProvider.h>
#include <berryIServiceScopes.h>
#include <QDir>
#include <QApplication>
#include <QMessageBox>
#include <Poco/FileStream.h>
#include <mitkIPreferences.h>
namespace berry
{
Workbench* Workbench::instance = nullptr;
WorkbenchTestable::Pointer Workbench::testableObject;
const unsigned int Workbench::VERSION_STRING_COUNT = 1;
const QString Workbench::VERSION_STRING[Workbench::VERSION_STRING_COUNT] =
{ "1.0" };
const QString Workbench::DEFAULT_WORKBENCH_STATE_FILENAME = "workbench.xml";
class RestoreStateRunnable: public SafeRunnable
{
private:
Workbench* workbench;
Poco::File stateFile;
bool& result;
public:
RestoreStateRunnable(Workbench* workbench, const QString& stateFile,
bool& result) :
SafeRunnable(
"Unable to read workbench state. Workbench UI layout will be reset."),
workbench(workbench), stateFile(stateFile.toStdString()), result(result)
{
}
void Run() override
{
Poco::FileInputStream input(stateFile.path());
IMemento::Pointer memento = XMLMemento::CreateReadRoot(input);
// Validate known version format
QString version;
memento->GetString(WorkbenchConstants::TAG_VERSION, version);
bool valid = false;
for (auto & elem : Workbench::VERSION_STRING)
{
if (elem == version)
{
valid = true;
break;
}
}
if (!valid)
{
input.close();
QString msg =
"Invalid workbench state version. workbench.xml will be deleted";
QMessageBox::critical(nullptr, "Restoring Problems", msg);
stateFile.remove();
// result[0] = new Status(IStatus.ERROR,
// WorkbenchPlugin.PI_WORKBENCH,
// IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, null);
result = false;
return;
}
// // Validate compatible version format
// // We no longer support the release 1.0 format
// if (VERSION_STRING[0].equals(version))
// {
// reader.close();
// QString msg = "The saved user interface layout is in an "
// "obsolete format and cannot be preserved. Your projects and files "
// "will not be affected. Press OK to convert to the new format. Press "
// "Cancel to exit with no changes.";
// QList<QString> dlgLabels;
// dlgLabels.push_back("Ok");
// dlgLabels.push_back("Cancel");
// IDialog::Pointer dlg = MessageDialog::CreateDialog(Shell::Pointer(0),
// "Cannot Preserve Layout", 0, msg, IDialog::WARNING, dlgLabels, 0);
// IDialog::ReturnCode ignoreSavedState = dlg->Open();
// // OK is the default
// if (ignoreSavedState == IDialog::OK)
// {
// stateFile.remove();
// // result[0] = new Status(IStatus.WARNING,
// // WorkbenchPlugin.PI_WORKBENCH,
// // IWorkbenchConfigurer.RESTORE_CODE_RESET, msg,
// // null);
// result = false;
// }
// else
// {
// // result[0] = new Status(IStatus.WARNING,
// // WorkbenchPlugin.PI_WORKBENCH,
// // IWorkbenchConfigurer.RESTORE_CODE_EXIT, msg,
// // null);
// result = false;
// }
// return;
// }
// Restore the saved state
//final IStatus restoreResult = restoreState(memento);
/*bool restoreResult =*/ workbench->RestoreState(memento);
input.close();
// if (restoreResult.getSeverity() == IStatus.ERROR) {
// StartupThreading
// .runWithoutExceptions(new StartupRunnable() {
//
// public void runWithException() throws Throwable {
// StatusManager.getManager().handle(restoreResult, StatusManager.LOG);
// }
// });
//
// }
}
void HandleException(const ctkException& e) override
{
//StartupThreading.runWithoutExceptions(new StartupRunnable() {
//public void runWithException() {
Handle(e);
// QString msg = e.getMessage() == null ? "" : e.getMessage(); //$NON-NLS-1$
// result[0] = new Status(IStatus.ERROR,
// WorkbenchPlugin.PI_WORKBENCH,
// IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, e);
result = false;
stateFile.remove();
// }});
}
private:
void Handle(const ctkException& e)
{
SafeRunnable::HandleException(e);
}
};
int Workbench::CreateAndRunWorkbench(Display* display,
WorkbenchAdvisor* advisor)
{
// create the workbench instance
Workbench workbench(display, advisor);
// run the workbench event loop
int returnCode = workbench.RunUI();
return returnCode;
}
Display* Workbench::CreateDisplay()
{
// create the display
Display* newDisplay = Tweaklets::Get(WorkbenchTweaklet::KEY)->CreateDisplay();
// workaround for 1GEZ9UR and 1GF07HN
//newDisplay.setWarnings(false);
// Set the priority higher than normal so as to be higher
// than the JobManager.
//Poco::Thread::current()->setPriority(Poco::Thread::PRIO_HIGH);
//initializeImages();
return newDisplay;
}
Workbench::ServiceLocatorOwner::ServiceLocatorOwner(Workbench* wb) :
workbench(wb)
{
}
void Workbench::ServiceLocatorOwner::Dispose()
{
QMessageBox::information(
nullptr,
"Restart needed",
"A required plug-in is no longer available and the Workbench needs "
"to be restarted. You will be prompted to save if there is any unsaved work.");
workbench->Close(PlatformUI::RETURN_RESTART, true);
}
Workbench::Workbench(Display* display, WorkbenchAdvisor* advisor)
: commandManager(nullptr), progressCount(-1)
, serviceLocatorOwner(new ServiceLocatorOwner(this))
, largeUpdates(0), introManager(nullptr), isStarting(true), isClosing(false)
, activeWorkbenchWindow(nullptr)
{
poco_check_ptr(display)
; poco_check_ptr(advisor);
// the reference count to the one and only workbench instance
// is increased, so that temporary smart pointer to the workbench
// do not delete it
this->Register();
this->display = display;
this->advisor = advisor;
Workbench::instance = this;
serviceLocatorCreator.reset(new ServiceLocatorCreator());
serviceLocatorCreator->Register();
this->serviceLocator = serviceLocatorCreator->CreateServiceLocator(
nullptr,
nullptr,
IDisposable::WeakPtr(serviceLocatorOwner)).Cast<ServiceLocator>();
serviceLocator->RegisterService(serviceLocatorCreator.data());
workbenchLocationService.reset(
new WorkbenchLocationService(IServiceScopes::WORKBENCH_SCOPE, this, nullptr, nullptr, 0));
workbenchLocationService->Register();
serviceLocator->RegisterService(workbenchLocationService.data());
returnCode = PlatformUI::RETURN_UNSTARTABLE;
}
Display* Workbench::GetDisplay() const
{
return display;
}
Workbench* Workbench::GetInstance()
{
return instance;
}
WorkbenchTestable::Pointer Workbench::GetWorkbenchTestable()
{
if (!testableObject)
{
testableObject = new WorkbenchTestable();
}
return testableObject;
}
Workbench::~Workbench()
{
this->instance = nullptr;
this->UnRegister(false);
}
Object* Workbench::GetService(const QString& key)
{
return serviceLocator->GetService(key);
}
bool Workbench::HasService(const QString& key) const
{
return serviceLocator->HasService(key);
}
bool Workbench::Init()
{
// // setup debug mode if required.
// if (WorkbenchPlugin.getDefault().isDebugging()) {
// WorkbenchPlugin.DEBUG = true;
// ModalContext.setDebugMode(true);
// }
bool bail = false;
// create workbench window manager
//windowManager = new WindowManager();
IIntroRegistry* introRegistry = WorkbenchPlugin::GetDefault()->GetIntroRegistry();
if (introRegistry->GetIntroCount() > 0)
{
IProduct::Pointer product = Platform::GetProduct();
if (product.IsNotNull())
{
introDescriptor = introRegistry->GetIntroForProduct(product->GetId()).Cast<IntroDescriptor>();
}
}
// TODO Correctly order service initialization
// there needs to be some serious consideration given to
// the services, and hooking them up in the correct order
evaluationService.reset(new EvaluationService());
evaluationService->Register();
// StartupThreading.runWithoutExceptions(new StartupRunnable() {
//
// public void runWithException() {
serviceLocator->RegisterService(evaluationService.data());
// }
// });
// Initialize the activity support.
//workbenchActivitySupport = new WorkbenchActivitySupport();
//activityHelper = ActivityPersistanceHelper.getInstance();
this->InitializeDefaultServices();
// initializeFonts();
// initializeColors();
// initializeApplicationColors();
// now that the workbench is sufficiently initialized, let the advisor
// have a turn.
advisor->InternalBasicInitialize(this->GetWorkbenchConfigurer());
// StartupThreading.runWithoutExceptions(new StartupRunnable() {
// public void runWithException() {
StartSourceProviders();
// }
// });
// StartupThreading.runWithoutExceptions(new StartupRunnable() {
// public void runWithException() {
// activateWorkbenchContext();
// }
// });
// StartupThreading.runWithoutExceptions(new StartupRunnable() {
// public void runWithException() {
// createApplicationMenu();
// }
// });
// attempt to restore a previous workbench state
advisor->PreStartup();
if (!advisor->OpenWindows())
{
bail = true;
}
if (bail)
return false;
//forceOpenPerspective();
return true;
}
bool Workbench::RestoreState()
{
//return false;
if (!GetWorkbenchConfigurer()->GetSaveAndRestore())
{
// QString msg = "This application does not save and restore previously saved state.";
// return new Status(IStatus.WARNING, WorkbenchPlugin.PI_WORKBENCH,
// IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, null);
return false;
}
// Read the workbench state file.
QString stateFile = GetWorkbenchStateFile();
// If there is no state file cause one to open.
if (stateFile.isEmpty() || !QFile::exists(stateFile))
{
// QString msg = "No previously saved state to restore.";
// return new Status(IStatus.WARNING, WorkbenchPlugin.PI_WORKBENCH,
// IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, null);
return false;
}
// final IStatus result[] = { new Status(IStatus.OK,
// WorkbenchPlugin.PI_WORKBENCH, IStatus.OK, "", null) }; //$NON-NLS-1$
bool result = true;
ISafeRunnable::Pointer runnable(new RestoreStateRunnable(this, stateFile, result));
SafeRunner::Run(runnable);
// ensure at least one window was opened
//if (result[0].isOK() && windowManager.getWindows().length == 0)
if (result && windowManager.GetWindowCount() == 0)
{
QString msg = "No windows restored.";
// result[0] = new Status(IStatus.ERROR, WorkbenchPlugin.PI_WORKBENCH,
// IWorkbenchConfigurer.RESTORE_CODE_RESET, msg, null);
result &= false;
}
return result;
}
bool Workbench::RestoreState(IMemento::Pointer memento)
{
// final MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID,
// IStatus.OK, WorkbenchMessages.Workbench_problemsRestoring, null);
bool result = true;
const bool showProgress = false;
//TODO restore state progress
// final boolean showProgress = PrefUtil.getAPIPreferenceStore()
// .getBoolean(
// IWorkbenchPreferenceConstants.SHOW_PROGRESS_ON_STARTUP);
try
{
/*
* Restored windows will be set in the createdWindows field to be
* used by the openWindowsAfterRestore() method
*/
if (!showProgress)
{
DoRestoreState(memento, result);
}
else
{
// Retrieve how many plug-ins were loaded while restoring the
// workbench
int lastProgressCount = -1;
memento->GetInteger(WorkbenchConstants::TAG_PROGRESS_COUNT, lastProgressCount);
// If we don't know how many plug-ins were loaded last time,
// assume we are loading half of the installed plug-ins.
/*const std::size_t expectedProgressCount =
std::max<std::size_t>(1,
lastProgressCount == -1 ? WorkbenchPlugin::GetDefault()->GetBundleCount() / 2
: lastProgressCount);*/
//TODO restore state progress
// RunStartupWithProgress(expectedProgressCount, new Runnable() {
// public void Run() {
// DoRestoreState(memento, result);
// }
// });
}
}
catch (...)
{
OpenWindowsAfterRestore();
throw;
}
OpenWindowsAfterRestore();
return result;
}
void Workbench::DoRestoreState(IMemento::Pointer memento, bool& status) // final MultiStatus status)
{
IMemento::Pointer childMem;
try
{
// UIStats.start(UIStats.RESTORE_WORKBENCH, "MRUList"); //$NON-NLS-1$
IMemento::Pointer mruMemento = memento
->GetChild(WorkbenchConstants::TAG_MRU_LIST);
if (mruMemento)
{
// TODO restore editor history
//status.add(getEditorHistory().restoreState(mruMemento));
}
//UIStats.end(UIStats.RESTORE_WORKBENCH, this, "MRUList"); //$NON-NLS-1$
}
catch (...)
{
//UIStats.end(UIStats.RESTORE_WORKBENCH, this, "MRUList"); //$NON-NLS-1$
throw;
}
// Restore advisor state.
IMemento::Pointer advisorState = memento
->GetChild(WorkbenchConstants::TAG_WORKBENCH_ADVISOR);
if (advisorState)
{
//status.add(getAdvisor().restoreState(advisorState));
status &= GetAdvisor()->RestoreState(advisorState);
}
// Get the child windows.
QList<IMemento::Pointer> children = memento
->GetChildren(WorkbenchConstants::TAG_WINDOW);
createdWindows.clear();
// Read the workbench windows.
for (int i = 0; i < children.size(); i++)
{
childMem = children[i];
WorkbenchWindow::Pointer newWindow;
//StartupThreading.runWithoutExceptions(new StartupRunnable() {
// public void runWithException() {
newWindow = NewWorkbenchWindow();
newWindow->Create();
// }});
createdWindows.push_back(newWindow);
// allow the application to specify an initial perspective to open
// @issue temporary workaround for ignoring initial perspective
// String initialPerspectiveId =
// getAdvisor().getInitialWindowPerspectiveId();
// if (initialPerspectiveId != null) {
// IPerspectiveDescriptor desc =
// getPerspectiveRegistry().findPerspectiveWithId(initialPerspectiveId);
// result.merge(newWindow.restoreState(childMem, desc));
// }
// add the window so that any work done in newWindow.restoreState
// that relies on Workbench methods has windows to work with
windowManager.Add(newWindow);
// now that we've added it to the window manager we need to listen
// for any exception that might hose us before we get a chance to
// open it. If one occurs, remove the new window from the manager.
// Assume that the new window is a phantom for now
try
{
//status.merge(newWindow[0].restoreState(childMem, null));
status &= newWindow->RestoreState(childMem, IPerspectiveDescriptor::Pointer(nullptr));
try
{
newWindow->FireWindowRestored();
}
catch (const WorkbenchException& /*e*/)
{
//status.add(e.getStatus());
status &= false;
}
// everything worked so far, don't close now
}
catch (...)
{
// null the window in newWindowHolder so that it won't be
// opened later on
createdWindows[i] = nullptr;
//StartupThreading.runWithoutExceptions(new StartupRunnable() {
// public void runWithException() throws Throwable {
newWindow->Close();
// }});
}
}
}
void Workbench::OpenWindowsAfterRestore()
{
if (createdWindows.empty())
{
return;
}
// now open the windows (except the ones that were nulled because we
// closed them above)
for (int i = 0; i < createdWindows.size(); i++)
{
if (createdWindows[i])
{
WorkbenchWindow::Pointer myWindow = createdWindows[i];
//StartupThreading.runWithoutExceptions(new StartupRunnable() {
// public void runWithException() throws Throwable {
try
{
myWindow->Open();
}
catch (...)
{
myWindow->Close();
throw;
}
// }});
}
}
createdWindows.clear();
}
void Workbench::InitializeDefaultServices()
{
// final IContributionService contributionService = new ContributionService(
// getAdvisor());
// serviceLocator.registerService(IContributionService.class,
// contributionService);
//
// // TODO Correctly order service initialization
// // there needs to be some serious consideration given to
// // the services, and hooking them up in the correct order
//
//
// StartupThreading.runWithoutExceptions(new StartupRunnable() {
//
// public void runWithException() {
saveablesList.reset(new SaveablesList());
saveablesList->Register();
serviceLocator->RegisterService(saveablesList.data());
// }});
//
/*
* Phase 1 of the initialization of commands. When this phase completes,
* all the services and managers will exist, and be accessible via the
* getService(Object) method.
*/
// StartupThreading.runWithoutExceptions(new StartupRunnable() {
//
// public void runWithException() {
Command::DEBUG_COMMAND_EXECUTION = Policy::DEBUG_COMMANDS();
Command::DEBUG_HANDLERS = Policy::DEBUG_HANDLERS_VERBOSE();
Command::DEBUG_HANDLERS_COMMAND_ID = Policy::DEBUG_HANDLERS_VERBOSE_COMMAND_ID();
commandManager.reset(new CommandManager());
// }});
//
// final CommandService [] commandService = new CommandService[1];
// StartupThreading.runWithoutExceptions(new StartupRunnable() {
//
// public void runWithException() {
commandService.reset(new CommandService(commandManager.data()));
commandService->Register();
commandService->ReadRegistry();
serviceLocator->RegisterService(commandService.data());
// }});
//
// StartupThreading.runWithoutExceptions(new StartupRunnable() {
//
// public void runWithException() {
// ContextManager.DEBUG = Policy.DEBUG_CONTEXTS;
// contextManager = new ContextManager();
// }});
//
// final IContextService contextService = new ContextService(
// contextManager);
//
// StartupThreading.runWithoutExceptions(new StartupRunnable() {
//
// public void runWithException() {
// contextService.readRegistry();
// }});
//
// serviceLocator.registerService(IContextService.class, contextService);
//
//
// final IBindingService [] bindingService = new BindingService[1];
//
// StartupThreading.runWithoutExceptions(new StartupRunnable() {
//
// public void runWithException() {
// BindingManager.DEBUG = Policy.DEBUG_KEY_BINDINGS;
// bindingManager = new BindingManager(contextManager, commandManager);
// bindingService[0] = new BindingService(
// bindingManager, commandService[0], Workbench.this);
//
// }});
//
// bindingService[0].readRegistryAndPreferences(commandService[0]);
// serviceLocator.registerService(IBindingService.class, bindingService[0]);
//
// final CommandImageManager commandImageManager = new CommandImageManager();
// final CommandImageService commandImageService = new CommandImageService(
// commandImageManager, commandService[0]);
// commandImageService.readRegistry();
// serviceLocator.registerService(ICommandImageService.class,
// commandImageService);
auto wms = new WorkbenchMenuService(serviceLocator.GetPointer());
menuService.reset(wms);
menuService->Register();
serviceLocator->RegisterService(menuService.data());
// the service must be registered before it is initialized - its
// initialization uses the service locator to address a dependency on
// the menu service
//StartupThreading.runWithoutExceptions(new StartupRunnable() {
// public void runWithException() {
wms->ReadRegistry();
// }});
// the source providers are now initialized in phase 3, but source
// priorities have to be set before handler initialization
// StartupThreading.runWithoutExceptions(new StartupRunnable() {
// public void runWithException() {
InitializeSourcePriorities();
// }
// });
/*
* Phase 2 of the initialization of commands. This handles the creation
* of wrappers for legacy APIs. By the time this phase completes, any
* code trying to access commands through legacy APIs should work.
*/
//IHandlerService* handlerService = nullptr;
// StartupThreading.runWithoutExceptions(new StartupRunnable() {
// public void runWithException() {
/*handlerService = */serviceLocator->GetService<IHandlerService>();
// }
// });
// workbenchContextSupport = new WorkbenchContextSupport(this,
// contextManager);
// workbenchCommandSupport = new WorkbenchCommandSupport(bindingManager,
// commandManager, contextManager, handlerService[0]);
// initializeCommandResolver();
this->AddWindowListener(this);
// bindingManager.addBindingManagerListener(bindingManagerListener);
// serviceLocator.registerService(ISelectionConversionService.class,
// new SelectionConversionService());
}
int Workbench::RunUI()
{
// initialize workbench and restore or open one window
bool initOK = this->Init();
// let the advisor run its start up code
if (initOK)
{
advisor->PostStartup(); // may trigger a close/restart
}
//TODO start eager plug-ins
//startPlugins();
//addStartupRegistryListener();
isStarting = false;
BERRY_INFO << "BlueBerry Workbench ready";
this->GetWorkbenchTestable()->Init(Display::GetDefault(), this);
// spin event loop
return display->RunEventLoop();
}
QString Workbench::GetDefaultPerspectiveId() const
{
return this->GetAdvisor()->GetInitialWindowPerspectiveId();
}
IAdaptable* Workbench::GetDefaultPageInput() const
{
return this->GetAdvisor()->GetDefaultPageInput();
}
QString Workbench::GetPresentationId() const
{
if (factoryID != "")
{
return factoryID;
}
//factoryID = PrefUtil.getAPIPreferenceStore().getString(
// IWorkbenchPreferenceConstants.PRESENTATION_FACTORY_ID);
// Workaround for bug 58975 - New preference mechanism does not properly
// initialize defaults
// Ensure that the UI plugin has started too.
factoryID = WorkbenchConstants::DEFAULT_PRESENTATION_ID;
return factoryID;
}
IElementFactory* Workbench::GetElementFactory(const QString& factoryId) const
{
return WorkbenchPlugin::GetDefault()->GetElementFactory(factoryId);
}
void Workbench::UpdateTheme()
{
WorkbenchPlugin::GetDefault()->GetPresentationFactory()->UpdateTheme();
}
void Workbench::LargeUpdateStart()
{
if (largeUpdates++ == 0)
{
// TODO Consider whether these lines still need to be here.
// workbenchCommandSupport.setProcessing(false);
// workbenchContextSupport.setProcessing(false);
QList<IWorkbenchWindow::Pointer> windows = this->GetWorkbenchWindows();
for (int i = 0; i < windows.size(); i++)
{
IWorkbenchWindow::Pointer window = windows[i];
if (window.Cast<WorkbenchWindow>() != 0)
{
window.Cast<WorkbenchWindow>()->LargeUpdateStart();
}
}
}
}
void Workbench::LargeUpdateEnd()
{
if (--largeUpdates == 0)
{
// TODO Consider whether these lines still need to be here.
// workbenchCommandSupport.setProcessing(true);
// workbenchContextSupport.setProcessing(true);
// Perform window-specific blocking.
QList<IWorkbenchWindow::Pointer> windows = this->GetWorkbenchWindows();
for (int i = 0; i < windows.size(); i++)
{
IWorkbenchWindow::Pointer window = windows[i];
if (window.Cast<WorkbenchWindow>() != 0)
{
window.Cast<WorkbenchWindow>()->LargeUpdateEnd();
}
}
}
}
IExtensionTracker*Workbench::GetExtensionTracker() const
{
if (tracker.isNull())
{
tracker.reset(new UIExtensionTracker(this->GetDisplay()));
}
return tracker.data();
}
void Workbench::OpenFirstTimeWindow()
{
try
{
IAdaptable* input;
//StartupThreading.runWithoutExceptions(new StartupRunnable() {
// public void runWithException() throws Throwable {
input = this->GetDefaultPageInput();
// }});
this->BusyOpenWorkbenchWindow(this->GetPerspectiveRegistry()->GetDefaultPerspective(), input);
}
catch (WorkbenchException& e)
{
// Don't use the window's shell as the dialog parent,
// as the window is not open yet (bug 76724).
//StartupThreading.runWithoutExceptions(new StartupRunnable() {
// public void runWithException() throws Throwable {
// ErrorDialog.openError(null,
// WorkbenchMessages.Problems_Opening_Page, e.getMessage(), e
// .getStatus());
// }});
BERRY_ERROR << "Error: Problems opening page. " << e.what() << std::endl;
}
}
WorkbenchConfigurer::Pointer Workbench::GetWorkbenchConfigurer()
{
if (workbenchConfigurer.IsNull())
{
workbenchConfigurer = new WorkbenchConfigurer();
}
return workbenchConfigurer;
}
WorkbenchAdvisor* Workbench::GetAdvisor() const
{
return advisor;
}
IViewRegistry* Workbench::GetViewRegistry() const
{
return WorkbenchPlugin::GetDefault()->GetViewRegistry();
}
IEditorRegistry* Workbench::GetEditorRegistry() const
{
return WorkbenchPlugin::GetDefault()->GetEditorRegistry();
}
EditorHistory* Workbench::GetEditorHistory() const
{
if (editorHistory.isNull())
{
editorHistory.reset(new EditorHistory());
}
return editorHistory.data();
}
IPerspectiveRegistry* Workbench::GetPerspectiveRegistry() const
{
return WorkbenchPlugin::GetDefault()->GetPerspectiveRegistry();
}
bool Workbench::Close()
{
return this->Close(PlatformUI::RETURN_OK, false);
}
bool Workbench::Close(int returnCode, bool force)
{
BERRY_INFO << "Closing workbench...";
this->returnCode = returnCode;
bool ret;
//BusyIndicator.showWhile(null, new Runnable() {
// public void run() {
ret = this->BusyClose(force);
// }
//});
return ret;
}
/**
* Closes the workbench. Assumes that the busy cursor is active.
*
* @param force
* true if the close is mandatory, and false if the close is
* allowed to fail
* @return true if the close succeeded, and false otherwise
*/
bool Workbench::BusyClose(bool force)
{
// notify the advisor of preShutdown and allow it to veto if not forced
isClosing = advisor->PreShutdown();
if (!force && !isClosing)
{
return false;
}
// notify regular workbench clients of preShutdown and allow them to
// veto if not forced
isClosing = this->FirePreShutdown(force);
if (!force && !isClosing)
{
return false;
}
// save any open editors if they are dirty
isClosing = this->SaveAllEditors(!force);
if (!force && !isClosing)
{
return false;
}
bool closeEditors = !force && false; // false is the default for the not yet implemented preference below
// && PrefUtil.getAPIPreferenceStore().getBoolean(
// IWorkbenchPreferenceConstants.CLOSE_EDITORS_ON_EXIT);
if (closeEditors)
{
// SafeRunner.run(new SafeRunnable() {
// public void run() {
QList<IWorkbenchWindow::Pointer> windows = this->GetWorkbenchWindows();
for (int i = 0; i < windows.size(); i++)
{
IWorkbenchPage::Pointer page = windows[i]->GetActivePage();
if (page)
isClosing = isClosing && page->CloseAllEditors(false);
}
// }
//});
if (!force && !isClosing)
{
return false;
}
}
if (this->GetWorkbenchConfigurer()->GetSaveAndRestore())
{
try
{
// SafeRunner.run(new SafeRunnable() {
// public void run() {
XMLMemento::Pointer mem = RecordWorkbenchState();
// Save the IMemento to a file.
SaveMementoToFile(mem);
// }
}
catch(const ctkException& e)
{
// public void handleException(Throwable e) {
QString message;
if (e.what() == nullptr)
{
message = "An error has occurred. See error log for more details. Do you want to exit?";
}
else
{
message = QString("An error has occurred: ") + e.what() + ". See error log for more details. Do you want to exit?";
}
if (QMessageBox::question(nullptr, "Error", message) != QMessageBox::Yes)
{
isClosing = false;
}
}
// }
// });
}
if (!force && !isClosing)
{
return false;
}
//SafeRunner.run(new SafeRunnable(WorkbenchMessages.ErrorClosing) {
// public void run() {
if (isClosing || force)
{
isClosing = windowManager.Close();
}
// }
//});
if (!force && !isClosing)
{
return false;
}
this->Shutdown();
display->ExitEventLoop(0);
return true;
}
QString Workbench::GetWorkbenchStateFile() const
{
QString path = WorkbenchPlugin::GetDefault()->GetDataLocation();
if (path.isNull())
{
return QString();
}
return QDir::cleanPath(path + "/" + DEFAULT_WORKBENCH_STATE_FILENAME);
}
/*
* Save the workbench UI in a persistence file.
*/
bool Workbench::SaveMementoToFile(XMLMemento::Pointer memento)
{
// Save it to a file.
// XXX: nobody currently checks the return value of this method.
QString stateFile = GetWorkbenchStateFile();
if (stateFile.isNull())
{
return false;
}
//BERRY_INFO << "Saving state to: " << stateFile.path() << std::endl;
try
{
Poco::FileOutputStream stream(stateFile.toStdString());
memento->Save(stream);
}
catch (const Poco::IOException& /*e*/)
{
QFile::remove(stateFile);
QMessageBox::critical(nullptr,
"Saving Problems",
"Unable to store workbench state.");
return false;
}
// Success !
return true;
}
IWorkbenchWindow::Pointer Workbench::GetActiveWorkbenchWindow() const
{
// Look for the window that was last known being
// the active one
WorkbenchWindow::Pointer win = this->GetActivatedWindow();
return win;
}
std::size_t Workbench::GetWorkbenchWindowCount() const
{
return windowManager.GetWindowCount();
}
QList<IWorkbenchWindow::Pointer> Workbench::GetWorkbenchWindows() const
{
QList<Window::Pointer> windows = windowManager.GetWindows();
QList<IWorkbenchWindow::Pointer> result;
for (QList<Window::Pointer>::iterator iter = windows.begin();
iter != windows.end(); ++iter)
{
result.push_back(iter->Cast<WorkbenchWindow>());
}
return result;
}
IWorkbenchWindow::Pointer Workbench::OpenWorkbenchWindow(
const QString& perspID, IAdaptable* input)
{
// Run op in busy cursor.
//final Object[] result = new Object[1];
//BusyIndicator.showWhile(null, new Runnable() {
// public void run() {
// try {
return this->BusyOpenWorkbenchWindow(perspID, input);
// } catch (WorkbenchException e) {
// result[0] = e;
// }
// }
//});
}
IWorkbenchWindow::Pointer Workbench::OpenWorkbenchWindow(IAdaptable* input)
{
return this->OpenWorkbenchWindow(this->GetPerspectiveRegistry()
->GetDefaultPerspective(), input);
}
IWorkbenchPage::Pointer Workbench::ShowPerspective(
const QString& perspectiveId, IWorkbenchWindow::Pointer window)
{
// If the specified window has the requested perspective open, then the
// window
// is given focus and the perspective is shown. The page's input is
// ignored.
WorkbenchWindow::Pointer win = window.Cast<WorkbenchWindow> ();
if (win)
{
IWorkbenchPage::Pointer page = win->GetActivePage();
if (page)
{
QList<IPerspectiveDescriptor::Pointer> perspectives(page
->GetOpenPerspectives());
for (int i = 0; i < perspectives.size(); i++)
{
IPerspectiveDescriptor::Pointer persp = perspectives[i];
if (perspectiveId == persp->GetId())
{
win->MakeVisible();
page->SetPerspective(persp);
return page;
}
}
}
}
// If another window that has the workspace root as input and the
// requested
- // perpective open and active, then the window is given focus.
+ // perspective open and active, then the window is given focus.
IAdaptable* input = GetDefaultPageInput();
QList<IWorkbenchWindow::Pointer> windows(GetWorkbenchWindows());
for (int i = 0; i < windows.size(); i++)
{
win = windows[i].Cast<WorkbenchWindow>();
if (window != win)
{
WorkbenchPage::Pointer page = win->GetActivePage().Cast<WorkbenchPage>();
if (page)
{
bool inputSame = false;
if (input == nullptr)
{
inputSame = (page->GetInput() == nullptr);
}
else
{
inputSame = input == page->GetInput();
}
if (inputSame)
{
Perspective::Pointer persp = page->GetActivePerspective();
if (persp)
{
IPerspectiveDescriptor::Pointer desc = persp->GetDesc();
if (desc)
{
if (perspectiveId == desc->GetId())
{
Shell::Pointer shell = win->GetShell();
shell->Open();
if (shell->GetMinimized())
{
shell->SetMinimized(false);
}
return page;
}
}
}
}
}
}
}
// Otherwise the requested perspective is opened and shown in the
// specified
// window or in a new window depending on the current user preference
// for opening
// perspectives, and that window is given focus.
win = window.Cast<WorkbenchWindow>();
if (win)
{
auto* prefs = WorkbenchPlugin::GetDefault()->GetPreferences();
int mode = prefs->GetInt(PreferenceConstants::OPEN_PERSP_MODE, PreferenceConstants::OPM_ACTIVE_PAGE);
IWorkbenchPage::Pointer page = win->GetActivePage();
IPerspectiveDescriptor::Pointer persp;
if (page)
{
persp = page->GetPerspective();
}
// Only open a new window if user preference is set and the window
// has an active perspective.
if (PreferenceConstants::OPM_NEW_WINDOW == mode && persp)
{
IWorkbenchWindow::Pointer newWindow = OpenWorkbenchWindow(perspectiveId,
input);
return newWindow->GetActivePage();
}
IPerspectiveDescriptor::Pointer desc = GetPerspectiveRegistry()
->FindPerspectiveWithId(perspectiveId);
if (desc == 0)
{
throw WorkbenchException(
"Unable to create perspective \"" + perspectiveId + "\". There is no corresponding perspective extension.");
}
win->GetShell()->Open();
if (page == 0)
{
page = win->OpenPage(perspectiveId, input);
}
else
{
page->SetPerspective(desc);
}
return page;
}
// Just throw an exception....
throw WorkbenchException("Problems opening perspective \"" +
perspectiveId + "\"");
}
IWorkbenchPage::Pointer Workbench::ShowPerspective(
const QString& /*perspectiveId*/,
IWorkbenchWindow::Pointer /*window*/,
IAdaptable* /*input*/)
{
return IWorkbenchPage::Pointer(nullptr);
// // If the specified window has the requested perspective open and the
// // same requested
// // input, then the window is given focus and the perspective is shown.
// bool inputSameAsWindow = false;
// WorkbenchWindow::Pointer win = window.Cast<WorkbenchWindow>();
// if (win.IsNotNull()) {
// WorkbenchPage::Pointer page = win->GetActiveWorkbenchPage();
// if (page.IsNotNull()) {
// bool inputSame = false;
// if (input == 0) {
// inputSame = (page->GetInput() == 0);
// } else {
// inputSame = input.equals(page.getInput());
// }
// if (inputSame) {
// inputSameAsWindow = true;
// IPerspectiveDescriptor perspectives[] = page
// .getOpenPerspectives();
// for (int i = 0; i < perspectives.length; i++) {
// IPerspectiveDescriptor persp = perspectives[i];
// if (perspectiveId.equals(persp.getId())) {
// win.makeVisible();
// page.setPerspective(persp);
// return page;
// }
// }
// }
// }
// }
//
// // If another window has the requested input and the requested
- // // perpective open and active, then that window is given focus.
+ // // perspective open and active, then that window is given focus.
// IWorkbenchWindow[] windows = getWorkbenchWindows();
// for (int i = 0; i < windows.length; i++) {
// win = (WorkbenchWindow) windows[i];
// if (window != win) {
// WorkbenchPage page = win.getActiveWorkbenchPage();
// if (page != null) {
// boolean inputSame = false;
// if (input == null) {
// inputSame = (page.getInput() == null);
// } else {
// inputSame = input.equals(page.getInput());
// }
// if (inputSame) {
// Perspective persp = page.getActivePerspective();
// if (persp != null) {
// IPerspectiveDescriptor desc = persp.getDesc();
// if (desc != null) {
// if (perspectiveId.equals(desc.getId())) {
// win.getShell().open();
// return page;
// }
// }
// }
// }
// }
// }
// }
//
// // If the specified window has the same requested input but not the
// // requested
// // perspective, then the window is given focus and the perspective is
// // opened and shown
// // on condition that the user preference is not to open perspectives in
// // a new window.
// win = (WorkbenchWindow) window;
// if (inputSameAsWindow && win != null) {
// IPreferenceStore store = WorkbenchPlugin.getDefault()
// .getPreferenceStore();
// int mode = store.getInt(IPreferenceConstants.OPEN_PERSP_MODE);
//
// if (IPreferenceConstants.OPM_NEW_WINDOW != mode) {
// IWorkbenchPage page = win.getActiveWorkbenchPage();
// IPerspectiveDescriptor desc = getPerspectiveRegistry()
// .findPerspectiveWithId(perspectiveId);
// if (desc == null) {
// throw new WorkbenchException(
// NLS
// .bind(
// WorkbenchMessages.WorkbenchPage_ErrorCreatingPerspective,
// perspectiveId));
// }
// win.getShell().open();
// if (page == null) {
// page = win.openPage(perspectiveId, input);
// } else {
// page.setPerspective(desc);
// }
// return page;
// }
// }
//
// // If the specified window has no active perspective, then open the
// // requested perspective and show the specified window.
// if (win != null) {
// IWorkbenchPage page = win.getActiveWorkbenchPage();
// IPerspectiveDescriptor persp = null;
// if (page != null) {
// persp = page.getPerspective();
// }
// if (persp == null) {
// IPerspectiveDescriptor desc = getPerspectiveRegistry()
// .findPerspectiveWithId(perspectiveId);
// if (desc == null) {
// throw new WorkbenchException(
// NLS
// .bind(
// WorkbenchMessages.WorkbenchPage_ErrorCreatingPerspective,
// perspectiveId));
// }
// win.getShell().open();
// if (page == null) {
// page = win.openPage(perspectiveId, input);
// } else {
// page.setPerspective(desc);
// }
// return page;
// }
// }
//
// // Otherwise the requested perspective is opened and shown in a new
// // window, and the
// // window is given focus.
// IWorkbenchWindow newWindow = openWorkbenchWindow(perspectiveId, input);
// return newWindow.getActivePage();
}
bool Workbench::SaveAllEditors(bool /*confirm*/)
{
return true;
}
IIntroManager* Workbench::GetIntroManager() const
{
return GetWorkbenchIntroManager();
}
WorkbenchIntroManager* Workbench::GetWorkbenchIntroManager() const
{
if (introManager.isNull())
{
introManager.reset(new WorkbenchIntroManager(const_cast<Workbench*>(this)));
}
return introManager.data();
}
IntroDescriptor::Pointer Workbench::GetIntroDescriptor() const
{
return introDescriptor;
}
void Workbench::SetIntroDescriptor(IntroDescriptor::Pointer descriptor)
{
if (GetIntroManager()->GetIntro())
{
GetIntroManager()->CloseIntro(GetIntroManager()->GetIntro());
}
introDescriptor = descriptor;
}
bool Workbench::IsRunning() const
{
return Tweaklets::Get(WorkbenchTweaklet::KEY)->IsRunning();
}
bool Workbench::IsStarting() const
{
return isStarting;
}
bool Workbench::IsClosing() const
{
return isClosing;
}
WorkbenchWindow::Pointer Workbench::GetActivatedWindow() const
{
return activatedWindow;
}
/*
* Sets the workbench window which was last known being the active one, or
* <code> null </code> .
*/
void Workbench::SetActivatedWindow(WorkbenchWindow::Pointer window)
{
activatedWindow = window;
}
WorkbenchWindow::Pointer Workbench::NewWorkbenchWindow()
{
WorkbenchWindow::Pointer wbw(new WorkbenchWindow(this->GetNewWindowNumber()));
return wbw;
}
int Workbench::GetNewWindowNumber()
{
// Get window list.
QList<Window::Pointer> windows = windowManager.GetWindows();
int count = static_cast<int>(windows.size());
// Create an array of booleans (size = window count).
// Cross off every number found in the window list.
auto checkArray = new bool[count];
for (int nX = 0; nX < count; ++nX)
{
if (windows[nX].Cast<WorkbenchWindow> ().IsNotNull())
{
WorkbenchWindow::Pointer ww = windows[nX].Cast<WorkbenchWindow> ();
int index = ww->GetNumber() - 1;
if (index >= 0 && index < count)
{
checkArray[index] = true;
}
}
}
// Return first index which is not used.
// If no empty index was found then every slot is full.
// Return next index.
for (int index = 0; index < count; index++)
{
if (!checkArray[index])
{
delete[] checkArray;
return index + 1;
}
}
delete[] checkArray;
return static_cast<int>(count + 1);
}
IWorkbenchWindow::Pointer Workbench::BusyOpenWorkbenchWindow(
const QString& perspID, IAdaptable* input)
{
// Create a workbench window (becomes active window)
//final WorkbenchWindow newWindowArray[] = new WorkbenchWindow[1];
//StartupThreading.runWithWorkbenchExceptions(new StartupRunnable() {
// public void runWithException() {
// newWindowArray[0] = newWorkbenchWindow();
WorkbenchWindow::Pointer newWindow = this->NewWorkbenchWindow();
// }
//});
//final WorkbenchWindow newWindow = newWindowArray[0];
//StartupThreading.runWithoutExceptions(new StartupRunnable() {
// public void runWithException() {
newWindow->Create(); // must be created before adding to window
// manager
// }
//});
windowManager.Add(newWindow);
//final WorkbenchException [] exceptions = new WorkbenchException[1];
// Create the initial page.
if (perspID != "")
{
//StartupThreading.runWithWorkbenchExceptions(new StartupRunnable() {
try
{
newWindow->BusyOpenPage(perspID, input);
}
catch (WorkbenchException& e)
{
windowManager.Remove(newWindow);
throw e;
}
}
// Open window after opening page, to avoid flicker.
//StartupThreading.runWithWorkbenchExceptions(new StartupRunnable() {
// public void runWithException() {
newWindow->Open();
// }
//});
return newWindow;
}
bool Workbench::SaveState(IMemento::Pointer memento)
{
// MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK,
// WorkbenchMessages.Workbench_problemsSaving, null);
bool result = true;
// Save the version number.
memento->PutString(WorkbenchConstants::TAG_VERSION, VERSION_STRING[0]);
// Save how many plug-ins were loaded while restoring the workbench
if (progressCount != -1)
{
memento->PutInteger(WorkbenchConstants::TAG_PROGRESS_COUNT,
progressCount);
}
// Save the advisor state.
IMemento::Pointer advisorState = memento
->CreateChild(WorkbenchConstants::TAG_WORKBENCH_ADVISOR);
//result.add(getAdvisor().saveState(advisorState));
result &= GetAdvisor()->SaveState(advisorState);
// Save the workbench windows.
QList<IWorkbenchWindow::Pointer> windows(GetWorkbenchWindows());
for (int nX = 0; nX < windows.size(); nX++)
{
WorkbenchWindow::Pointer window = windows[nX].Cast<WorkbenchWindow>();
IMemento::Pointer childMem = memento->CreateChild(WorkbenchConstants::TAG_WINDOW);
//result.merge(window.saveState(childMem));
result &= window->SaveState(childMem);
}
// result.add(getEditorHistory().saveState(
// memento.createChild(IWorkbenchConstants.TAG_MRU_LIST)));
return result;
}
XMLMemento::Pointer Workbench::RecordWorkbenchState()
{
XMLMemento::Pointer memento = XMLMemento
::CreateWriteRoot(WorkbenchConstants::TAG_WORKBENCH);
//final IStatus status = saveState(memento);
bool status = SaveState(memento);
//if (status.getSeverity() != IStatus.OK) {
if (!status)
{
// // don't use newWindow as parent because it has not yet been opened
// // (bug 76724)
// StartupThreading.runWithoutExceptions(new StartupRunnable() {
//
// public void runWithException() throws Throwable {
// ErrorDialog.openError(null,
// WorkbenchMessages.Workbench_problemsSaving,
// WorkbenchMessages.Workbench_problemsSavingMsg, status);
// }});
}
return memento;
}
void Workbench::AddWorkbenchListener(IWorkbenchListener* listener)
{
workbenchEvents.AddListener(listener);
}
void Workbench::RemoveWorkbenchListener(IWorkbenchListener* listener)
{
workbenchEvents.RemoveListener(listener);
}
IWorkbenchListener::Events& Workbench::GetWorkbenchEvents()
{
return workbenchEvents;
}
void Workbench::AddWindowListener(IWindowListener* l)
{
windowEvents.AddListener(l);
}
void Workbench::RemoveWindowListener(IWindowListener* l)
{
windowEvents.RemoveListener(l);
}
IWindowListener::Events& Workbench::GetWindowEvents()
{
return windowEvents;
}
bool Workbench::FirePreShutdown(bool forced)
{
//SafeRunnable.run(new SafeRunnable() {
// public void run() {
typedef IWorkbenchListener::Events::PreShutdownEvent::ListenerList ListenerList;
const ListenerList& listeners = workbenchEvents.preShutdown.GetListeners();
for ( auto iter = listeners.begin();
iter != listeners.end(); ++iter )
{
// notify each listener
if (! (*iter)->Execute(dynamic_cast<IWorkbench*>(this), forced))
return false;
}
// }
return true;
}
/**
* Fire workbench postShutdown event.
*
* @since 3.2
*/
void Workbench::FirePostShutdown()
{
// SafeRunnable.run(new SafeRunnable() {
// public void run() {
workbenchEvents.postShutdown(this);
// }
}
void Workbench::FireWindowOpened(IWorkbenchWindow::Pointer window)
{
// SafeRunner.run(new SafeRunnable() {
// public void run() {
windowEvents.windowOpened(window);
// }
}
void Workbench::FireWindowClosed(IWorkbenchWindow::Pointer window)
{
if (activatedWindow == window)
{
// Do not hang onto it so it can be GC'ed
activatedWindow = nullptr;
}
// SafeRunner.run(new SafeRunnable() {
// public void run() {
windowEvents.windowClosed(window);
// }
}
void Workbench::FireWindowActivated(IWorkbenchWindow::Pointer window)
{
// SafeRunner.run(new SafeRunnable() {
// public void run() {
windowEvents.windowActivated(window);
// }
}
void Workbench::FireWindowDeactivated(IWorkbenchWindow::Pointer window)
{
// SafeRunner.run(new SafeRunnable() {
// public void run() {
windowEvents.windowDeactivated(window);
// }
}
IWorkbenchWindow::Pointer Workbench::RestoreWorkbenchWindow(IMemento::Pointer memento)
{
WorkbenchWindow::Pointer newWindow = this->NewWorkbenchWindow();
//newWindow.create();
windowManager.Add(newWindow);
// whether the window was opened
bool opened = false;
try
{
newWindow->RestoreState(memento, IPerspectiveDescriptor::Pointer(nullptr));
newWindow->FireWindowRestored();
newWindow->Open();
opened = true;
}
catch (...)
{
if (!opened)
{
newWindow->Close();
}
}
return newWindow;
}
void Workbench::Shutdown()
{
// shutdown application-specific portions first
advisor->PostShutdown();
// notify regular workbench clients of shutdown, and clear the list when
// done
this->FirePostShutdown();
//workbenchListeners.clear();
//cancelEarlyStartup();
// for dynamic UI
// Platform.getExtensionRegistry().removeRegistryChangeListener(
// extensionEventHandler);
// Platform.getExtensionRegistry().removeRegistryChangeListener(
// startupRegistryListener);
// ((GrabFocus) Tweaklets.get(GrabFocus.KEY)).dispose();
// Bring down all of the services.
serviceLocator->Dispose();
// workbenchActivitySupport.dispose();
// WorkbenchHelpSystem.disposeIfNecessary();
// shutdown the rest of the workbench
// WorkbenchColors.shutdown();
// activityHelper.shutdown();
// uninitializeImages();
// if (WorkbenchPlugin.getDefault() != null) {
// WorkbenchPlugin.getDefault().reset();
// }
// WorkbenchThemeManager.getInstance().dispose();
// PropertyPageContributorManager.getManager().dispose();
// ObjectActionContributorManager.getManager().dispose();
// if (tracker != null) {
// tracker.close();
// }
Tweaklets::Clear();
}
void Workbench::InitializeSourcePriorities()
{
WorkbenchServiceRegistry::GetRegistry()->InitializeSourcePriorities();
}
void Workbench::StartSourceProviders()
{
/*
* Phase 3 of the initialization of commands. The source providers that
* the workbench provides are creating and registered with the above
* services. These source providers notify the services when particular
* pieces of workbench state change.
*/
IEvaluationService* const evaluationService = serviceLocator->GetService<IEvaluationService>();
//IContextService* const contextService = serviceLocator->GetService<IContextService>();
auto sps = new SourceProviderService(serviceLocator.GetPointer());
sourceProviderService.reset(sps);
sourceProviderService->Register();
serviceLocator->RegisterService<ISourceProviderService>(sourceProviderService.data());
struct SafeSourceProviderRunnable : public ISafeRunnable {
SafeSourceProviderRunnable(SourceProviderService* sps, IEvaluationService* es) : sps(sps), es(es)
{}
void Run() override {
// this currently instantiates all players ... sigh
sps->ReadRegistry();
QList<ISourceProvider::Pointer> sp = sps->GetSourceProviders();
for (int i = 0; i < sp.size(); i++)
{
es->AddSourceProvider(sp[i]);
//if (!(sp[i] instanceof ActiveContextSourceProvider))
//{
// contextService.addSourceProvider(sp[i]);
//}
}
}
void HandleException(const ctkException& exception) override
{
WorkbenchPlugin::Log("Failed to initialize a source provider", exception);
}
private:
SourceProviderService* sps;
IEvaluationService* es;
};
ISafeRunnable::Pointer sourceProviderRunnable(
new SafeSourceProviderRunnable(sps, evaluationService));
SafeRunner::Run(sourceProviderRunnable);
// SafeRunner.run(new ISafeRunnable() {
// public void run() throws Exception {
// // these guys are need to provide the variables they say
// // they source
// actionSetSourceProvider = (ActionSetSourceProvider) sourceProviderService
// .getSourceProvider(ISources.ACTIVE_ACTION_SETS_NAME);
// FocusControlSourceProvider focusControl = (FocusControlSourceProvider) sourceProviderService
// .getSourceProvider(ISources.ACTIVE_FOCUS_CONTROL_ID_NAME);
// serviceLocator.registerService(IFocusService.class, focusControl);
// menuSourceProvider = (MenuSourceProvider) sourceProviderService
// .getSourceProvider(ISources.ACTIVE_MENU_NAME);
// }
// public void handleException(Throwable exception) {
// WorkbenchPlugin.log("Failed to initialize a source provider", exception); //$NON-NLS-1$
// }
// });
}
void Workbench::UpdateActiveWorkbenchWindowMenuManager(bool textOnly)
{
if (activeWorkbenchWindow != nullptr)
{
// if (actionSetSourceProvider != null)
// {
// activeWorkbenchWindow->RemoveActionSetsListener(actionSetSourceProvider);
// }
activeWorkbenchWindow = nullptr;
}
//bool actionSetsUpdated = false;
IWorkbenchWindow::Pointer workbenchWindow = GetActiveWorkbenchWindow();
if (WorkbenchWindow::Pointer wbWnd = workbenchWindow.Cast<WorkbenchWindow>())
{
activeWorkbenchWindow = wbWnd.GetPointer();
if (activeWorkbenchWindow->IsClosing())
{
return;
}
// Update the action sets.
// Shell::Pointer windowShell = activeWorkbenchWindow->GetShell();
// Shell::Pointer activeShell = GetDisplay()->GetActiveShell();
// IContextService* service = GetService<IContextService>();
// if ((Util.equals(windowShell, activeShell) || service.getShellType(activeShell) == IContextService.TYPE_WINDOW)
// && actionSetSourceProvider != null)
// {
// activeWorkbenchWindow->AddActionSetsListener(actionSetSourceProvider);
// final WorkbenchPage page = activeWorkbenchWindow.getActiveWorkbenchPage();
// final IActionSetDescriptor[] newActionSets;
// if (page != null)
// {
// newActionSets = page.getActionSets();
// final ActionSetsEvent event = new ActionSetsEvent(newActionSets);
// actionSetSourceProvider.actionSetsChanged(event);
// actionSetsUpdated = true;
// }
// }
MenuManager* menuManager = activeWorkbenchWindow->GetMenuManager();
if (textOnly)
{
menuManager->Update(QActionProperties::TEXT);
}
else
{
menuManager->Update(true);
}
}
// if (!actionSetsUpdated && actionSetSourceProvider != null)
// {
// ActionSetsEvent event = new ActionSetsEvent(null);
// actionSetSourceProvider.actionSetsChanged(event);
// }
}
void Workbench::WindowActivated(const IWorkbenchWindow::Pointer&)
{
this->UpdateActiveWorkbenchWindowMenuManager(true);
}
void Workbench::WindowDeactivated(const IWorkbenchWindow::Pointer&)
{
this->UpdateActiveWorkbenchWindowMenuManager(true);
}
void Workbench::WindowClosed(const IWorkbenchWindow::Pointer&)
{
this->UpdateActiveWorkbenchWindowMenuManager(true);
}
void Workbench::WindowOpened(const IWorkbenchWindow::Pointer&)
{
this->UpdateActiveWorkbenchWindowMenuManager(true);
}
}
diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbenchPage.h b/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbenchPage.h
index 85d05670da..a1b83bac33 100644
--- a/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbenchPage.h
+++ b/Plugins/org.blueberry.ui.qt/src/internal/berryWorkbenchPage.h
@@ -1,1428 +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);
/*
* Returns 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 persistence 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 highlighted.
*/
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 unhighlighted.
*/
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
* propagate 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
+ * Checks perspectives in the order they were activated
* for the specified 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 Assertion 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
{
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.common/src/mitkIRenderWindowPart.h b/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPart.h
index 38bd85cbb5..b1993e66d8 100644
--- a/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPart.h
+++ b/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPart.h
@@ -1,230 +1,230 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 mitkIRenderWindowPart_h
#define mitkIRenderWindowPart_h
#include <QString>
#include <QStringList>
#include <QHash>
#include <QtPlugin>
#include <mitkBaseRenderer.h>
#include <mitkNumericTypes.h>
#include <mitkRenderingManager.h>
#include <org_mitk_gui_common_Export.h>
class QmitkRenderWindow;
namespace mitk {
struct IRenderingManager;
class TimeNavigationController;
/**
* \ingroup org_mitk_gui_common
*
* \brief Interface for a MITK Workbench Part providing a render window.
*
* This interface allows generic access to Workbench parts which provide some
* kind of render window. The interface is intended to be implemented by
* subclasses of berry::IWorkbenchPart. Usually, the interface is implemented
* by a Workbench editor.
*
* A IRenderWindowPart provides zero or more QmitkRenderWindow instances which can
* be controlled via this interface. QmitkRenderWindow instances have an associated
* \e id, which is implementation specific.
* Additionally the defined values Axial, Sagittal, Coronal and Original from mitk::AnatomicalPlane
* can be used to retrieve a specific QmitkRenderWindow.
*
* \see ILinkedRenderWindowPart
* \see IRenderWindowPartListener
* \see QmitkAbstractRenderEditor
*/
struct MITK_GUI_COMMON_PLUGIN IRenderWindowPart {
static const QString DECORATION_BORDER; // = "border"
static const QString DECORATION_LOGO; // = "logo"
static const QString DECORATION_MENU; // = "menu"
static const QString DECORATION_BACKGROUND; // = "background"
static const QString DECORATION_CORNER_ANNOTATION; // = "corner annotation"
virtual ~IRenderWindowPart();
/**
* Get the currently active (focused) render window.
* Focus handling is implementation specific.
*
* \return The active QmitkRenderWindow instance; <code>nullptr</code>
* if no render window is active.
*/
virtual QmitkRenderWindow* GetActiveQmitkRenderWindow() const = 0;
/**
* Get all render windows with their ids.
*
* \return A hash map mapping the render window id to the QmitkRenderWindow instance.
*/
virtual QHash<QString,QmitkRenderWindow*> GetQmitkRenderWindows() const = 0;
/**
* Get a render window with a specific id.
*
* \param id The render window id.
* \return The QmitkRenderWindow instance for <code>id</code>
*/
virtual QmitkRenderWindow* GetQmitkRenderWindow(const QString& id) const = 0;
/**
* Get a render window with a specific plane orientation.
*
* \param orientation The render window plane orientation.
* \return The QmitkRenderWindow instance for <code>orientation</code>
*/
virtual QmitkRenderWindow* GetQmitkRenderWindow(const mitk::AnatomicalPlane& orientation) const = 0;
/**
* Get the rendering manager used by this render window part.
*
* \return The current IRenderingManager instance or <code>nullptr</code>
* if no rendering manager is used.
*/
virtual mitk::IRenderingManager* GetRenderingManager() const = 0;
/**
* Request an update of all render windows.
*
* \param requestType Specifies the type of render windows for which an update
* will be requested.
*/
virtual void RequestUpdate(mitk::RenderingManager::RequestType requestType = mitk::RenderingManager::REQUEST_UPDATE_ALL) = 0;
/**
* Force an immediate update of all render windows.
*
* \param requestType Specifies the type of render windows for which an immediate update
* will be requested.
*/
virtual void ForceImmediateUpdate(mitk::RenderingManager::RequestType requestType = mitk::RenderingManager::REQUEST_UPDATE_ALL) = 0;
/**
* @brief Initialize the render windows of this render window part to the given geometry.
*
* @param geometry The geometry to be used to initialize / update a
* render window's time and slice navigation controller.
* @param resetCamera If true, the camera and crosshair will be reset to the default view (centered, no zoom).
* If false, the current crosshair position and the camera zoom will be stored and reset
* after the reference geometry has been updated.
*/
virtual void InitializeViews(const mitk::TimeGeometry* geometry, bool resetCamera) = 0;
/**
- * @brief Define the reference geometry for interaction withing a render window.
+ * @brief Define the reference geometry for interaction within a render window.
*
* The concrete implementation is subclass-specific, no default implementation is provided here.
* An implementation can be found in 'QmitkAbstractMultiWidgetEditor' and will just
* forward the argument to the contained multi widget.
*
* @param referenceGeometry The interaction reference geometry for the concrete multi widget.
* For more details, see 'BaseRenderer::SetInteractionReferenceGeometry'.
*/
virtual void SetInteractionReferenceGeometry(const mitk::TimeGeometry* referenceGeometry) = 0;
/**
* @brief Returns true if the render windows are coupled; false if not.
*
* Render windows are coupled if the slice navigation controller of the render windows
* are connected which means that always the same geometry is used for the render windows.
*/
virtual bool HasCoupledRenderWindows() const = 0;
/**
* Get the TimeNavigationController for controlling time positions.
*
* \return A TimeNavigationController if the render window supports this
* operation; otherwise returns <code>nullptr</code>.
*/
virtual mitk::TimeNavigationController* GetTimeNavigationController() const = 0;
/**
* Get the selected position in the render window with id <code>id</code>
* or in the active render window if <code>id</code> is an empty string.
*
* \param id The render window id.
* \return The currently selected position in world coordinates.
*/
virtual mitk::Point3D GetSelectedPosition(const QString& id = QString()) const = 0;
/**
* Set the selected position in the render window with id <code>id</code>
* or in the active render window if <code>id</code> is nullptr.
*
* \param pos The position in world coordinates which should be selected.
* \param id The render window id in which the selection should take place.
*/
virtual void SetSelectedPosition(const mitk::Point3D& pos, const QString& id = QString()) = 0;
/**
* Get the time point selected in the render window with id <code>id</code>
* or in the active render window if <code>id</code> is an empty string.
*
* \param id The render window id.
* \return The currently selected position in world coordinates.
*/
virtual TimePointType GetSelectedTimePoint(const QString& id = QString()) const = 0;
/**
* Enable \e decorations like colored borders, menu widgets, logos, text annotations, etc.
*
* Decorations are implementation specific. A set of standardized decoration names is listed
* in GetDecorations().
*
* \param enable If <code>true</code> enable the decorations specified in <code>decorations</code>,
* otherwise disable them.
* \param decorations A list of decoration names. If empty, all supported decorations are affected.
*
* \see GetDecorations()
*/
virtual void EnableDecorations(bool enable, const QStringList& decorations = QStringList()) = 0;
/**
* Return if a specific decoration is enabled.
*
* \return <code>true</code> if the decoration is enabled, <code>false</code> if it is disabled
* or unknown.
*
* \see GetDecorations()
*/
virtual bool IsDecorationEnabled(const QString& decoration) const = 0;
/**
* Get a list of supported decorations.
*
* The following decoration names are standardized and should not be used for other decoration types:
* <ul>
* <li>\e DECORATION_BORDER Any border decorations like colored rectangles, etc.
* <li>\e DECORATION_MENU Menus associated with render windows
* <li>\e DECORATION_BACKGROUND All kinds of backgrounds (patterns, gradients, etc.) except for solid colored backgrounds
* <li>\e DECORATION_LOGO Any kind of logo overlayed on the rendered scene
* </ul>
*
* \return A list of supported decoration names.
*/
virtual QStringList GetDecorations() const = 0;
};
}
Q_DECLARE_INTERFACE(mitk::IRenderWindowPart, "org.mitk.ui.IRenderWindowPart")
#endif
diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractMultiWidgetEditor.cpp b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractMultiWidgetEditor.cpp
index 9a3ff2d024..d7296bd69c 100644
--- a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractMultiWidgetEditor.cpp
+++ b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractMultiWidgetEditor.cpp
@@ -1,252 +1,257 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkAbstractMultiWidgetEditor.h"
// mitk qt widgets module
#include <QmitkAbstractMultiWidget.h>
#include <QmitkRenderWindowWidget.h>
// mitk gui qt common plugin
#include "QmitkMultiWidgetDecorationManager.h"
// berry
#include <berryIWorkbenchPartConstants.h>
const QString QmitkAbstractMultiWidgetEditor::EDITOR_ID = "org.mitk.editors.abstractmultiwidget";
struct QmitkAbstractMultiWidgetEditor::Impl final
{
Impl();
~Impl() = default;
QmitkAbstractMultiWidget* m_MultiWidget;
std::unique_ptr<QmitkMultiWidgetDecorationManager> m_MultiWidgetDecorationManager;
};
QmitkAbstractMultiWidgetEditor::Impl::Impl()
: m_MultiWidget(nullptr)
{
// nothing here
}
QmitkAbstractMultiWidgetEditor::QmitkAbstractMultiWidgetEditor()
: m_Impl(std::make_unique<Impl>())
{
// nothing here
}
QmitkAbstractMultiWidgetEditor::~QmitkAbstractMultiWidgetEditor() {}
QmitkRenderWindow* QmitkAbstractMultiWidgetEditor::GetActiveQmitkRenderWindow() const
{
const auto& multiWidget = GetMultiWidget();
if (nullptr != multiWidget)
{
auto activeRenderWindowWidget = multiWidget->GetActiveRenderWindowWidget();
if (nullptr != activeRenderWindowWidget)
{
return activeRenderWindowWidget->GetRenderWindow();
}
}
return nullptr;
}
QHash<QString, QmitkRenderWindow*> QmitkAbstractMultiWidgetEditor::GetQmitkRenderWindows() const
{
QHash<QString, QmitkRenderWindow*> result;
const auto& multiWidget = GetMultiWidget();
if (nullptr == multiWidget)
{
return result;
}
result = multiWidget->GetRenderWindows();
return result;
}
QmitkRenderWindow* QmitkAbstractMultiWidgetEditor::GetQmitkRenderWindow(const QString& id) const
{
const auto& multiWidget = GetMultiWidget();
if (nullptr == multiWidget)
{
return nullptr;
}
return multiWidget->GetRenderWindow(id);
}
QmitkRenderWindow* QmitkAbstractMultiWidgetEditor::GetQmitkRenderWindow(const mitk::AnatomicalPlane& orientation) const
{
const auto& multiWidget = GetMultiWidget();
if (nullptr == multiWidget)
{
return nullptr;
}
return multiWidget->GetRenderWindow(orientation);
}
void QmitkAbstractMultiWidgetEditor::InitializeViews(const mitk::TimeGeometry* geometry, bool resetCamera)
{
const auto& multiWidget = this->GetMultiWidget();
if (nullptr == multiWidget)
{
return;
}
multiWidget->InitializeViews(geometry, resetCamera);
}
void QmitkAbstractMultiWidgetEditor::SetInteractionReferenceGeometry(const mitk::TimeGeometry* referenceGeometry)
{
const auto& multiWidget = this->GetMultiWidget();
if (nullptr == multiWidget)
{
return;
}
multiWidget->SetInteractionReferenceGeometry(referenceGeometry);
}
bool QmitkAbstractMultiWidgetEditor::HasCoupledRenderWindows() const
{
const auto& multiWidget = GetMultiWidget();
if (nullptr == multiWidget)
{
return false;
}
return multiWidget->HasCoupledRenderWindows();
}
mitk::Point3D QmitkAbstractMultiWidgetEditor::GetSelectedPosition(const QString& id/* = QString()*/) const
{
const auto& multiWidget = GetMultiWidget();
if (nullptr == multiWidget)
{
return mitk::Point3D();
}
return multiWidget->GetSelectedPosition(id);
}
void QmitkAbstractMultiWidgetEditor::SetSelectedPosition(const mitk::Point3D& pos, const QString& id/* = QString()*/)
{
const auto& multiWidget = GetMultiWidget();
if (nullptr != multiWidget)
{
return multiWidget->SetSelectedPosition(pos, id);
}
}
void QmitkAbstractMultiWidgetEditor::EnableDecorations(bool enable, const QStringList& decorations)
{
m_Impl->m_MultiWidgetDecorationManager->ShowDecorations(enable, decorations);
}
bool QmitkAbstractMultiWidgetEditor::IsDecorationEnabled(const QString& decoration) const
{
return m_Impl->m_MultiWidgetDecorationManager->IsDecorationVisible(decoration);
}
QStringList QmitkAbstractMultiWidgetEditor::GetDecorations() const
{
return m_Impl->m_MultiWidgetDecorationManager->GetDecorations();
}
QmitkRenderWindow* QmitkAbstractMultiWidgetEditor::GetQmitkRenderWindowByIndex(int index) const
{
const auto& multiWidget = GetMultiWidget();
if (nullptr == multiWidget)
{
return nullptr;
}
QString renderWindowName = multiWidget->GetNameFromIndex(index);
return multiWidget->GetRenderWindow(renderWindowName);
}
QmitkRenderWindow* QmitkAbstractMultiWidgetEditor::GetQmitkRenderWindowByIndex(int row, int column) const
{
const auto& multiWidget = GetMultiWidget();
if (nullptr == multiWidget)
{
return nullptr;
}
QString renderWindowName = multiWidget->GetNameFromIndex(row, column);
return multiWidget->GetRenderWindow(renderWindowName);
}
void QmitkAbstractMultiWidgetEditor::SetMultiWidget(QmitkAbstractMultiWidget* multiWidget)
{
m_Impl->m_MultiWidget = multiWidget;
m_Impl->m_MultiWidgetDecorationManager.reset(new QmitkMultiWidgetDecorationManager(multiWidget));
}
QmitkAbstractMultiWidget* QmitkAbstractMultiWidgetEditor::GetMultiWidget() const
{
return m_Impl->m_MultiWidget;
}
int QmitkAbstractMultiWidgetEditor::GetRowCount() const
{
const auto& multiWidget = GetMultiWidget();
if (nullptr == multiWidget)
{
return 0;
}
return multiWidget->GetRowCount();
}
int QmitkAbstractMultiWidgetEditor::GetColumnCount() const
{
const auto& multiWidget = GetMultiWidget();
if (nullptr == multiWidget)
{
return 0;
}
return multiWidget->GetColumnCount();
}
void QmitkAbstractMultiWidgetEditor::OnLayoutSet(int row, int column)
{
const auto& multiWidget = GetMultiWidget();
if (nullptr != multiWidget)
{
multiWidget->SetLayout(row, column);
FirePropertyChange(berry::IWorkbenchPartConstants::PROP_INPUT);
}
}
void QmitkAbstractMultiWidgetEditor::OnSynchronize(bool synchronized)
{
const auto& multiWidget = GetMultiWidget();
if (nullptr != multiWidget)
{
multiWidget->Synchronize(synchronized);
}
}
void QmitkAbstractMultiWidgetEditor::OnInteractionSchemeChanged(mitk::InteractionSchemeSwitcher::InteractionScheme scheme)
{
const auto& multiWidget = GetMultiWidget();
if (nullptr != multiWidget)
{
multiWidget->SetInteractionScheme(scheme);
}
}
+
+ QmitkMultiWidgetDecorationManager* QmitkAbstractMultiWidgetEditor::GetDecorationManager() const
+ {
+ return m_Impl->m_MultiWidgetDecorationManager.get();
+ }
diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractMultiWidgetEditor.h b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractMultiWidgetEditor.h
index 4485beaf66..f997b527a0 100644
--- a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractMultiWidgetEditor.h
+++ b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractMultiWidgetEditor.h
@@ -1,139 +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.
============================================================================*/
#ifndef QmitkAbstractMultiWidgetEditor_h
#define QmitkAbstractMultiWidgetEditor_h
#include <org_mitk_gui_qt_common_Export.h>
// org mitk gui qt common plugin
#include <QmitkAbstractRenderEditor.h>
// mitk core
#include <mitkInteractionSchemeSwitcher.h>
// berry
#include <berryIPartListener.h>
// c++
#include <memory>
class QmitkAbstractMultiWidget;
class QmitkLevelWindowWidget;
+class QmitkMultiWidgetDecorationManager;
class MITK_QT_COMMON QmitkAbstractMultiWidgetEditor : public QmitkAbstractRenderEditor, public berry::IPartListener
{
Q_OBJECT
public:
berryObjectMacro(QmitkAbstractMultiWidgetEditor, QmitkAbstractRenderEditor, IPartListener);
static const QString EDITOR_ID;
QmitkAbstractMultiWidgetEditor();
virtual ~QmitkAbstractMultiWidgetEditor() override;
/**
* @brief Overridden from QmitkAbstractRenderEditor : IRenderWindowPart
*/
virtual QmitkRenderWindow* GetActiveQmitkRenderWindow() const override;
/**
* @brief Overridden from QmitkAbstractRenderEditor : IRenderWindowPart
*/
virtual QHash<QString, QmitkRenderWindow*> GetQmitkRenderWindows() const override;
/**
* @brief Overridden from QmitkAbstractRenderEditor : IRenderWindowPart
*/
virtual QmitkRenderWindow* GetQmitkRenderWindow(const QString& id) const override;
/**
* @brief Overridden from QmitkAbstractRenderEditor : IRenderWindowPart
*/
virtual QmitkRenderWindow* GetQmitkRenderWindow(const mitk::AnatomicalPlane& orientation) const override;
/**
* @brief Overridden from QmitkAbstractRenderEditor : IRenderWindowPart
*/
void InitializeViews(const mitk::TimeGeometry* geometry, bool resetCamera) override;
/**
* @brief Overridden from QmitkAbstractRenderEditor : IRenderWindowPart
*/
void SetInteractionReferenceGeometry(const mitk::TimeGeometry* referenceGeometry) override;
/**
* @brief Overridden from QmitkAbstractRenderEditor : IRenderWindowPart
*/
bool HasCoupledRenderWindows() const override;
/**
* @brief Overridden from QmitkAbstractRenderEditor : IRenderWindowPart
*/
virtual mitk::Point3D GetSelectedPosition(const QString& id = QString()) const override;
/**
* @brief Overridden from QmitkAbstractRenderEditor : IRenderWindowPart
*/
virtual void SetSelectedPosition(const mitk::Point3D& pos, const QString& id = QString()) override;
/**
* @brief Overridden from QmitkAbstractRenderEditor : IRenderWindowPart
*/
virtual void EnableDecorations(bool enable, const QStringList& decorations = QStringList()) override;
/**
* @brief Overridden from QmitkAbstractRenderEditor : IRenderWindowPart
*/
virtual bool IsDecorationEnabled(const QString& decoration) const override;
/**
* @brief Overridden from QmitkAbstractRenderEditor : IRenderWindowPart
*/
virtual QStringList GetDecorations() const override;
/**
* @brief Retrieve a QmitkRenderWindow by its index.
*/
virtual QmitkRenderWindow* GetQmitkRenderWindowByIndex(int index) const;
/**
* @brief Retrieve a QmitkRenderWindow by the row and column position.
*/
virtual QmitkRenderWindow* GetQmitkRenderWindowByIndex(int row, int column) const;
/**
* @brief Set the current multi widget of this editor.
*/
virtual void SetMultiWidget(QmitkAbstractMultiWidget* multiWidget);
/**
* @brief Return the current multi widget of this editor.
*/
virtual QmitkAbstractMultiWidget* GetMultiWidget() const;
/**
* @brief Return the number of rows of the underlying multi widget.
*/
virtual int GetRowCount() const;
/**
* @brief Return the number of columns of the underlying multi widget.
*/
virtual int GetColumnCount() const;
virtual QmitkLevelWindowWidget* GetLevelWindowWidget() const = 0;
public Q_SLOTS:
/**
* @brief A slot that can be called if the layout has been changed.
* This function will call the function 'SetLayout' of the multi widget where
* custom behavior can be implemented.
* Finally 'FirePropertyChange' is called to inform the workbench about an input change.
*/
virtual void OnLayoutSet(int row, int column);
virtual void OnSynchronize(bool synchronized);
virtual void OnInteractionSchemeChanged(mitk::InteractionSchemeSwitcher::InteractionScheme scheme);
+protected:
+ QmitkMultiWidgetDecorationManager* GetDecorationManager() const;
+
private:
struct Impl;
std::unique_ptr<Impl> m_Impl;
};
#endif
diff --git a/Plugins/org.mitk.gui.qt.dicombrowser/documentation/UserManual/QmitkDicom.dox b/Plugins/org.mitk.gui.qt.dicombrowser/documentation/UserManual/QmitkDicom.dox
index 913fea2601..8748c9a517 100644
--- a/Plugins/org.mitk.gui.qt.dicombrowser/documentation/UserManual/QmitkDicom.dox
+++ b/Plugins/org.mitk.gui.qt.dicombrowser/documentation/UserManual/QmitkDicom.dox
@@ -1,118 +1,118 @@
/**
\page org_mitk_editors_dicombrowser The Dicom Browser Plugin
\imageMacro{dicombrowser-dox.svg,"Icon of the DICOM Plugin",2.00}
\note This article requires a basic knowledge of the DICOM Standard.
\tableofcontents
\section org_mitk_gui_qt_dicomOverview Overview
The DICOM editor is an experimental editor which allows for loading of DICOM images as well as server communication.
It features a highly experimental query/retrieve (you need to configure your PACS correspondingly) as well as a DICOM browser.
The DICOM browser allows you to navigate the DICOM folder/cd depending on its metadata (patient/study/series)
and import selected series for viewing in your MITK based application.
It also allows you to store your dicom data in an internal database so you can easily access often used dicom images.
-It is based on the <a href="https://commontk.org/index.php/Documentation/DICOM_Overview">commonTK (CTK) DICOM funcionality</a>.
+It is based on the <a href="https://commontk.org/index.php/Documentation/DICOM_Overview">commonTK (CTK) DICOM functionality</a>.
\section org_mitk_gui_qt_dicomDataHandling Data handling
\imageMacro{QmitkDicom_PluginControls.png,"The dicom Plugin controls",7.37}
In the image above you see the start page of the dicom plugin. On top of the start page you see four buttons. The Local Storage,
the Import CD, the Import Folder and the Query Retrieve button. If you press one of these buttons, the dicom plugin will switch to your local dicom image storage or will start importing dicom images
from CD or a folder on your hard drive or it will open the query retrieve screen.
<ul>
<li> Click the 'Local Storage' button to open the local storage screen.
<li> Click the 'Import CD' button to import DICOM data from a CD.
<li> Click the 'Import Folder' button to import DICOM date from a directory.
<li> Click the 'Query Retrieve' button to open the query retrieve screen.
</ul>
\subsection org_mitk_gui_qt_dicomStorage Data storage
\imageMacro{QmitkDicom_PluginExtended.png,"The DICOM data storage",16.00}
If you open the dicom plugin the dicom data storage will be displayed. You are able to see all your stored dicom image data.
You can browse your data by clicking on the left arrow beside the name of your data. There are three levels available.
The first level is the patient level where you can see the patient data. On the second level you can see the dicom studies for the patient.
on the third level you can see all available series referring to its study.
You can delete the data by selecting it and pressing the delete button.
Be careful if you have selected a patient or a study all referring data be deleted.
So if you delete a patient the patient and all studies and series referred to the patient will be deleted.
If you delete a study all series of the study will be deleted.
If you want to view the dicom data you have to select a series and click on the View button.
The data will appear in the DataManager and will be displayed.
\imageMacro{QmitkDicom_DisplayDataManager.png,"Viewed image",16.00}
<ul>
<li> Click on the arrow on the left of your data to expand or hide dicom data levels.
<li> Click the 'Delete' button to delete selected DICOM data.
<li> Click the 'View' button to view DICOM data.
</ul>
\subsection org_mitk_gui_qt_dicomImport Data import
\imageMacro{QmitkDicom_ImportDialog.png,"The import dialog checked",9.53}
There are two different ways to import DICOM data.
The First one is to directly import it into your DICOM data storage. To achieve this you should toggle the checkbox 'Copy on import'.
The second approach is, to have a look at the data first before importing it.
To do that you simply don't check 'Copy on import'.
This will leed you to the leed you to the 'External Dicom Data' screen which provides you a preview of the data containing in you're chosen folder.
You can import the data here by selecting it and pressing the 'Download' button.
It is also possible to view DICOM series directly in Mitk by selecting it here and pressing the 'View' button.
<ul>
<li> Click 'Import Folder' or 'Import CD' button to open the import dialog.</li>
<ul>
<li> Enable the 'Copy on import' checkbox and choose a folder to import into data storage directly.</li>
<li> Disable the 'Copy on import' checkbox to get to the 'External Dicom Data' screen.</li>
<ul>
<li> Click on the arrow on the left of your data to expand or hide dicom data levels.
<li> Click the 'Download' button to download selected DICOM data to your DICOM data storage.
<li> Click the 'View' button to view DICOM data.
</ul>
</ul>
</ul>
\section org_mitk_gui_qt_dicomQueryRetrieve Query/Retrieve
\warning This plugin is experimental and not all of the described features behave as expected.
\note The query retrieve plugin only works if the PACS you are calling knows your machine settings.
There are also issues when you are running a firewall.
The query retrieve workflow allows you to get DICOM data from a server.
\imageMacro{QmitkDicom_QueryRetrieve.png,"The query retrieve screen",16.00}
\subsection org_mitk_gui_qt_dicomQuery Query
\imageMacro{QmitkDicom_Nodes.png,"The DICOM network configuration",11.26}
By performing a DICOM query you will ask a server for it's DICOM data.
This requires to setup the DICOM network configuration of your system and the server.
By clicking on 'Add Server' a new plain server field will appear. Now you can give it a name of your choice.
Fill the servers "DICOM name" the AETitle. Type in it's url, it's port and the specific DICOM protocol you want to use for image transfer.
\note I recommend not to use CGET because most of the PACS systems (Image Servers) don't support that protocol.
You can configure the DICOM network configuration of your machine by editing the 'Calling AETiltle', the 'Storage AETitle' and The 'Storage Port' text fields.
But normally you don't have to change your configuration.
\imageMacro{QmitkDicom_FilterWidget.png,"The DICOM search options",3.66}
After you have finished your network configuration and before you start the query you should use the 'Search Options' to specify your query.
Otherwise all data on the server will be queried and you will have to wait for a long time.
You can specify your query by searching for a specific patient name or a study or a serie or a specific DICOM object by it's id.
You are allowed to include or exclude DICOM modalities from your query and you can specify a specific time in which the DICOM images you are searching for might been captured.
When you finished that you can click the query button and the queried DICOM data will appear.
<ul>
<li> Click on the 'Add Server' button.
<ul>
<li> Edit 'Name' field.
<li> Edit 'AETitle' field.
<li> Edit 'Address' field.
<li> Edit 'Port' field.
</ul>
<li> Set search options.
<li> Click on 'Query' button.
</ul>
\subsection org_mitk_gui_qt_dicomRetrieve Retrieve
\imageMacro{QmitkDicom_Retrieve.png,"The queried DICOM data.",15.22}
After the query you are able to select the queried data and click the 'Retrieve' button.
This will store the queried DICOM data into your DICOM storage.
Click on the 'Local Storage' button and work with your new data.
<ul>
<li> Click on the 'Retrieve' button to retrieve the data to your DICOM storage.
<li> Click on the 'Local Storage' button.
</ul>
*/
diff --git a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp
index b7351fe0f3..38992f91f4 100644
--- a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp
+++ b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp
@@ -1,918 +1,925 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "ModelFitInspectorView.h"
// Blueberry
#include <berryISelectionService.h>
#include <berryIWorkbenchWindow.h>
#include <berryIWorkbenchPage.h>
// mitk
#include <QmitkRenderWindow.h>
// Qt
#include <QMessageBox>
#include <QFileDialog>
#include <QTableWidget>
#include <qwt_plot_marker.h>
#include <QmitkPlotWidget.h>
#include <mitkNodePredicateFunction.h>
#include <mitkScalarListLookupTableProperty.h>
#include <mitkModelFitConstants.h>
#include <mitkExtractTimeGrid.h>
#include <mitkModelGenerator.h>
#include <mitkModelFitException.h>
#include <mitkModelFitParameterValueExtraction.h>
#include <mitkModelFitResultRelationRule.h>
#include <mitkModelFitPlotDataHelper.h>
#include <mitkTimeGridHelper.h>
#include <mitkTimeNavigationController.h>
const std::string ModelFitInspectorView::VIEW_ID = "org.mitk.views.fit.inspector";
const unsigned int ModelFitInspectorView::INTERPOLATION_STEPS = 10;
const std::string DEFAULT_X_AXIS = "Time [s]";
ModelFitInspectorView::ModelFitInspectorView() :
m_renderWindowPart(nullptr),
m_internalUpdateFlag(false),
m_currentFit(nullptr),
m_currentModelParameterizer(nullptr),
m_currentModelProviderService(nullptr),
m_currentSelectedTimeStep(0),
m_currentSelectedNode(nullptr)
{
m_currentSelectedPosition.Fill(0.0);
m_modelfitList.clear();
}
ModelFitInspectorView::~ModelFitInspectorView()
{
}
void ModelFitInspectorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart)
{
if (m_renderWindowPart != renderWindowPart)
{
m_renderWindowPart = renderWindowPart;
}
this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart);
}
void ModelFitInspectorView::RenderWindowPartDeactivated(
mitk::IRenderWindowPart* renderWindowPart)
{
m_renderWindowPart = nullptr;
this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart);
}
void ModelFitInspectorView::CreateQtPartControl(QWidget* parent)
{
m_Controls.setupUi(parent);
m_SelectionServiceConnector = std::make_unique<QmitkSelectionServiceConnector>();
m_SelectionServiceConnector->AddPostSelectionListener(this->GetSite()->GetWorkbenchWindow()->GetSelectionService());
m_Controls.inputNodeSelector->SetDataStorage(GetDataStorage());
m_Controls.inputNodeSelector->SetEmptyInfo(QString("Please select input data to be viewed."));
m_Controls.inputNodeSelector->SetInvalidInfo(QString("<b><font color=\"red\">No input data is selected</font></b>"));
m_Controls.inputNodeSelector->SetPopUpTitel(QString("Choose 3D+t input data that should be viewed!"));
m_Controls.inputNodeSelector->SetSelectionIsOptional(false);
m_Controls.inputNodeSelector->SetSelectOnlyVisibleNodes(true);
+ m_Controls.groupSettings->setVisible(false);
auto predicate = mitk::NodePredicateFunction::New([](const mitk::DataNode *node) {
bool isModelFitNode = node->GetData() && node->GetData()->GetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str()).IsNotNull();
return isModelFitNode || (node && node->GetData() && node->GetData()->GetTimeSteps() > 1);
});
m_Controls.inputNodeSelector->SetNodePredicate(predicate);
connect(m_SelectionServiceConnector.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.inputNodeSelector, &QmitkSingleNodeSelectionWidget::SetCurrentSelection);
connect(m_Controls.inputNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ModelFitInspectorView::OnInputChanged);
this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart());
connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged()));
connect(m_Controls.cmbFit, SIGNAL(currentIndexChanged(int)), this,
SLOT(OnFitSelectionChanged(int)));
connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.sbFixMin,
SLOT(setEnabled(bool)));
connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.sbFixMax,
SLOT(setEnabled(bool)));
connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.labelFixMin,
SLOT(setEnabled(bool)));
connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.labelFixMax,
SLOT(setEnabled(bool)));
connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.btnScaleToData,
SLOT(setEnabled(bool)));
connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), this, SLOT(OnScaleFixedYChecked(bool)));
connect(m_Controls.btnScaleToData, SIGNAL(clicked()), this, SLOT(OnScaleToDataYClicked()));
connect(m_Controls.sbFixMax, SIGNAL(valueChanged(double)), this,
SLOT(OnFixedScalingYChanged(double)));
connect(m_Controls.sbFixMin, SIGNAL(valueChanged(double)), this,
SLOT(OnFixedScalingYChanged(double)));
connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.sbFixMin_x,
SLOT(setEnabled(bool)));
connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.sbFixMax_x,
SLOT(setEnabled(bool)));
connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.labelFixMin_x,
SLOT(setEnabled(bool)));
connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.labelFixMax_x,
SLOT(setEnabled(bool)));
connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.btnScaleToData_x,
SLOT(setEnabled(bool)));
connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), this, SLOT(OnScaleFixedXChecked(bool)));
connect(m_Controls.btnScaleToData_x, SIGNAL(clicked()), this, SLOT(OnScaleToDataXClicked()));
connect(m_Controls.sbFixMax_x, SIGNAL(valueChanged(double)), this,
SLOT(OnFixedScalingXChanged(double)));
connect(m_Controls.sbFixMin_x, SIGNAL(valueChanged(double)), this,
SLOT(OnFixedScalingXChanged(double)));
connect(m_Controls.btnFullPlot, SIGNAL(clicked(bool)), this, SLOT(OnFullPlotClicked(bool)));
this->EnsureBookmarkPointSet();
- m_Controls.inspectionPositionWidget->SetPositionBookmarkNode(m_PositionBookmarksNode.Lock());
+ m_Controls.inspectionPositionWidget->SetPositionBookmarkNode(m_PositionBookmarksNode);
+
+
+
connect(m_Controls.inspectionPositionWidget, SIGNAL(PositionBookmarksChanged()), this, SLOT(OnPositionBookmarksChanged()));
// For some reason this needs to be called to set the plot widget's minimum width to an
// acceptable level (since Qwt 6).
// Otherwise it tries to keep both axes equal in length, resulting in a minimum width of
// 400-500px which is way too much.
m_Controls.widgetPlot->GetPlot()->updateAxes();
m_Controls.cmbFit->clear();
mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart();
RenderWindowPartActivated(renderWindowPart);
}
void ModelFitInspectorView::SetFocus()
{
}
void ModelFitInspectorView::NodeRemoved(const mitk::DataNode* node)
{
if (node == this->m_currentSelectedNode)
{
QmitkSingleNodeSelectionWidget::NodeList emptylist;
this->m_Controls.inputNodeSelector->SetCurrentSelection(emptylist);
}
}
void ModelFitInspectorView::OnScaleFixedYChecked(bool checked)
{
m_Controls.widgetPlot->GetPlot()->setAxisAutoScale(QwtPlot::yLeft, !checked);
if (checked)
{
OnScaleToDataYClicked();
}
m_Controls.widgetPlot->GetPlot()->replot();
};
void ModelFitInspectorView::OnScaleFixedXChecked(bool checked)
{
m_Controls.widgetPlot->GetPlot()->setAxisAutoScale(QwtPlot::xBottom, !checked);
if (checked)
{
OnScaleToDataXClicked();
}
m_Controls.widgetPlot->GetPlot()->replot();
};
void ModelFitInspectorView::OnScaleToDataYClicked()
{
auto minmax = this->m_PlotCurves.GetYMinMax();
auto min = minmax.first - std::abs(minmax.first) * 0.01;
auto max = minmax.second + std::abs(minmax.second) * 0.01;
m_Controls.sbFixMin->setValue(min);
m_Controls.sbFixMax->setValue(max);
};
void ModelFitInspectorView::OnScaleToDataXClicked()
{
auto minmax = this->m_PlotCurves.GetXMinMax();
auto min = minmax.first - std::abs(minmax.first) * 0.01;
auto max = minmax.second + std::abs(minmax.second) * 0.01;
m_Controls.sbFixMin_x->setValue(min);
m_Controls.sbFixMax_x->setValue(max);
};
void ModelFitInspectorView::OnFixedScalingYChanged(double /*value*/)
{
m_Controls.widgetPlot->GetPlot()->setAxisScale(QwtPlot::yLeft, m_Controls.sbFixMin->value(),
m_Controls.sbFixMax->value());
m_Controls.widgetPlot->GetPlot()->replot();
};
void ModelFitInspectorView::OnFixedScalingXChanged(double /*value*/)
{
m_Controls.widgetPlot->GetPlot()->setAxisScale(QwtPlot::xBottom, m_Controls.sbFixMin_x->value(),
m_Controls.sbFixMax_x->value());
m_Controls.widgetPlot->GetPlot()->replot();
};
void ModelFitInspectorView::OnFullPlotClicked(bool checked)
{
m_Controls.tabWidget->setVisible(!checked);
};
int ModelFitInspectorView::ActualizeFitSelectionWidget()
{
mitk::modelFit::ModelFitInfo::UIDType selectedFitUD = "";
bool isModelFitNode = false;
if (this->m_Controls.inputNodeSelector->GetSelectedNode().IsNotNull())
{
isModelFitNode = this->m_Controls.inputNodeSelector->GetSelectedNode()->GetData()->GetPropertyList()->GetStringProperty(
mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), selectedFitUD);
}
mitk::DataStorage::Pointer storage = this->GetDataStorage();
mitk::modelFit::NodeUIDSetType fitUIDs = mitk::modelFit::GetFitUIDsOfNode(
this->m_currentSelectedNode, storage);
this->m_modelfitList.clear();
this->m_Controls.cmbFit->clear();
for (const auto & fitUID : fitUIDs)
{
mitk::modelFit::ModelFitInfo::ConstPointer info = mitk::modelFit::CreateFitInfoFromNode(fitUID,
storage).GetPointer();
if (info.IsNotNull())
{
this->m_modelfitList.insert(std::make_pair(info->uid, info));
std::ostringstream nameStrm;
if (info->fitName.empty())
{
nameStrm << info->uid;
}
else
{
nameStrm << info->fitName;
}
nameStrm << " (" << info->modelName << ")";
QVariant data(info->uid.c_str());
m_Controls.cmbFit->addItem(QString::fromStdString(nameStrm.str()), data);
}
else
{
MITK_ERROR <<
"Was not able to extract model fit information from storage. Node properties in storage may be invalid. Failed fit UID:"
<< fitUID;
}
}
int cmbIndex = 0;
if (m_modelfitList.empty())
{
cmbIndex = -1;
};
if (isModelFitNode)
{
//model was selected, thus select this one in combobox
QVariant data(selectedFitUD.c_str());
cmbIndex = m_Controls.cmbFit->findData(data);
if (cmbIndex == -1)
{
MITK_WARN <<
"Model fit Inspector in invalid state. Selected fit seems to be not available in plugin selection. Failed fit UID:"
<< selectedFitUD;
}
};
m_Controls.cmbFit->setCurrentIndex(cmbIndex);
return cmbIndex;
}
void ModelFitInspectorView::OnInputChanged(const QList<mitk::DataNode::Pointer>& nodes)
{
if (nodes.size() > 0)
{
if (nodes.front() != this->m_currentSelectedNode)
{
m_internalUpdateFlag = true;
this->m_currentSelectedNode = nodes.front();
mitk::modelFit::ModelFitInfo::UIDType selectedFitUD = "";
bool isModelFitNode = this->m_currentSelectedNode->GetData()->GetPropertyList()->GetStringProperty(
mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), selectedFitUD);
if (isModelFitNode)
{
this->m_currentSelectedNode = this->GetInputNode(this->m_currentSelectedNode);
if (this->m_currentSelectedNode.IsNull())
{
MITK_WARN <<
"Model fit Inspector in invalid state. Input image for selected fit cannot be found in data storage. Failed fit UID:"
<< selectedFitUD;
}
}
auto cmbIndex = ActualizeFitSelectionWidget();
m_internalUpdateFlag = false;
m_selectedNodeTime.Modified();
if (cmbIndex == -1)
{
//only raw 4D data selected. Just update plots for current position
m_currentFit = nullptr;
m_currentFitTime.Modified();
OnSliceChanged();
m_Controls.plotDataWidget->SetXName(DEFAULT_X_AXIS);
}
else
{
//refresh fit selection (and implicitly update plots)
OnFitSelectionChanged(cmbIndex);
}
}
}
else
{
if (this->m_currentSelectedNode.IsNotNull())
{
m_internalUpdateFlag = true;
this->m_currentSelectedNode = nullptr;
this->m_currentFit = nullptr;
this->m_modelfitList.clear();
this->m_Controls.cmbFit->clear();
m_internalUpdateFlag = false;
m_selectedNodeTime.Modified();
OnFitSelectionChanged(0);
RefreshPlotData();
m_Controls.plotDataWidget->SetPlotData(&(this->m_PlotCurves));
m_Controls.fitParametersWidget->setFits(QmitkFitParameterModel::FitVectorType());
RenderPlot();
}
}
}
mitk::DataNode::ConstPointer
ModelFitInspectorView::GetInputNode(mitk::DataNode::ConstPointer node)
{
if (node.IsNotNull())
{
std::string selectedFitUD = "";
auto rule = mitk::ModelFitResultRelationRule::New();
auto predicate = rule->GetDestinationsDetector(node);
mitk::DataStorage::SetOfObjects::ConstPointer parentNodeList =
GetDataStorage()->GetSubset(predicate);
if (parentNodeList->size() > 0)
{
return parentNodeList->front().GetPointer();
}
}
return mitk::DataNode::ConstPointer();
}
void ModelFitInspectorView::ValidateAndSetCurrentPosition()
{
const mitk::Point3D currentSelectedPosition = GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(nullptr);
const unsigned int currentSelectedTimestep = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimeStep();
if (m_currentSelectedPosition != currentSelectedPosition
|| m_currentSelectedTimeStep != currentSelectedTimestep
|| m_selectedNodeTime > m_currentPositionTime)
{
//the current position has been changed or the selected node has been changed since the last position validation -> check position
m_currentSelectedPosition = currentSelectedPosition;
m_currentSelectedTimeStep = currentSelectedTimestep;
m_currentPositionTime.Modified();
m_validSelectedPosition = false;
auto inputImage = this->GetCurrentInputImage();
if (inputImage.IsNull())
{
return;
}
mitk::BaseGeometry::ConstPointer geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep(
m_currentSelectedTimeStep).GetPointer();
// check for invalid time step
if (geometry.IsNull())
{
geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep(0);
}
if (geometry.IsNull())
{
return;
}
m_validSelectedPosition = geometry->IsInside(m_currentSelectedPosition);
}
}
mitk::Image::ConstPointer ModelFitInspectorView::GetCurrentInputImage() const
{
mitk::Image::ConstPointer result = nullptr;
if (this->m_currentFit.IsNotNull())
{
result = m_currentFit->inputImage;
}
else if (this->m_currentSelectedNode.IsNotNull())
{
result = dynamic_cast<mitk::Image*>(this->m_currentSelectedNode->GetData());
if (result.IsNotNull() && result->GetTimeSteps() <= 1)
{
//if the image is not dynamic, we can't use it.
result = nullptr;
}
}
return result;
};
const mitk::ModelBase::TimeGridType ModelFitInspectorView::GetCurrentTimeGrid() const
{
if (m_currentModelProviderService && m_currentFit.IsNotNull())
{
return m_currentModelProviderService->GetVariableGrid(m_currentFit);
}
else
{ //fall back if there is no model provider we assume to use the normal time grid.
return ExtractTimeGrid(GetCurrentInputImage());
}
};
void ModelFitInspectorView::OnSliceChanged()
{
ValidateAndSetCurrentPosition();
m_Controls.widgetPlot->setEnabled(m_validSelectedPosition);
if (m_currentSelectedNode.IsNotNull())
{
m_Controls.inspectionPositionWidget->SetCurrentPosition(m_currentSelectedPosition);
if (RefreshPlotData())
{
RenderPlot();
m_Controls.plotDataWidget->SetPlotData(&m_PlotCurves);
RenderFitInfo();
}
}
}
void ModelFitInspectorView::OnPositionBookmarksChanged()
{
+ m_PositionBookmarks = dynamic_cast<mitk::PointSet*>(m_PositionBookmarksNode->GetData());
+ m_PositionBookmarks->Modified();
+
if (RefreshPlotData())
{
RenderPlot();
m_Controls.plotDataWidget->SetPlotData(&m_PlotCurves);
RenderFitInfo();
}
}
void ModelFitInspectorView::OnFitSelectionChanged(int index)
{
if (!m_internalUpdateFlag)
{
MITK_DEBUG << "selected fit index: " << index;
std::string uid = "";
if (m_Controls.cmbFit->count() > index)
{
uid = m_Controls.cmbFit->itemData(index).toString().toStdString();
}
mitk::modelFit::ModelFitInfo::ConstPointer newFit = nullptr;
ModelFitInfoListType::iterator finding = m_modelfitList.find(uid);
if (finding != m_modelfitList.end())
{
newFit = finding->second;
}
if (m_currentFit != newFit)
{
m_currentModelParameterizer = nullptr;
m_currentModelProviderService = nullptr;
if (newFit.IsNotNull())
{
m_currentModelParameterizer = mitk::ModelGenerator::GenerateModelParameterizer(*newFit);
m_currentModelProviderService = mitk::ModelGenerator::GetProviderService(newFit->functionClassID);
}
m_currentFit = newFit;
m_currentFitTime.Modified();
auto name = m_currentFit->xAxisName;
if (!m_currentFit->xAxisUnit.empty())
{
name += " [" + m_currentFit->xAxisUnit + "]";
}
m_Controls.plotDataWidget->SetXName(name);
OnSliceChanged();
}
}
}
mitk::PlotDataCurveCollection::Pointer ModelFitInspectorView::RefreshPlotDataCurveCollection(const mitk::Point3D& position,
const mitk::Image* input, const mitk::modelFit::ModelFitInfo* fitInfo,
const mitk::ModelBase::TimeGridType& timeGrid, mitk::ModelParameterizerBase* parameterizer)
{
mitk::PlotDataCurveCollection::Pointer result = mitk::PlotDataCurveCollection::New();
//sample curve
if (input)
{
result->InsertElement(mitk::MODEL_FIT_PLOT_SAMPLE_NAME(), GenerateImageSamplePlotData(position, input, timeGrid));
}
//model signal curve
if (fitInfo)
{
// Interpolate time grid (x values) so the curve looks smooth
const mitk::ModelBase::TimeGridType interpolatedTimeGrid = mitk::GenerateSupersampledTimeGrid(timeGrid, INTERPOLATION_STEPS);
auto hires_curve = mitk::GenerateModelSignalPlotData(position, fitInfo, interpolatedTimeGrid, parameterizer);
result->InsertElement(mitk::MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME(), hires_curve);
auto curve = mitk::GenerateModelSignalPlotData(position, fitInfo, timeGrid, parameterizer);
result->InsertElement(mitk::MODEL_FIT_PLOT_SIGNAL_NAME(), curve);
}
return result;
};
bool ModelFitInspectorView::RefreshPlotData()
{
bool changed = false;
if (m_currentSelectedNode.IsNull())
{
this->m_PlotCurves = mitk::ModelFitPlotData();
changed = m_selectedNodeTime > m_lastRefreshTime;
m_lastRefreshTime.Modified();
}
else
{
assert(GetRenderWindowPart() != NULL);
const mitk::Image* input = GetCurrentInputImage();
const mitk::ModelBase::TimeGridType timeGrid = GetCurrentTimeGrid();
if (m_currentFitTime > m_lastRefreshTime || m_currentPositionTime > m_lastRefreshTime)
{
if (m_validSelectedPosition)
{
- m_PlotCurves.currentPositionPlots = RefreshPlotDataCurveCollection(m_currentSelectedPosition,input,m_currentFit, timeGrid, m_currentModelParameterizer);
+ m_PlotCurves.currentPositionPlots = RefreshPlotDataCurveCollection(m_currentSelectedPosition, input, m_currentFit, timeGrid, m_currentModelParameterizer);
}
else
{
m_PlotCurves.currentPositionPlots = mitk::PlotDataCurveCollection::New();
}
changed = true;
}
- auto bookmarks = m_PositionBookmarks.Lock();
- if (bookmarks.IsNotNull())
- {
+ auto bookmarks = m_PositionBookmarks;
+
if (m_currentFitTime > m_lastRefreshTime || bookmarks->GetMTime() > m_lastRefreshTime)
{
m_PlotCurves.positionalPlots.clear();
-
- auto endIter = bookmarks->End();
- for (auto iter = bookmarks->Begin(); iter != endIter; iter++)
+ if (!bookmarks->IsEmpty())
{
- auto collection = RefreshPlotDataCurveCollection(iter.Value(), input, m_currentFit, timeGrid, m_currentModelParameterizer);
- m_PlotCurves.positionalPlots.emplace(iter.Index(), std::make_pair(iter.Value(), collection));
+ auto endIter = bookmarks->End();
+ for (auto iter = bookmarks->Begin(); iter != endIter; iter++)
+ {
+ auto collection = RefreshPlotDataCurveCollection(iter.Value(), input, m_currentFit, timeGrid, m_currentModelParameterizer);
+ m_PlotCurves.positionalPlots.emplace(iter.Index(), std::make_pair(iter.Value(), collection));
+ }
+ }
+ else
+ {
+ m_PlotCurves.positionalPlots.clear();
}
-
changed = true;
}
- }
- else
- {
- m_PlotCurves.positionalPlots.clear();
- }
// input data curve
if (m_currentFitTime > m_lastRefreshTime)
{
m_PlotCurves.staticPlots->clear();
if (m_currentFit.IsNotNull())
{
m_PlotCurves.staticPlots = GenerateAdditionalModelFitPlotData(m_currentSelectedPosition, m_currentFit, timeGrid);
}
changed = true;
}
m_lastRefreshTime.Modified();
}
-
return changed;
}
+
void ModelFitInspectorView::RenderFitInfo()
{
assert(m_renderWindowPart != nullptr);
// configure fit information
if (m_currentFit.IsNull())
{
m_Controls.lFitType->setText("");
m_Controls.lFitUID->setText("");
m_Controls.lModelName->setText("");
m_Controls.lModelType->setText("");
}
else
{
m_Controls.lFitType->setText(QString::fromStdString(m_currentFit->fitType));
m_Controls.lFitUID->setText(QString::fromStdString(m_currentFit->uid));
m_Controls.lModelName->setText(QString::fromStdString(m_currentFit->modelName));
m_Controls.lModelType->setText(QString::fromStdString(m_currentFit->modelType));
}
// print results
std::stringstream infoOutput;
m_Controls.fitParametersWidget->setVisible(false);
- m_Controls.groupSettings->setVisible(false);
if (m_currentFit.IsNull())
{
infoOutput << "No fit selected. Only raw image data is plotted.";
}
else if (!m_validSelectedPosition)
{
infoOutput <<
"Current position is outside of the input image of the selected fit.\nInspector is deactivated.";
}
else
{
m_Controls.fitParametersWidget->setVisible(true);
m_Controls.fitParametersWidget->setFits({ m_currentFit });
- m_Controls.fitParametersWidget->setPositionBookmarks(m_PositionBookmarks.Lock());
+ m_Controls.fitParametersWidget->setPositionBookmarks(m_PositionBookmarks);
m_Controls.fitParametersWidget->setCurrentPosition(m_currentSelectedPosition);
}
// configure data table
m_Controls.tableInputData->clearContents();
- if (m_currentFit.IsNull())
- {
- infoOutput << "No fit selected. Only raw image data is plotted.";
- }
- else
+ if (m_currentFit.IsNotNull())
{
- m_Controls.groupSettings->setVisible(true);
+ m_Controls.groupSettings->setVisible(false);
m_Controls.tableInputData->setRowCount(m_PlotCurves.staticPlots->size());
unsigned int rowIndex = 0;
for (mitk::PlotDataCurveCollection::const_iterator pos = m_PlotCurves.staticPlots->begin();
pos != m_PlotCurves.staticPlots->end(); ++pos, ++rowIndex)
{
QColor dataColor;
if (pos->first == "ROI")
{
dataColor = QColor(0, 190, 0);
}
else
{
//Use HSV schema of QColor to calculate a different color depending on the
//number of already existing free iso lines.
- dataColor.setHsv(((rowIndex + 1) * 85) % 360, 255, 255);
+ dataColor.setHsv(((rowIndex + 1) * 85) % 360, 255, 150);
}
QTableWidgetItem* newItem = new QTableWidgetItem(QString::fromStdString(pos->first));
m_Controls.tableInputData->setItem(rowIndex, 0, newItem);
newItem = new QTableWidgetItem();
newItem->setBackground(dataColor);
+
+
+
m_Controls.tableInputData->setItem(rowIndex, 1, newItem);
}
}
m_Controls.lInfo->setText(QString::fromStdString(infoOutput.str()));
}
void ModelFitInspectorView::RenderPlotCurve(const mitk::PlotDataCurveCollection* curveCollection, const QColor& sampleColor, const QColor& signalColor, const std::string& posString)
{
auto sampleCurve = mitk::ModelFitPlotData::GetSamplePlot(curveCollection);
if (sampleCurve)
{
std::string name = mitk::MODEL_FIT_PLOT_SAMPLE_NAME() + posString;
unsigned int curveId = m_Controls.widgetPlot->InsertCurve(name.c_str());
m_Controls.widgetPlot->SetCurveData(curveId, sampleCurve->GetValues());
m_Controls.widgetPlot->SetCurvePen(curveId, QPen(Qt::NoPen));
// QwtSymbol needs to passed as a real pointer from MITK v2013.09.0 on
// (QwtPlotCurve deletes it on destruction and assignment).
QwtSymbol* dataSymbol = new QwtSymbol(QwtSymbol::Diamond, sampleColor, sampleColor, QSize(8, 8));
m_Controls.widgetPlot->SetCurveSymbol(curveId, dataSymbol);
// Again, there is no way to set a curve's legend attributes via QmitkPlotWidget so this
// gets unnecessarily complicated.
QwtPlotCurve* measurementCurve = dynamic_cast<QwtPlotCurve*>(m_Controls.widgetPlot->
GetPlot()->itemList(QwtPlotItem::Rtti_PlotCurve).back());
measurementCurve->setLegendAttribute(QwtPlotCurve::LegendShowSymbol);
measurementCurve->setLegendIconSize(QSize(8, 8));
}
//draw model curve
auto signalCurve = mitk::ModelFitPlotData::GetInterpolatedSignalPlot(curveCollection);
if (signalCurve)
{
std::string name = mitk::MODEL_FIT_PLOT_SIGNAL_NAME() + posString;
QPen pen;
pen.setColor(signalColor);
pen.setWidth(2);
unsigned int curveId = m_Controls.widgetPlot->InsertCurve(name.c_str());
m_Controls.widgetPlot->SetCurveData(curveId, signalCurve->GetValues());
m_Controls.widgetPlot->SetCurvePen(curveId, pen);
// Manually set the legend attribute to use the symbol as the legend icon and alter its
// size. Otherwise it would revert to default which is drawing a square which is the color
// of the curve's pen, so in this case none which defaults to black.
// Unfortunately, QmitkPlotWidget offers no way to set the legend attribute and icon size so
// this looks a bit hacky.
QwtPlotCurve* fitCurve = dynamic_cast<QwtPlotCurve*>(m_Controls.widgetPlot->GetPlot()->
itemList(QwtPlotItem::Rtti_PlotCurve).back());
fitCurve->setLegendAttribute(QwtPlotCurve::LegendShowLine);
}
}
void ModelFitInspectorView::RenderPlot()
{
m_Controls.widgetPlot->Clear();
std::string xAxis = DEFAULT_X_AXIS;
std::string yAxis = "Intensity";
std::string plotTitle = "Raw data plot: no data";
if (m_currentSelectedNode.IsNotNull())
{
plotTitle = "Raw data plot: " + m_currentSelectedNode->GetName();
}
if (m_currentFit.IsNotNull())
{
plotTitle = m_currentFit->modelName.c_str();
xAxis = m_currentFit->xAxisName;
if (!m_currentFit->xAxisUnit.empty())
{
xAxis += " [" + m_currentFit->xAxisUnit + "]";
}
yAxis = m_currentFit->yAxisName;
if (!m_currentFit->yAxisUnit.empty())
{
yAxis += " [" + m_currentFit->yAxisUnit + "]";
}
}
m_Controls.widgetPlot->SetAxisTitle(QwtPlot::xBottom, xAxis.c_str());
m_Controls.widgetPlot->SetAxisTitle(QwtPlot::yLeft, yAxis.c_str());
m_Controls.widgetPlot->SetPlotTitle(plotTitle.c_str());
// Draw static curves
unsigned int colorIndex = 0;
for (mitk::PlotDataCurveCollection::const_iterator pos = m_PlotCurves.staticPlots->begin();
pos != m_PlotCurves.staticPlots->end(); ++pos)
{
QColor dataColor;
unsigned int curveId = m_Controls.widgetPlot->InsertCurve(pos->first.c_str());
m_Controls.widgetPlot->SetCurveData(curveId, pos->second->GetValues());
if (pos->first == "ROI")
{
dataColor = QColor(0, 190, 0);
QPen pen;
pen.setColor(dataColor);
pen.setStyle(Qt::SolidLine);
m_Controls.widgetPlot->SetCurvePen(curveId, pen);
}
else
{
//Use HSV schema of QColor to calculate a different color depending on the
//number of already existing curves.
dataColor.setHsv((++colorIndex * 85) % 360, 255, 150);
m_Controls.widgetPlot->SetCurvePen(curveId, QPen(Qt::NoPen));
}
// QwtSymbol needs to passed as a real pointer from MITK v2013.09.0 on
// (QwtPlotCurve deletes it on destruction and assignment).
QwtSymbol* dataSymbol = new QwtSymbol(QwtSymbol::Triangle, dataColor, dataColor,
QSize(8, 8));
m_Controls.widgetPlot->SetCurveSymbol(curveId, dataSymbol);
// Again, there is no way to set a curve's legend attributes via QmitkPlotWidget so this
// gets unnecessarily complicated.
QwtPlotCurve* measurementCurve = dynamic_cast<QwtPlotCurve*>(m_Controls.widgetPlot->
GetPlot()->itemList(QwtPlotItem::Rtti_PlotCurve).back());
measurementCurve->setLegendAttribute(QwtPlotCurve::LegendShowSymbol);
measurementCurve->setLegendIconSize(QSize(8, 8));
}
// Draw positional curves
for (const auto& posIter : this->m_PlotCurves.positionalPlots)
{
QColor dataColor;
dataColor.setHsv((++colorIndex * 85) % 360, 255, 150);
this->RenderPlotCurve(posIter.second.second, dataColor, dataColor, " @ "+mitk::ModelFitPlotData::GetPositionalCollectionName(posIter));
}
// Draw current pos curve
this->RenderPlotCurve(m_PlotCurves.currentPositionPlots, QColor(Qt::red), QColor(Qt::black), "");
QwtLegend* legend = new QwtLegend();
legend->setFrameShape(QFrame::Box);
legend->setFrameShadow(QFrame::Sunken);
legend->setLineWidth(1);
m_Controls.widgetPlot->SetLegend(legend, QwtPlot::BottomLegend);
m_Controls.widgetPlot->Replot();
}
void ModelFitInspectorView::EnsureBookmarkPointSet()
{
- if (m_PositionBookmarks.IsExpired() || m_PositionBookmarksNode.IsExpired())
+ if (m_PositionBookmarks.IsNull()|| m_PositionBookmarksNode.IsNull())
{
const char* nodeName = "org.mitk.gui.qt.fit.inspector.positions";
mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode(nodeName);
if (!node)
{
node = mitk::DataNode::New();
node->SetName(nodeName);
node->SetBoolProperty("helper object", true);
this->GetDataStorage()->Add(node);
}
m_PositionBookmarksNode = node;
mitk::PointSet::Pointer pointSet = dynamic_cast<mitk::PointSet*>(node->GetData());
if (pointSet.IsNull())
{
pointSet = mitk::PointSet::New();
node->SetData(pointSet);
}
m_PositionBookmarks = pointSet;
+
+
}
+
}
diff --git a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.h b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.h
index 45287d9aaf..54be3a6ef2 100644
--- a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.h
+++ b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.h
@@ -1,202 +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.
============================================================================*/
#ifndef ModelFitInspectorView_h
#define ModelFitInspectorView_h
// Blueberry
//#include <berryISelectionListener.h>
#include <berryIPartListener.h>
// mitk
#include <QmitkAbstractView.h>
#include <mitkIRenderWindowPartListener.h>
#include "QmitkSliceNavigationListener.h"
#include "mitkModelFitStaticParameterMap.h"
#include "mitkModelParameterizerBase.h"
#include "mitkModelFitInfo.h"
#include "mitkIModelFitProvider.h"
#include "mitkModelFitPlotDataHelper.h"
#include "QmitkSelectionServiceConnector.h"
#include "QmitkFitParameterModel.h"
// Qt
#include "ui_ModelFitInspectorViewControls.h"
/**
* @brief View class defining the UI part of the ModelFitInspector plug-in.
*/
class ModelFitInspectorView :
public QmitkAbstractView,
public mitk::IRenderWindowPartListener
{
// this is needed for all Qt objects that should have a Qt meta-object
// (everything that derives from QObject and wants to have signal/slots)
Q_OBJECT
public:
ModelFitInspectorView();
~ModelFitInspectorView() override;
static const std::string VIEW_ID;
protected slots:
void OnSliceChanged();
/**
* @brief Triggered when the selection of the "Modelfit" combo box changes.
* Sets the selected fit as the current one.
* @param index The index (in the combo box) of the selected item.
*/
void OnFitSelectionChanged(int index);
void OnInputChanged(const QList<mitk::DataNode::Pointer>& nodes);
void OnPositionBookmarksChanged();
/** Triggered when the selection of "fixed" y axis scaling changes*/
void OnScaleFixedYChecked(bool checked);
void OnScaleToDataYClicked();
void OnFixedScalingYChanged(double value);
/** Triggered when the selection of "fixed" x axis scaling changes*/
void OnScaleFixedXChecked(bool checked);
void OnScaleToDataXClicked();
void OnFixedScalingXChanged(double value);
void OnFullPlotClicked(bool checked);
protected:
void CreateQtPartControl(QWidget* parent) override;
void SetFocus() override;
void NodeRemoved(const mitk::DataNode* node) override;
/** Helper that actualizes the fit selection widget and returns the index of the currently selected
* fit.*/
int ActualizeFitSelectionWidget();
void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override;
void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) override;
/**
* @brief Calculates the curve data using the current fit's model and parameterizer.
* @param position Indicating the point in the input image for which the model curve should be calculated.
* @return A vector of points for the curve data.
*/
QmitkPlotWidget::XYDataVector CalcCurveFromModel(const mitk::Point3D& position);
/**
* @brief Calculates the curve data using the current fit's function string.
* @param timeGrid The time grid containing the (interpolated) x values for the curve data.
* @return A vector of points for the curve data.
*/
QmitkPlotWidget::XYDataVector CalcCurveFromFunction(const mitk::Point3D& position,
const mitk::ModelBase::TimeGridType&
timeGrid);
/**
* @brief Returns the input node that was used to generate the passed model fit node if it exists.
* @param node Node that is a model fit result.
* @return The input node that was used to generate the passed node. If the input node cannot
* be found in the storage or the passed node is not a model fit result node, a nullptr will be returned.
*/
mitk::DataNode::ConstPointer GetInputNode(mitk::DataNode::ConstPointer node);
/** Sets m_currentSelectedPosition to the current selection and validates if this position is valid
* for the input image of the currently selected fit. If it is valid, m_validSelectedPosition is set to true.
* If the fit, his input image or geometry is not specified, it will also handled as invalid.*/
void ValidateAndSetCurrentPosition();
/** Returns the current input image. If a current fit is set it will be its input image.
* Otherwise it will be the image stored in the currently selected node. If the node is not set, contains no image
* or the image is not 4D, NULL will be returned.*/
mitk::Image::ConstPointer GetCurrentInputImage() const;
/** Returns the variable/time grid of the GetCurrentInputImage(). If a model fit is selected its provider will be used
to get the correct grid, otherwise just a simple time grid will be extracted.*/
const mitk::ModelBase::TimeGridType GetCurrentTimeGrid() const;
Ui::ModelFitInspectorViewControls m_Controls;
mitk::IRenderWindowPart* m_renderWindowPart;
/** @brief Is a visualization currently running? */
bool m_internalUpdateFlag;
/** @brief List of modelfits currently in the data manager */
typedef std::map<const mitk::modelFit::ModelFitInfo::UIDType, mitk::modelFit::ModelFitInfo::ConstPointer>
ModelFitInfoListType;
ModelFitInfoListType m_modelfitList;
/** @brief The currently selected modelfit */
mitk::modelFit::ModelFitInfo::ConstPointer m_currentFit;
/** @brief Pointer to the instance of the model parameterizer for the current fit */
mitk::ModelParameterizerBase::Pointer m_currentModelParameterizer;
mitk::IModelFitProvider* m_currentModelProviderService;
/** @brief currently valid selected position in the inspector*/
mitk::Point3D m_currentSelectedPosition;
/** @brief indicates if the currently selected position is valid for the currently selected fit.
* This it is within the input image */
bool m_validSelectedPosition;
/** @brief currently selected time step of the selected node for the visualization logic*/
unsigned int m_currentSelectedTimeStep;
/** @brief currently selected node for the visualization logic*/
mitk::DataNode::ConstPointer m_currentSelectedNode;
- mitk::WeakPointer<mitk::DataNode> m_PositionBookmarksNode;
- mitk::WeakPointer<mitk::PointSet> m_PositionBookmarks;
+ mitk::DataNode::Pointer m_PositionBookmarksNode;
+ mitk::PointSet::Pointer m_PositionBookmarks;
/** @brief Number of interpolation steps between two x values */
static const unsigned int INTERPOLATION_STEPS;
/*************************************/
/* Members for visualizing the model */
itk::TimeStamp m_selectedNodeTime;
itk::TimeStamp m_currentFitTime;
itk::TimeStamp m_currentPositionTime;
itk::TimeStamp m_lastRefreshTime;
mitk::ModelFitPlotData m_PlotCurves;
std::unique_ptr<QmitkSelectionServiceConnector> m_SelectionServiceConnector;
QmitkFitParameterModel* m_FitParameterModel;
QmitkSliceNavigationListener m_SliceChangeListener;
/** Check and updates the plot data if needed.
* @return indicates if something was refreshed (true)*/
bool RefreshPlotData();
void RenderPlot();
void RenderPlotCurve(const mitk::PlotDataCurveCollection* curveCollection, const QColor& sampleColor, const QColor& signalColor, const std::string& posString);
void RenderFitInfo();
void EnsureBookmarkPointSet();
static mitk::PlotDataCurveCollection::Pointer RefreshPlotDataCurveCollection(const mitk::Point3D& position,
const mitk::Image* input, const mitk::modelFit::ModelFitInfo* fitInfo,
const mitk::ModelBase::TimeGridType& timeGrid, mitk::ModelParameterizerBase* parameterizer);
};
#endif // ModelFitInspectorView_h
diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.cpp
index f982b5807e..33fe0dd3f5 100644
--- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.cpp
+++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.cpp
@@ -1,189 +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.
============================================================================*/
#include "QmitkConvertGeometryDataToROIAction.h"
#include <mitkGeometryData.h>
#include <mitkImage.h>
#include <mitkNodePredicateDataType.h>
#include <mitkROI.h>
#include <QMessageBox>
namespace
{
void HandleInvalidNodeSelection()
{
auto message = QStringLiteral(
- "All selected bounding boxes must be child nodes of a single common reference image "
- "with a non-rotated geometry!");
+ "All selected bounding boxes must be child nodes of a single common reference image!");
MITK_ERROR << message;
QMessageBox::warning(nullptr, QStringLiteral("Convert to ROI"), message);
}
- bool IsRotated(const mitk::BaseGeometry* geometry)
- {
- auto matrix = geometry->GetVtkMatrix();
-
- for (int j = 0; j < 3; ++j)
- {
- for (int i = 0; i < 3; ++i)
- {
- if (i != j && std::abs(matrix->GetElement(i, j)) > mitk::eps)
- return true;
- }
- }
-
- return false;
- }
-
- void FlipAxis(mitk::BaseGeometry* geometry, int axis)
- {
- auto matrix = geometry->GetVtkMatrix();
- matrix->SetElement(axis, axis, -matrix->GetElement(axis, axis));
- matrix->SetElement(axis, 3, matrix->GetElement(axis, 3) - geometry->GetExtentInMM(axis));
-
- geometry->SetIndexToWorldTransformByVtkMatrix(matrix);
-
- auto bounds = geometry->GetBounds();
- int minIndex = 2 * axis;
- bounds[minIndex] *= -1;
- bounds[minIndex + 1] += 2 * bounds[minIndex];
-
- geometry->SetBounds(bounds);
- }
-
- mitk::BaseGeometry::Pointer RectifyGeometry(const mitk::BaseGeometry* geometry)
- {
- auto rectifiedGeometry = geometry->Clone();
- auto matrix = rectifiedGeometry->GetVtkMatrix();
-
- for (int axis = 0; axis < 3; ++axis)
- {
- if (matrix->GetElement(axis, axis) < 0.0)
- FlipAxis(rectifiedGeometry, axis);
- }
-
- return rectifiedGeometry;
- }
-
std::pair<std::vector<const mitk::DataNode*>, mitk::DataNode*> GetValidInput(const QList<mitk::DataNode::Pointer>& selectedNodes, const mitk::DataStorage* dataStorage)
{
std::pair<std::vector<const mitk::DataNode*>, mitk::DataNode*> result;
result.first.reserve(selectedNodes.size());
std::copy_if(selectedNodes.cbegin(), selectedNodes.cend(), std::back_inserter(result.first), [](const mitk::DataNode* node) {
return node != nullptr && dynamic_cast<mitk::GeometryData*>(node->GetData()) != nullptr;
});
for (auto node : result.first)
{
auto sourceNodes = dataStorage->GetSources(node, mitk::TNodePredicateDataType<mitk::Image>::New());
if (sourceNodes->size() != 1)
mitkThrow();
if (result.second == nullptr)
{
- auto geometry = sourceNodes->front()->GetData()->GetGeometry();
-
- if (IsRotated(geometry))
- mitkThrow();
-
result.second = sourceNodes->front();
}
else if (result.second != sourceNodes->front())
{
mitkThrow();
}
}
return result;
}
}
QmitkConvertGeometryDataToROIAction::QmitkConvertGeometryDataToROIAction()
{
}
QmitkConvertGeometryDataToROIAction::~QmitkConvertGeometryDataToROIAction()
{
}
void QmitkConvertGeometryDataToROIAction::Run(const QList<mitk::DataNode::Pointer>& selectedNodes)
{
try
{
auto [nodes, referenceNode] = GetValidInput(selectedNodes, m_DataStorage);
auto roi = mitk::ROI::New();
- roi->SetGeometry(RectifyGeometry(referenceNode->GetData()->GetGeometry()));
+ roi->SetGeometry(referenceNode->GetData()->GetGeometry());
unsigned int id = 0;
for (auto node : nodes)
{
mitk::ROI::Element element(id++);
element.SetProperty("name", mitk::StringProperty::New(node->GetName()));
if (auto* color = node->GetProperty("Bounding Shape.Deselected Color"); color != nullptr)
element.SetProperty("color", color);
- auto geometry = RectifyGeometry(node->GetData()->GetGeometry());
- const auto origin = geometry->GetOrigin() - roi->GetGeometry()->GetOrigin();
- const auto spacing = geometry->GetSpacing();
+ const auto* geometry = node->GetData()->GetGeometry();
+
+ mitk::Vector3D origin = geometry->GetOrigin() - roi->GetGeometry()->GetOrigin();
+ geometry->WorldToIndex(origin, origin);
+
const auto bounds = geometry->GetBounds();
mitk::Point3D min;
mitk::Point3D max;
for (size_t i = 0; i < 3; ++i)
{
- min[i] = origin[i] / spacing[i] + bounds[2 * i];
- max[i] = origin[i] / spacing[i] + bounds[2 * i + 1] - 1;
+ min[i] = origin[i] + bounds[2 * i] - 0.5;
+ max[i] = origin[i] + bounds[2 * i + 1] - 1 + 0.5;
}
element.SetMin(min);
element.SetMax(max);
roi->AddElement(element);
}
auto roiNode = mitk::DataNode::New();
roiNode->SetName(referenceNode->GetName() + " ROI" + (roi->GetNumberOfElements() > 1 ? "s" : ""));
roiNode->SetData(roi);
m_DataStorage->Add(roiNode, referenceNode);
}
catch (const mitk::Exception&)
{
HandleInvalidNodeSelection();
}
}
void QmitkConvertGeometryDataToROIAction::SetDataStorage(mitk::DataStorage* dataStorage)
{
m_DataStorage = dataStorage;
}
void QmitkConvertGeometryDataToROIAction::SetFunctionality(berry::QtViewPart*)
{
}
void QmitkConvertGeometryDataToROIAction::SetSmoothed(bool)
{
}
void QmitkConvertGeometryDataToROIAction::SetDecimated(bool)
{
}
diff --git a/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.cpp b/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.cpp
index 3f723975ff..5a11521f4b 100644
--- a/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.cpp
+++ b/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.cpp
@@ -1,724 +1,724 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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_framereg_Activator.h"
// Blueberry
#include <berryISelectionService.h>
#include <berryIWorkbenchWindow.h>
#include <berryISelectionProvider.h>
#include <berryQModelIndexObject.h>
// Mitk
#include <mitkStatusBar.h>
#include <mitkPointSet.h>
#include <mitkImageTimeSelector.h>
#include <mitkNodePredicateDataType.h>
#include <mitkNodePredicateOr.h>
#include <mitkNodePredicateAnd.h>
#include <mitkNodePredicateProperty.h>
#include <mitkNodePredicateDimension.h>
#include <mitkNodePredicateGeometry.h>
#include <mitkMAPAlgorithmInfoSelection.h>
#include <mitkRegistrationHelper.h>
#include <mitkResultNodeGenerationHelper.h>
// Qmitk
#include "QmitkMatchPointFrameCorrection.h"
#include <QmitkRegistrationJob.h>
#include <QmitkMappingJob.h>
// Qt
#include <QMessageBox>
#include <QFileDialog>
#include <QErrorMessage>
#include <QThreadPool>
#include <QDateTime>
// MatchPoint
#include <mapImageRegistrationAlgorithmInterface.h>
#include <mapPointSetRegistrationAlgorithmInterface.h>
#include <mapRegistrationAlgorithmInterface.h>
#include <mapMaskedRegistrationAlgorithmInterface.h>
#include <mapAlgorithmEvents.h>
#include <mapAlgorithmWrapperEvent.h>
#include <mapExceptionObjectMacros.h>
#include <mapConvert.h>
#include <mapDeploymentDLLAccess.h>
const std::string QmitkMatchPointFrameCorrection::VIEW_ID =
"org.mitk.views.matchpoint.algorithm.framereg";
QmitkMatchPointFrameCorrection::QmitkMatchPointFrameCorrection()
: m_Parent(nullptr), m_LoadedDLLHandle(nullptr), m_LoadedAlgorithm(nullptr), m_CanLoadAlgorithm(false), m_Working(false)
{
m_spSelectedTargetData = nullptr;
m_spSelectedTargetMaskData = nullptr;
}
QmitkMatchPointFrameCorrection::~QmitkMatchPointFrameCorrection()
{
// remove selection service
berry::ISelectionService* s = this->GetSite()->GetWorkbenchWindow()->GetSelectionService();
if (s)
{
s->RemoveSelectionListener(m_AlgorithmSelectionListener.data());
}
}
void QmitkMatchPointFrameCorrection::SetFocus()
{
}
void QmitkMatchPointFrameCorrection::CreateConnections()
{
connect(m_Controls.imageNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointFrameCorrection::OnNodeSelectionChanged);
connect(m_Controls.maskNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointFrameCorrection::OnNodeSelectionChanged);
// ------
// Tab 1 - Shared library loading interface
// ------
connect(m_Controls.m_pbLoadSelected, SIGNAL(clicked()), this, SLOT(OnLoadAlgorithmButtonPushed()));
// -----
// Tab 2 - Execution
// -----
connect(m_Controls.m_pbStartReg, SIGNAL(clicked()), this, SLOT(OnStartRegBtnPushed()));
connect(m_Controls.m_pbSaveLog, SIGNAL(clicked()), this, SLOT(OnSaveLogBtnPushed()));
// -----
// Tab 4 - Frames
// -----
connect(m_Controls.m_btnFrameSelAll, SIGNAL(clicked()), this, SLOT(OnFramesSelectAllPushed()));
connect(m_Controls.m_btnFrameDeSelAll, SIGNAL(clicked()), this, SLOT(OnFramesDeSelectAllPushed()));
connect(m_Controls.m_btnFrameInvert, SIGNAL(clicked()), this, SLOT(OnFramesInvertPushed()));
}
const map::deployment::DLLInfo* QmitkMatchPointFrameCorrection::GetSelectedAlgorithmDLL() const
{
return m_SelectedAlgorithmInfo;
}
void QmitkMatchPointFrameCorrection::OnSelectedAlgorithmChanged()
{
std::stringstream descriptionString;
::map::deployment::DLLInfo::ConstPointer currentItemInfo = GetSelectedAlgorithmDLL();
if (!currentItemInfo)
{
return;
}
m_Controls.m_teAlgorithmDetails->updateInfo(currentItemInfo);
m_Controls.m_lbSelectedAlgorithm->setText(QString::fromStdString(
currentItemInfo->getAlgorithmUID().getName()));
// enable loading
m_CanLoadAlgorithm = true;
this->AdaptFolderGUIElements();
}
void QmitkMatchPointFrameCorrection::OnLoadAlgorithmButtonPushed()
{
map::deployment::DLLInfo::ConstPointer dllInfo = GetSelectedAlgorithmDLL();
if (!dllInfo)
{
Error(QStringLiteral("No valid algorithm is selected. Cannot load algorithm. ABORTING."));
return;
}
::map::deployment::DLLHandle::Pointer tempDLLHandle = ::map::deployment::openDeploymentDLL(
dllInfo->getLibraryFilePath());
::map::algorithm::RegistrationAlgorithmBase::Pointer tempAlgorithm
= ::map::deployment::getRegistrationAlgorithm(tempDLLHandle);
if (tempAlgorithm.IsNull())
{
Error(QStringLiteral("Error. Cannot load selected algorithm."));
return;
}
this->m_LoadedAlgorithm = tempAlgorithm;
this->m_LoadedDLLHandle = tempDLLHandle;
this->m_Controls.m_AlgoConfigurator->setAlgorithm(m_LoadedAlgorithm);
this->AdaptFolderGUIElements();
this->ConfigureNodeSelectorPredicates();
this->CheckInputs();
this->ConfigureRegistrationControls();
this->ConfigureProgressInfos();
this->m_Controls.m_tabs->setCurrentIndex(1);
}
void QmitkMatchPointFrameCorrection::Error(QString msg)
{
mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1());
MITK_ERROR << msg.toStdString().c_str();
m_Controls.m_teLog->append(QString("<font color='red'><b>") + msg + QString("</b></font>"));
}
void QmitkMatchPointFrameCorrection::AdaptFolderGUIElements()
{
m_Controls.m_pbLoadSelected->setEnabled(m_CanLoadAlgorithm);
}
void QmitkMatchPointFrameCorrection::CreateQtPartControl(QWidget* parent)
{
// create GUI widgets from the Qt Designer's .ui file
m_Controls.setupUi(parent);
m_Parent = parent;
this->m_Controls.imageNodeSelector->SetDataStorage(this->GetDataStorage());
this->m_Controls.imageNodeSelector->SetSelectionIsOptional(false);
this->m_Controls.maskNodeSelector->SetDataStorage(this->GetDataStorage());
this->m_Controls.maskNodeSelector->SetSelectionIsOptional(true);
- this->m_Controls.imageNodeSelector->SetInvalidInfo("Select dymamic image.");
+ this->m_Controls.imageNodeSelector->SetInvalidInfo("Select dynamic image.");
this->m_Controls.imageNodeSelector->SetPopUpTitel("Select dynamic image.");
this->m_Controls.imageNodeSelector->SetPopUpHint("Select a dynamic image (time resolved) that should be frame corrected.");
this->m_Controls.maskNodeSelector->SetInvalidInfo("Select target mask.");
this->m_Controls.maskNodeSelector->SetPopUpTitel("Select target mask.");
this->m_Controls.maskNodeSelector->SetPopUpHint("Select a target mask (mask of the target/first frame).");
m_Controls.m_tabs->setCurrentIndex(0);
m_Controls.m_mapperSettings->AllowSampling(false);
m_AlgorithmSelectionListener.reset(new
berry::SelectionChangedAdapter<QmitkMatchPointFrameCorrection>(this,
&QmitkMatchPointFrameCorrection::OnAlgorithmSelectionChanged));
// register selection listener
GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddSelectionListener(
m_AlgorithmSelectionListener.data());
this->ConfigureNodeSelectorPredicates();
this->CreateConnections();
this->AdaptFolderGUIElements();
this->CheckInputs();
this->ConfigureProgressInfos();
this->ConfigureRegistrationControls();
berry::ISelection::ConstPointer selection =
GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.matchpoint.algorithm.browser");
this->UpdateAlgorithmSelection(selection);
}
void QmitkMatchPointFrameCorrection::ConfigureNodeSelectorPredicates()
{
auto isImage = mitk::MITKRegistrationHelper::ImageNodePredicate();
mitk::NodePredicateBase::Pointer maskDimensionPredicate = mitk::NodePredicateOr::New(mitk::NodePredicateDimension::New(3), mitk::NodePredicateDimension::New(4)).GetPointer();
mitk::NodePredicateDimension::Pointer imageDimensionPredicate = mitk::NodePredicateDimension::New(4);
m_Controls.imageNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull());
m_Controls.maskNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull());
m_Controls.imageNodeSelector->SetNodePredicate(mitk::NodePredicateAnd::New(isImage, imageDimensionPredicate));
mitk::NodePredicateAnd::Pointer maskPredicate = mitk::NodePredicateAnd::New(mitk::MITKRegistrationHelper::MaskNodePredicate(), maskDimensionPredicate);
if (m_spSelectedTargetData != nullptr)
{
auto hasSameGeometry = mitk::NodePredicateGeometry::New(m_spSelectedTargetData->GetGeometry());
hasSameGeometry->SetCheckPrecision(1e-10);
maskPredicate = mitk::NodePredicateAnd::New(maskPredicate, hasSameGeometry);
}
m_Controls.maskNodeSelector->SetNodePredicate(maskPredicate);
}
void QmitkMatchPointFrameCorrection::CheckInputs()
{
mitk::DataNode::Pointer oldTargetNode = m_spSelectedTargetNode;
m_spSelectedTargetNode = nullptr;
m_spSelectedTargetData = nullptr;
m_spSelectedTargetMaskNode = nullptr;
m_spSelectedTargetMaskData = nullptr;
if (m_LoadedAlgorithm.IsNull())
{
m_Controls.m_lbLoadedAlgorithmName->setText(
QStringLiteral("<font color='red'>No algorithm selected!</font>"));
}
else
{
m_spSelectedTargetNode = m_Controls.imageNodeSelector->GetSelectedNode();
if (m_spSelectedTargetNode.IsNotNull())
{
m_spSelectedTargetData = m_spSelectedTargetNode->GetData();
}
m_spSelectedTargetMaskNode = m_Controls.maskNodeSelector->GetSelectedNode();
if (m_spSelectedTargetMaskNode.IsNotNull())
{
m_spSelectedTargetMaskData = dynamic_cast<mitk::Image*>(m_spSelectedTargetMaskNode->GetData());
}
}
if (oldTargetNode != m_spSelectedTargetNode)
{
ConfigureFrameList();
}
}
mitk::DataStorage::SetOfObjects::Pointer QmitkMatchPointFrameCorrection::GetRegNodes() const
{
mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetDataStorage()->GetAll();
mitk::DataStorage::SetOfObjects::Pointer result = mitk::DataStorage::SetOfObjects::New();
for (mitk::DataStorage::SetOfObjects::const_iterator pos = nodes->begin(); pos != nodes->end();
++pos)
{
if (mitk::MITKRegistrationHelper::IsRegNode(*pos))
{
result->push_back(*pos);
}
}
return result;
}
std::string QmitkMatchPointFrameCorrection::GetDefaultJobName() const
{
mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetRegNodes().GetPointer();
mitk::DataStorage::SetOfObjects::ElementIdentifier newIndex = 0;
bool isUnique = false;
std::string baseName = "corrected #";
if (m_spSelectedTargetNode.IsNotNull())
{
baseName = m_spSelectedTargetNode->GetName() + "corrected #";
}
std::string result = baseName;
while (!isUnique)
{
++newIndex;
result = baseName + ::map::core::convert::toStr(newIndex);
isUnique = this->GetDataStorage()->GetNamedNode(result) == nullptr;
}
return result;
}
void QmitkMatchPointFrameCorrection::ConfigureRegistrationControls()
{
m_Controls.m_tabSelection->setEnabled(!m_Working);
m_Controls.m_leRegJobName->setEnabled(!m_Working);
m_Controls.m_pbStartReg->setEnabled(false);
m_Controls.imageNodeSelector->setEnabled(!m_Working);
m_Controls.maskNodeSelector->setEnabled(!m_Working);
if (m_LoadedAlgorithm.IsNotNull())
{
m_Controls.m_tabSettings->setEnabled(!m_Working);
m_Controls.m_tabExclusion->setEnabled(!m_Working);
m_Controls.m_tabExecution->setEnabled(true);
m_Controls.m_pbStartReg->setEnabled(m_spSelectedTargetNode.IsNotNull() && !m_Working);
m_Controls.m_leRegJobName->setEnabled(!m_Working);
typedef ::map::algorithm::facet::MaskedRegistrationAlgorithmInterface<3, 3> MaskRegInterface;
const MaskRegInterface* pMaskReg = dynamic_cast<const MaskRegInterface*>
(m_LoadedAlgorithm.GetPointer());
m_Controls.maskNodeSelector->setVisible(pMaskReg != nullptr);
m_Controls.label_TargetMask->setVisible(pMaskReg != nullptr);
if (!pMaskReg)
{
m_Controls.maskNodeSelector->SetCurrentSelection(QmitkSingleNodeSelectionWidget::NodeList());
}
this->m_Controls.m_lbLoadedAlgorithmName->setText(
QString::fromStdString(m_LoadedAlgorithm->getUID()->toStr()));
}
else
{
m_Controls.m_tabSettings->setEnabled(false);
m_Controls.m_tabExclusion->setEnabled(false);
m_Controls.m_tabExecution->setEnabled(false);
this->m_Controls.m_lbLoadedAlgorithmName->setText(
QStringLiteral("<font color='red'>no algorithm loaded!</font>"));
m_Controls.maskNodeSelector->setVisible(false);
m_Controls.label_TargetMask->setVisible(false);
}
if (!m_Working)
{
this->m_Controls.m_leRegJobName->setText(QString::fromStdString(this->GetDefaultJobName()));
}
}
void QmitkMatchPointFrameCorrection::ConfigureProgressInfos()
{
const IIterativeAlgorithm* pIterative = dynamic_cast<const IIterativeAlgorithm*>
(m_LoadedAlgorithm.GetPointer());
const IMultiResAlgorithm* pMultiRes = dynamic_cast<const IMultiResAlgorithm*>
(m_LoadedAlgorithm.GetPointer());
m_Controls.m_progBarIteration->setVisible(pIterative);
m_Controls.m_lbProgBarIteration->setVisible(pIterative);
if (pIterative)
{
QString format = "%p% (%v/%m)";
if (!pIterative->hasMaxIterationCount())
{
format = "%v";
m_Controls.m_progBarIteration->setMaximum(0);
}
else
{
m_Controls.m_progBarIteration->setMaximum(pIterative->getMaxIterations());
}
m_Controls.m_progBarIteration->setFormat(format);
}
if (pMultiRes)
{
m_Controls.m_progBarLevel->setMaximum(pMultiRes->getResolutionLevels());
}
else
{
m_Controls.m_progBarLevel->setMaximum(1);
}
m_Controls.m_progBarIteration->reset();
m_Controls.m_progBarLevel->reset();
m_Controls.m_progBarFrame->reset();
}
void QmitkMatchPointFrameCorrection::ConfigureFrameList()
{
m_Controls.m_listFrames->clear();
if (m_spSelectedTargetData.IsNotNull())
{
mitk::TimeGeometry::ConstPointer tg = m_spSelectedTargetData->GetTimeGeometry();
for (unsigned int i = 1; i < tg->CountTimeSteps(); ++i)
{
QString lable = "Timepoint #" + QString::number(i) + QString(" (") + QString::number(
tg->GetMinimumTimePoint(i)) + QString(" ms - " + QString::number(tg->GetMaximumTimePoint(
i)) + QString(" ms)"));
QListWidgetItem* item = new QListWidgetItem(lable, m_Controls.m_listFrames);
item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
item->setCheckState(Qt::Checked);
}
}
}
void QmitkMatchPointFrameCorrection::OnFramesSelectAllPushed()
{
for (int row = 0; row < m_Controls.m_listFrames->count(); row++)
{
QListWidgetItem* item = m_Controls.m_listFrames->item(row);
item->setCheckState(Qt::Checked);
}
};
void QmitkMatchPointFrameCorrection::OnFramesDeSelectAllPushed()
{
for (int row = 0; row < m_Controls.m_listFrames->count(); row++)
{
QListWidgetItem* item = m_Controls.m_listFrames->item(row);
item->setCheckState(Qt::Unchecked);
}
};
void QmitkMatchPointFrameCorrection::OnFramesInvertPushed()
{
for (int row = 0; row < m_Controls.m_listFrames->count(); row++)
{
QListWidgetItem* item = m_Controls.m_listFrames->item(row);
if (item->checkState() == Qt::Unchecked)
{
item->setCheckState(Qt::Checked);
}
else
{
item->setCheckState(Qt::Unchecked);
}
}
};
mitk::TimeFramesRegistrationHelper::IgnoreListType
QmitkMatchPointFrameCorrection::GenerateIgnoreList() const
{
mitk::TimeFramesRegistrationHelper::IgnoreListType result;
for (int row = 0; row < m_Controls.m_listFrames->count(); row++)
{
QListWidgetItem* item = m_Controls.m_listFrames->item(row);
if (item->checkState() == Qt::Unchecked)
{
result.push_back(row + 1);
}
}
return result;
}
void QmitkMatchPointFrameCorrection::OnNodeSelectionChanged(QList<mitk::DataNode::Pointer> /*nodes*/)
{
if (!m_Working)
{
ConfigureNodeSelectorPredicates();
CheckInputs();
ConfigureRegistrationControls();
}
}
void QmitkMatchPointFrameCorrection::OnStartRegBtnPushed()
{
this->m_Working = true;
////////////////////////////////
//configure GUI
this->ConfigureProgressInfos();
m_Controls.m_progBarIteration->reset();
m_Controls.m_progBarLevel->reset();
this->ConfigureRegistrationControls();
if (m_Controls.m_checkClearLog->checkState() == Qt::Checked)
{
this->m_Controls.m_teLog->clear();
}
/////////////////////////
//create job and put it into the thread pool
QmitkFramesRegistrationJob* pJob = new QmitkFramesRegistrationJob(m_LoadedAlgorithm);
pJob->setAutoDelete(true);
pJob->m_spTargetData = m_spSelectedTargetData;
pJob->m_TargetDataUID = mitk::EnsureUID(this->m_spSelectedTargetNode->GetData());
pJob->m_IgnoreList = this->GenerateIgnoreList();
if (m_spSelectedTargetMaskData.IsNotNull())
{
pJob->m_spTargetMask = m_spSelectedTargetMaskData;
pJob->m_TargetMaskDataUID = mitk::EnsureUID(this->m_spSelectedTargetMaskNode->GetData());
}
pJob->m_MappedName = m_Controls.m_leRegJobName->text().toStdString();
m_Controls.m_mapperSettings->ConfigureJobSettings(pJob);
connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnRegJobError(QString)));
connect(pJob, SIGNAL(Finished()), this, SLOT(OnRegJobFinished()));
connect(pJob, SIGNAL(ResultIsAvailable(mitk::Image::Pointer, const QmitkFramesRegistrationJob*)),
this, SLOT(OnMapResultIsAvailable(mitk::Image::Pointer, const QmitkFramesRegistrationJob*)),
Qt::BlockingQueuedConnection);
connect(pJob, SIGNAL(AlgorithmInfo(QString)), this, SLOT(OnAlgorithmInfo(QString)));
connect(pJob, SIGNAL(AlgorithmStatusChanged(QString)), this,
SLOT(OnAlgorithmStatusChanged(QString)));
connect(pJob, SIGNAL(AlgorithmIterated(QString, bool, unsigned long)), this,
SLOT(OnAlgorithmIterated(QString, bool, unsigned long)));
connect(pJob, SIGNAL(LevelChanged(QString, bool, unsigned long)), this, SLOT(OnLevelChanged(QString,
bool, unsigned long)));
connect(pJob, SIGNAL(FrameRegistered(double)), this, SLOT(OnFrameRegistered(double)));
connect(pJob, SIGNAL(FrameMapped(double)), this, SLOT(OnFrameMapped(double)));
connect(pJob, SIGNAL(FrameProcessed(double)), this, SLOT(OnFrameProcessed(double)));
QThreadPool* threadPool = QThreadPool::globalInstance();
threadPool->start(pJob);
}
void QmitkMatchPointFrameCorrection::OnSaveLogBtnPushed()
{
QDateTime currentTime = QDateTime::currentDateTime();
QString fileName = tr("registration_log_") + currentTime.toString(tr("yyyy-MM-dd_hh-mm-ss")) +
tr(".txt");
fileName = QFileDialog::getSaveFileName(nullptr, tr("Save registration log"), fileName,
tr("Text files (*.txt)"));
if (fileName.isEmpty())
{
QMessageBox::critical(nullptr, tr("No file selected!"),
tr("Cannot save registration log file. Please selected a file."));
}
else
{
std::ofstream file;
std::ios_base::openmode iOpenFlag = std::ios_base::out | std::ios_base::trunc;
file.open(fileName.toStdString().c_str(), iOpenFlag);
if (!file.is_open())
{
mitkThrow() << "Cannot open or create specified file to save. File path: "
<< fileName.toStdString();
}
file << this->m_Controls.m_teLog->toPlainText().toStdString() << std::endl;
file.close();
}
}
void QmitkMatchPointFrameCorrection::OnRegJobError(QString err)
{
Error(err);
};
void QmitkMatchPointFrameCorrection::OnRegJobFinished()
{
this->m_Working = false;
auto* renderWindowPart = this->GetRenderWindowPart();
if (nullptr != renderWindowPart)
renderWindowPart->RequestUpdate();
this->CheckInputs();
this->ConfigureRegistrationControls();
this->ConfigureProgressInfos();
};
void QmitkMatchPointFrameCorrection::OnMapResultIsAvailable(mitk::Image::Pointer spMappedData,
const QmitkFramesRegistrationJob* job)
{
m_Controls.m_teLog->append(QString("<b><font color='blue'>Corrected image stored. Name: ") +
QString::fromStdString(job->m_MappedName) + QString("</font></b>"));
mitk::DataNode::Pointer spResultNode = mitk::generateMappedResultNode(job->m_MappedName,
spMappedData.GetPointer(), "", job->m_TargetDataUID, false, job->m_InterpolatorLabel);
this->GetDataStorage()->Add(spResultNode, this->m_spSelectedTargetNode);
auto* renderWindowPart = this->GetRenderWindowPart();
if (nullptr != renderWindowPart)
renderWindowPart->RequestUpdate();
};
void QmitkMatchPointFrameCorrection::OnMapJobError(QString err)
{
Error(err);
};
void QmitkMatchPointFrameCorrection::OnAlgorithmIterated(QString info, bool hasIterationCount,
unsigned long currentIteration)
{
if (hasIterationCount)
{
m_Controls.m_progBarIteration->setValue(currentIteration);
}
m_Controls.m_teLog->append(info);
};
void QmitkMatchPointFrameCorrection::OnLevelChanged(QString info, bool hasLevelCount,
unsigned long currentLevel)
{
if (hasLevelCount)
{
m_Controls.m_progBarLevel->setValue(currentLevel);
}
m_Controls.m_teLog->append(QString("<b><font color='green'>") + info + QString("</font></b>"));
};
void QmitkMatchPointFrameCorrection::OnAlgorithmStatusChanged(QString info)
{
m_Controls.m_teLog->append(QString("<b><font color='blue'>") + info + QString(" </font></b>"));
};
void QmitkMatchPointFrameCorrection::OnAlgorithmInfo(QString info)
{
m_Controls.m_teLog->append(QString("<font color='gray'><i>") + info + QString("</i></font>"));
};
void QmitkMatchPointFrameCorrection::OnFrameProcessed(double progress)
{
m_Controls.m_teLog->append(QStringLiteral("<b><font color='blue'>Frame processed...</font></b>"));
m_Controls.m_progBarFrame->setValue(100 * progress);
};
void QmitkMatchPointFrameCorrection::OnFrameRegistered(double progress)
{
m_Controls.m_teLog->append(QStringLiteral("<b><font color='blue'>Frame registered...</font></b>"));
m_Controls.m_progBarFrame->setValue(100 * progress);
};
void QmitkMatchPointFrameCorrection::OnFrameMapped(double progress)
{
m_Controls.m_teLog->append(QStringLiteral("<b><font color='blue'>Frame mapped...</font></b>"));
m_Controls.m_progBarFrame->setValue(100 * progress);
};
void QmitkMatchPointFrameCorrection::OnAlgorithmSelectionChanged(const
berry::IWorkbenchPart::Pointer& sourcepart,
const berry::ISelection::ConstPointer& selection)
{
// check for null selection
if (selection.IsNull())
{
return;
}
if (sourcepart != this)
{
UpdateAlgorithmSelection(selection);
}
}
void QmitkMatchPointFrameCorrection::UpdateAlgorithmSelection(berry::ISelection::ConstPointer
selection)
{
mitk::MAPAlgorithmInfoSelection::ConstPointer currentSelection =
selection.Cast<const mitk::MAPAlgorithmInfoSelection>();
if (currentSelection)
{
mitk::MAPAlgorithmInfoSelection::AlgorithmInfoVectorType infoVector =
currentSelection->GetSelectedAlgorithmInfo();
if (!infoVector.empty())
{
// only the first selection is of interest, the rest will be skipped.
this->m_SelectedAlgorithmInfo = infoVector[0];
}
this->OnSelectedAlgorithmChanged();
}
};
diff --git a/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.h b/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.h
index d2edbd4687..1bdf92e044 100644
--- a/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.h
+++ b/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.h
@@ -1,191 +1,191 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 QmitkMatchPointFrameCorrection_h
#define QmitkMatchPointFrameCorrection_h
#include <berryISelectionListener.h>
#include <QmitkAbstractView.h>
#include "ui_QmitkMatchPointFrameCorrectionControls.h"
#include <mitkImage.h>
// MatchPoint
#include <mapDeploymentDLLInfo.h>
#include <mapDeploymentDLLHandle.h>
#include <mapRegistrationAlgorithmBase.h>
#include <mapIterativeAlgorithmInterface.h>
#include <mapMultiResRegistrationAlgorithmInterface.h>
#include <mapStoppableAlgorithmInterface.h>
#include <mitkMAPRegistrationWrapper.h>
#include <QmitkFramesRegistrationJob.h>
/*!
\brief View for motion artefact correction of images.
-The view utalizes MatchPoint registration algorithms and the mitk::TimeFramesRegistrationHelper and implemnts the GUI
+The view utalizes MatchPoint registration algorithms and the mitk::TimeFramesRegistrationHelper and implements the GUI
business logic to make frame correction aka motion artefact correction on 3D+t images.
*/
class QmitkMatchPointFrameCorrection : public QmitkAbstractView
{
// this is needed for all Qt objects that should have a Qt meta-object
// (everything that derives from QObject and wants to have signal/slots)
Q_OBJECT
public:
static const std::string VIEW_ID;
/**
* Creates smartpointer typedefs
*/
berryObjectMacro(QmitkMatchPointFrameCorrection);
QmitkMatchPointFrameCorrection();
~QmitkMatchPointFrameCorrection() override;
protected slots:
/**
* @brief Connect all GUI elements to its corresponding slots
*/
virtual void CreateConnections();
void OnLoadAlgorithmButtonPushed();
void OnSelectedAlgorithmChanged();
void OnStartRegBtnPushed();
void OnSaveLogBtnPushed();
void OnFramesSelectAllPushed();
void OnFramesDeSelectAllPushed();
void OnFramesInvertPushed();
void OnRegJobError(QString err);
void OnRegJobFinished();
void OnMapJobError(QString err);
void OnMapResultIsAvailable(mitk::Image::Pointer spMappedData,
const QmitkFramesRegistrationJob* job);
void OnAlgorithmIterated(QString info, bool hasIterationCount, unsigned long currentIteration);
void OnLevelChanged(QString info, bool hasLevelCount, unsigned long currentLevel);
void OnAlgorithmStatusChanged(QString info);
void OnAlgorithmInfo(QString info);
void OnFrameProcessed(double progress);
void OnFrameRegistered(double progress);
void OnFrameMapped(double progress);
void OnNodeSelectionChanged(QList<mitk::DataNode::Pointer> nodes);
protected:
void CreateQtPartControl(QWidget* parent) override;
void SetFocus() override;
private:
/**
* @brief Adapt the visibility of GUI elements depending on the current data loaded
*/
void AdaptFolderGUIElements();
void Error(QString msg);
/**
* checks if appropriated nodes are selected in the data manager. If nodes are selected,
* they are stored m_MovingData and m_TargetData.*/
void CheckInputs();
/**
* Updates the state of registration control buttons. Regarding to selected
* inputs, loaded algorithm and its state.*/
void ConfigureRegistrationControls();
/**
Configure the node selectors predicates according to the selected algorithm.
*/
void ConfigureNodeSelectorPredicates();
/**
* Configures the progress bars according to the chosen algorithm.
*/
void ConfigureProgressInfos();
/**configure the frame list widget based on the selected target.*/
void ConfigureFrameList();
/**generates the ignore list based on the frame list widget selection.*/
mitk::TimeFramesRegistrationHelper::IgnoreListType GenerateIgnoreList() const;
/** Methods returns a list of all nodes in the data manager containing a registration wrapper.
* The list may be empty.*/
mitk::DataStorage::SetOfObjects::Pointer GetRegNodes() const;
/** Returns a proposal for a (unique) default reg job name */
std::string GetDefaultJobName() const;
/** Returns the Pointer to the DLL info of the algorithm currently selected by the system.
The info is received via m_AlgorithmSelectionListener.
@return If there is no item selected the returning pointer
will be null.
*/
const map::deployment::DLLInfo* GetSelectedAlgorithmDLL() const;
//! [Qt Selection Listener method and pointer]
/**
* @brief Method of berry::ISelectionListener that implements the selection listener functionality.
* @param sourcepart The workbench part responsible for the selection change.
* @param selection This parameter holds the current selection.
*
* @see ISelectionListener
*/
void OnAlgorithmSelectionChanged(const berry::IWorkbenchPart::Pointer& sourcepart,
const berry::ISelection::ConstPointer& selection);
void UpdateAlgorithmSelection(berry::ISelection::ConstPointer selection);
friend struct berry::SelectionChangedAdapter<QmitkMatchPointFrameCorrection>;
QWidget* m_Parent;
/** @brief this pointer holds the algorithm selection listener */
QScopedPointer<berry::ISelectionListener> m_AlgorithmSelectionListener;
::map::deployment::DLLHandle::Pointer m_LoadedDLLHandle;
::map::algorithm::RegistrationAlgorithmBase::Pointer m_LoadedAlgorithm;
::map::deployment::DLLInfo::ConstPointer m_SelectedAlgorithmInfo;
typedef map::algorithm::facet::IterativeAlgorithmInterface IIterativeAlgorithm;
typedef map::algorithm::facet::MultiResRegistrationAlgorithmInterface IMultiResAlgorithm;
typedef map::algorithm::facet::StoppableAlgorithmInterface IStoppableAlgorithm;
mitk::DataNode::Pointer m_spSelectedTargetNode;
/*Data of the selected target node that should be used for registration.
Can be the direct return of node->GetData(), but can also be a sub
set (like a special time frame).*/
mitk::BaseData::ConstPointer m_spSelectedTargetData;
mitk::DataNode::Pointer m_spSelectedTargetMaskNode;
mitk::Image::ConstPointer m_spSelectedTargetMaskData;
// boolean variables to control visibility of GUI elements
bool m_CanLoadAlgorithm;
bool m_Working;
Ui::MatchPointFrameCorrectionControls m_Controls;
};
#endif
diff --git a/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp b/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp
index fd78a425ed..b129a3289a 100644
--- a/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp
+++ b/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp
@@ -1,559 +1,559 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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_mapper_Activator.h"
// Blueberry
#include <berryISelectionService.h>
#include <berryIWorkbenchWindow.h>
// Mitk
#include <mitkImageAccessByItk.h>
#include <mitkStatusBar.h>
#include "mitkImageMappingHelper.h"
#include "mitkMAPRegistrationWrapper.h"
#include "mitkMatchPointPropertyTags.h"
#include "mitkRegistrationHelper.h"
#include <mitkResultNodeGenerationHelper.h>
#include <mitkUIDHelper.h>
#include <mitkMAPAlgorithmHelper.h>
#include <mitkResultNodeGenerationHelper.h>
#include <mitkNodePredicateFunction.h>
#include <mitkNodePredicateOr.h>
#include <mitkNodePredicateAnd.h>
#include <mitkNodePredicateDataProperty.h>
// Qmitk
#include "QmitkMatchPointMapper.h"
// Qt
#include <QMessageBox>
#include <QFileDialog>
#include <QErrorMessage>
#include <QThreadPool>
const std::string QmitkMatchPointMapper::VIEW_ID = "org.mitk.views.matchpoint.mapper";
QmitkMatchPointMapper::QmitkMatchPointMapper()
: m_Parent(nullptr), m_preparedForBinaryInput(false)
{
}
void QmitkMatchPointMapper::SetFocus()
{
//m_Controls.buttonPerformImageProcessing->setFocus();
}
void QmitkMatchPointMapper::CreateConnections()
{
connect(m_Controls.registrationNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnRegNodeSelectionChanged);
connect(m_Controls.inputNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnInputNodeSelectionChanged);
connect(m_Controls.referenceNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnReferenceNodeSelectionChanged);
connect(m_Controls.m_cbManualRef, SIGNAL(clicked()), this, SLOT(OnManualRefChecked()));
connect(m_Controls.m_cbLinkFactors, SIGNAL(clicked()), this, SLOT(OnLinkSampleFactorChecked()));
connect(m_Controls.m_sbXFactor, SIGNAL(valueChanged(double)), this, SLOT(OnXFactorChanged(double)));
connect(m_Controls.m_pbMap, SIGNAL(clicked()), this, SLOT(OnMapBtnPushed()));
connect(m_Controls.m_pbRefine, SIGNAL(clicked()), this, SLOT(OnRefineBtnPushed()));
}
void QmitkMatchPointMapper::Error(QString msg)
{
mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1());
MITK_ERROR << msg.toStdString().c_str();
m_Controls.m_teLog->append(QStringLiteral("<font color='red'><b>") + msg + QStringLiteral("</b></font>"));
}
void QmitkMatchPointMapper::CreateQtPartControl(QWidget* parent)
{
// 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(true);
this->m_Controls.inputNodeSelector->SetDataStorage(this->GetDataStorage());
this->m_Controls.inputNodeSelector->SetSelectionIsOptional(false);
this->m_Controls.referenceNodeSelector->SetDataStorage(this->GetDataStorage());
this->m_Controls.referenceNodeSelector->SetSelectionIsOptional(false);
this->m_Controls.registrationNodeSelector->SetInvalidInfo("Select valid registration.");
this->m_Controls.registrationNodeSelector->SetEmptyInfo("Assuming identity mapping. Select registration to change.");
this->m_Controls.registrationNodeSelector->SetPopUpTitel("Select registration.");
this->m_Controls.registrationNodeSelector->SetPopUpHint("Select a registration object that should be used for the mapping of the input data. If no registration is selected, identity will be assumed for the mapping.");
this->m_Controls.inputNodeSelector->SetInvalidInfo("Select input data.");
this->m_Controls.inputNodeSelector->SetPopUpTitel("Select input data.");
this->m_Controls.inputNodeSelector->SetPopUpHint("Select the input data for the mapping. (Images or point sets are supported so far).");
this->m_Controls.referenceNodeSelector->SetInvalidInfo("Select the reference image.");
this->m_Controls.referenceNodeSelector->SetPopUpTitel("Select the reference image.");
this->m_Controls.referenceNodeSelector->SetPopUpHint("Select the reference image that specifies the target geometry the input should be mapped into.");
this->ConfigureRegNodePredicate();
this->ConfigureNodePredicates();
// show first page
m_Controls.m_tabs->setCurrentIndex(0);
this->CreateConnections();
this->CheckInputs();
this->ConfigureProgressInfos();
this->ConfigureMappingControls();
}
/** Method checks if the currently selected reg node has a direct kernel that
* can be decomposed in a rotation matrix and a offset. If this is true, true
* is returned. In all other cases false is returned.*/
bool QmitkMatchPointMapper::IsAbleToRefineGeometry() const
{
bool result = false;
if (this->m_spSelectedRegNode.IsNotNull())
{
const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast<const mitk::MAPRegistrationWrapper*>
(this->m_spSelectedRegNode->GetData());
//if the helper does not return null, we can refine the geometry.
result = mitk::MITKRegistrationHelper::getAffineMatrix(wrapper, false).IsNotNull();
}
return result;
}
bool QmitkMatchPointMapper::IsBinaryInput() const
{
auto maskPredicate = mitk::MITKRegistrationHelper::MaskNodePredicate();
bool result = false;
if(this->m_spSelectedInputNode.IsNotNull())
{
result = maskPredicate->CheckNode(this->m_spSelectedInputNode);
}
return result;
}
bool QmitkMatchPointMapper::IsPointSetInput() const
{
bool result = false;
if (this->m_spSelectedInputNode.IsNotNull())
{
result = dynamic_cast<const mitk::PointSet*>(this->m_spSelectedInputNode->GetData()) != nullptr;
}
return result;
}
mitk::DataNode::Pointer QmitkMatchPointMapper::GetAutoRefNodeByReg()
{
mitk::DataNode::Pointer spResult = nullptr;
if (this->m_spSelectedRegNode.IsNotNull() && this->m_spSelectedRegNode->GetData())
{
std::string nodeName;
mitk::BaseProperty* uidProp = m_spSelectedRegNode->GetData()->GetProperty(mitk::Prop_RegAlgTargetData);
if (uidProp)
{
//search for the target node
mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID,
uidProp);
spResult = this->GetDataStorage()->GetNode(predicate);
}
}
if (spResult.IsNull() && this->m_spSelectedInputNode.IsNotNull())
{
//no really reference is available -> use the input as reference
spResult = this->m_spSelectedInputNode;
if (this->m_spSelectedRefNode != spResult)
{
m_Controls.m_teLog->append(
QStringLiteral("<font color='gray'><i>Cannot determine reference automatically. Use input image as reference.</i></font>"));
}
}
return spResult;
}
void QmitkMatchPointMapper::ConfigureRegNodePredicate(const mitk::DataNode* input)
{
mitk::NodePredicateBase::ConstPointer nodePredicate = mitk::MITKRegistrationHelper::RegNodePredicate();
if (input != nullptr)
{
unsigned int dimension = 0;
auto inputImage = dynamic_cast<mitk::Image*>(input->GetData());
auto pointset = dynamic_cast<const mitk::PointSet*>(input->GetData());
if (inputImage)
{
dimension = inputImage->GetDimension();
if (inputImage->GetTimeSteps() > 1)
{
//images has multiple time steps -> remove one dimension.
dimension -= 1;
}
}
else if (pointset)
{
dimension = 3;
}
auto dimCheck = [dimension](const mitk::DataNode * node)
{
const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast < const mitk::MAPRegistrationWrapper* >(node->GetData());
return wrapper != nullptr && wrapper->GetMovingDimensions() == dimension;
};
mitk::NodePredicateFunction::Pointer hasCorrectDim = mitk::NodePredicateFunction::New(dimCheck);
nodePredicate = mitk::NodePredicateAnd::New(nodePredicate, hasCorrectDim).GetPointer();
}
this->m_Controls.registrationNodeSelector->SetNodePredicate(nodePredicate);
}
std::function<bool(const mitk::DataNode *)> GenerateDimCheckLambda(unsigned int dim)
{
auto dimCheck = [dim](const mitk::DataNode * node)
{
auto inputImage = dynamic_cast<mitk::Image*>(node->GetData());
return inputImage != nullptr &&
(inputImage->GetDimension() == dim ||
(inputImage->GetDimension() == dim + 1 && inputImage->GetTimeSteps()>1));
};
return dimCheck;
}
void QmitkMatchPointMapper::ConfigureNodePredicates(const mitk::DataNode* reg)
{
auto isImage = mitk::MITKRegistrationHelper::ImageNodePredicate();
auto isPointSet = mitk::MITKRegistrationHelper::PointSetNodePredicate();
auto isData = mitk::NodePredicateOr::New(isImage, isPointSet);
mitk::NodePredicateBase::ConstPointer inputPredicate = isData.GetPointer();
mitk::NodePredicateBase::ConstPointer refPredicate = isImage.GetPointer();
if (reg != nullptr)
{
const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast <const mitk::MAPRegistrationWrapper*>(reg->GetData());
if (wrapper != nullptr)
{
auto movingDim = wrapper->GetMovingDimensions();
auto dimCheck = GenerateDimCheckLambda(movingDim);
auto hasCorrectDim = mitk::NodePredicateFunction::New(dimCheck);
if (movingDim == 3)
{
//Remark: Point sets are always 3D
auto is3DInput = mitk::NodePredicateOr::New(isPointSet, mitk::NodePredicateAnd::New(isImage, hasCorrectDim));
inputPredicate = is3DInput.GetPointer();
}
else
{
auto is2DInput = mitk::NodePredicateAnd::New(isImage, hasCorrectDim);
inputPredicate = is2DInput.GetPointer();
}
auto targetDim = wrapper->GetTargetDimensions();
auto targetDimCheck = GenerateDimCheckLambda(targetDim);
auto hasCorrectTargetDim = mitk::NodePredicateFunction::New(targetDimCheck);
auto isRef = mitk::NodePredicateAnd::New(isImage, hasCorrectTargetDim);
refPredicate = isRef;
}
}
this->m_Controls.inputNodeSelector->SetNodePredicate(inputPredicate);
this->m_Controls.referenceNodeSelector->SetNodePredicate(refPredicate);
}
void QmitkMatchPointMapper::CheckInputs()
{
this->m_spSelectedRegNode = this->m_Controls.registrationNodeSelector->GetSelectedNode();
this->m_spSelectedInputNode = this->m_Controls.inputNodeSelector->GetSelectedNode();
this->m_spSelectedRefNode = this->m_Controls.referenceNodeSelector->GetSelectedNode();
if (!(m_Controls.m_cbManualRef->isChecked()))
{
auto autoRefNode = this->GetAutoRefNodeByReg();
if (this->m_spSelectedRefNode != autoRefNode)
{
this->m_spSelectedRefNode = autoRefNode;
QmitkSingleNodeSelectionWidget::NodeList selection;
if (this->m_spSelectedRefNode.IsNotNull())
{
selection.append(this->m_spSelectedRefNode);
}
this->m_Controls.referenceNodeSelector->SetCurrentSelection(selection);
}
}
if (this->m_spSelectedRefNode.IsNotNull() && this->m_spSelectedRefNode->GetData()
&& this->m_spSelectedRefNode->GetData()->GetTimeSteps() > 1)
{
m_Controls.m_teLog->append(
QStringLiteral("<font color='gray'><i>Selected reference image has multiple time steps. Only geometry of time step 1 is used as reference.</i></font>"));
}
}
void QmitkMatchPointMapper::ConfigureMappingControls()
{
bool validInput = m_spSelectedInputNode.IsNotNull();
bool validRef = m_spSelectedRefNode.IsNotNull();
this->m_Controls.referenceNodeSelector->setEnabled(this->m_Controls.m_cbManualRef->isChecked());
this->m_Controls.m_pbMap->setEnabled(validInput && validRef);
this->m_Controls.m_pbRefine->setEnabled(validInput && this->IsAbleToRefineGeometry() && !this->IsPointSetInput());
if (validInput)
{
if (m_spSelectedRegNode.IsNotNull())
{
this->m_Controls.m_leMappedName->setText(tr("mapped_") + QString::fromStdString(m_spSelectedInputNode->GetName())
+ tr("_by_") + QString::fromStdString(m_spSelectedRegNode->GetName()));
}
else
{
this->m_Controls.m_leMappedName->setText(tr("resampled_") + QString::fromStdString(m_spSelectedInputNode->GetName()));
}
}
else
{
this->m_Controls.m_leMappedName->setText(tr("mappedData"));
}
if (this->IsBinaryInput() != this->m_preparedForBinaryInput)
{
if (this->IsBinaryInput())
{
m_Controls.m_teLog->append(
QStringLiteral("<font color='gray'><i>Binary input (mask) detected. Preparing for mask mapping (default interpolation: nearest neighbour; padding value: 0)</i></font>"));
this->m_Controls.m_comboInterpolator->setCurrentIndex(0);
this->m_Controls.m_sbErrorValue->setValue(0);
this->m_Controls.m_sbPaddingValue->setValue(0);
}
else
{
this->m_Controls.m_comboInterpolator->setCurrentIndex(1);
}
this->m_preparedForBinaryInput = this->IsBinaryInput();
}
OnLinkSampleFactorChecked();
}
void QmitkMatchPointMapper::ConfigureProgressInfos()
{
}
void QmitkMatchPointMapper::OnRegNodeSelectionChanged(QList<mitk::DataNode::Pointer> nodes)
{
mitk::DataNode::Pointer regNode;
if (!nodes.isEmpty())
{
regNode = nodes.front();
}
this->ConfigureNodePredicates(regNode);
this->CheckInputs();
this->ConfigureMappingControls();
}
void QmitkMatchPointMapper::OnInputNodeSelectionChanged(QList<mitk::DataNode::Pointer> nodes)
{
mitk::DataNode::Pointer inputNode;
if (!nodes.isEmpty())
{
inputNode = nodes.front();
}
this->ConfigureRegNodePredicate(inputNode);
this->CheckInputs();
this->ConfigureMappingControls();
}
void QmitkMatchPointMapper::OnReferenceNodeSelectionChanged(QList<mitk::DataNode::Pointer> /*nodes*/)
{
this->CheckInputs();
this->ConfigureMappingControls();
}
void QmitkMatchPointMapper::OnManualRefChecked()
{
this->CheckInputs();
this->ConfigureMappingControls();
}
void QmitkMatchPointMapper::OnLinkSampleFactorChecked()
{
this->m_Controls.m_sbYFactor->setEnabled(!(this->m_Controls.m_cbLinkFactors->isChecked()));
this->m_Controls.m_sbZFactor->setEnabled(!(this->m_Controls.m_cbLinkFactors->isChecked()));
if (m_Controls.m_cbLinkFactors->isChecked())
{
this->m_Controls.m_sbYFactor->setValue(this->m_Controls.m_sbXFactor->value());
this->m_Controls.m_sbZFactor->setValue(this->m_Controls.m_sbXFactor->value());
}
}
void QmitkMatchPointMapper::OnMapBtnPushed()
{
SpawnMappingJob();
}
void QmitkMatchPointMapper::OnRefineBtnPushed()
{
SpawnMappingJob(true);
}
void QmitkMatchPointMapper::SpawnMappingJob(bool doGeometryRefinement)
{
if (m_Controls.m_checkClearLog->checkState() == Qt::Checked)
{
this->m_Controls.m_teLog->clear();
}
/////////////////////////
//create job and put it into the thread pool
QmitkMappingJob* pJob = new QmitkMappingJob();
pJob->setAutoDelete(true);
pJob->m_spInputData = this->m_spSelectedInputNode->GetData();
pJob->m_InputDataUID = mitk::EnsureUID(this->m_spSelectedInputNode->GetData());
pJob->m_doGeometryRefinement = doGeometryRefinement;
pJob->m_spRegNode = m_spSelectedRegNode;
if (m_spSelectedRegNode.IsNull())
{
pJob->m_spRegNode = mitk::DataNode::New();
pJob->m_spRegNode->SetData(mitk::GenerateIdentityRegistration3D().GetPointer());
pJob->m_spRegNode->SetName("Auto_Generated_Identity_Transform");
m_Controls.m_teLog->append(
- QStringLiteral("<font color='gray'><i>No registration selected. Preforming mapping with identity transform</i></font>"));
+ QStringLiteral("<font color='gray'><i>No registration selected. Performing mapping with identity transform</i></font>"));
}
if (!doGeometryRefinement)
{
pJob->m_spRefGeometry = m_spSelectedRefNode->GetData()->GetGeometry()->Clone().GetPointer();
//check for super/sub sampling
if (m_Controls.m_groupActivateSampling->isChecked())
{
pJob->m_spRefGeometry = mitk::ImageMappingHelper::GenerateSuperSampledGeometry(pJob->m_spRefGeometry,
m_Controls.m_sbXFactor->value(),
m_Controls.m_sbYFactor->value(),
m_Controls.m_sbZFactor->value());
}
}
pJob->m_MappedName = m_Controls.m_leMappedName->text().toStdString();
pJob->m_allowUndefPixels = m_Controls.m_groupAllowUndefPixels->isChecked();
pJob->m_paddingValue = m_Controls.m_sbPaddingValue->value();
pJob->m_allowUnregPixels = m_Controls.m_groupAllowUnregPixels->isChecked();
pJob->m_errorValue = m_Controls.m_sbErrorValue->value();
pJob->m_InterpolatorLabel = m_Controls.m_comboInterpolator->currentText().toStdString();
switch (m_Controls.m_comboInterpolator->currentIndex())
{
case 0:
pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::NearestNeighbor;
break;
case 1:
pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::Linear;
break;
case 2:
pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::BSpline_3;
break;
case 3:
pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::WSinc_Hamming;
break;
case 4:
pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::WSinc_Welch;
break;
}
connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnMapJobError(QString)));
connect(pJob, SIGNAL(MapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), this,
SLOT(OnMapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)),
Qt::BlockingQueuedConnection);
connect(pJob, SIGNAL(AlgorithmInfo(QString)), this, SLOT(OnMappingInfo(QString)));
m_Controls.m_teLog->append(QStringLiteral("<b><font color='blue'>Started mapping job. Name: ") +
m_Controls.m_leMappedName->text() + QStringLiteral("</font></b>"));
QThreadPool* threadPool = QThreadPool::globalInstance();
threadPool->start(pJob);
}
void QmitkMatchPointMapper::OnMapJobError(QString err)
{
Error(err);
}
void QmitkMatchPointMapper::OnMapResultIsAvailable(mitk::BaseData::Pointer spMappedData,
const QmitkMappingJob* job)
{
m_Controls.m_teLog->append(QStringLiteral("<b><font color='blue'>Mapped entity stored. Name: ") +
QString::fromStdString(job->m_MappedName) + QStringLiteral("</font></b>"));
mitk::DataNode::Pointer spMappedNode = mitk::generateMappedResultNode(job->m_MappedName,
spMappedData, job->GetRegistration()->getRegistrationUID(), job->m_InputDataUID,
job->m_doGeometryRefinement, job->m_InterpolatorLabel);
this->GetDataStorage()->Add(spMappedNode);
auto* renderWindowPart = this->GetRenderWindowPart();
if (nullptr != renderWindowPart)
renderWindowPart->RequestUpdate();
this->CheckInputs();
this->ConfigureMappingControls();
}
void QmitkMatchPointMapper::OnMappingInfo(QString info)
{
m_Controls.m_teLog->append(QStringLiteral("<font color='gray'><i>") + info + QStringLiteral("</i></font>"));
}
void QmitkMatchPointMapper::OnXFactorChanged(double d)
{
if (m_Controls.m_cbLinkFactors->isChecked())
{
this->m_Controls.m_sbYFactor->setValue(d);
this->m_Controls.m_sbZFactor->setValue(d);
}
}
diff --git a/Plugins/org.mitk.gui.qt.matchpoint.visualizer/documentation/UserManual/map_view_visualizer_example.svg b/Plugins/org.mitk.gui.qt.matchpoint.visualizer/documentation/UserManual/map_view_visualizer_example.svg
index 92c711068f..8feea4588c 100644
--- a/Plugins/org.mitk.gui.qt.matchpoint.visualizer/documentation/UserManual/map_view_visualizer_example.svg
+++ b/Plugins/org.mitk.gui.qt.matchpoint.visualizer/documentation/UserManual/map_view_visualizer_example.svg
@@ -1,399 +1,399 @@
<?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"
version="1.1"
id="svg4485"
width="678.44788"
height="707"
viewBox="0 0 678.44788 707"
sodipodi:docname="map_view_visualizer_example.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
inkscape:export-filename="D:\Dev\MITK\sMaster\Plugins\org.mitk.gui.qt.matchpoint.manipulator\documentation\UserManual\map_view_manipulator_example.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<metadata
id="metadata4491">
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs4489">
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker5562"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotS">
<path
transform="matrix(0.2,0,0,0.2,1.48,0.2)"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path5560"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker5474"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotS">
<path
transform="matrix(0.2,0,0,0.2,1.48,0.2)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path5472"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="DotM"
orient="auto"
refY="0"
refX="0"
id="marker4945"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4943"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.4,0,0,0.4,2.96,0.4)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="DotS"
orient="auto"
refY="0"
refX="0"
id="DotS"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4564"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.2,0,0,0.2,1.48,0.2)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="DotM"
orient="auto"
refY="0"
refX="0"
id="DotM"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4561"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.4,0,0,0.4,2.96,0.4)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="DotS"
orient="auto"
refY="0"
refX="0"
id="marker5281-6"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
inkscape:connector-curvature="0"
id="path5279-4"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
</marker>
<marker
inkscape:stockid="DotS"
orient="auto"
refY="0"
refX="0"
id="marker5281-0"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path5279-5"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
</marker>
<marker
inkscape:stockid="DotS"
orient="auto"
refY="0"
refX="0"
id="marker5281-0-5"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path5279-5-0"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
</marker>
<marker
inkscape:stockid="DotS"
orient="auto"
refY="0"
refX="0"
id="marker5281-0-0"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path5279-5-3"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
</marker>
<marker
inkscape:stockid="DotS"
orient="auto"
refY="0"
refX="0"
id="marker5281-0-01"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path5279-5-7"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
</marker>
<marker
inkscape:stockid="DotS"
orient="auto"
refY="0"
refX="0"
id="marker5281-0-9"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path5279-5-1"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker5562-8"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotS">
<path
transform="matrix(0.2,0,0,0.2,1.48,0.2)"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path5560-3"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="DotS"
orient="auto"
refY="0"
refX="0"
id="marker5281-0-9-8"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path5279-5-1-6"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
</marker>
<marker
inkscape:stockid="DotS"
orient="auto"
refY="0"
refX="0"
id="marker5281-0-3"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path5279-5-31"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
</marker>
<marker
inkscape:stockid="DotS"
orient="auto"
refY="0"
refX="0"
id="marker5281-0-4"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path5279-5-16"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.2,0,0,0.2,1.48,0.2)" />
</marker>
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="987"
id="namedview4487"
showgrid="false"
inkscape:zoom="1"
inkscape:cx="278.46875"
inkscape:cy="320.89936"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg4485" />
<image
sodipodi:absref="D:\Dev\MITK\sMaster\Plugins\org.mitk.gui.qt.matchpoint.visualizer\documentation\UserManual\map_view_visualizer_example_raw.png"
xlink:href="map_view_visualizer_example_raw.png"
width="353"
height="707"
preserveAspectRatio="none"
id="image4493"
x="0"
y="0" />
<path
style="fill:#ff0000;stroke:#ff0000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5281-6)"
d="m 329.59771,196.14016 h 29.07449 -1.03837"
id="path4495-3"
inkscape:connector-curvature="0" />
<path
style="fill:#ff0000;stroke:#ff0000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5281-0)"
d="m 329.59771,295.00464 h 29.07449 -1.03837"
id="path4495-7"
inkscape:connector-curvature="0" />
<path
style="fill:#ff0000;stroke:#ff0000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5281-0-9)"
d="m 329.59771,99.47931 h 29.07449 -1.03837"
id="path4495-7-6"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none"
x="361.88541"
y="105.52083"
id="text9677-5"><tspan
sodipodi:role="line"
id="tspan9675-8"
x="361.88541"
y="105.52083"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;font-family:Arial;-inkscape-font-specification:Arial;fill:#ff0000">(1) Registration slot</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none"
x="361.88541"
y="202.52083"
id="text9677-1"><tspan
sodipodi:role="line"
id="tspan9675-1"
x="361.88541"
y="202.52083"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;font-family:Arial;-inkscape-font-specification:Arial;fill:#ff0000">(2) Registation information</tspan></text>
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;font-family:Arial;-inkscape-font-specification:Arial;fill:#ff0000">(2) Registration information</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none"
x="361.88541"
y="300.52081"
id="text9677-09"><tspan
sodipodi:role="line"
id="tspan9675-0"
x="361.88541"
y="300.52081"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;font-family:Arial;-inkscape-font-specification:Arial;fill:#ff0000">(3) Update visual settings</tspan><tspan
sodipodi:role="line"
x="361.88541"
y="350.52081"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;font-family:Arial;-inkscape-font-specification:Arial;fill:#ff0000"
id="tspan4725" /></text>
<flowRoot
xml:space="preserve"
id="flowRoot4717"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="translate(0,-1)"><flowRegion
id="flowRegion4719"><rect
id="rect4721"
width="337"
height="48"
x="353"
y="188" /></flowRegion><flowPara
id="flowPara4723" /></flowRoot> <path
style="fill:#ff0000;stroke:#ff0000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5281-0-3)"
d="m 329.59771,345.00466 h 29.07449 -1.03837"
id="path4495-7-8"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none"
x="361.88541"
y="350.52081"
id="text9677-09-1"><tspan
sodipodi:role="line"
id="tspan9675-0-1"
x="361.88541"
y="350.52081"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;font-family:Arial;-inkscape-font-specification:Arial;fill:#ff0000">(4) Selection of mapping direction</tspan><tspan
sodipodi:role="line"
x="361.88541"
y="400.52081"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;font-family:Arial;-inkscape-font-specification:Arial;fill:#ff0000"
id="tspan4725-9" /></text>
<path
style="fill:#ff0000;stroke:#ff0000;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker5281-0-4)"
d="m 329.59771,377.00466 h 29.07449 -1.03837"
id="path4495-7-5"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none"
x="361.88541"
y="382.52084"
id="text9677-09-9"><tspan
sodipodi:role="line"
id="tspan9675-0-2"
x="361.88541"
y="382.52084"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;font-family:Arial;-inkscape-font-specification:Arial;fill:#ff0000">(5) Selection of visualization style</tspan><tspan
sodipodi:role="line"
x="361.88541"
y="432.52084"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;font-family:Arial;-inkscape-font-specification:Arial;fill:#ff0000"
id="tspan4725-7" /></text>
</svg>
diff --git a/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.ui b/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.ui
index 4dc8b4a05e..7cf2d36e25 100644
--- a/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.ui
+++ b/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.ui
@@ -1,1570 +1,1570 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MatchPointRegVisControls</class>
<widget class="QWidget" name="MatchPointRegVisControls">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>332</width>
<height>738</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>120</red>
<green>120</green>
<blue>120</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="text">
<string>Warning: This plugin is in an experimental state!</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Registration:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>3</number>
</property>
<item>
<widget class="QmitkSingleNodeSelectionWidget" name="registrationNodeSelector" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Info:</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="m_teRegInfo">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>75</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>130</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupViz">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string/>
</property>
<property name="flat">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="m_pbUpdateViz">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Updates the visualization with the current settings.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Update visualization</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_13">
<property name="text">
<string>Show mapping direction:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="m_comboDirection">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select the direction/kernel of the registration you want to visualize:&lt;/p&gt;&lt;p&gt;- &amp;quot;direct&amp;quot;: select to show the direct mapping kernel (used for continuous spaced data like point sets).&lt;/p&gt;&lt;p&gt;- &amp;quot;inverse&amp;quot;: select to show the inverse mapping kernel (used for discrete spaced data like voxel images).&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;If the selected registration does not support a direction, you cannot select it.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<item>
<property name="text">
<string>Direct</string>
</property>
</item>
<item>
<property name="text">
<string>Inverse</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QWidget" name="m_boxStyles" 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>30</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="m_pbStyleGrid">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select to visualize registration via grid(s).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Grid</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pbStyleGlyph">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Select to visualize registration via glyphs.</string>
</property>
<property name="text">
<string>Glyph</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pbStylePoints">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Select to visualize registration via a pointset selected from the data manager...</string>
</property>
<property name="text">
<string>Points</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTabWidget" name="m_boxSettings">
<property name="currentIndex">
<number>2</number>
</property>
<widget class="QWidget" name="tabViz">
<attribute name="title">
<string>Visualization</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QLabel" name="label_15">
<property name="text">
<string>Color style:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QRadioButton" name="radioColorUni">
<property name="text">
<string>uni color:</string>
</property>
</widget>
</item>
<item>
<widget class="ctkColorPickerButton" name="btnUniColor">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QRadioButton" name="radioColorVecMag">
<property name="text">
<string>vector magnitude:</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupColorCoding">
<property name="title">
<string>Color coding:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="horizontalSpacing">
<number>7</number>
</property>
<property name="verticalSpacing">
<number>3</number>
</property>
<item row="3" column="7">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="7">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="7">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="7">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="5">
<widget class="QLabel" name="label_29">
<property name="text">
<string>mm</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>small:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>medium:</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLabel" name="labelVecMagMedium">
<property name="maximumSize">
<size>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QDoubleSpinBox" name="sbVecMagSmall">
<property name="maximum">
<double>999.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QDoubleSpinBox" name="sbVecMagLarge">
<property name="maximum">
<double>999.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_9">
<property name="text">
<string>large:</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QLabel" name="labelVecMagLarge">
<property name="maximumSize">
<size>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="5">
<widget class="QLabel" name="label_30">
<property name="text">
<string>mm</string>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QDoubleSpinBox" name="sbVecMagMedium">
<property name="maximum">
<double>999.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="5">
<widget class="QLabel" name="label_31">
<property name="text">
<string>mm</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="labelVecMagSmall">
<property name="maximumSize">
<size>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string>negligible:</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="ctkColorPickerButton" name="btnVecMagColorLarge">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="ctkColorPickerButton" name="btnVecMagColorMedium">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="ctkColorPickerButton" name="btnVecMagColorSmall">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="ctkColorPickerButton" name="btnVecMagColorNeg">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="cbVevMagInterlolate">
<property name="text">
<string>interpolate colors</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
<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="tabGrid">
<property name="enabled">
<bool>true</bool>
</property>
<attribute name="title">
<string>Grid</string>
</attribute>
<attribute name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;General grid visualization settings.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>5</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Grid frequency:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="m_sbGridFrequency">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indicate the frequency of visible gridlines used for visualization.&lt;/p&gt;&lt;p&gt;e.g. 1: every line of the grid is visible; 2: only every second line is visible ...&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>200</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="m_groupShowStartGrid">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Allows that pixels may not be defined in the mapped image because they are outside of the field of view of the used input image.&lt;/p&gt;&lt;p&gt;The pixels will be marked with the given padding value.&lt;/p&gt;&lt;p&gt;If unchecked the mapping will be aborted in a case of undefined pixels.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="title">
<string>Show start grid</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>11</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>11</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="label_14">
<property name="text">
<string>Color</string>
</property>
</widget>
</item>
<item>
<widget class="ctkColorPickerButton" name="btnStartGridColor">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>300</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabFOV">
<attribute name="title">
<string>FOV</string>
</attribute>
<attribute name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Field of view settings (FOV) for vizualizing the grid correctly (its origin, size and orientation).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Field of view settings (FOV) for visualizing the grid correctly (its origin, size and orientation).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="label_18">
<property name="text">
<string>Size (in mm):</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_20">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>x:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbFOVSizeX">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indicate the size (in mm) of the grid in the x direction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="decimals">
<number>2</number>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>9999.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_21">
<property name="text">
<string>y:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbFOVSizeY">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indicate the size (in mm) of the grid in the y direction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="decimals">
<number>2</number>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>9999.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_19">
<property name="text">
<string>z:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbFOVSizeZ">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indicate the size (in mm) of the grid in the z direction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="decimals">
<number>2</number>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>9999.000000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_24">
<property name="text">
<string>Origin (in mm):</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_25">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>x:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbFOVOriginX">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indicate the origin (in mm) of the grid in the x direction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<double>-99999.000000000000000</double>
</property>
<property name="maximum">
<double>99999.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_22">
<property name="text">
<string>y:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbFOVOriginY">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indicate the origin (in mm) of the grid in the y direction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<double>-99999.000000000000000</double>
</property>
<property name="maximum">
<double>99999.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_23">
<property name="text">
<string>z:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbFOVOriginZ">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indicate the origin (in mm) of the grid in the z direction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<double>-99999.000000000000000</double>
</property>
<property name="maximum">
<double>99999.000000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_17">
<property name="text">
<string>Spacing (in mm):</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>x:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbGridSpX">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indicate the spacing/resolution of the grid in the x direction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>9999.000000000000000</double>
</property>
<property name="value">
<double>0.010000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>y:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbGridSpY">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indicate the spacing/resolution of the grid in the y direction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>9999.000000000000000</double>
</property>
<property name="value">
<double>0.010000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>z:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="m_sbGridSpZ">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indicate the spacing/resolution of the grid in the z direction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>9999.000000000000000</double>
</property>
<property name="value">
<double>0.010000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_27">
<property name="text">
<string>Orientation:</string>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="m_tableOrientation">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>160</width>
<height>50</height>
</size>
</property>
<property name="font">
<font>
<pointsize>7</pointsize>
</font>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>20</number>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>50</number>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>20</number>
</attribute>
<row>
<property name="text">
<string>R1</string>
</property>
</row>
<row>
<property name="text">
<string>R2</string>
</property>
</row>
<row>
<property name="text">
<string>R3</string>
</property>
</row>
<column>
<property name="text">
<string>C1</string>
</property>
</column>
<column>
<property name="text">
<string>C2</string>
</property>
</column>
<column>
<property name="text">
<string>C3</string>
</property>
</column>
<item row="0" column="0">
<property name="text">
<string>0</string>
</property>
<property name="flags">
<set>ItemIsEnabled</set>
</property>
</item>
<item row="0" column="1">
<property name="text">
<string>0</string>
</property>
<property name="flags">
<set>ItemIsEnabled</set>
</property>
</item>
<item row="0" column="2">
<property name="text">
<string>0</string>
</property>
<property name="flags">
<set>ItemIsEnabled</set>
</property>
</item>
<item row="1" column="0">
<property name="text">
<string>0</string>
</property>
<property name="flags">
<set>ItemIsEnabled</set>
</property>
</item>
<item row="1" column="1">
<property name="text">
<string>0</string>
</property>
<property name="flags">
<set>ItemIsEnabled</set>
</property>
</item>
<item row="1" column="2">
<property name="text">
<string>0</string>
</property>
<property name="flags">
<set>ItemIsEnabled</set>
</property>
</item>
<item row="2" column="0">
<property name="text">
<string>0</string>
</property>
<property name="flags">
<set>ItemIsEnabled</set>
</property>
</item>
<item row="2" column="1">
<property name="text">
<string>0</string>
</property>
<property name="flags">
<set>ItemIsEnabled</set>
</property>
</item>
<item row="2" column="2">
<property name="text">
<string>0</string>
</property>
<property name="flags">
<set>ItemIsEnabled</set>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_26">
<property name="text">
<string>FOV Reference:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="spacing">
<number>3</number>
</property>
<item>
<widget class="QmitkSingleNodeSelectionWidget" name="fovReferenceNodeSelector" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="m_checkUseRefSize">
<property name="text">
<string>size</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="m_checkUseRefOrigin">
<property name="text">
<string>origin</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="m_checkUseRefSpacing">
<property name="text">
<string>spacing</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="m_checkUseRefOrientation">
<property name="text">
<string>orientation</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</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>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_5">
<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>
<customwidgets>
<customwidget>
<class>QmitkSingleNodeSelectionWidget</class>
<extends>QWidget</extends>
<header location="global">QmitkSingleNodeSelectionWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ctkColorPickerButton</class>
<extends>QPushButton</extends>
<header>ctkColorPickerButton.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
<designerdata>
<property name="gridDeltaX">
<number>5</number>
</property>
<property name="gridDeltaY">
<number>5</number>
</property>
<property name="gridSnapX">
<bool>true</bool>
</property>
<property name="gridSnapY">
<bool>true</bool>
</property>
<property name="gridVisible">
<bool>true</bool>
</property>
</designerdata>
</ui>
diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp
index f0df3dcc23..55003f6815 100644
--- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp
+++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp
@@ -1,821 +1,850 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkMeasurementView.h"
#include <QAction>
#include <QActionGroup>
#include <QApplication>
#include <QClipboard>
#include <QGridLayout>
#include <QToolBar>
#include <QTextBrowser>
#include <QCheckBox>
#include <QGroupBox>
#include <mitkCoreServices.h>
#include <mitkIPropertyFilters.h>
#include <mitkPropertyFilter.h>
#include <mitkVtkLayerController.h>
#include <mitkPlanarCircle.h>
#include <mitkPlanarEllipse.h>
#include <mitkPlanarPolygon.h>
#include <mitkPlanarAngle.h>
#include <mitkPlanarRectangle.h>
#include <mitkPlanarLine.h>
#include <mitkPlanarCross.h>
#include <mitkPlanarFourPointAngle.h>
#include <mitkPlanarDoubleEllipse.h>
#include <mitkPlanarBezierCurve.h>
#include <mitkPlanarSubdivisionPolygon.h>
#include <mitkPlanarFigureInteractor.h>
#include <mitkPlaneGeometry.h>
#include <mitkILinkedRenderWindowPart.h>
#include <mitkImage.h>
#include <mitkNodePredicateDataType.h>
#include <mitkNodePredicateProperty.h>
#include <mitkNodePredicateAnd.h>
#include <mitkNodePredicateNot.h>
#include <QmitkRenderWindow.h>
#include <QmitkSingleNodeSelectionWidget.h>
#include "ctkDoubleSpinBox.h"
#include "mitkPluginActivator.h"
#include "usModuleRegistry.h"
#include "usGetModuleContext.h"
#include "usModuleContext.h"
#include <usModuleInitialization.h>
US_INITIALIZE_MODULE
struct QmitkPlanarFigureData
{
QmitkPlanarFigureData()
: m_EndPlacementObserverTag(0),
m_SelectObserverTag(0),
m_StartInteractionObserverTag(0),
m_EndInteractionObserverTag(0),
m_DuringInteractionObserverTag(0)
{
}
mitk::PlanarFigure::Pointer m_Figure;
unsigned int m_EndPlacementObserverTag;
unsigned int m_SelectObserverTag;
unsigned int m_StartInteractionObserverTag;
unsigned int m_EndInteractionObserverTag;
unsigned int m_DuringInteractionObserverTag;
};
struct QmitkMeasurementViewData
{
QmitkMeasurementViewData()
: m_LineCounter(0),
m_PathCounter(0),
m_AngleCounter(0),
m_FourPointAngleCounter(0),
m_CircleCounter(0),
m_EllipseCounter(0),
m_DoubleEllipseCounter(0),
m_RectangleCounter(0),
m_PolygonCounter(0),
m_BezierCurveCounter(0),
m_SubdivisionPolygonCounter(0),
m_UnintializedPlanarFigure(false),
m_Parent(nullptr),
m_SingleNodeSelectionWidget(nullptr),
m_DrawLine(nullptr),
m_DrawPath(nullptr),
m_DrawAngle(nullptr),
m_DrawFourPointAngle(nullptr),
m_DrawRectangle(nullptr),
m_DrawPolygon(nullptr),
m_DrawCircle(nullptr),
m_DrawEllipse(nullptr),
m_DrawDoubleEllipse(nullptr),
m_DrawBezierCurve(nullptr),
m_DrawSubdivisionPolygon(nullptr),
m_DrawActionsToolBar(nullptr),
m_DrawActionsGroup(nullptr),
m_SelectedPlanarFiguresText(nullptr),
m_CopyToClipboard(nullptr),
m_Layout(nullptr),
m_Radius(nullptr),
m_Thickness(nullptr),
- m_FixedParameterBox(nullptr)
+ m_FixedParameterBox(nullptr),
+ m_DrawLabel(nullptr),
+ m_HintLabel(nullptr)
{
}
unsigned int m_LineCounter;
unsigned int m_PathCounter;
unsigned int m_AngleCounter;
unsigned int m_FourPointAngleCounter;
unsigned int m_CircleCounter;
unsigned int m_EllipseCounter;
unsigned int m_DoubleEllipseCounter;
unsigned int m_RectangleCounter;
unsigned int m_PolygonCounter;
unsigned int m_BezierCurveCounter;
unsigned int m_SubdivisionPolygonCounter;
QList<mitk::DataNode::Pointer> m_CurrentSelection;
std::map<mitk::DataNode::Pointer, QmitkPlanarFigureData> m_DataNodeToPlanarFigureData;
mitk::DataNode::Pointer m_SelectedImageNode;
bool m_UnintializedPlanarFigure;
QWidget* m_Parent;
QLabel* m_SelectedImageLabel;
QmitkSingleNodeSelectionWidget* m_SingleNodeSelectionWidget;
QAction* m_DrawLine;
QAction* m_DrawPath;
QAction* m_DrawAngle;
QAction* m_DrawFourPointAngle;
QAction* m_DrawRectangle;
QAction* m_DrawPolygon;
QAction* m_DrawCircle;
QAction* m_DrawEllipse;
QAction* m_DrawDoubleEllipse;
QAction* m_DrawBezierCurve;
QAction* m_DrawSubdivisionPolygon;
QToolBar* m_DrawActionsToolBar;
QActionGroup* m_DrawActionsGroup;
QTextBrowser* m_SelectedPlanarFiguresText;
QPushButton* m_CopyToClipboard;
QGridLayout* m_Layout;
ctkDoubleSpinBox* m_Radius;
ctkDoubleSpinBox* m_Thickness;
QGroupBox* m_FixedParameterBox;
+ QLabel* m_DrawLabel;
+ QLabel* m_HintLabel;
};
const std::string QmitkMeasurementView::VIEW_ID = "org.mitk.views.measurement";
QmitkMeasurementView::QmitkMeasurementView()
: d(new QmitkMeasurementViewData)
{
}
QmitkMeasurementView::~QmitkMeasurementView()
{
auto planarFigures = this->GetAllPlanarFigures();
for (auto it = planarFigures->Begin(); it != planarFigures->End(); ++it)
this->NodeRemoved(it.Value());
delete d;
}
void QmitkMeasurementView::CreateQtPartControl(QWidget* parent)
{
d->m_Parent = parent;
d->m_SingleNodeSelectionWidget = new QmitkSingleNodeSelectionWidget();
d->m_SingleNodeSelectionWidget->SetDataStorage(GetDataStorage());
d->m_SingleNodeSelectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New(
mitk::TNodePredicateDataType<mitk::Image>::New(),
mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))));
d->m_SingleNodeSelectionWidget->SetSelectionIsOptional(true);
d->m_SingleNodeSelectionWidget->SetEmptyInfo(QStringLiteral("Please select a reference image"));
d->m_SingleNodeSelectionWidget->SetPopUpTitel(QStringLiteral("Select a reference image"));
d->m_DrawActionsToolBar = new QToolBar;
d->m_DrawActionsGroup = new QActionGroup(this);
d->m_DrawActionsGroup->setExclusive(true);
auto* currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/line.png"), tr("Draw Line"));
currentAction->setCheckable(true);
d->m_DrawLine = currentAction;
currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/path.png"), tr("Draw Path"));
currentAction->setCheckable(true);
d->m_DrawPath = currentAction;
currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/angle.png"), tr("Draw Angle"));
currentAction->setCheckable(true);
d->m_DrawAngle = currentAction;
currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/four-point-angle.png"), tr("Draw Four Point Angle"));
currentAction->setCheckable(true);
d->m_DrawFourPointAngle = currentAction;
currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/circle.png"), tr("Draw Circle"));
currentAction->setCheckable(true);
d->m_DrawCircle = currentAction;
currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/ellipse.png"), tr("Draw Ellipse"));
currentAction->setCheckable(true);
d->m_DrawEllipse = currentAction;
currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/doubleellipse.png"), tr("Draw Double Ellipse"));
currentAction->setCheckable(true);
d->m_DrawDoubleEllipse = currentAction;
currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/rectangle.png"), tr("Draw Rectangle"));
currentAction->setCheckable(true);
d->m_DrawRectangle = currentAction;
currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/polygon.png"), tr("Draw Polygon"));
currentAction->setCheckable(true);
d->m_DrawPolygon = currentAction;
currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/beziercurve.png"), tr("Draw Bezier Curve"));
currentAction->setCheckable(true);
d->m_DrawBezierCurve = currentAction;
currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/subdivisionpolygon.png"), tr("Draw Subdivision Polygon"));
currentAction->setCheckable(true);
d->m_DrawSubdivisionPolygon = currentAction;
d->m_DrawActionsToolBar->setEnabled(false);
// fixed parameter section
auto fixedLayout = new QGridLayout();
d->m_FixedParameterBox = new QGroupBox();
d->m_FixedParameterBox->setCheckable(true);
d->m_FixedParameterBox->setChecked(false);
d->m_FixedParameterBox->setEnabled(false);
d->m_FixedParameterBox->setTitle("Fixed sized circle/double ellipse");
d->m_FixedParameterBox->setToolTip("If activated, circles and double ellipses (as rings) figures will always be created with the set parameters as fixed size.");
d->m_FixedParameterBox->setAlignment(Qt::AlignLeft);
auto labelRadius1 = new QLabel(QString("Radius"));
d->m_Radius = new ctkDoubleSpinBox();
d->m_Radius->setMinimum(0);
d->m_Radius->setValue(10);
d->m_Radius->setSuffix(" mm");
d->m_Radius->setAlignment(Qt::AlignLeft);
d->m_Radius->setToolTip("Sets the radius for following planar figures: circle, double ellipse (as ring).");
auto labelThickness = new QLabel(QString("Thickness"));
d->m_Thickness = new ctkDoubleSpinBox();
d->m_Thickness->setMinimum(0);
d->m_Thickness->setMaximum(10);
d->m_Thickness->setValue(5);
d->m_Thickness->setSuffix(" mm");
d->m_Thickness->setAlignment(Qt::AlignLeft);
d->m_Thickness->setToolTip("Sets the thickness for following planar figures: double ellipse (as ring).");
fixedLayout->addWidget(labelRadius1,0,0);
fixedLayout->addWidget(d->m_Radius,0,1);
fixedLayout->addWidget(labelThickness,1,0);
fixedLayout->addWidget(d->m_Thickness,1,1);
d->m_FixedParameterBox->setLayout(fixedLayout);
// planar figure details text
d->m_SelectedPlanarFiguresText = new QTextBrowser;
// copy to clipboard button
d->m_CopyToClipboard = new QPushButton(tr("Copy to Clipboard"));
d->m_SelectedImageLabel = new QLabel("Selected Image");
+
+ d->m_DrawLabel = new QLabel("Place points to draw the measurement...");
+ d->m_DrawLabel->setTextFormat(Qt::PlainText);
+ d->m_DrawLabel->setWordWrap(true);
+ d->m_DrawLabel->hide();
+
+ d->m_HintLabel = new QLabel(R"(<font color="red"><b>Hint:</b></font> Double-click to place the last point and end interaction.)");
+ d->m_HintLabel->setTextFormat(Qt::RichText);
+ d->m_HintLabel->setWordWrap(true);
+ d->m_HintLabel->hide();
+
d->m_Layout = new QGridLayout;
d->m_Layout->addWidget(d->m_SelectedImageLabel, 0, 0);
d->m_Layout->addWidget(d->m_SingleNodeSelectionWidget, 0, 1, 1, 1);
d->m_Layout->addWidget(d->m_DrawActionsToolBar, 1, 0, 1, 2);
- d->m_Layout->addWidget(d->m_FixedParameterBox, 2, 0, 1, 2);
- d->m_Layout->addWidget(d->m_SelectedPlanarFiguresText, 3, 0, 1, 2);
- d->m_Layout->addWidget(d->m_CopyToClipboard, 4, 0, 1, 2);
+ d->m_Layout->addWidget(d->m_DrawLabel, 2, 0, 1, 2);
+ d->m_Layout->addWidget(d->m_HintLabel, 3, 0, 1, 2);
+ d->m_Layout->addWidget(d->m_FixedParameterBox, 4, 0, 1, 2);
+ d->m_Layout->addWidget(d->m_SelectedPlanarFiguresText, 5, 0, 1, 2);
+ d->m_Layout->addWidget(d->m_CopyToClipboard, 6, 0, 1, 2);
d->m_Parent->setLayout(d->m_Layout);
this->CreateConnections();
this->AddAllInteractors();
// placed after CreateConnections to trigger update of the current selection
d->m_SingleNodeSelectionWidget->SetAutoSelectNewNodes(true);
}
void QmitkMeasurementView::CreateConnections()
{
connect(d->m_SingleNodeSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged,
this, &QmitkMeasurementView::OnCurrentSelectionChanged);
connect(d->m_DrawLine, SIGNAL(triggered(bool)), this, SLOT(OnDrawLineTriggered(bool)));
connect(d->m_DrawPath, SIGNAL(triggered(bool)), this, SLOT(OnDrawPathTriggered(bool)));
connect(d->m_DrawAngle, SIGNAL(triggered(bool)), this, SLOT(OnDrawAngleTriggered(bool)));
connect(d->m_DrawFourPointAngle, SIGNAL(triggered(bool)), this, SLOT(OnDrawFourPointAngleTriggered(bool)));
connect(d->m_DrawCircle, SIGNAL(triggered(bool)), this, SLOT(OnDrawCircleTriggered(bool)));
connect(d->m_DrawEllipse, SIGNAL(triggered(bool)), this, SLOT(OnDrawEllipseTriggered(bool)));
connect(d->m_DrawDoubleEllipse, SIGNAL(triggered(bool)), this, SLOT(OnDrawDoubleEllipseTriggered(bool)));
connect(d->m_DrawRectangle, SIGNAL(triggered(bool)), this, SLOT(OnDrawRectangleTriggered(bool)));
connect(d->m_DrawPolygon, SIGNAL(triggered(bool)), this, SLOT(OnDrawPolygonTriggered(bool)));
connect(d->m_DrawBezierCurve, SIGNAL(triggered(bool)), this, SLOT(OnDrawBezierCurveTriggered(bool)));
connect(d->m_DrawSubdivisionPolygon, SIGNAL(triggered(bool)), this, SLOT(OnDrawSubdivisionPolygonTriggered(bool)));
connect(d->m_CopyToClipboard, SIGNAL(clicked(bool)), this, SLOT(OnCopyToClipboard(bool)));
connect(d->m_Radius, QOverload<double>::of(&ctkDoubleSpinBox::valueChanged), d->m_Thickness, &ctkDoubleSpinBox::setMaximum);
}
void QmitkMeasurementView::OnCurrentSelectionChanged(QList<mitk::DataNode::Pointer> nodes)
{
if (nodes.empty() || nodes.front().IsNull())
{
d->m_SelectedImageNode = nullptr;
d->m_DrawActionsToolBar->setEnabled(false);
d->m_FixedParameterBox->setEnabled(false);
}
else
{
d->m_SelectedImageNode = nodes.front();
d->m_DrawActionsToolBar->setEnabled(true);
d->m_FixedParameterBox->setEnabled(true);
}
}
void QmitkMeasurementView::NodeAdded(const mitk::DataNode* node)
{
// add observer for selection in renderwindow
mitk::PlanarFigure::Pointer planarFigure = dynamic_cast<mitk::PlanarFigure*>(node->GetData());
auto isPositionMarker = false;
node->GetBoolProperty("isContourMarker", isPositionMarker);
if (planarFigure.IsNotNull() && !isPositionMarker)
{
auto nonConstNode = const_cast<mitk::DataNode*>(node);
mitk::PlanarFigureInteractor::Pointer interactor = dynamic_cast<mitk::PlanarFigureInteractor*>(node->GetDataInteractor().GetPointer());
if (interactor.IsNull())
{
interactor = mitk::PlanarFigureInteractor::New();
auto planarFigureModule = us::ModuleRegistry::GetModule("MitkPlanarFigure");
interactor->LoadStateMachine("PlanarFigureInteraction.xml", planarFigureModule);
interactor->SetEventConfig("PlanarFigureConfig.xml", planarFigureModule);
}
interactor->SetDataNode(nonConstNode);
QmitkPlanarFigureData data;
data.m_Figure = planarFigure;
typedef itk::SimpleMemberCommand<QmitkMeasurementView> SimpleCommandType;
typedef itk::MemberCommand<QmitkMeasurementView> MemberCommandType;
// add observer for event when figure has been placed
auto initializationCommand = SimpleCommandType::New();
initializationCommand->SetCallbackFunction(this, &QmitkMeasurementView::PlanarFigureInitialized);
data.m_EndPlacementObserverTag = planarFigure->AddObserver(mitk::EndPlacementPlanarFigureEvent(), initializationCommand);
// add observer for event when figure is picked (selected)
auto selectCommand = MemberCommandType::New();
selectCommand->SetCallbackFunction(this, &QmitkMeasurementView::PlanarFigureSelected);
data.m_SelectObserverTag = planarFigure->AddObserver(mitk::SelectPlanarFigureEvent(), selectCommand);
// add observer for event during interaction when a point is moved
auto duringInteractionCommand = SimpleCommandType::New();
duringInteractionCommand->SetCallbackFunction(this, &QmitkMeasurementView::UpdateMeasurementText);
data.m_DuringInteractionObserverTag = planarFigure->AddObserver(mitk::PointMovedPlanarFigureEvent(), duringInteractionCommand);
// adding to the map of tracked planarfigures
d->m_DataNodeToPlanarFigureData[nonConstNode] = data;
}
}
void QmitkMeasurementView::NodeChanged(const mitk::DataNode* node)
{
auto it = std::find(d->m_CurrentSelection.begin(), d->m_CurrentSelection.end(), node);
if (it != d->m_CurrentSelection.end())
{
this->UpdateMeasurementText();
}
}
void QmitkMeasurementView::NodeRemoved(const mitk::DataNode* node)
{
auto nonConstNode = const_cast<mitk::DataNode*>(node);
auto it = d->m_DataNodeToPlanarFigureData.find(nonConstNode);
auto isFigureFinished = false;
auto isPlaced = false;
if (it != d->m_DataNodeToPlanarFigureData.end())
{
QmitkPlanarFigureData& data = it->second;
data.m_Figure->RemoveObserver(data.m_EndPlacementObserverTag);
data.m_Figure->RemoveObserver(data.m_SelectObserverTag);
data.m_Figure->RemoveObserver(data.m_StartInteractionObserverTag);
data.m_Figure->RemoveObserver(data.m_EndInteractionObserverTag);
data.m_Figure->RemoveObserver(data.m_DuringInteractionObserverTag);
isFigureFinished = data.m_Figure->GetPropertyList()->GetBoolProperty("initiallyplaced", isPlaced);
if (!isFigureFinished) // if the property does not yet exist or is false, drop the datanode
this->PlanarFigureInitialized(); // normally called when a figure is finished, to reset all buttons
d->m_DataNodeToPlanarFigureData.erase( it );
}
if (nonConstNode != nullptr)
nonConstNode->SetDataInteractor(nullptr);
auto isPlanarFigure = mitk::TNodePredicateDataType<mitk::PlanarFigure>::New();
auto nodes = this->GetDataStorage()->GetDerivations(node, isPlanarFigure);
for (unsigned int x = 0; x < nodes->size(); ++x)
{
mitk::PlanarFigure::Pointer planarFigure = dynamic_cast<mitk::PlanarFigure*>(nodes->at(x)->GetData());
if (planarFigure.IsNotNull())
{
isFigureFinished = planarFigure->GetPropertyList()->GetBoolProperty("initiallyplaced",isPlaced);
if (!isFigureFinished) // if the property does not yet exist or is false, drop the datanode
{
this->GetDataStorage()->Remove(nodes->at(x));
if (!d->m_DataNodeToPlanarFigureData.empty())
{
it = d->m_DataNodeToPlanarFigureData.find(nodes->at(x));
if (it != d->m_DataNodeToPlanarFigureData.end())
{
d->m_DataNodeToPlanarFigureData.erase(it);
this->PlanarFigureInitialized(); // normally called when a figure is finished, to reset all buttons
}
}
}
}
}
}
void QmitkMeasurementView::PlanarFigureSelected(itk::Object* object, const itk::EventObject&)
{
d->m_CurrentSelection.clear();
auto lambda = [&object](const std::pair<mitk::DataNode::Pointer, QmitkPlanarFigureData>& element)
{
return element.second.m_Figure == object;
};
auto it = std::find_if(d->m_DataNodeToPlanarFigureData.begin(), d->m_DataNodeToPlanarFigureData.end(), lambda);
if (it != d->m_DataNodeToPlanarFigureData.end())
{
d->m_CurrentSelection.push_back(it->first);
}
this->UpdateMeasurementText();
this->RequestRenderWindowUpdate();
}
void QmitkMeasurementView::PlanarFigureInitialized()
{
d->m_UnintializedPlanarFigure = false;
d->m_DrawActionsToolBar->setEnabled(true);
d->m_DrawLine->setChecked(false);
d->m_DrawPath->setChecked(false);
d->m_DrawAngle->setChecked(false);
d->m_DrawFourPointAngle->setChecked(false);
d->m_DrawCircle->setChecked(false);
d->m_DrawEllipse->setChecked(false);
d->m_DrawDoubleEllipse->setChecked(false);
d->m_DrawRectangle->setChecked(false);
d->m_DrawPolygon->setChecked(false);
d->m_DrawBezierCurve->setChecked(false);
d->m_DrawSubdivisionPolygon->setChecked(false);
+
+ d->m_DrawLabel->hide();
+ d->m_HintLabel->hide();
}
void QmitkMeasurementView::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList<mitk::DataNode::Pointer>& nodes)
{
d->m_CurrentSelection = nodes;
this->UpdateMeasurementText();
// bug 16600: deselecting all planarfigures by clicking on datamanager when no node is selected
if (d->m_CurrentSelection.size() == 0)
{
auto isPlanarFigure = mitk::TNodePredicateDataType<mitk::PlanarFigure>::New();
auto planarFigures = this->GetDataStorage()->GetSubset(isPlanarFigure);
// setting all planar figures which are not helper objects not selected
for (mitk::DataStorage::SetOfObjects::ConstIterator it = planarFigures->Begin(); it != planarFigures->End(); ++it)
{
auto node = it.Value();
auto isHelperObject = false;
node->GetBoolProperty("helper object", isHelperObject);
if (!isHelperObject)
node->SetSelected(false);
}
}
for (int i = d->m_CurrentSelection.size() - 1; i >= 0; --i)
{
auto node = d->m_CurrentSelection[i];
mitk::PlanarFigure::Pointer planarFigure = dynamic_cast<mitk::PlanarFigure*>(node->GetData());
// the last selected planar figure
if (planarFigure.IsNotNull() && planarFigure->GetPlaneGeometry())
{
auto planarFigureInitializedWindow = false;
auto linkedRenderWindow = dynamic_cast<mitk::ILinkedRenderWindowPart*>(this->GetRenderWindowPart());
QmitkRenderWindow* selectedRenderWindow;
if (!linkedRenderWindow)
return;
auto axialRenderWindow = linkedRenderWindow->GetQmitkRenderWindow("axial");
auto sagittalRenderWindow = linkedRenderWindow->GetQmitkRenderWindow("sagittal");
auto coronalRenderWindow = linkedRenderWindow->GetQmitkRenderWindow("coronal");
auto threeDimRenderWindow = linkedRenderWindow->GetQmitkRenderWindow("3d");
if (node->GetBoolProperty("planarFigureInitializedWindow", planarFigureInitializedWindow, axialRenderWindow->GetRenderer()))
{
selectedRenderWindow = axialRenderWindow;
}
else if (node->GetBoolProperty("planarFigureInitializedWindow", planarFigureInitializedWindow, sagittalRenderWindow->GetRenderer()))
{
selectedRenderWindow = sagittalRenderWindow;
}
else if (node->GetBoolProperty("planarFigureInitializedWindow", planarFigureInitializedWindow, coronalRenderWindow->GetRenderer()))
{
selectedRenderWindow = coronalRenderWindow;
}
else if (node->GetBoolProperty("planarFigureInitializedWindow", planarFigureInitializedWindow, threeDimRenderWindow->GetRenderer()))
{
selectedRenderWindow = threeDimRenderWindow;
}
else
{
selectedRenderWindow = nullptr;
}
auto planeGeometry = dynamic_cast<const mitk::PlaneGeometry*>(planarFigure->GetPlaneGeometry());
auto normal = planeGeometry->GetNormalVnl();
mitk::PlaneGeometry::ConstPointer axialPlane = axialRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry();
auto axialNormal = axialPlane->GetNormalVnl();
mitk::PlaneGeometry::ConstPointer sagittalPlane = sagittalRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry();
auto sagittalNormal = sagittalPlane->GetNormalVnl();
mitk::PlaneGeometry::ConstPointer coronalPlane = coronalRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry();
auto coronalNormal = coronalPlane->GetNormalVnl();
normal[0] = fabs(normal[0]);
normal[1] = fabs(normal[1]);
normal[2] = fabs(normal[2]);
axialNormal[0] = fabs(axialNormal[0]);
axialNormal[1] = fabs(axialNormal[1]);
axialNormal[2] = fabs(axialNormal[2]);
sagittalNormal[0] = fabs(sagittalNormal[0]);
sagittalNormal[1] = fabs(sagittalNormal[1]);
sagittalNormal[2] = fabs(sagittalNormal[2]);
coronalNormal[0] = fabs(coronalNormal[0]);
coronalNormal[1] = fabs(coronalNormal[1]);
coronalNormal[2] = fabs(coronalNormal[2]);
auto ang1 = angle(normal, axialNormal);
auto ang2 = angle(normal, sagittalNormal);
auto ang3 = angle(normal, coronalNormal);
if (ang1 < ang2 && ang1 < ang3)
{
selectedRenderWindow = axialRenderWindow;
}
else
{
if (ang2 < ang3)
{
selectedRenderWindow = sagittalRenderWindow;
}
else
{
selectedRenderWindow = coronalRenderWindow;
}
}
// re-orient view
if (selectedRenderWindow)
selectedRenderWindow->GetSliceNavigationController()->ReorientSlices(planeGeometry->GetOrigin(), planeGeometry->GetNormal());
}
break;
}
this->RequestRenderWindowUpdate();
}
void QmitkMeasurementView::OnDrawLineTriggered(bool)
{
this->AddFigureToDataStorage(
mitk::PlanarLine::New(),
QString("Line%1").arg(++d->m_LineCounter));
}
void QmitkMeasurementView::OnDrawPathTriggered(bool)
{
mitk::CoreServicePointer<mitk::IPropertyFilters> propertyFilters(mitk::CoreServices::GetPropertyFilters());
mitk::PropertyFilter filter;
filter.AddEntry("ClosedPlanarPolygon", mitk::PropertyFilter::Blacklist);
propertyFilters->AddFilter(filter, "PlanarPolygon");
mitk::PlanarPolygon::Pointer planarFigure = mitk::PlanarPolygon::New();
planarFigure->ClosedOff();
auto node = this->AddFigureToDataStorage(
planarFigure,
QString("Path%1").arg(++d->m_PathCounter));
node->SetProperty("ClosedPlanarPolygon", mitk::BoolProperty::New(false));
node->SetProperty("planarfigure.isextendable", mitk::BoolProperty::New(true));
+
+ d->m_HintLabel->show();
}
void QmitkMeasurementView::OnDrawAngleTriggered(bool)
{
this->AddFigureToDataStorage(
mitk::PlanarAngle::New(),
QString("Angle%1").arg(++d->m_AngleCounter));
}
void QmitkMeasurementView::OnDrawFourPointAngleTriggered(bool)
{
this->AddFigureToDataStorage(
mitk::PlanarFourPointAngle::New(),
QString("Four Point Angle%1").arg(++d->m_FourPointAngleCounter));
}
void QmitkMeasurementView::OnDrawCircleTriggered(bool)
{
auto circle = (d->m_FixedParameterBox->isChecked()) ? mitk::PlanarCircle::New(d->m_Radius->value()) : mitk::PlanarCircle::New();
this->AddFigureToDataStorage(circle, QString("Circle%1").arg(++d->m_CircleCounter));
}
void QmitkMeasurementView::OnDrawEllipseTriggered(bool)
{
this->AddFigureToDataStorage(
mitk::PlanarEllipse::New(),
QString("Ellipse%1").arg(++d->m_EllipseCounter));
}
void QmitkMeasurementView::OnDrawDoubleEllipseTriggered(bool)
{
auto ellipse = (d->m_FixedParameterBox->isChecked()) ? mitk::PlanarDoubleEllipse::New(d->m_Radius->value(),d->m_Thickness->value()) : mitk::PlanarDoubleEllipse::New();
this->AddFigureToDataStorage(ellipse, QString("DoubleEllipse%1").arg(++d->m_DoubleEllipseCounter));
}
void QmitkMeasurementView::OnDrawBezierCurveTriggered(bool)
{
this->AddFigureToDataStorage(
mitk::PlanarBezierCurve::New(),
QString("BezierCurve%1").arg(++d->m_BezierCurveCounter));
+
+ d->m_HintLabel->show();
}
void QmitkMeasurementView::OnDrawSubdivisionPolygonTriggered(bool)
{
this->AddFigureToDataStorage(
mitk::PlanarSubdivisionPolygon::New(),
QString("SubdivisionPolygon%1").arg(++d->m_SubdivisionPolygonCounter));
+
+ d->m_HintLabel->show();
}
void QmitkMeasurementView::OnDrawRectangleTriggered(bool)
{
this->AddFigureToDataStorage(
mitk::PlanarRectangle::New(),
QString("Rectangle%1").arg(++d->m_RectangleCounter));
}
void QmitkMeasurementView::OnDrawPolygonTriggered(bool)
{
auto planarFigure = mitk::PlanarPolygon::New();
planarFigure->ClosedOn();
auto node = this->AddFigureToDataStorage(
planarFigure,
QString("Polygon%1").arg(++d->m_PolygonCounter));
node->SetProperty("planarfigure.isextendable", mitk::BoolProperty::New(true));
+
+ d->m_HintLabel->show();
}
void QmitkMeasurementView::OnCopyToClipboard(bool)
{
QApplication::clipboard()->setText(d->m_SelectedPlanarFiguresText->toPlainText(), QClipboard::Clipboard);
}
mitk::DataNode::Pointer QmitkMeasurementView::AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name)
{
auto newNode = mitk::DataNode::New();
newNode->SetName(name.toStdString());
newNode->SetData(figure);
newNode->SetSelected(true);
if (d->m_SelectedImageNode.IsNotNull())
{
this->GetDataStorage()->Add(newNode, d->m_SelectedImageNode);
}
else
{
this->GetDataStorage()->Add(newNode);
}
for (auto &node : d->m_CurrentSelection)
node->SetSelected(false);
d->m_CurrentSelection.clear();
d->m_CurrentSelection.push_back(newNode);
this->UpdateMeasurementText();
d->m_DrawActionsToolBar->setEnabled(false);
d->m_UnintializedPlanarFigure = true;
+ d->m_DrawLabel->show();
return newNode;
}
void QmitkMeasurementView::UpdateMeasurementText()
{
d->m_SelectedPlanarFiguresText->clear();
QString infoText;
QString plainInfoText;
int j = 1;
mitk::PlanarFigure::Pointer planarFigure;
mitk::PlanarAngle::Pointer planarAngle;
mitk::PlanarFourPointAngle::Pointer planarFourPointAngle;
mitk::DataNode::Pointer node;
for (int i = 0; i < d->m_CurrentSelection.size(); ++i, ++j)
{
plainInfoText.clear();
node = d->m_CurrentSelection[i];
planarFigure = dynamic_cast<mitk::PlanarFigure*>(node->GetData());
if (planarFigure.IsNull())
continue;
if (j > 1)
infoText.append("<br />");
infoText.append(QString("<b>%1</b><hr />").arg(QString::fromStdString(node->GetName())));
plainInfoText.append(QString("%1").arg(QString::fromStdString(node->GetName())));
planarAngle = dynamic_cast<mitk::PlanarAngle*> (planarFigure.GetPointer());
if (planarAngle.IsNull())
planarFourPointAngle = dynamic_cast<mitk::PlanarFourPointAngle*> (planarFigure.GetPointer());
double featureQuantity = 0.0;
for (unsigned int k = 0; k < planarFigure->GetNumberOfFeatures(); ++k)
{
if (!planarFigure->IsFeatureActive(k))
continue;
featureQuantity = planarFigure->GetQuantity(k);
if ((planarAngle.IsNotNull() && k == planarAngle->FEATURE_ID_ANGLE) || (planarFourPointAngle.IsNotNull() && k == planarFourPointAngle->FEATURE_ID_ANGLE))
featureQuantity = featureQuantity * 180 / vnl_math::pi;
infoText.append(QString("<i>%1</i>: %2 %3")
.arg(QString(planarFigure->GetFeatureName(k)))
.arg(featureQuantity, 0, 'f', 2)
.arg(QString(planarFigure->GetFeatureUnit(k))));
plainInfoText.append(QString("\n%1: %2 %3")
.arg(QString(planarFigure->GetFeatureName(k)))
.arg(featureQuantity, 0, 'f', 2)
.arg(QString(planarFigure->GetFeatureUnit(k))));
if (k + 1 != planarFigure->GetNumberOfFeatures())
infoText.append("<br />");
}
if (j != d->m_CurrentSelection.size())
infoText.append("<br />");
}
d->m_SelectedPlanarFiguresText->setHtml(infoText);
}
void QmitkMeasurementView::AddAllInteractors()
{
auto planarFigures = this->GetAllPlanarFigures();
for (auto it = planarFigures->Begin(); it != planarFigures->End(); ++it)
this->NodeAdded(it.Value());
}
mitk::DataStorage::SetOfObjects::ConstPointer QmitkMeasurementView::GetAllPlanarFigures() const
{
auto isPlanarFigure = mitk::TNodePredicateDataType<mitk::PlanarFigure>::New();
auto isNotHelperObject = mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(false));
auto isNotHelperButPlanarFigure = mitk::NodePredicateAnd::New( isPlanarFigure, isNotHelperObject );
return this->GetDataStorage()->GetSubset(isPlanarFigure);
}
diff --git a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkScreenshotMaker.cpp b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkScreenshotMaker.cpp
index 81fdc308ee..b10c469f8d 100644
--- a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkScreenshotMaker.cpp
+++ b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkScreenshotMaker.cpp
@@ -1,512 +1,508 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkScreenshotMaker.h"
//#include "QmitkMovieMakerControls.h"
#include "QmitkStepperAdapter.h"
#include "mitkVtkPropRenderer.h"
#include <QmitkRenderWindow.h>
#include <QmitkRenderWindowWidget.h>
#include <iostream>
#include <vtkRenderer.h>
#include <vtkCamera.h>
#include <QAction>
#include <QFileDialog>
#include <QTimer>
#include <QDateTime>
#include <QSpinBox>
#include <QComboBox>
#include <QColor>
#include <QColorDialog>
#include <QApplication>
#include "vtkImageWriter.h"
#include "vtkJPEGWriter.h"
#include "vtkPNGWriter.h"
#include "vtkRenderLargeImage.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkTestUtilities.h"
#include <vtkActor.h>
#include "vtkMitkRenderProp.h"
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include "vtkRenderWindowInteractor.h"
#include <qradiobutton.h>
#include "mitkSliceNavigationController.h"
#include "mitkPlanarFigure.h"
#include <mitkWorkbenchUtil.h>
#include <mitkImage.h>
#include <itksys/SystemTools.hxx>
QmitkScreenshotMaker::QmitkScreenshotMaker(QObject *parent, const char * /*name*/)
: QmitkAbstractView(),
m_Controls(nullptr),
m_BackgroundColor(QColor(0,0,0)),
m_SelectedNode(nullptr)
{
parentWidget = parent;
}
QmitkScreenshotMaker::~QmitkScreenshotMaker()
{
}
void QmitkScreenshotMaker::CreateConnections()
{
if (m_Controls)
{
connect((QObject*) m_Controls->m_AllViews, SIGNAL(clicked()), (QObject*) this, SLOT(GenerateMultiplanar3DHighresScreenshot()));
connect((QObject*) m_Controls->m_Shot, SIGNAL(clicked()), (QObject*) this, SLOT(GenerateMultiplanarScreenshots()));
connect((QObject*) m_Controls->m_BackgroundColor, SIGNAL(clicked()), (QObject*) this, SLOT(SelectBackgroundColor()));
connect((QObject*) m_Controls->btnScreenshot, SIGNAL(clicked()), this, SLOT(GenerateScreenshot()));
connect((QObject*) m_Controls->m_HRScreenshot, SIGNAL(clicked()), this, SLOT(Generate3DHighresScreenshot()));
QString styleSheet = "background-color:rgb(0,0,0)";
m_Controls->m_BackgroundColor->setStyleSheet(styleSheet);
}
}
mitk::DataNode::Pointer QmitkScreenshotMaker::GetTopLayerNode()
{
mitk::DataNode::Pointer out = nullptr;
int layer = -1;
auto nodes = GetDataStorage()->GetAll();
for (auto node = nodes->begin(); node!=nodes->end(); ++node)
{
if (!(*node)->IsVisible(nullptr))
continue;
int current_layer;
(*node)->GetIntProperty("layer", current_layer);
if (current_layer>layer)
{
out = (*node);
layer = current_layer;
}
}
return out;
}
void QmitkScreenshotMaker::MultichannelScreenshot(mitk::VtkPropRenderer* renderer, QString fileName, QString filter)
{
auto node = GetTopLayerNode();
if (node.IsNotNull() && dynamic_cast<mitk::Image*>(node->GetData()))
{
auto image = dynamic_cast<mitk::Image*>(node->GetData());
std::string fname = itksys::SystemTools::GetFilenamePath(fileName.toStdString()) + "/" + itksys::SystemTools::GetFilenameWithoutExtension(fileName.toStdString());
std::string ext = itksys::SystemTools::GetFilenameExtension(fileName.toStdString());
mitk::PixelType chPixelType = image->GetImageDescriptor()->GetChannelTypeById(0);
if (image->GetDimension() == 4)
{
MITK_INFO << "LOOPING THROUGH FOURTH DIMENSION IS NOT IMPLEMENTED";
}
else if (chPixelType.GetNumberOfComponents()>1)
{
for(int unsigned c=0; c<chPixelType.GetNumberOfComponents(); ++c)
{
node->SetProperty("Image.Displayed Component", mitk::IntProperty::New(c));
this->TakeScreenshot(renderer->GetVtkRenderer(), 1, QString(fname.c_str()) + "_" + QString::number(c) + QString(ext.c_str()), filter);
}
}
else
this->TakeScreenshot(renderer->GetVtkRenderer(), 1, fileName, filter);
}
else
this->TakeScreenshot(renderer->GetVtkRenderer(), 1, fileName, filter);
}
void QmitkScreenshotMaker::GenerateScreenshot()
{
if (m_LastFile.size()==0)
m_LastFile = QDir::currentPath()+"/screenshot.png";
QString filter;
QString fileName = QFileDialog::getSaveFileName(nullptr, "Save screenshot to...", m_LastFile, m_PNGExtension + ";;" + m_JPGExtension, &filter);
if (fileName.size()>0)
m_LastFile = fileName;
auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN);
auto* renderer = renderWindowPart->GetQmitkRenderWindow(m_Controls->m_DirectionBox->currentText())->GetRenderer();
if (renderer == nullptr)
return;
if (m_Controls->m_AllChannelsBox->isChecked())
MultichannelScreenshot(renderer, fileName, filter);
else
this->TakeScreenshot(renderer->GetVtkRenderer(), 1, fileName, filter);
}
void QmitkScreenshotMaker::GenerateMultiplanarScreenshots()
{
if (m_LastPath.size()==0)
m_LastPath = QDir::currentPath();
QString filePath = QFileDialog::getExistingDirectory(nullptr, "Save screenshots to...", m_LastPath);
if (filePath.size()>0)
m_LastPath = filePath;
if( filePath.isEmpty() )
{
return;
}
//emit StartBlockControls();
auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN);
renderWindowPart->EnableDecorations(false, QStringList{mitk::IRenderWindowPart::DECORATION_CORNER_ANNOTATION});
auto windowCount = m_Controls->m_DirectionBox->count();
for (int i = 0; i < windowCount; ++i)
{
auto windowName = m_Controls->m_DirectionBox->itemText(i);
QString fileName = QDir::separator() + windowName + QString::fromStdString(".png");
int c = 1;
while (QFile::exists(filePath + fileName))
{
fileName = QDir::separator() + windowName + QString::fromStdString("_");
fileName += QString::number(c);
fileName += ".png";
c++;
}
vtkRenderer* renderer = renderWindowPart->GetQmitkRenderWindow(windowName)->GetRenderer()->GetVtkRenderer();
if (renderer != nullptr)
{
if (m_Controls->m_AllChannelsBox->isChecked())
MultichannelScreenshot(renderWindowPart->GetQmitkRenderWindow(windowName)->GetRenderer(), filePath + fileName, m_PNGExtension);
else
this->TakeScreenshot(renderer, 1, filePath + fileName);
}
}
/// TODO I do not find a simple way of doing this through the render window part API,
/// however, I am also not convinced that this code is needed at all. The colour
/// of the crosshair planes is never set to any colour other than these.
/// I suggest a new 'mitk::DataNode* mitk::ILinkedRendererPart::GetSlicingPlane(const std::string& name) const'
/// function to introduce that could return the individual ("axial", "sagittal" or
/// "coronal" crosshair planes.
// mitk::DataNode* n = renderWindowPart->GetSlicingPlane("axial");
// if (n)
// {
// n->SetProperty( "color", mitk::ColorProperty::New( 1,0,0 ) );
// }
//
// n = renderWindowPart->GetSlicingPlane("sagittal");
// if (n)
// {
// n->SetProperty( "color", mitk::ColorProperty::New( 0,1,0 ) );
// }
//
// n = renderWindowPart->GetSlicingPlane("coronal");
// if (n)
// {
// n->SetProperty( "color", mitk::ColorProperty::New( 0,0,1 ) );
// }
renderWindowPart->EnableDecorations(true, QStringList{mitk::IRenderWindowPart::DECORATION_CORNER_ANNOTATION});
}
void QmitkScreenshotMaker::Generate3DHighresScreenshot()
{
if (m_LastFile.size()==0)
m_LastFile = QDir::currentPath()+"/3D_screenshot.png";
QString filter;
QString fileName = QFileDialog::getSaveFileName(nullptr, "Save screenshot to...", m_LastFile, m_PNGExtension + ";;" + m_JPGExtension, &filter);
if (fileName.size()>0)
m_LastFile = fileName;
GenerateHR3DAtlasScreenshots(fileName, filter);
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkScreenshotMaker::GenerateMultiplanar3DHighresScreenshot()
{
if (m_LastPath.size()==0)
m_LastPath = QDir::currentPath();
QString filePath = QFileDialog::getExistingDirectory( nullptr, "Save screenshots to...", m_LastPath);
if (filePath.size()>0)
m_LastPath = filePath;
if( filePath.isEmpty() )
{
return;
}
QString fileName = "/3D_View1.png";
int c = 1;
while (QFile::exists(filePath+fileName))
{
fileName = QString("/3D_View1_");
fileName += QString::number(c);
fileName += ".png";
c++;
}
GetCam()->Azimuth( -7.5 );
GetCam()->Roll(-4);
GenerateHR3DAtlasScreenshots(filePath+fileName);
GetCam()->Roll(4);
fileName = "/3D_View2.png";
c = 1;
while (QFile::exists(filePath+fileName))
{
fileName = QString("/3D_View2_");
fileName += QString::number(c);
fileName += ".png";
c++;
}
GetCam()->Azimuth( 90 );
GetCam()->Elevation( 4 );
GenerateHR3DAtlasScreenshots(filePath+fileName);
fileName = "/3D_View3.png";
c = 1;
while (QFile::exists(filePath+fileName))
{
fileName = QString("/3D_View3_");
fileName += QString::number(c);
fileName += ".png";
c++;
}
GetCam()->Elevation( 90 );
GetCam()->Roll( -2.5 );
GenerateHR3DAtlasScreenshots(filePath+fileName);
GetCam()->Roll( 2.5 );
// not negative because direction of elevation has flipped
GetCam()->Elevation( 94 );
GetCam()->Azimuth( -82.5 );
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkScreenshotMaker::GenerateHR3DAtlasScreenshots(QString fileName, QString filter)
{
// only works correctly for 3D RenderWindow
auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN);
auto* renderer = renderWindowPart->GetQmitkRenderWindow("3d")->GetRenderer()->GetVtkRenderer();
if (nullptr != renderer)
- {
- renderWindowPart->EnableDecorations(false);
this->TakeScreenshot(renderer, this->m_Controls->m_MagFactor->text().toFloat(), fileName, filter);
- renderWindowPart->EnableDecorations(true);
- }
}
vtkCamera* QmitkScreenshotMaker::GetCam()
{
auto* renderer = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("3d")->GetRenderer();
vtkCamera* cam = nullptr;
const mitk::VtkPropRenderer *propRenderer = dynamic_cast<const mitk::VtkPropRenderer * >( renderer );
if (propRenderer)
{
// get vtk renderer
vtkRenderer* vtkrenderer = propRenderer->GetVtkRenderer();
if (vtkrenderer)
{
// get vtk camera
vtkCamera* vtkcam = vtkrenderer->GetActiveCamera();
if (vtkcam)
{
// vtk smart pointer handling
cam = vtkcam;
cam->Register( nullptr );
}
}
}
return cam;
}
void QmitkScreenshotMaker::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList<mitk::DataNode::Pointer>& nodes)
{
if(nodes.size())
m_SelectedNode = nodes[0];
}
void QmitkScreenshotMaker::UpdateDirectionBox(mitk::IRenderWindowPart* renderWindowPart)
{
m_Controls->m_DirectionBox->clear();
auto renderWindows = renderWindowPart->GetQmitkRenderWindows();
bool has3DWindow = false;
for (auto name : renderWindows.keys())
{
auto window = renderWindows[name];
if (window->GetRenderer()->GetMapperID() == mitk::BaseRenderer::Standard3D)
{
has3DWindow = true;
}
else
{
auto windowName = name;
auto renderWindowWidget = dynamic_cast<QmitkRenderWindowWidget*>(window->parentWidget());
if (renderWindowWidget)
{
windowName = QString::fromStdString(renderWindowWidget->GetCornerAnnotationText());
}
m_Controls->m_DirectionBox->insertItem(m_Controls->m_DirectionBox->count() + 1, windowName);
}
}
renderWindows = renderWindowPart->GetQmitkRenderWindows();
m_Controls->groupBox->setEnabled(has3DWindow);
}
void QmitkScreenshotMaker::CreateQtPartControl(QWidget *parent)
{
if (!m_Controls)
{
m_Parent = parent;
m_Controls = new Ui::QmitkScreenshotMakerControls;
m_Controls->setupUi(parent);
auto renderWindowPart = this->GetRenderWindowPart();
if (renderWindowPart)
{
this->UpdateDirectionBox(renderWindowPart);
}
// Initialize "Selected Window" combo box
const mitk::RenderingManager::RenderWindowVector rwv =
mitk::RenderingManager::GetInstance()->GetAllRegisteredRenderWindows();
}
this->CreateConnections();
}
void QmitkScreenshotMaker::SetFocus()
{
m_Controls->btnScreenshot->setFocus();
}
void QmitkScreenshotMaker::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart)
{
m_Parent->setEnabled(true);
this->UpdateDirectionBox(renderWindowPart);
}
void QmitkScreenshotMaker::RenderWindowPartInputChanged(mitk::IRenderWindowPart* renderWindowPart)
{
this->UpdateDirectionBox(renderWindowPart);
}
void QmitkScreenshotMaker::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/)
{
m_Parent->setEnabled(false);
m_Controls->m_DirectionBox->clear();
}
void QmitkScreenshotMaker::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);
//magnifier->Update();
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);
// QColor color = QColorDialog::getColor();
double bgcolor[] = {m_BackgroundColor.red()/255.0, m_BackgroundColor.green()/255.0, m_BackgroundColor.blue()/255.0};
renderer->SetBackground(bgcolor);
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 QmitkScreenshotMaker::SelectBackgroundColor()
{
m_BackgroundColor = QColorDialog::getColor();
m_Controls->m_BackgroundColor->setAutoFillBackground(true);
QString styleSheet = "background-color:rgb(";
styleSheet.append(QString::number(m_BackgroundColor.red()));
styleSheet.append(",");
styleSheet.append(QString::number(m_BackgroundColor.green()));
styleSheet.append(",");
styleSheet.append(QString::number(m_BackgroundColor.blue()));
styleSheet.append(")");
m_Controls->m_BackgroundColor->setStyleSheet(styleSheet);
}
diff --git a/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/documentation/UserManual/QmitkMxNMultiWidgetEditor.dox b/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/documentation/UserManual/QmitkMxNMultiWidgetEditor.dox
index f826235ca1..d6b5c15000 100644
--- a/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/documentation/UserManual/QmitkMxNMultiWidgetEditor.dox
+++ b/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/documentation/UserManual/QmitkMxNMultiWidgetEditor.dox
@@ -1,150 +1,150 @@
/**
\page org_mitk_editors_mxnmultiwidget The MxN Display
\imageMacro{QmitkMxNMultiWidgetEditor.svg,"Icon of the MxN Display",16.00}
\tableofcontents
\section org_mitk_editors_mxnmultiwidget_F1Help General remark on F1 help
This is the help window of the MxN display. To display the help windows of other plugins, press F1 when the plugin of interest is active
(please make sure the tab of the plugin of interest is highlighted).
Alternatively, go to the <i>Help</i> menu and select <i>Open Help Perspective</i>. A help window opens from which you can select the documentation of the plugin of interest.
\section org_mitk_editors_mxnmultiwidget_Overview Overview
The MxN display is an alternative image viewer to the Standard display.
It is a more flexible, customizable approach to displaying images with a strong focus on displaying different images
simultaneously but independently.
Unlike than the Standard Display it allows to show a different image for each window view while also allowing independent image navigation.
The number of window views can be freely chosen and anatomical view planes / view directions can be changed for each window view.
\section org_mitk_editors_mxnmultiwidget_Working Working with the MxN display
The following sections describe how to work with the MxN display. The MxN display can be customized and several
properties can be set to modify the behavior.
\subsection org_mitk_editors_mxnmultiwidget_Configuration Configuration Toolbar
The MxNDisplay is initialized with a single window view and a configuration toolbar on the right side.
In the toolbar you can find three icons / actions, where two buttons have an on/off state.
<table style="border: none;">
<tr><td>\imageMacro{QtWidgets/resource/mwLayout.png, "Layout button", 16.00}
<tr><td>\imageMacro{QtWidgets/resource/mwSynchronized.png, "Synchronize button", 16.00}
<td>\imageMacro{QtWidgets/resource/mwDesynchronized.png, "Desynchronize button", 16.00}
<tr><td>\imageMacro{QtWidgets/resource/mwMITK.png, "MITK mode button", 16.00}
<td>\imageMacro{QtWidgets/resource/mwPACS.png, "PACS mode button", 16.00}
</table>
<ul>
<li> Layout button: Choose a layout from presets or select a custom number of available window views and their layout in a grid
<li> Synchronization button: Enable or disable synchronized interaction across the window views
- <li> Interaction mode button: Switch between the MITK interaction mouse mode and the PACS interation mouse mode
+ <li> Interaction mode button: Switch between the MITK interaction mouse mode and the PACS interaction mouse mode
</ul>
\subsubsection org_mitk_editors_mxnmultiwidget_Layout MxN display layout
There are several options to customize the MxN display layout.
Using the layout button from the configuration toolbar allows to customize the layout the following way:
<ul>
<li> By choosing the desired amount of windows and confirming via "Set multi widget layout". The window views are arranged in a rectangular grid, as specified.
Depending on the selected number of window views, existing window views will be removed or additional window views will be added.
New window views will have a sagittal view direction. Window views that stay untouched will keep their settings and continue displaying their selected data.
<li> By selecting one of the provided layout presets.
<li> By saving / loading a custom layout, either created using the previous methods and further refinement or by handcrafting a suitable file.
</ul>
\imageMacro{layoutWidget.png, "Layout widget" , 16.00}
By moving the cursor to the upper right corner of any window you can activate the window menu. It consists of three buttons:
<ul>
<li> The crosshair button allows to toggle the crosshair and to reset the view.
<li> The middle button expands the corresponding window to fullscreen.
<li> The right button allows you to choose between many different arrangements of the available windows.
</ul>
\imageMacro{layoutArrangements.png, "Layout arrangement", 16.00}
\subsubsection org_mitk_editors_mxnmultiwidget_Synchronization Window synchronization
If data is displayed inside a window view you can interact with the window / loaded image using your mouse.
As long as the interaction synchronization is disabled (<img src="mwDesynchronized.png">), mouse interactions,
such as zooming, setting the point of interest, mouse wheel scrolling etc. will affect only the window view you click in.
If the interaction synchronization is enabled (<img src="mwSynchronized.png">), performing any of the mouse interactions inside a window view
will perform the same interaction in any other visible window view.
\subsubsection org_mitk_editors_mxnmultiwidget_Interaction_Mode Mouse interaction mode
As mentioned before, you can use the mouse to interact with each window / loaded image.
There are two interaction modes which can be switched between using the interaction mode button:
the MITK interaction mouse mode and the PACS interaction mouse mode.
If the PACS interaction mouse mode is selected, an interaction tool bar is added to the left side of the
MxN display: It allows to select the action to perform for the left mouse button.
The difference between the MITK and PACS mode is as follows:
MITK interaction mouse mode <img src="mwMITK.png">
<ul>
<li> left mouse button: setting the point of interest
<li> right mouse button: zooming in and out while moving the mouse
<li> middle mouse button / pressing mouse wheel: moving the image while moving the mouse
<li> mouse wheel: scrolling through the displayed image slices
</ul>
PACS interaction mouse mode <img src="mwPACS.png">
<ul>
<li> left mouse button: depending on the selected mode in the interaction tool bar
<li> right mouse button: changing the level window for the topmost visible image under the mouse cursor
<li> middle mouse button / clicking mouse wheel: no interaction
<li> mouse wheel: scrolling through the displayed image slices
<li> strg-key + right mouse button: zooming in and out while moving the mouse
<li> shif-key + right mouse button: moving the image while moving the mouse
</ul>
\subsection org_mitk_editors_mxnmultiwidget_Utility Utility Menu
Each window view has a utility menu at the top with four UI elements that allow to perform the following actions:
<ul>
<li> Data selection button: Select which data should be selected and visible inside the window view
<li> Lock / unlock button: Set the lock state of a window to couple / decouple the window from other windows.
<li> Slice slider: Navigate along the view direction of this window
<li> View direction drop down menu: Select the current anatomical view plane / view direction
</ul>
\imageMacro{utilityMenu.png, "Utility menu", 8.00}
\subsubsection org_mitk_editors_mxnmultiwidget_Data_Selection Data node selection
Using the data selection button of a window view you can open a data node selection dialog which shows the currently
selected data nodes for the window view.
"Currently selected" means that you can either decide to show all available data nodes from the data storage
or - for a better data node overview - only show an individually selected subset of the available data nodes.
You can either remove a node from the selection by clicking the "x" on a data node or use the "Change selection"
button to individually select data nodes.
The "Select all nodes" checkbox denotes which of the selection types is currently used - showing all available data nodes
or showing only a subset of data nodes. Checking the checkbox will reset the selection to all available data nodes.
Selecting data nodes does not mean that this node always has to be visible in the window view (rendered). If you want to hide
a node, e.g. for temporarily inspecting underlying images, you can do so by clicking the "eye" icon for each selected data node.
If you want to reset the window view to a specific node / image, you can use the "arrow" icon. This will reset the camera to focus
on the selected node, using default zoom and centered camera position.
If you want so reset the camera to focus on the whole scene / all selected nodes, you can use the window menu, see
\ref org_mitk_editors_mxnmultiwidget_Layout.
Resetting a window only affects the corresponding window view, regardless of any synchronization / lock state.
\subsubsection org_mitk_editors_mxnmultiwidget_Lock_Button Lock button
So far showing / hiding nodes or removing them from a selection / changing a selection affected all window views.
If you want to use individual selections for a window view, you can unlock / decouple the window view from other window views
by clicking on the lock button in the utility menu.
This will decouple the current window view. Changing the visibility of a node or selecting individual nodes / removing nodes
using the data node selection button will affect only the current window view.
This allows to show different images in each window view, e.g. by decoupling multiple window views and selecting individual nodes
in each window.
It is still possible to have a synchronized mouse interaction, see \ref org_mitk_editors_mxnmultiwidget_Synchronization and
\ref org_mitk_editors_mxnmultiwidget_Interaction_Mode.
\subsubsection org_mitk_editors_mxnmultiwidget_Slice_Slider Slice selection slider
Displayed data is typically 3D data but each window view shows a 2D slice of the 3D volume. The direction in which the
3D volume is sliced is defined by the "view direction" (or "anatomical plane").
To define at which position in the view direction the 3D volume should be sliced, the slice selection slider can be used.
It can either be moved using the mouse or by using the appropriate mouse interaction (see above, e.g. mouse wheel scroling).
\subsubsection org_mitk_editors_mxnmultiwidget_View_Direction View direction
- Each window view "is looking in" one of the three avaiable view directions, namely "Axial", "Sagittal" or "Coronal".
+ Each window view "is looking in" one of the three available view directions, namely "Axial", "Sagittal" or "Coronal".
You can change the view direction by switching to another anatomical plane in the view direction drop down menu.
Changing the view direction will reset the camera to its default for the new view direction, meaning default zoom and
centered camera position.
*/
diff --git a/Plugins/org.mitk.gui.qt.pixelvalue/src/internal/QmitkPixelValueView.cpp b/Plugins/org.mitk.gui.qt.pixelvalue/src/internal/QmitkPixelValueView.cpp
index 6c76d46de3..c1bcdfc1e6 100644
--- a/Plugins/org.mitk.gui.qt.pixelvalue/src/internal/QmitkPixelValueView.cpp
+++ b/Plugins/org.mitk.gui.qt.pixelvalue/src/internal/QmitkPixelValueView.cpp
@@ -1,192 +1,200 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkPixelValueView.h"
+#include <mitkNodePredicateAnd.h>
#include <mitkNodePredicateDataType.h>
+#include <mitkNodePredicateNot.h>
#include <mitkImage.h>
#include <mitkCompositePixelValueToString.h>
#include <mitkPixelTypeMultiplex.h>
#include <mitkImagePixelReadAccessor.h>
#include <ui_QmitkPixelValueView.h>
const std::string QmitkPixelValueView::VIEW_ID = "org.mitk.views.pixelvalue";
QmitkPixelValueView::QmitkPixelValueView(QObject*)
: m_Ui(new Ui::QmitkPixelValueView)
{
}
QmitkPixelValueView::~QmitkPixelValueView()
{
}
void QmitkPixelValueView::CreateQtPartControl(QWidget* parent)
{
m_Ui->setupUi(parent);
this->m_SliceNavigationListener.RenderWindowPartActivated(this->GetRenderWindowPart());
connect(&m_SliceNavigationListener, &QmitkSliceNavigationListener::SelectedPositionChanged, this, &QmitkPixelValueView::OnSelectedPositionChanged);
connect(&m_SliceNavigationListener, &QmitkSliceNavigationListener::SelectedTimePointChanged, this, &QmitkPixelValueView::OnSelectedTimePointChanged);
this->Update();
}
void QmitkPixelValueView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart)
{
this->m_SliceNavigationListener.RenderWindowPartActivated(renderWindowPart);
}
void QmitkPixelValueView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart)
{
this->m_SliceNavigationListener.RenderWindowPartDeactivated(renderWindowPart);
}
void QmitkPixelValueView::RenderWindowPartInputChanged(mitk::IRenderWindowPart* renderWindowPart)
{
this->m_SliceNavigationListener.RenderWindowPartInputChanged(renderWindowPart);
}
void QmitkPixelValueView::OnSelectedPositionChanged(const mitk::Point3D&)
{
this->Update();
}
void QmitkPixelValueView::OnSelectedTimePointChanged(const mitk::TimePointType&)
{
this->Update();
}
void QmitkPixelValueView::NodeChanged(const mitk::DataNode*)
{
this->Update();
}
void QmitkPixelValueView::Update()
{
const auto position = m_SliceNavigationListener.GetCurrentSelectedPosition();
const auto timePoint = m_SliceNavigationListener.GetCurrentSelectedTimePoint();
- auto isImageData = mitk::TNodePredicateDataType<mitk::Image>::New();
- auto nodes = GetDataStorage()->GetSubset(isImageData);
+ auto isImage = mitk::TNodePredicateDataType<mitk::Image>::New();
+ auto isSegmentation = mitk::NodePredicateDataType::New("LabelSetImage");
+
+ auto predicate = mitk::NodePredicateAnd::New();
+ predicate->AddPredicate(mitk::NodePredicateNot::New(isSegmentation));
+ predicate->AddPredicate(isImage);
+
+ auto nodes = GetDataStorage()->GetSubset(predicate);
if (nodes.IsNull())
{
this->Clear();
return;
}
mitk::Image::Pointer image;
mitk::DataNode::Pointer topParentNode;
int component = 0;
auto node = mitk::FindTopmostVisibleNode(nodes, position, timePoint, nullptr);
if (node.IsNull())
{
this->Clear();
return;
}
bool isBinary = false;
node->GetBoolProperty("binary", isBinary);
if (isBinary)
{
auto parentNodes = GetDataStorage()->GetSources(node, nullptr, true);
if (!parentNodes->empty())
topParentNode = FindTopmostVisibleNode(nodes, position, timePoint, nullptr);
if (topParentNode.IsNotNull())
{
image = dynamic_cast<mitk::Image*>(topParentNode->GetData());
topParentNode->GetIntProperty("Image.Displayed Component", component);
}
else
{
image = dynamic_cast<mitk::Image*>(node->GetData());
node->GetIntProperty("Image.Displayed Component", component);
}
}
else
{
image = dynamic_cast<mitk::Image*>(node->GetData());
node->GetIntProperty("Image.Displayed Component", component);
}
if (image.IsNotNull())
{
m_Ui->imageNameLineEdit->setText(QString::fromStdString(node->GetName()));
itk::Index<3> index;
image->GetGeometry()->WorldToIndex(position, index);
auto pixelType = image->GetChannelDescriptor().GetPixelType().GetPixelType();
if (pixelType == itk::IOPixelEnum::RGB || pixelType == itk::IOPixelEnum::RGBA)
{
m_Ui->pixelValueLineEdit->setText(QString::fromStdString(mitk::ConvertCompositePixelValueToString(image, index)));
return;
}
if (pixelType == itk::IOPixelEnum::DIFFUSIONTENSOR3D || pixelType == itk::IOPixelEnum::SYMMETRICSECONDRANKTENSOR)
{
m_Ui->pixelValueLineEdit->setText(QStringLiteral("See ODF Details view."));
return;
}
mitk::ScalarType pixelValue = 0.0;
mitkPixelTypeMultiplex5(
mitk::FastSinglePixelAccess,
image->GetChannelDescriptor().GetPixelType(),
image,
image->GetVolumeData(image->GetTimeGeometry()->TimePointToTimeStep(timePoint)),
index,
pixelValue,
component);
std::ostringstream stream;
stream.imbue(std::locale::classic());
stream.precision(2);
if (fabs(pixelValue) > 1000000 || fabs(pixelValue) < 0.01)
{
stream << std::scientific;
}
else
{
stream << std::fixed;
}
stream << pixelValue;
m_Ui->pixelValueLineEdit->setText(QString::fromStdString(stream.str()));
}
else
{
this->Clear();
}
}
void QmitkPixelValueView::Clear()
{
m_Ui->imageNameLineEdit->clear();
m_Ui->pixelValueLineEdit->clear();
}
void QmitkPixelValueView::SetFocus()
{
}
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 594bc6d5fb..16a151964a 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox
+++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox
@@ -1,491 +1,520 @@
/**
\page org_mitk_views_segmentation The Segmentation View
\imageMacro{segmentation-dox.svg,"Icon of the Segmentation View",2.00}
\tableofcontents
\section org_mitk_views_segmentationoverview Overview
Segmentation is the act of separating an image into foreground and background subsets by manual or automated delineation, while the foreground is defined to be part of the segmentation.
Such a segmented image subset is also called a label as it typically labels a specific region of interest.
A multilabel segmentation may contain multiple labels organized in distinct groups.
You can create multiple labels for different regions of interest contained within a single segmentation image.
Labels in the same group cannot overlap each other but labels from different groups may overlap.
The MITK <b>Segmentation Plugin</b> allows you to create multilabel segmentations of anatomical and pathological structures in medical images.
The plugin consists of three views:
<ul>
<li> <b>Segmentation View</b>: Manual and (semi-)automatic segmentation
<li> \subpage org_mitk_views_segmentationutilities : Post-processing of segmentations
<li> \subpage org_mitk_views_segmentationtasklist : Optimized workflow for batches of segmentation tasks based on a user-defined task list
</ul>
In this user guide, the features of the <b>Segmentation View</b> are described.
For an introduction to the Segmentation Utilities or Segmentation Task List, refer to the respective user guides.
\imageMacro{QmitkSegmentationPlugin_Overview.png,"Segmentation View", 16.00}
\section org_mitk_views_segmentationtechnicalissues Image and segmentation prerequisites
The Segmentation View has a few prerequisites regarding segmentations and their reference image:
<ul>
<li> Images must be two or three-dimensional and may be either static or dynamic, e.g., are time-resolved resp. have different pixel values for different time steps.
<li> Images must be single-valued, i.e. CT, MRI or 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 must be congruent to their reference images.
</ul>
\section org_mitk_views_segmentationdataselection Image selection and creating new segmentations
To select a reference image for a new segmentation, click on the <i>Image</i> widget in the Data selection section at the very top of the Segmentation View.
Choose an image from the displayed list of Data Manager images.
Once an image is selected, a new segmentation for this reference image can be created by clicking the button right next to the <i>Segmentation</i> widget in the Data selection section.
A new multilabel segmentation with an initial, empty label is automatically generated if not set otherwise in the preferences.
The new segmentation will be added to the Data Manager as a child node of its reference image node.
It is automatically selected and can be edited in the Segmentation View right away.
Instead of creating a new segmentation, an existing segmentation can be selected and edited as well.
The selection list of existing segmentations for a certain reference image consists of matching/congruent segmentations only.
\imageMacro{"QmitkSegmentation_DataSelection.png","Data selection and creating new segmentations",12}
\section org_mitk_views_segmentationgroups Groups
Segmentation images consist of at least a single group called "Group 0" in which the first default label is created.
More groups can be added and removed but there will always be at least a single group.
Labels of the same group cannot overlap each other.
Labels of different groups may overlap each other.
For example, you could segment the whole heart as "Heart" label in "Group 0", add "Group 1" and create multiple labels of the anatomical details of the heart in that group.
Naturally, all these labels lie within the extents of the "Heart" label of "Group 0" but in principle they are completely independent of "Group 0".
Some pixels are now labelled twice, e.g., as "Heart" and "Left ventricle".
Since the labels of "Group 1" cannot overlap each other, it is impossible to accidentally label a pixel as both "Left ventricle" and "Right ventricle".
If you would like to segment even more details you could create "Group 2" to have up to three labels per pixel.
Nevertheless, groups are technically a flat data structure and cannot contain nested groups.
It is all about possibly overlapping labels from distinct groups and spatially exclusive, non-overlapping labels within the same group.
\imageMacro{"QmitkSegmentation_Groups.png","Groups",10}
\section org_mitk_views_segmentationlabelinstances Labels vs. label instances
The Segmentation View supports label instances.
That is, segmenting multiple distributed entities of the same thing like metastases for example.
A label, as we used the term before, is already a single instance of itself but it may consist of multiple label instances.
If a label consists of multiple label instances, they each show their own distinct pixel value in square brackets as a clue for distinction and identification.
-It is important to understand that this number is not a separate, consequtive index for each label.
+It is important to understand that this number is not a separate, consecutive index for each label.
It is just the plain pixel value of the label instance, which is unique across all label instances of the whole segmentation.
\imageMacro{"QmitkSegmentation_LabelInstances.png","Label instances",10}
\section org_mitk_views_segmentationlock_color_visibility Unlocking, changing color of, and hiding label instances
Label instances are locked by default: label instances from the same group cannot accidentally override pixels from other label instances.
Locked label instances behave like cookie cutters for other label instances of the same group.
You can unlock label instances to remove that protection from other label instances of the same group.
Their pixel contents can then be overridden by other label instances of the same group.
Remember that label instances from distinct groups do not interact with each other.
They can always overlap (not override) each other.
You can also change the color of label instances as well as show (default) or hide their pixel contents.
The icons at the right side of the rows of the groups and labels widget reflect their state in all these regards.
Renaming of labels and label instances can be found in their content menu as shown further below.
\imageMacro{"QmitkSegmentation_LockColorVisibility.png","Unlocking\, changing color of\, and hiding label instances",10}
+\section org_mitk_views_segmentationlabelhighlighting Label highlighting
+
+Especially if you have segmentations with many label instances or the label names are not telling, it can be nontrivial to identify the label instance in the label inspector of the segmentation view. To mitigate this problem MITK uses label highlighting in the render windows. As long as you hover with the mouse cursor over a group, label or label instance, the respective label instances will be highlighted in the render windows. Highlighted labels will visually pop out by being shown with full opacity (no transparency) while the opacity of all non-highlighted labels of the same segmentation will be reduced to 30% of the current opacity value (they become very transparent).
+
+By default, label instances that are set to be invisible are not shown while highlighted. By pressing the shift key, one can enforce also invisible label instances to be shown while highlighting.
+
+Remark: the highlighting is supported in all views that use the label inspector (e.g. also the segmentation utilities).
+
\section org_mitk_views_segmentationcontextmenus Context menus
Actions for organization of groups, labels, and label instances (as well as other operations) can be also found in their right-click context menus.
\imageMacro{"QmitkSegmentation_ContextMenus.png","Context menus of groups\, labels\, and label instances",12}
Most actions available in these context menus are self-explanatory or were already described above by other means of access like the tool button bar for adding and removing groups, labels, and label instances.
Labels and label instances can be <b>renamed</b>, while groups have fixed names.
Note that renaming a label instance will make a separate label out of it, since all instances of the same label share a single common name.
<b>Clear content</b> only clears the pixels of a label instance but won't delete the actual label instance.
-Groups can be <b>locked</b> and <b>unlocked</b> as a whole from their context menu, while label instances can be directly locked and unlocked outside the context menu as decribed further below.
+Groups can be <b>locked</b> and <b>unlocked</b> as a whole from their context menu, while label instances can be directly locked and unlocked outside the context menu as described further below.
\section org_mitk_views_segmentationlabelsuggestions Label name and color suggestions
When renaming label instances or creating new label instances with enforced manual naming in the Segmentation preferences, entering names is supported by auto-completion for common label names.
The list of predefined label names and colors for the auto-completion feature can be either extended or replaced by a custom list of label name and color suggestions.
This custom list must be specified as a JSON file, just containing an array of objects, each with a mandatory "name" string and an optional "color" string.
The JSON file can be set in the Segmentation preferences as well as a few options on how to apply these suggestions.
\section org_mitk_views_segmentationlabelpresets Saving and loading label set presets
Label set presets are useful to share a certain style or scheme between different segmentation sessions or to provide templates for new segmentation sessions.
The properties of all label instances in all groups like their names, colors, and visibilities are saved as a label set preset by clicking on the 'Save label set preset' button.
Label set presets are applied to any segmentation session by clicking on the 'Load label set preset' button.
If a label instance for a certain value already exists, its properties are overridden by the preset.
If a label instance for a certain value does not yet exist, an empty label instance with the label properties of the preset is created.
The actual pixel contents of label instances are unaffected as label set presets only store label properties.
\imageMacro{QmitkSegmentation_Preset.png,"Saving and loading label set presets", 10.00}
If you work on a repetitive segmentation task, manually loading the same label set preset for each and every new segmentation can be tedious.
To streamline your workflow, you can set a default label set preset in the Segmentation preferences (Ctrl+P). When set, this label set preset will be applied to all new segmentations instead of creating the default red "Label 1" label instance.
If you work on a repetitive segmentation task, manually loading the same label set preset for each and every new segmentation can be tedious.
To streamline your workflow, you can set a default label set preset in the Segmentation preferences (Ctrl+P). When set, this label set preset will be applied to all new segmentations instead of creating the default red "Label 1" label instance.
\section org_mitk_views_segmentationpreferences Preferences
The Segmentation Plugin offers a number of preferences which can be set via the MITK Workbench application preferences (Ctrl+P):
\imageMacro{QmitkSegmentationPreferences.png,"Segmentation preferences", 10.00}
<ul>
<li> <b>Compact view:</b> Hide the tool button texts to save some space on screen (6 instead of 4 buttons per row)
<li> <b>2D display:</b> Draw segmentations as as outlines or transparent overlays
<li> <b>Data node selection mode:</b> Hide everything but the selected segmentation and its reference image
<li> <b>Default label set preset:</b> Start a new segmentation with this preset instead of a default label
<li> <b>Label creation:</b> Assign default names and colors to new label instances or ask users for name and color
<li> <b>Label suggestions:</b> Specify custom suggestions for label names and colors
</ul>
\section org_mitk_views_segmentationtooloverview 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_segmentation2dsegmentation 2D segmentation tools
With 2D manual contouring you define which voxels are part of the segmentation and which 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.
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.).
All of the editing tools work by the same principle: using the mouse (left button) to click anywhere in a 2D window (any of the orientations axial, sagittal, or coronal),
moving the mouse while holding the mouse button and releasing the button to finish the editing action.
Multi-step undo and redo is fully supported by all editing tools by using the application-wide undo / redo buttons in the toolbar.
<i>Remark</i>: Clicking and moving the mouse in any of the 2D render windows will move the crosshair that defines what part of the image is displayed.
This behavior is disabled as long as 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_segmentationaddsubtracttools 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_segmentationlassotool Lasso tool
\imageMacro{QmitkSegmentation_Lasso.png,"Lasso tool",7.70}
The tool is a more advanced version of the add/subtract tool. It offers you the following features:
<ol>
<li> Generating a polygon segmentation (click left mouse button to set ancor point)
<li> Freehand contouring (like the add tool; press left mouse button while moving the mouse)
<li> Move ancor points (select an ancor point, press left mouse button while moving the mouse)
<li> Add new ancor points (press CTRL while click left mouse to add an ancor to the contour)
<li> Delete an ancor point (press Del while ancor point is selected)
<li> Segmentation can be added to the label (Add mode) or subtracted (Subtract mode)
</ol>
To start a segmentation double left click where the first ancor point should be. To end the segmentation double left click where the last ancor point should be.
Please note that:
<ul>
<li> feature 3-6 are only available, if auto confirm is *not* activated
<li> feature 3-5 is not available for freehand contour segments
</ul>
\subsection org_mitk_views_segmentationpaintwipetools 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_segmentationregiongrowingtool 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 allows 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_Leakage.png,"Leakage correction feature of the region growing tool",11.28}
\endif
\subsection org_mitk_views_segmentationfilltool Fill tool
\imageMacro{QmitkSegmentation_IMGIconFill.png,"Fill tool",3.81}
Left-click inside a region/segmentation to flood fill all connected pixels that have the same label with the active label. This tool will only work on regions of unlocked labels or on regions that are not labeled at all.
\subsection org_mitk_views_segmentationerasetool 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 segmented regions (left-click on specific segmentation) or to clear a whole slice at once (left-click at the non-labeled background).
+This tool removes a connected part of pixels that form a segmentation. You may use it to remove single segmented regions by left-click on specific segmentation region.
This tool will only work and regions of unlocked labels or on regions of the active label.
\subsection org_mitk_views_segmentationclosetool Close tool
\imageMacro{QmitkSegmentation_IMGIconClose.png,"Close tool",3.79}
Left-click inside the region/segmentation to fill all "holes" (pixels labelled with another label or no label) inside the region.
Therefore this tool behaves like a local closing operation. This tool will not work, when a non-labeled region is selected and holes of locked labels will not be filled.
\remark This tool always uses the label of the selected region (even if this label is not the active label). Therefore you can use this tool on regions of the active label and of none locked labels (without the need to change the active label).
\subsection org_mitk_views_segmentationlivewiretool 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}
The tool handling is the same like the Lasso tool (see for more info), except it generates live wire contours instead of straight lines.
\subsection org_mitk_views_segmentationSegmentAnything Segment Anything Tool
\imageMacro{QmitkSegmentation_nnUnetTool.png,"Segment Anything tool",10.00}
\imageMacro{QmitkSegmentation_SAMTool.png,"Segment Anything tool",10.00}
The Segment Anything Tool is a deep learning-based interactive segmentation tool. Originally created by MetaAI, MITK presents this model for medical image segmentation tasks.
The tool requires that you have Python 3 installed and available on your machine. Note: On Debian/Ubuntu systems, you need to install git, python3-pip, python3-venv package using the following command: `apt install git python3-pip python3-venv`. For best experience, your machine should be ideally equipped with a CUDA-enabled GPU.
For a detailed explanation of what this algorithm is able to, please refer to https://ai.facebook.com/research/publications/segment-anything/ <br>
Any adjustments to the \subpage org_mitk_editors_stdmultiwidget_Levelwindow setting impacts the segmentation. However, any applied color maps are ignored.
\subsubsection org_mitk_views_segmentationSegmentAnythingWorkflow Workflow:
-# Install Segment Anything: Goto Preferences (Ctrl+P) > Segment Anything and click "Install Segment Anything with MedSAM" to install Segment Anything (version: 1.0).
The installation process implicitly creates a python virtual environment using the Python located in "System Python" in an MITK mainitained directory. Make sure you have a working internet connection. This might take a while. It is a one time job, though.
Once installed, the "Install Segment Anything" button is grayed out.
\imageMacro{QmitkSegmentation_SAMTool_Preferences.png,"Segment Anything Preferences",10.00}
-# Note: If Python is not listed by MITK in "System Python", click "Select..." in the dropdown to choose an unlisted installation of Python. Note that, while selecting an arbitrary environment folder, only select the base folder, e.g. "/usr/bin/".
No need to navigate all the way into "../usr/bin/python3", for example.
-# Select a specific model type in the "Model Type" dropdown. The default is "vit_b" for low memory footprint. However, more larger models "vit_l" and "vit_h" are also available for selection.
-# Select inference hardware, i.e. any GPU or CPU. This is internally equivalent to setting the <b>CUDA_VISIBLE_DEVICES</b> environment variable.
-# Click "OK" to save the preference settings.
-# Goto Segmentation View > 2D tools > Segment Anything.
-# Click "Initialize Segment Anything" to start the tool backend. This will invoke downloading of the selected model type from the internet. This might take a while. It is a one time job, though.
-# Once the tool is initialized, Press SHIFT+Left Click on any of the 3 render windows to start click guided segmentation on that slice.
-# Press SHIFT+Right Click for negative clicks to adjust the preview mask on the render window.
\subsection org_mitk_views_segmentationMedSAM MedSAM Tool
\imageMacro{QmitkSegmentation_nnUnetTool.png,"MedSAM tool",10.00}
\imageMacro{QmitkSegmentation_MedSAMTool.png,"MedSAM tool",10.00}
The MedSAM (Segment Anything in Medical Images) tool is a specialization of the the Segment Anything (SAM) tool. A new foundation model in the back end is dedicated to universal medical image segmentation.
Just like the Segment Anything tool, the MedSAM tool requires that you have Python 3 installed and available on your machine. Note: On Debian/Ubuntu systems, you need to install the git, python3-pip, and python3-venv packages using the following command: `sudo apt install git python3-pip python3-venv`.
For best experience, your machine should be ideally equipped with a CUDA-enabled GPU.
Any adjustments to the \subpage org_mitk_editors_stdmultiwidget_Levelwindow setting impacts the segmentation. However, any applied color maps are ignored.
\subsubsection org_mitk_views_segmentationMedSAMWorkflow Workflow
-# Install MedSAM: Goto Preferences (Ctrl+P) > Segment Anything and click "Install Segment Anything with MedSAM" to install Segment Anything (version: 1.0) & MedSAM tool backends together.
The installation process implicitly creates a python virtual environment using the Python located in "System Python" in a directory maintained by MITK. Make sure you have a working internet connection, which might take a while. It is a one-time job, though.
Once installed, the "Install Segment Anything with MedSAM" button is grayed out. For details, refer to the Segment Anything tool workflow.
-# Goto Segmentation View > 2D tools > MedSAM.
-# Click "Initialize MedSAM" to start the tool. This will start downloading the model weights from the internet first, if not done before. This might take a while. It is a one-time job, though.
-# Once the tool is initialized, press Shift+Left Click on any of the render windows to create a bounding box for that image slice.
-# Click on the anchor points of the bounding box to adjust the region of interest.
-# Click on Preview to generate segmentation from MedSAM model.
Note: For a detailed explanation of what this algorithm is able to, please refer to https://www.nature.com/articles/s41467-024-44824-z
+\subsection org_mitk_views_segmentationMonaiLabel2D MONAI Label 2D Tool
+
+\imageMacro{QmitkSegmentation_nnUnetTool.png,"MONAILabel2D tool",10.00}
+Read more about the tool here at \subpage org_mitk_views_segmentationMonaiLabel3D
+
\subsection org_mitk_views_segmentationinterpolation 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 undo / redo 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_segmentation3dsegmentation 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_segmentation3dthresholdtool 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_segmentation3dulthresholdTool 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_segmentation3dotsutool 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_segmentation3dgrowcuttool 3D GrowCut tool
The 3D GrowCut tool uses previously created segmentation labels (e.g. by the "Add"-tool) stored in the segmentation layer 0.
The GrowCut tool will use these segmentation labels to create a seedimage that will serve as input to the algorithm.
As an advanced setting option, a Distance penalty can be set, which increases the cohesion in the immediate surroundings of the initial labels.
Based on the seedimage and the Distance penalty, a growing is started, which includes all areas that are not initially assigned to a specific label.
During this process, initially unassigned areas are assigned to the best fitting labels.
After the segmentation process, the user can decide which newly generated labels should be confirmed.
\imageMacro{QmitkSegmentation_3DGrowCutTool.png,"3D GrowCut tool",16.00}
\subsection org_mitk_views_segmentationpickingtool Picking Tool
The Picking tool offers two modes that allow you to manipulate "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.
- Picking mode: Allows you to select certain "islands". When the pick is confirmed, the complete content of the active label will be removed except the pick. This mode is beneficial if you have a lot segmentation noise and want to pick the relevant parts and dismiss the rest. Hint: You can also pick from other labels, but this will only work if these labels are unlocked.
- Relabel mode: Allows you to select certain "islands". When the pick is confirmed, it will be relabeled and added to the active label content. Hint: This mode ignores the locks of other labels, hence you do not need to unlock them explicitly.
\imageMacro{QmitkSegmentation_PickingTool.png,"Picking tool",10.00}
\subsection org_mitk_views_segmentationnnUNetTool nnU-Net Tool (Ubuntu only)
\imageMacro{QmitkSegmentation_nnUnetTool.png,"nnUNet tool",10.00}
This tool provides a GUI to the deep learning-based segmentation algorithm called the nnU-Net v1. 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 nnU-Net v1 (pip) installed. Your machine should be also equipped with a CUDA enabled GPU.
\subsubsection org_mitk_views_segmentationnnUNetToolWorkflow Workflow:
-# 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, e.g. "myenv".
No need to select all the way until "../myenv/bin/python", for example.
-# Click on the "nnUNet Results Folder" directory icon to 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. Note that MITK automatically checks for the
<b>RESULTS_FOLDER</b> environment variable value and, if found, auto parses that directory when the tool is started.
\imageMacro{QmitkSegmentation_nnUNet_Settings.png,"nnUNet Segmentation Settings",10}
-# Choose your required Task-Configuration-Trainer-Planner-Fold parameters, sequentially. By default, all entries are selected inside the "Fold" dropdown (shown: "All").
Note that, even 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 is trained with multi-modal inputs, then "Multi-Modal" checkbox is checked and the no.of modalities are preloaded and shown next to "Required Modalities".
Instantly, as much node selectors with corresponding modality names should appear below to select the Data Manager along including a selector with preselected with the reference node.
Now, select the image nodes in the node selectors accordingly for accurate inferencing.
\imageMacro{QmitkSegmentation_nnUNet_multimodal.png,"nnUNet Multi Modal Settings",10.00}
-# Click on "Preview".
-# In the "Advanced" section, you can also activate other options like "Mixed Precision" and "Enable Mirroring" (for test time data augmentation) pertaining to nnUNet.
\imageMacro{QmitkSegmentation_nnUNet_Advanced.png,"nnUNet Advanced Settings",10.00}
-# Use "Advanced" > "GPU Id" combobox to change the preferred GPU for inferencing. This is internally 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 when Preview is clicked.
-# You may always clear all the cached segmentations by clicking "Clear Cache" button.
\subsubsection org_mitk_views_segmentationnnUNetToolMisc 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.
-# 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.
-# The "Advanced" > "Available Models" lists the available pre-trained tasks for download. Make sure you have internet connection. Then, choose a Task from the dropdown and click the Download button. The pre-trained models for the selected Task
will be downloaded and placed to the <b>RESULTS_FOLDER</b> directory automatically.
-# In the <b>RESULTS_FOLDER</b> directory, inside the trainer-planner folder of every task, MITK keeps a "mitk_export.json" file for fast loading for multi-modal information. It is recommended not to delete this file(s) for a fast responsive UI.
Tip: If multi-modal information shown on MITK is not correct for a given task, you may modify this JSON file and try again.
\subsection org_mitk_views_segmentationTotalSegmentator TotalSegmentator Tool
\imageMacro{QmitkSegmentation_nnUnetTool.png,"TotalSegmentator tool",10.00}
This tool provides a GUI to the deep learning-based segmentation algorithm called the TotalSegmentator (v2). With this tool, you can get a segmentation mask predicted for 117 classes in CT images, loaded in MITK.
For a detailed explanation on tasks and supported classes etc., please refer to https://github.com/wasserth/TotalSegmentator <br>
The tool assumes that you have Python >= 3.9 installed and available on your machine. We recommend to install TotalSegmentator via MITK. The "Install TotalSegmentator" action implicitly creates a python virtual environment in an MITK mainitained
directory. Note: on Debian/Ubuntu systems, you need to install the python3-venv package using the following command: `apt install python3-venv`. For best results, your machine should be ideally equipped with a CUDA-enabled GPU.
\imageMacro{QmitkSegmentation_TotalsegmentatorTool.png, "TotalSegmentator Settings",5}
\subsubsection org_mitk_views_segmentationTotalSegmentatorWorkflow Workflow:
-# Install TotalSegmentator: Click "Install TotalSegmentator" to install TotalSegmentator (version: 2.0.5) in a virtual environment. Make sure you have a working internet connection. This might take a while. It is a one time job, though.
Once installed, the "Install TotalSegmentator" button is grayed out.
-# If Python is not found by MITK goto "Install Options" & select the "System Python Path" drop-down to see if MITK has automatically detected other Python environments.
Click on a fitting Python installation for TotalSegmentator to use or click "Select" in the dropdown to choose an unlisted installation of Python. Note that, while selecting an arbitrary environment folder, only select the base folder, e.g. "/usr/bin/".
No need to navigate all the way into "../usr/bin/python3", for example.
-# Select a specific subtask in the "Tasks" drop-downs. The default is "total" for non-specific total segmentation.
-# Click on "Run TotalSegmentator" for a preview.
-# In the "Advanced" section, you can also activate other options like "Fast" for faster runtime and less memory requirements. Use "Fast" if you only have a CPU for inferencing.
-# Use "Advanced" > "GPU Id" combobox to change the preferred GPU for inferencing. This is internally equivalent to setting the <b>CUDA_VISIBLE_DEVICES</b> environment variable.
-# In case you want to use your own virtual environment containing TotalSegmentator, goto "Install Options" & check "Use Custom Installation" checkbox. Then, select the environment of your choice by using "Custom Env. Path".
+\subsection org_mitk_views_segmentationMonaiLabel3D MONAI Label 3D Tool
+
+\imageMacro{QmitkSegmentation_nnUnetTool.png,"MONAILabel3D tool",10.00}
+
+\imageMacro{QmitkSegmentation_MonaiLabelTool.png,"MONAI Label tool",10.00}
+
+MONAI Label is a server-client system that facilitates interactive medical image annotation by using AI. MONAI Label typically hosts different "Apps", e.g. Radiology or Pathology. Each App hosts a set of pretrained models on pertaining datasets.
+MITK is tested for the Radiology app and only supports auto and click-based "deepgrow" models. For internal reasons, MITK doesn't support "deepedit" and "localization_spine" models specifically.
+The tool requires that you have a URL to a (self-) hosted MONAI Label server.
+For a detailed explanation of what MONAI Label is capable of, please refer to https://docs.monai.io/projects/label/en/latest/. <br>
+Any adjustments to the \subpage org_mitk_editors_stdmultiwidget_Levelwindow setting impacts the segmentation. However, any applied color maps are ignored.
+
+\subsubsection org_mitk_views_segmentationSegmentAnythingWorkflow Workflow:
+ -# Enter your URL in the "MONAI Server URL" box and press the refresh button next to it. This connects MITK to the MONAI Label server and fetches exposed metadata.
+ -# Select the Radiology app e.g. from the "Available Apps" combobox. This will load all supported model names in the "Models" combo box below it.
+ -# From the "Models" box select the model you want to use. Immediately, all supported classes of the model on which the model was trained will be shown below it.
+ -# If an interactive model is selected e.g. "deepgrow_3d", Press and hold SHIFT and left click on any of the 3 render windows to start click guided segmentation.
+ Press SHIFT+Right click for negative clicks to adjust the preview mask on the render window.
+ -# If an auto-segmentation model is selected e.g. "deepedit_seg", simply click "Preview" for generating masks.
+
+
\section org_mitk_views_segmentationpostprocessing 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_segmentationtechnicaldetail 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/QmitkSegmentationTaskList.dox b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationTaskList.dox
index af1669d092..bb31c61acd 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationTaskList.dox
+++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationTaskList.dox
@@ -1,186 +1,186 @@
/**
\page org_mitk_views_segmentationtasklist The Segmentation Task List View
\imageMacro{segmentation_task_list-dox.svg,"Icon of the Segmentation Task List View",2.00}
\tableofcontents
\section org_mitk_views_segmentationtasklist_disclaimer Disclaimer
- The Segmentation Task List View and MITK Segmentation Task Lists in general are still in an <b>experimental</b> stage.
- While we try to minimize breaking changes in the future, we cannot give any guarantees at the moment.
- We strongly advise to ignore the Data Manager regarding loading and unloading of data while using the Segmentation Task List View, as it will completely take over these operations and may run into invalid states otherwise.
\section org_mitk_views_segmentationtasklist_overview Overview
\imageMacro{QmitkSegmentationTaskList.png,"Segmentation Task List View", 12.00}
As the Segmentation Task List View is typically used in combination with the Segmentation View, we suggest to move the Segmentatiom View to the other side of the application (e.g. on top of the Data Manager), to see both views at the same time.
To unlock the Segmentation Task List View, unload everything but a single MITK Segmentation Task List.
The remaining Segmentation Task List will be automatically selected.
The Segmentation Task List View shows the progress of the whole Segmentation Task List, e.g., the number of the tasks marked as <em>done</em> vs. the total number of available tasks.
Below the progress indictator you can navigate between tasks, read their descriptions and related information, as well as load/activate the currently shown task.
This will unload all data from a previously active task, if any, and load all data of the current task.
-To prevent any accidental data loss, unsaved task data will interfer task switches and you can decide on how to proceed.
+To prevent any accidental data loss, unsaved task data will interfere task switches and you can decide on how to proceed.
Above the task description, the status of tasks is displayed as a pair of colored labels, indicating if a task is either active or inactive and if it is considered not to be done, having unsaved changes, or to be done.
With the bottom two buttons you can either save an interim result (the task is considered not to be done), or accept and save the task result.
You can still edit accepted tasks but in contrast to interim results, the task is considered to be done.
\section org_mitk_views_segmentationtasklist_shortcuts Keyboard shortcuts
The Segmentation Task List View can be used even more efficiently with the following set of keyboard shortcuts:
- <tt>Ctrl</tt> + <tt>Alt</tt> + <tt>P</tt>: Navigate to <b>p</b>revious task
- <tt>Ctrl</tt> + <tt>Shift</tt> + <tt>P</tt>: Navigate to <b>p</b>revious undone task (or <tt>Shift</tt> + click on resp. button)
- <tt>Ctrl</tt> + <tt>Alt</tt> + <tt>N</tt>: Navigate to <b>n</b>ext task
- <tt>Ctrl</tt> + <tt>Shift</tt> + <tt>N</tt>: Navigate to <b>n</b>ext undone task (or <tt>Shift</tt> + click on resp. button)
- <tt>Ctrl</tt> + <tt>Alt</tt> + <tt>L</tt>: <b>L</b>oad currently shown task
- <tt>Ctrl</tt> + <tt>Alt</tt> + <tt>S</tt>: <b>S</b>tore interim result
- <tt>Ctrl</tt> + <tt>Alt</tt> + <tt>A</tt>: <b>A</b>ccept task and store result
- <tt>Ctrl</tt> + <tt>F</tt>: <b>F</b>ind task...
\section org_mitk_views_segmentationtasklist_find Finding tasks
\imageMacro{QmitkFindSegmentationTaskDialog.png,"Find Segmentation Task dialog", 12.00}
To find a specific task in a segmentation task list, click on the "Find task..." button (magnifier icon) or press <tt>Ctrl</tt> + <tt>F</tt>.
A dedicated dialog will appear for filtering and searching the current segmentation task list.
Enter a task number and press <tt>Return</tt> to immediately load the according task or filter the shown list of tasks and double click on a result to load the task.
\section org_mitk_views_segmentationtasklist_fileformat MITK Segmentation Task List file format
MITK Segmentation Task List files are JSON files containing a JSON object as root.
It must contain the two mandatory properties <tt>FileFormat</tt> and <tt>Version</tt>:
\code{.json}
{
"FileFormat": "MITK Segmentation Task List",
"Version": 1
}
\endcode
We also recommend to specify an optional <tt>Name</tt> that is used in the application if present instead of the plain filename of the JSON file:
\code{.json}
{
"FileFormat": "MITK Segmentation Task List",
"Version": 1,
"Name": "My First Task List"
}
\endcode
\subsection org_mitk_views_segmentationtasklist_fileformat_tasks Tasks
The root object must also contain a mandatory <tt>Tasks</tt> array, containing JSON objects that specify the individual tasks of the task list.
A minimum task object must contain <tt>Image</tt> and <tt>Result</tt> file paths.
<tt>Image</tt> refers to the patient image and <tt>Result</tt> refers to the path were the resulting segmentation is expected to be stored.
Paths can be absolute or relative to the JSON file.
\code{.json}
{
"FileFormat": "MITK Segmentation Task List",
"Version": 1,
"Tasks": [
{
"Image": "images/Pic3D.nrrd",
"Result": "results/liver.nrrd"
}
]
}
\endcode
In addition, tasks can contain a bunch of optional properties that mainly specify a segmentation a user starts with:
- <tt>Name</tt> (<em>string</em>): A name for the task.
- <tt>Description</tt> (<em>string</em>): A short description/definition of the task.
- <tt>LabelName</tt> (<em>string</em>): The name of the first label in a new segmentation that is created for the task on the fly.
- <tt>LabelNameSuggestions</tt> (<em>file path</em>): A Label Suggestions JSON file (example in next comment) specifying names and optional colors, that are suggested to the user for new labels in the segmentation.
- <tt>Preset</tt> (<em>file path</em>): A Label Set Preset XML file in MITK's .lsetp file format. The preset is applied to a new segmentation that is created for the task on the fly. We recommend to use the Segmentation plugin of the MITK Workbench to create such label set preset files as described in its {key F1} user guide.
- <tt>Segmentation</tt> (<em>file path</em>): A pre-segmentation that a user can start with or has to refine for example.
- <tt>Dynamic</tt> (<em>boolean</em>): In case <tt>Image</tt> refers to a dynamic (3d+t) image, specifies whether the segmentation should be static (<em>false</em>), i.e. equal for all time steps, or dynamic (<em>true</em>), i.e. individual for each time step.
\subsection org_mitk_views_segmentationtasklist_fileformat_taskdefaults Task defaults / common properties
If a task list contains multiple tasks with common properties, they do not have to be specified for each and every task again and again.
Instead, the root object can contain an optional <tt>Defaults</tt> object that is identical in format to the tasks specified above.
As the name indicates, default properties can still be overridden by individual tasks if they are specified explicitly.
There is a single exception, though: A <tt>Defaults</tt> object must not contain a <tt>Result</tt> file path, since result files of tasks must be distinct by definition.
\subsection org_mitk_views_segmentationtasklist_fileformat_example Example
The following example is a complete showcase of the properties and features listed above.
It specifies 4 tasks.
3 tasks refer to the same patient image so it is specified as default.
Remember that the only task property required to be distinct is <tt>Result</tt> so you are pretty free in your task design.
For simplicity, we chose to define tasks around organs for this example and named the tasks accordingly:
\code{.json}
{
"FileFormat": "MITK Segmentation Task List",
"Version": 1,
"Name": "Example Segmentation Task List",
"Defaults": {
"Image": "images/Pic3D.nrrd"
},
"Tasks": [
{
"Name": "Liver",
"LabelName": "Liver",
"LabelNameSuggestions": "suggestions/label_suggestions.json",
"Description": "This task provides an image and label name suggestions for new labels. The segmentation will start with an empty label named Liver.",
"Result": "results/liver.nrrd"
},
{
"Name": "Kidneys",
"Description": "This task provides an image and a label set preset that is applied to the new segmentation.",
"Preset": "presets/kidneys.lsetp",
"Result": "results/kidneys.nrrd"
},
{
"Name": "Spleen",
"Description": "This task provides an image and an initial (pre-)segmentation.",
"Segmentation": "segmentations/spleen.nrrd",
"Result": "results/spleen.nrrd"
},
{
"Name": "Surprise",
"Description": "And now for something completely different. This task overrides the default Image and starts with an empty static segmentation for a dynamic image.",
"Image": "images/US4DCyl.nrrd",
"Result": "results/US4DCyl.nrrd",
"Dynamic": false
}
]
}
\endcode
\section org_mitk_views_segmentationtasklist_labelsuggestions MITK Label Suggestions file format
The Label Suggestions JSON file format mentioned above to specify a list of suggested names and optional colors for new labels is as follows:
\code{.json}
[
{
"name": "Abdomen",
"color": "red"
},
{
"name": "Lung",
"color": "#00ff00"
},
{
"name": "Heart"
},
{
"name": "Aortic Valve",
"color": "CornflowerBlue"
}
]
\endcode
*/
diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox
index 731dd23efb..3c26d47e63 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox
+++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox
@@ -1,99 +1,99 @@
/**
\page org_mitk_views_segmentationutilities The Segmentation Utilities View
\imageMacro{segmentation_utilities-dox.svg,"Icon of the Segmentation Utilities View",5.00}
\imageMacro{QmitkSegmentationUtilities_Overview.png,"The Segmentation Utilities View",16.00}
\tableofcontents
\section org_mitk_views_segmentationUtilitiesManualOverview Overview
The <b>Segmentation Utilities View</b> allows to postprocess existing segmentations. Currently five different operations exist:
<ul>
<li> \ref org_mitk_views_segmentationUtilitiesBooleanOperations
<li> \ref org_mitk_views_segmentationUtilitiesImageMasking
<li> \ref org_mitk_views_segmentationUtilitiesMorphologicalOperations
<li> \ref org_mitk_views_segmentationUtilitiesConvertToSegmentation
<li> \ref org_mitk_views_segmentationUtilitiesExtractFromSegmentation
</ul>
\section org_mitk_views_segmentationUtilitiesDataSelection Data Selection
All postprocessing operations provide one or more selection widgets, which allow to select the data for the operation.
\section org_mitk_views_segmentationUtilitiesBooleanOperations Boolean operations
-Boolean operations allows to perform the following fundamental operations on two segmentations:
+Boolean operations allows to perform the following fundamental operations on two or more segmentations:
<ul>
<li> <b>Difference:</b> Subtracts the second segmentation from the first segmentation.
<li> <b>Intersection:</b> Extracts the overlapping areas of the two selected segmentations.
<li> <b>Union:</b> Combines the two existing segmentations.
</ul>
The selected segmentations must have the same geometry (size, spacing, ...) in order for the operations to work correctly.
The result will be stored in a new data node as a child node of the first selected segmentation.
\imageMacro{QmitkSegmentationUtilities_BooleanOperations.png,"Boolean operations",6.00}
\section org_mitk_views_segmentationUtilitiesImageMasking Image masking
Image masking allows to mask an image with either an existing segmentation or a surface.
The operation requires an image and a segmentation or a surface.
The result will be an image containing only the pixels that are covered by the respective mask.
The default background pixel value is zero.
It can be changed to the minimum existing pixel value of the image or to a custom pixel value.
If the custom pixel value is out of the valid bounds of the pixel type, it is optionally clamped accordingly.
\imageMacro{QmitkSegmentationUtilities_ImageMasking.png,"Image masking",6.00}
\section org_mitk_views_segmentationUtilitiesMorphologicalOperations Morphological operations
Morphological operations are applied to a single segmentation image. Based on a given structuring element the underlying segmentation will be modified.
-The plugin provides a <b>ball</b> and a <b>cross</b> as structuring elements. The follow operations are available:
+The plugin provides a <b>ball</b> and a <b>cross</b> as structuring elements. The following operations are available:
<ul>
<li> <b>Dilation:</b> Each labeled pixel within the segmentation will be dilated based on the selected structuring element.
<li> <b>Erosion:</b> Each labeled pixel within the segmentation will be eroded based on the selected structuring element.
<li> <b>Opening:</b> A dilation followed by an erosion, used for smoothing edges or eliminating small objects.
<li> <b>Closing:</b> An erosion followed by an dilation, used for filling small holes.
<li> <b>Fill Holes:</b> Fills bigger holes within a segmentation.
</ul>
\imageMacro{QmitkSegmentationUtilities_MorphologicalOperations.png,"Morphological operations",6.00}
\section org_mitk_views_segmentationUtilitiesConvertToSegmentation Convert to Segmentation
-Convert to segmentation allows to convert one or multiple input data into a multi-label segmentation. The supported input data types are surfaces, contour models and images. Surfaces and contour models will be converted into a new label (with the same color then the respective input data). Input images will be analyzed for their pixel content. Each pixel value (different to 0; which marks unlabeled pixels) will be associated with the new label.
+Convert to segmentation allows to convert one or multiple input data into a multi-label segmentation. The supported input data types are surfaces, contour models and images. Surfaces and contour models will be converted into a new label (with the same color as the respective input data). Input images will be analyzed for their pixel content. Each pixel value (different to 0; which marks unlabeled pixels) will be associated with the new label.
The conversion can have the following outputs:
<ul>
<li> <b>Convert to new segmentation:</b> The inputs will be converted and integrated in a new segmentation. The geometry of the output segmentation is determined by the largest geometry of the input images. If no images are selected as input, one has to specify an image or segmentation as reference. If sub mode "Convert inputs separately" is selected, each input will be converted into its own multi-label segmentation output.
<li> <b>Add to existing segmentation:</b> The inputs will be converted and integrated as new group(s) in a selected segmentation.
</ul>
For the output the following grouping modes can be selected (if the output is not new separate segmentation):
<ul>
<li> <b>Merge all inputs ...:</b> All inputs will be merged into a new group. Remark: If inputs overlap the inputs might overwrite each other.
<li> <b>New group per input:</b> Each input will be added as a new group to the multi-label segmentation.
</ul>
-<b>Remark on label values:</b> when converting inputs into a multi-labels segmentation the conversion process tries to keep the label value the same like the original value. In cases of surfaces or contour models it would be 1. In case of images it would be the respective pixel value. In cases of the label value collision (the label value is already used in the segmentation) a new value will be assigned to resolve the conflict. Therefore it is only guaranteed that values are kept, if you convert only one input into a new or empty segmentation.
+<b>Remark on label values:</b> when converting inputs into a multi-label segmentation, the conversion process tries to keep the label value the same like the original value. In cases of surfaces or contour models it would be 1. In case of images it would be the respective pixel value. In cases of the label value collision (the label value is already used in the segmentation) a new value will be assigned to resolve the conflict. Therefore it is only guaranteed that values are kept, if you convert only one input into a new or empty segmentation.
-<b>Remark unsupported geometries:</b> if you select images as input, these images have to have either the same geometry or must be a sub geometry. If an reference is defined or you want to add them to an existing segmentation, the imports images also have to have the same geometry like the target or be a sub geometry of it.
+<b>Remark unsupported geometries:</b> if you select images as input, these images have to have either the same geometry or must be a sub geometry. If a reference is defined or you want to add them to an existing segmentation, the input images also have to have the same geometry like the target or be a sub geometry of it.
\section org_mitk_views_segmentationUtilitiesExtractFromSegmentation Extract from Segmentation
-Extract from segmentation allows to extract the content of a multi-label segmentation (either all labeled or a selection of labels) in various ways and stores it as simple images in the data storage.
+Extract from segmentation allows to extract the content of a multi-label segmentation (either all labels or a selection of labels) in various ways and stores it as simple images in the data storage.
The following output options are available (at least one must be selected):
<ul>
- <li> <b>label class map(s):</b> This generates an image where each pixel value (different from 0, which indicates unlabeled pixels) indicates a label class. Label class comprises all label instances that have the same label name. One can also see that has the semantic segmentation result. This mode will generate an output for each group indicated by the selected labels (or all groups).
- <li> <b>Label instance map(s):</b> This generates an image where each pixel value (different from 0, which indicates unlabeled pixels) indicates a label instance. The used pixel values are equivalent to the label value of each instance. This mode will generate an output for each group indicated by the selected labels (or all groups).
- <li> <b>Label instance mask(s):</b> This generates an image for one label instance where each pixel value (different from 0, which indicates unlabeled pixels) indicates the label instance. The used pixel value is equivalent to the label value of the label instance.
+ <li> <b>Label class map(s):</b> This generates an image where each pixel value (different from 0, which indicates unlabeled pixels) indicates a label class. Label classes comprise of all label instances that have the same label name. One can also see this as the semantic segmentation result. This mode will generate an output for each group indicated by the selected labels (or all groups).
+ <li> <b>Label instance map(s):</b> This generates an image where each pixel value (different from 0, which indicates unlabeled pixels) indicates a label instance. The used pixel values are equivalent to the label value of each instance. This mode will generate an output for each group indicated by the selected labels (or all groups). If the segmentation contains only labels with one / no instance(s), the result will be the same as from "Label class map(s)".
+ <li> <b>Label instance mask(s):</b> This generates an image for each selected / all label instance(s), where each pixel value (different from 0, which indicates unlabeled pixels) indicates the label instance. The used pixel value is equivalent to the label value of the label instance.
</ul>
**/
diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_BooleanOperations.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_BooleanOperations.png
index 279d519f73..b3aca4f52e 100644
Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_BooleanOperations.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_BooleanOperations.png differ
diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_ImageMasking.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_ImageMasking.png
index 1a28460a18..c029820e55 100644
Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_ImageMasking.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_ImageMasking.png differ
diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_MorphologicalOperations.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_MorphologicalOperations.png
index 21791ca18e..47821f82ec 100644
Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_MorphologicalOperations.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_MorphologicalOperations.png differ
diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_Overview.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_Overview.png
index 418deb4520..5ffc3341ed 100644
Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_Overview.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_Overview.png differ
diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_MonaiLabelTool.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_MonaiLabelTool.png
new file mode 100644
index 0000000000..fed858a94f
Binary files /dev/null and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_MonaiLabelTool.png differ
diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_Technical.dox b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_Technical.dox
deleted file mode 100644
index d8b04fb961..0000000000
--- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_Technical.dox
+++ /dev/null
@@ -1,98 +0,0 @@
-/**
-
-\page QmitkSegmentationTechnicalPage Technical design of QmitkSegmentation
-
-\li \ref QmitkSegmentationTechnicalPage2
-\li \ref QmitkSegmentationTechnicalPage3
-\li \ref QmitkSegmentationTechnicalPage4
-
-\section QmitkSegmentationTechnicalPage2 Introduction
-
-QmitkSegmentation was designed for the liver resection planning
-project "ReLiver".
-The goal was a stable, well-documented, extensible, and testable
-re-implementation of a functionality called "ERIS", which was used for manual
-segmentation in 2D slices of 3D or 3D+t images.
-Re-implementation was chosen because it seemed to be easier to write
-documentation and tests for newly developed code. In addition, the old code had
-some design weaknesses (e.g. a monolithic class), which would be hard to
-maintain in the future.
-
-By now Segmentation is a well tested and easily extensible vehicle for all kinds of interactive
-segmentation applications. A separate page describes how you can extend Segmentation with new
-tools in a shared object (DLL): \ref toolextensions.
-
-\section QmitkSegmentationTechnicalPage3 Overview of tasks
-
-We identified the following major tasks:
-
-<ol>
-<li> <b>Management of images</b>: what is the original patient image, what
-images are the active segmentations?
-<li> <b>Management of drawing tools</b>: there is a set of drawing tools, one at
-a time is active, that is, someone has to decide which tool will receive mouse
-(and other) events.
-<li> <b>Drawing tools</b>: each tool can modify a segmentation in reaction to
-user interaction. To do so, the tools have to know about the relevant images.
-<li> <b>Slice manipulation</b>: drawing tools need to have means to extract a
-single slice from an image volume and to write a single slice back into an image
-volume.
-<li> <b>Interpolation of unsegmented slices</b>: some class has to keep track of
-all the segmentations in a volume and generate suggestions for missing slices.
-This should be possible in all three orthogonal slice direction.
-<li> <b>Undo</b>: Slice manipulations should be undoable, no matter whether a
-tool or the interpolation mechanism changed something.
-<li> <b>GUI</b>: Integration of everything.
-</ol>
-
-
-\section QmitkSegmentationTechnicalPage4 Classes involved
-
-The above blocks correspond to a number of classes. Here is an overview of all
-related classes with their responsibilities and relations:
-
-\imageMacro{QmitkSegmentation_InteractiveSegmentationClasses.png,"",16.00}
-
-<ol>
-<li> <b>Management of images</b>: mitk::ToolManager has a set of reference
-data (original images) and a second set of working data (segmentations).
-mitk::Tool objects know a ToolManager and can ask the manager for the currently
-relevant images. GUI and non-GUI
-classes are coupled by itk::Events (non-GUI to GUI) and direct method calls (GUI
-to non-GUI).
-<li> <b>Management of drawing tools</b>: As a second task, ToolManager manages all available tools and makes sure that one at a time is able to receive MITK events.
-The GUI for selecting tools is implemented in QmitkToolSelectionBox.
-<li> <b>Drawing tools</b>: Drawing tools all inherit from mitk::Tool, which is a
-mitk::StateMachine. There is a number of derivations from Tool, each offering
-some helper methods for specific sub-classes, like manipulation of 2D slices.
-Tools are instantiated through the itk::ObjectFactory, which means that there is
-also one factory for each tool (e.g. mitk::AddContourToolFactory). For the GUI representation, each tool has an
-identification, consisting of a name and an icon (XPM). The actual drawing
-methods are mainly implemented in mitk::SegTool2D (helper methods) and its
-sub-classes for region growing, freehand drawing, etc.
-<li> <b>Slice manipulation</b>: There are two ways to manipulate slices
-inside a 3D image volume. mitk::ExtractImageFilter retrieves a single 2D slice
-from a 3D volume. mitkVtkImageOverwrite replaces a slice inside a 3D
-volume with a second slice. These classes are used extensively by most of the tools
-to fulfill their task.
-<li> <b>Interpolation of unsegmented slices</b>: There are two classes involved
-in interpolation: mitk::SegmentationInterpolationController knows a mitk::Image (the
-segmentation) and scans its contents for slices with non-zero pixels. It keeps
-track of changes in the image and is always able to tell, which neighbors of a
-slice (in the three orthogonal slice directions) contain segmentations. The
-class also performs this interpolation for single slices on demand.
-Again, we have a second class responsible for the GUI:
-QmitkSlicesInterpolator enables/disables interpolation and offers to
-accept interpolations for one or all slices.
-<li> <b>Undo</b>: Undo functionality is implemented using mitk::DiffSliceOperation.
-mitk::SegTool2D::WriteSliceToVolume stores the original image and the modified slice to the undo stack as an
-mitk::DiffSliceOperation. When the user requests undo, this mitk::DiffSliceOperation will be executed by a singleton
-class DiffSliceOperationApplier. The operation itself observes the image, which it refers to, for itk::DeleteEvent,
-so no undo operation will be executed on/for images that have already been destroyed.
-<li> <b>GUI</b>: The top-level GUI is the view
-QmitkSegmentation, which is very thin in comparison to ERIS. There are
-separate widgets for image and tool selection, for interpolation. Additionally,
-there are some methods to create, delete, crop, load and save segmentations.
-</ol>
-
-**/
diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_ToolExtensionsGeneralOverview.dox b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_ToolExtensionsGeneralOverview.dox
deleted file mode 100644
index 97e995e9bd..0000000000
--- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_ToolExtensionsGeneralOverview.dox
+++ /dev/null
@@ -1,182 +0,0 @@
-/**
-
-\page toolextensions How to extend the Segmentation view with external tools
-
-\warning This documentation is outdated (see bug 19726).
-
-<ul>
-<li> \ref ToolExtensionsGeneralOverview2 </li>
-<li> \ref ToolExtensionsGeneralOverview3 </li>
- <ul>
- <li> \ref ToolExtensionsGeneralOverview31 </li>
- <li> \ref ToolExtensionsGeneralOverview32 </li>
- <li> \ref ToolExtensionsGeneralOverview33 </li>
- </ul>
-<li> \ref ToolExtensionsGeneralOverview4 </li>
-<li> \ref ToolExtensionsGeneralOverview5 </li>
-<li> \ref ToolExtensionsGeneralOverview6 </li>
-</ul>
-
-\section ToolExtensionsGeneralOverview2 Introduction
-
-The application for manual segmentation in MITK (Segmentation view) comes
-with a tool class framework that is extensible with new tools (description at
-\ref QmitkSegmentationTechnicalPage). The usual way
-to create new tools (since it is mostly used inside DKFZ) is to just add new
-files to the MITK source code tree. However, this requires to be familiar with
-the MITK build system and turnaround time during development might be long
-(recompiling parts of MITK again and again).
-
-For external users who just want to use
-MITK as a library and application, there is a way to create new segmentation
-tools in an MITK external project, which will compile the new tools into a
-shared object (DLL). Such shared objects can be loaded via the ITK object factory
-and its autoload feature on application startup. This document describes
-how to build such external extensions.
-
-<b>Example files can be found</b> in the MITK source code in the directory
-<tt>${MITK_SOURCE_DIR}/QApplications/ToolExtensionsExample/</tt>.
-
-\section ToolExtensionsGeneralOverview3 What might be part of an extension
-
-The extension concept assumes that you want to create one or several new
-interactive segmentation tools for Segmentation or another MITK
-view that uses the tools infrastructure. In the result you
-will create a shared object (DLL), which contains several tools and their GUI
-counterparts, plus optional code that your extension requires. The following
-sections shortly describe each of these parts.
-
-\subsection ToolExtensionsGeneralOverview31 Tool classes
-
-A tool is basically any subclass of mitk::Tool. Tools are created at runtime
-through the ITK object factory (so they inherit from itk::Object). Tools
-should handle the interaction part of a segmentation method, i.e. create
-seed points, draw contours, etc., in order to parameterize segmentation algorithms.
-Simple algorithms can even be part of a tool. A tools is identified by icon (XPM format),
-name (short string) and optionally a group name (e.g. the group name for Segmentation
-is "default").
-
-<b>There is a naming convention</b>: you should put a tool called \c mitk::ExternalTool into
-files called \c mitkExternalTool.h and \c mitkExternalTool.cpp. This is \e required if
-you use the convenience macros described below, because there need to be ITK factories,
-which names are directly derived from the file names of the tools. For the example of mitk::ExternalTool
-there would be a factory called \c mitk::ExternalToolFactory in a file named \c mitkExternalToolFactory.cpp.
-
-\subsection ToolExtensionsGeneralOverview32 GUI classes for tools
-
-Tools are non-graphical classes that only implement interactions in renderwindows. However,
-some tools will need a means to allow the user to set some parameters -- a graphical user interface, GUI.
-In the Qt3 case, tool GUIs inherit from QmitkToolGUI, which is a mixture of QWidget and itk::Object.
-Tool GUIs are also created through the ITK object factory.
-
-Tools inform their GUIs about state changes by messages. Tool GUIs communicate with their associated tools
-via direct method calls (they know their tools). See mitk::BinaryThresholdTool for examples.
-
-<b>Again a naming convention</b>: if the convenience macros for tool extension shared objects are used,
-you have to put a tool GUI called \c QmitkExternalToolGUI into a files named \c QmitkExternalToolGUI.cpp
-and \c QmitkExternalToolGUI.h. The convenience macro will create a factory called \c QmitkExternalToolGUIFactory
-into a file named \c QmitkExternalToolGUIFactory.cpp.
-
-\subsection ToolExtensionsGeneralOverview33 Additional files
-
-If you are writing tools MITK externally, these tools might depend on additional files, e.g.
-segmentation algorithms. These can also be compiled into a tool extension shared object.
-
-\section ToolExtensionsGeneralOverview4 Writing a CMake file for a tool extension
-
-Summing up the last section, an example tool extension could comprise the following files:
-\verbatim
-mitkExternalTool.h \
-mitkExternalTool.png >--- implementing mitk::ExternalTool (header, icon, implementation)
-mitkExternalTool.cpp /
-
-QmitkExternalToolGUI.h ,-- implementing a GUI for mitk::ExternalTool
-QmitkExternalToolGUI.cpp /
-
-externalalgorithm.h \
-externalalgorithm.cpp \
-externalalgorithmsolver.h >-- a couple of files (not related to MITK tools)
-externalalgorithmsolver.cpp /
-\endverbatim
-
-This should all be compiled into one shared object. Just like ITK, VTK and MITK we
-will use CMake for this purpose (I assume you either know or are willing to learn about
-<a href="htpp://www.cmake.org">www.cmake.org</a>)
-A CMake file for the above example would look like this:
-
-\code
-project( ExternalTool )
-
-find_package(ITK)
-find_package(MITK)
-find_package(Qt3)
-
-add_definitions(${QT_DEFINITIONS})
-
-set( TOOL_QT3GUI_FILES
- QmitkExternalToolGUI.cpp
- )
-
-set( TOOL_FILES
- mitkExternalTool.cpp
- )
-
-set( TOOL_ADDITIONAL_CPPS
- externalalgorithm.cpp
- externalalgorithmsolver.cpp
- )
-
-set( TOOL_ADDITIONAL_MOC_H
-)
-
-MITK_GENERATE_TOOLS_LIBRARY(mitkExternalTools)
-\endcode
-
-Basically, you only have to change the definitions of \c TOOL_FILES and, optionally,
-\c TOOL_QT3GUI_FILES, \c TOOL_ADDITIONAL_CPPS and \c TOOL_ADDITIONAL_MOC_H.
-For all .cpp files in \c TOOL_FILES and \c TOOL_QT3GUI_FILES there will be factories
-created assuming the naming conventions described in the sections above.
-Files listed in \c TOOL_ADDITIONAL_CPPS will just be compiled. Files listed in
-\c TOOL_ADDITIONAL_MOC_H will be run through Qts meta object compiler \c moc --
-this is necessary for all objects that have the Q_OBJECT macro in their declaration.
-\c moc will create new files that will also be compiled into the library.
-
-\section ToolExtensionsGeneralOverview5 Compiling the extension
-
-For compiling a tool extension, you will need a compiled version of MITK. We will
-assume MITK was compiled into /home/user/mitk/debug. You need to build MITK with
-<b>BUILD_SHARED_CORE</b> turned on!
-
-You build the tool extension just like any other CMake based project:
-\li know where your source code is (e.g. /home/user/mitk/tool-extension-src)
-\li change into the directory, where you want to compile the shared object (e.g. /home/user/mitk/tool-extension-debug)
-\li invoke cmake: <tt>ccmake /home/user/mitk/tool-extension-src</tt>
-\li configure (press c or the "configure" button)
-\li set the ITK_DIR variable to the directory, where you compiled ITK
-\li set the MITK_DIR variable to the directory, where you compiled MITK: <tt>/home/user/mitk/debug</tt>
-\li configure (press "c" or the "configure" button)
-\li generate (press "g" or the "generate" button)
-
-This should do it and leave you with a or project file or Makefile that you can compile (using make or VisualStudio).
-
-\section ToolExtensionsGeneralOverview6 Configuring ITK autoload
-
-If the compile succeeds, you will get a library mitkExternalTools.dll or libmitkExternalTools.so.
-This library exports a symbol \c itkLoad which is expected by the ITK object factory.
-
-On application startup the ITK object factory will search a list of directories from
-the environment variable \c ITK_AUTOLOAD_PATH. Set this environment variable to your binary directory (<tt>/home/user/mitk/tool-extension-debug</tt>).
-
-The ITK object factory will load all shared objects that it finds in the specified directories
-and will test if they contain a symbol (function pointer) \c itkLoad, which is expected
-to return a pointer to a itk::ObjectFactoryBase instance. If such a symbol is found, the
-returned factory will be registered with the ITK object factory.
-
-If you successfully followed all the steps above, MITK will find your mitk::ExternalTool on
-application startup, when the ITK object factory is asked to create all known instances of
-mitk::Tool. Furthermore, if your mitk::ExternalTool claims to be part of the "default" group,
-there will be a new icon in Segmentation, which activates your tool.
-
-<b>Have fun!</b> (And Windows users: welcome to the world of DLLs)
-
-**/
diff --git a/Plugins/org.mitk.gui.qt.segmentation/resources/Extract_48x48.png b/Plugins/org.mitk.gui.qt.segmentation/resources/Extract_48x48.png
new file mode 100644
index 0000000000..021a72e5fb
Binary files /dev/null and b/Plugins/org.mitk.gui.qt.segmentation/resources/Extract_48x48.png differ
diff --git a/Plugins/org.mitk.gui.qt.segmentation/resources/Icons.svg b/Plugins/org.mitk.gui.qt.segmentation/resources/Icons.svg
index 2fd004fb64..ba16b72337 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/resources/Icons.svg
+++ b/Plugins/org.mitk.gui.qt.segmentation/resources/Icons.svg
@@ -1,3069 +1,3236 @@
<?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"
version="1.1"
- inkscape:version="0.48.4 r9939"
- sodipodi:docname="Icons.svg">
+ inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
+ sodipodi:docname="Icons.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#ffffff"
borderopacity="0.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="0"
- inkscape:zoom="8"
- inkscape:cx="-137.69045"
- inkscape:cy="-24.220797"
+ inkscape:zoom="16"
+ inkscape:cx="452"
+ inkscape:cy="14.125"
inkscape:document-units="px"
- inkscape:current-layer="g4297-7"
+ inkscape:current-layer="layer11"
showgrid="false"
- inkscape:window-width="1920"
- inkscape:window-height="1018"
+ inkscape:window-width="5120"
+ inkscape:window-height="1377"
inkscape:window-x="-8"
inkscape:window-y="-8"
- inkscape:window-maximized="1" />
+ inkscape:window-maximized="1"
+ inkscape:showpageshadow="0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1" />
<defs
id="defs2987">
<marker
style="overflow:visible"
id="TriangleInM"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInM">
<path
transform="scale(-0.4)"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path4144" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutM"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleOutM">
<path
transform="scale(0.4)"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path4153" />
</marker>
<marker
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:#000000;stroke-width:1.0pt"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path4156" />
</marker>
<marker
style="overflow:visible;"
id="Arrow2Send"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Send">
<path
transform="scale(0.3) rotate(180) translate(-2.3,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.62500000;stroke-linejoin:round;"
id="path4041" />
</marker>
<marker
style="overflow:visible;"
id="Arrow1Send"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow1Send">
<path
transform="scale(0.2) rotate(180) translate(6,0)"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
id="path4023" />
</marker>
<marker
style="overflow:visible;"
id="Arrow1Lend"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow1Lend">
<path
transform="scale(0.8) rotate(180) translate(12.5,0)"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
id="path4011" />
</marker>
<linearGradient
id="linearGradient4088">
<stop
id="stop4090"
offset="0"
style="stop-color:#5987bf;stop-opacity:1;" />
<stop
id="stop4092"
offset="1"
style="stop-color:#7399c9;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient4072">
<stop
id="stop4074"
offset="0"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
id="stop4076"
offset="1"
style="stop-color:#d0d0d0;stop-opacity:1;" />
</linearGradient>
<radialGradient
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.1851902,1.2536729,-0.63371657,0.5990994,644.67638,376.10397)"
r="15.388403"
fy="1025.4568"
fx="27.931225"
cy="1025.4568"
cx="27.931225"
id="radialGradient4078"
xlink:href="#linearGradient4072"
inkscape:collect="always" />
<radialGradient
r="15.388403"
fy="1025.4568"
fx="27.931225"
cy="1025.4568"
cx="27.931225"
gradientTransform="matrix(1.1851902,1.2536729,-0.63371657,0.5990994,644.67638,376.10397)"
gradientUnits="userSpaceOnUse"
id="radialGradient4170"
xlink:href="#linearGradient4072"
inkscape:collect="always" />
<radialGradient
r="9.2674198"
fy="1025.1075"
fx="41.430496"
cy="1025.1075"
cx="41.430496"
gradientTransform="matrix(1,0,0,1.0441688,1.7301136,-46.291947)"
gradientUnits="userSpaceOnUse"
id="radialGradient4324"
xlink:href="#linearGradient4072"
inkscape:collect="always" />
<linearGradient
y2="1038.6952"
x2="26.321873"
y1="1042.3344"
x1="30.288902"
gradientUnits="userSpaceOnUse"
id="linearGradient4326"
xlink:href="#linearGradient4088"
inkscape:collect="always" />
<radialGradient
r="15.388403"
fy="1025.4568"
fx="27.931225"
cy="1025.4568"
cx="27.931225"
gradientTransform="matrix(1.1851902,1.2536729,-0.63371657,0.5990994,644.67638,376.10397)"
gradientUnits="userSpaceOnUse"
id="radialGradient4328"
xlink:href="#linearGradient4072"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4826"
id="linearGradient3095"
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" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5120"
id="linearGradient5143"
gradientUnits="userSpaceOnUse"
x1="61.28059"
y1="451.38803"
x2="50.8452"
y2="451.70779" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5112"
id="linearGradient5141"
gradientUnits="userSpaceOnUse"
x1="70.3125"
y1="413.84039"
x2="66.868843"
y2="406.96539" />
<linearGradient
id="linearGradient4826">
<stop
style="stop-color:#f0a0a0;stop-opacity:1;"
offset="0"
id="stop4828" />
<stop
style="stop-color:#c86464;stop-opacity:0;"
offset="1"
id="stop4830" />
</linearGradient>
<inkscape:path-effect
effect="spiro"
id="path-effect4914"
is_visible="true" />
<linearGradient
id="linearGradient4928">
<stop
id="stop4930"
offset="0"
style="stop-color:#f0a0a0;stop-opacity:1;" />
<stop
id="stop4932"
offset="1"
style="stop-color:#f0a0a0;stop-opacity:1;" />
</linearGradient>
<inkscape:path-effect
effect="spiro"
id="path-effect4938"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect5003"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect5007"
is_visible="true" />
<inkscape:path-effect
effect="spiro"
id="path-effect5017"
is_visible="true" />
<linearGradient
id="linearGradient5112">
<stop
style="stop-color:#00adff;stop-opacity:1;"
offset="0"
id="stop5114" />
<stop
style="stop-color:#bfebff;stop-opacity:1;"
offset="1"
id="stop5116" />
</linearGradient>
<linearGradient
id="linearGradient5120">
<stop
style="stop-color:#00a6f4;stop-opacity:1;"
offset="0"
id="stop5122" />
<stop
style="stop-color:#00618e;stop-opacity:1;"
offset="1"
id="stop5124" />
</linearGradient>
<marker
style="overflow:visible"
id="TriangleOutM-1"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="TriangleOutM">
<path
transform="scale(0.4,0.4)"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
id="path4153-7"
inkscape:connector-curvature="0" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutM-0"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="TriangleOutM">
<path
transform="scale(0.4,0.4)"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
id="path4153-9"
inkscape:connector-curvature="0" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutM-8"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="TriangleOutM">
<path
transform="scale(0.4,0.4)"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
id="path4153-2"
inkscape:connector-curvature="0" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutM-5"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="TriangleOutM">
<path
transform="scale(0.4,0.4)"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
id="path4153-1"
inkscape:connector-curvature="0" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutM-11"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="TriangleOutM">
<path
transform="scale(0.4,0.4)"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
id="path4153-5"
inkscape:connector-curvature="0" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutMx"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleOutMx">
<path
transform="scale(0.4)"
style="stroke:#00adff;stroke-width:1.0pt;fill:#00adff;fill-rule:evenodd"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path5510" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutMo"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleOutMo">
<path
transform="scale(0.4)"
style="stroke:#00adff;stroke-width:1.0pt;fill:#00adff;fill-rule:evenodd"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path5513" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutMi"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleOutMi">
<path
transform="scale(0.4)"
style="stroke:#00adff;stroke-width:1.0pt;fill:#00adff;fill-rule:evenodd"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path5516" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutMX"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleOutMX">
<path
transform="scale(0.4)"
style="stroke:#00adff;stroke-width:1.0pt;fill:#00adff;fill-rule:evenodd"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path5519" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutMI"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleOutMI">
<path
transform="scale(0.4)"
style="stroke:#00adff;stroke-width:1.0pt;fill:#00adff;fill-rule:evenodd"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path5522" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutMW"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleOutMW">
<path
transform="scale(0.4)"
style="stroke:#00adff;stroke-width:1.0pt;fill:#00adff;fill-rule:evenodd"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path5525" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutMxc"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleOutMxc">
<path
transform="scale(0.4)"
style="fill-rule:evenodd;stroke:#0064ff;stroke-width:1.0pt;fill:#0064ff"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path6105" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutMoV"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleOutMoV">
<path
transform="scale(0.4)"
style="fill-rule:evenodd;stroke:#0064ff;stroke-width:1.0pt;fill:#0064ff"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path6108" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutMib"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleOutMib">
<path
transform="scale(0.4)"
style="fill-rule:evenodd;stroke:#0064ff;stroke-width:1.0pt;fill:#0064ff"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path6111" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutMXW"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleOutMXW">
<path
transform="scale(0.4)"
style="fill-rule:evenodd;stroke:#0064ff;stroke-width:1.0pt;fill:#0064ff"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path6114" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutMIc"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleOutMIc">
<path
transform="scale(0.4)"
style="fill-rule:evenodd;stroke:#0064ff;stroke-width:1.0pt;fill:#0064ff"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path6117" />
</marker>
<marker
style="overflow:visible"
id="TriangleOutMWR"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleOutMWR">
<path
transform="scale(0.4)"
style="fill-rule:evenodd;stroke:#0064ff;stroke-width:1.0pt;fill:#0064ff"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path6120" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMt"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInMt">
<path
transform="scale(-0.4)"
style="stroke:#0064ff;stroke-width:1.0pt;fill:#0064ff;fill-rule:evenodd"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path7211" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMF"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInMF">
<path
transform="scale(-0.4)"
style="stroke:#0064ff;stroke-width:1.0pt;fill:#0064ff;fill-rule:evenodd"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path7214" />
</marker>
<marker
style="overflow:visible"
id="TriangleInM2"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInM2">
<path
transform="scale(-0.4)"
style="stroke:#0064ff;stroke-width:1.0pt;fill:#0064ff;fill-rule:evenodd"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path7217" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMU"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInMU">
<path
transform="scale(-0.4)"
style="stroke:#0064ff;stroke-width:1.0pt;fill:#0064ff;fill-rule:evenodd"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path7220" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMf"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInMf">
<path
transform="scale(-0.4)"
style="stroke:#0064ff;stroke-width:1.0pt;fill:#0064ff;fill-rule:evenodd"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path7223" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMb"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInMb">
<path
transform="scale(-0.4)"
style="stroke:#0064ff;stroke-width:1.0pt;fill:#0064ff;fill-rule:evenodd"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path7226" />
</marker>
<marker
inkscape:stockid="TriangleOutM"
orient="auto"
refY="0"
refX="0"
id="TriangleOutM-7"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4153-6"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="scale(0.4,0.4)" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMU-4"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="TriangleInMU">
<path
inkscape:connector-curvature="0"
transform="scale(-0.4,-0.4)"
style="fill:#0064ff;fill-rule:evenodd;stroke:#0064ff;stroke-width:1pt"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
id="path7220-2" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMU-2"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="TriangleInMU">
<path
inkscape:connector-curvature="0"
transform="scale(-0.4,-0.4)"
style="fill:#0064ff;fill-rule:evenodd;stroke:#0064ff;stroke-width:1pt"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
id="path7220-21" />
</marker>
<marker
style="overflow:visible"
id="marker7835"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="TriangleInMU">
<path
inkscape:connector-curvature="0"
transform="scale(-0.4,-0.4)"
style="fill:#0064ff;fill-rule:evenodd;stroke:#0064ff;stroke-width:1pt"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
id="path7837" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMU-5"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="TriangleInMU">
<path
inkscape:connector-curvature="0"
transform="scale(-0.4,-0.4)"
style="fill:#0064ff;fill-rule:evenodd;stroke:#0064ff;stroke-width:1pt"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
id="path7220-7" />
</marker>
<marker
style="overflow:visible"
id="marker7876"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="TriangleInMU">
<path
inkscape:connector-curvature="0"
transform="scale(-0.4,-0.4)"
style="fill:#0064ff;fill-rule:evenodd;stroke:#0064ff;stroke-width:1pt"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
id="path7878" />
</marker>
<marker
style="overflow:visible"
id="marker7880"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="TriangleInMU">
<path
inkscape:connector-curvature="0"
transform="scale(-0.4,-0.4)"
style="fill:#0064ff;fill-rule:evenodd;stroke:#0064ff;stroke-width:1pt"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
id="path7882" />
</marker>
<marker
style="overflow:visible"
id="marker7884"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="TriangleInMU">
<path
inkscape:connector-curvature="0"
transform="scale(-0.4,-0.4)"
style="fill:#0064ff;fill-rule:evenodd;stroke:#0064ff;stroke-width:1pt"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
id="path7886" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMUl"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInMUl">
<path
transform="scale(-0.4)"
style="fill-rule:evenodd;stroke:#00adff;stroke-width:1.0pt;fill:#00adff"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path4164" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMUX"
- refX="0.0"
- refY="0.0"
+ refX="0"
+ refY="0"
orient="auto"
inkscape:stockid="TriangleInMUX">
<path
transform="scale(-0.4)"
- style="fill-rule:evenodd;stroke:#00adff;stroke-width:1.0pt;fill:#00adff"
- d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
+ style="fill:#00adff;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt"
+ d="M 5.77,0 -2.88,5 V -5 Z"
id="path4167" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMUm"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInMUm">
<path
transform="scale(-0.4)"
style="fill-rule:evenodd;stroke:#00adff;stroke-width:1.0pt;fill:#00adff"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path4170" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMUF"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInMUF">
<path
transform="scale(-0.4)"
style="fill-rule:evenodd;stroke:#00adff;stroke-width:1.0pt;fill:#00adff"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path4173" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMUg"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInMUg">
<path
transform="scale(-0.4)"
style="fill-rule:evenodd;stroke:#00adff;stroke-width:1.0pt;fill:#00adff"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path5644" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMUe"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInMUe">
<path
transform="scale(-0.4)"
style="fill-rule:evenodd;stroke:#00adff;stroke-width:1.0pt;fill:#00adff"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path5647" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMUZ"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInMUZ">
<path
transform="scale(-0.4)"
style="fill-rule:evenodd;stroke:#00adff;stroke-width:1.0pt;fill:#00adff"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path5650" />
</marker>
<marker
style="overflow:visible"
id="TriangleInMUt"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="TriangleInMUt">
<path
transform="scale(-0.4)"
style="fill-rule:evenodd;stroke:#00adff;stroke-width:1.0pt;fill:#00adff"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
id="path5653" />
</marker>
+ <marker
+ style="overflow:visible"
+ id="TriangleInMUX-6"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="TriangleInMUX">
+ <path
+ transform="scale(-0.4)"
+ style="fill:#00adff;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ id="path4167-2" />
+ </marker>
+ <filter
+ style="color-interpolation-filters:sRGB;"
+ inkscape:label="Drop Shadow"
+ id="filter27856"
+ x="-0.15"
+ y="-0.15"
+ width="1.2166667"
+ height="1.2166667">
+ <feFlood
+ flood-opacity="0.396078"
+ flood-color="rgb(0,0,0)"
+ result="flood"
+ id="feFlood27846" />
+ <feComposite
+ in="flood"
+ in2="SourceGraphic"
+ operator="in"
+ result="composite1"
+ id="feComposite27848" />
+ <feGaussianBlur
+ in="composite1"
+ stdDeviation="0.5"
+ result="blur"
+ id="feGaussianBlur27850" />
+ <feOffset
+ dx="-1.5"
+ dy="-1.5"
+ result="offset"
+ id="feOffset27852" />
+ <feComposite
+ in="SourceGraphic"
+ in2="offset"
+ operator="over"
+ result="composite2"
+ id="feComposite27854" />
+ </filter>
+ <marker
+ style="overflow:visible"
+ id="TriangleInMUe-3"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="TriangleInMUe">
+ <path
+ transform="scale(-0.4)"
+ style="fill:#00adff;fill-rule:evenodd;stroke:#00adff;stroke-width:1pt"
+ d="M 5.77,0 -2.88,5 V -5 Z"
+ id="path5647-1" />
+ </marker>
+ <filter
+ style="color-interpolation-filters:sRGB;"
+ inkscape:label="Drop Shadow"
+ id="filter34631"
+ x="-0.15"
+ y="-0.15"
+ width="1.2166667"
+ height="1.2166667">
+ <feFlood
+ flood-opacity="0.396078"
+ flood-color="rgb(0,0,0)"
+ result="flood"
+ id="feFlood34621" />
+ <feComposite
+ in="flood"
+ in2="SourceGraphic"
+ operator="in"
+ result="composite1"
+ id="feComposite34623" />
+ <feGaussianBlur
+ in="composite1"
+ stdDeviation="0.5"
+ result="blur"
+ id="feGaussianBlur34625" />
+ <feOffset
+ dx="-1.5"
+ dy="-1.5"
+ result="offset"
+ id="feOffset34627" />
+ <feComposite
+ in="SourceGraphic"
+ in2="offset"
+ operator="over"
+ result="composite2"
+ id="feComposite34629" />
+ </filter>
</defs>
<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
- sodipodi:insensitive="true"
style="display:inline"
inkscape:label="Boolean Operations"
id="layer2"
inkscape:groupmode="layer">
<g
inkscape:export-ydpi="91.087982"
inkscape:export-xdpi="91.087982"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\boolean_48x48.png"
- transform="translate(-922.66699,114.10169)"
+ transform="translate(-572.66773,-890.71912)"
id="g4197">
<g
id="g4126"
transform="translate(49.125,0.0625)">
<rect
y="914.43219"
x="873.54199"
height="5.4800777"
width="5.4800777"
id="rect3005"
style="fill:#999999;fill-opacity:1;stroke:none" />
<rect
y="914.43219"
x="879.58081"
height="5.4800777"
width="5.4800777"
id="rect3005-1"
style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
<rect
y="914.43219"
x="885.61963"
height="5.4800777"
width="5.4800777"
id="rect3005-7"
style="fill:#d499a6;fill-opacity:1;stroke:none" />
<rect
y="914.43219"
x="891.65845"
height="5.4800777"
width="5.4800777"
id="rect3005-4"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="914.43219"
x="897.69727"
height="5.4800777"
width="5.4800777"
id="rect3005-4-9"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="920.56781"
x="879.58081"
height="5.4800777"
width="5.4800777"
id="rect3005-4-4"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="920.56781"
x="891.65845"
height="5.4800777"
width="5.4800777"
id="rect3005-4-8"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="920.56781"
x="897.69727"
height="5.4800777"
width="5.4800777"
id="rect3005-4-82"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="920.56781"
x="903.73608"
height="5.4800777"
width="5.4800777"
id="rect3005-4-45"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="926.70343"
x="879.58081"
height="5.4800777"
width="5.4800777"
id="rect3005-4-5"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="926.70343"
x="885.61963"
height="5.4800777"
width="5.4800777"
id="rect3005-4-1"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="926.70343"
x="897.69727"
height="5.4800777"
width="5.4800777"
id="rect3005-4-7"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="926.70343"
x="903.73608"
height="5.4800777"
width="5.4800777"
id="rect3005-4-11"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="932.83905"
x="891.65845"
height="5.4800777"
width="5.4800777"
id="rect3005-4-52"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="932.83905"
x="897.69727"
height="5.4800777"
width="5.4800777"
id="rect3005-4-76"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="932.83905"
x="903.73608"
height="5.4800777"
width="5.4800777"
id="rect3005-4-14"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="926.70343"
x="873.54199"
height="5.4800777"
width="5.4800777"
id="rect3005-1-2"
style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
<rect
y="926.70343"
x="915.81372"
height="5.4800777"
width="5.4800777"
id="rect3005-1-3"
style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
<rect
y="932.83905"
x="915.81372"
height="5.4800777"
width="5.4800777"
id="rect3005-1-22"
style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
<rect
y="920.56781"
x="909.7749"
height="5.4800777"
width="5.4800777"
id="rect3005-1-1"
style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
<rect
y="914.43219"
x="909.7749"
height="5.4800777"
width="5.4800777"
id="rect3005-1-6"
style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
<rect
y="920.56781"
x="885.61963"
height="5.4800777"
width="5.4800777"
id="rect3005-1-8"
style="fill:#e1d0da;fill-opacity:1;stroke:none" />
<rect
y="926.70343"
x="891.65845"
height="5.4800777"
width="5.4800777"
id="rect3005-1-8-7"
style="fill:#e1d0da;fill-opacity:1;stroke:none" />
<rect
y="932.83905"
x="885.61963"
height="5.4800777"
width="5.4800777"
id="rect3005-1-8-6"
style="fill:#e1d0da;fill-opacity:1;stroke:none" />
<rect
y="914.43219"
x="903.73608"
height="5.4800777"
width="5.4800777"
id="rect3005-1-8-1"
style="fill:#e1d0da;fill-opacity:1;stroke:none" />
<rect
y="920.56781"
x="873.54199"
height="5.4800777"
width="5.4800777"
id="rect3005-8"
style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
<rect
y="932.83905"
x="873.54199"
height="5.4800777"
width="5.4800777"
id="rect3005-8-2"
style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
<rect
y="926.70343"
x="909.7749"
height="5.4800777"
width="5.4800777"
id="rect3005-8-7"
style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
<rect
y="914.43219"
x="915.81372"
height="5.4800777"
width="5.4800777"
id="rect3005-8-9"
style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
<rect
y="932.83905"
x="879.58081"
height="5.4800777"
width="5.4800777"
id="rect3005-8-5"
style="fill:#cf98a3;fill-opacity:1;stroke:none" />
<rect
y="932.83905"
x="909.7749"
height="5.4800777"
width="5.4800777"
id="rect3005-8-5-3"
style="fill:#cf98a3;fill-opacity:1;stroke:none" />
<rect
y="920.56781"
x="915.81372"
height="5.4800777"
width="5.4800777"
id="rect3005-2"
style="fill:#999999;fill-opacity:1;stroke:none" />
</g>
<g
id="g4191">
<text
- sodipodi:linespacing="125%"
id="text4160"
y="905.98718"
x="922.875"
- style="font-size:22px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#00afdc;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:#00afdc;fill-opacity:1;stroke:none"
xml:space="preserve"><tspan
dx="0"
- style="font-size:21px;font-weight:bold;letter-spacing:-0.99000007px;-inkscape-font-specification:Sans Bold"
+ style="font-weight:bold;font-size:21px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';letter-spacing:-0.99px"
id="tspan4164"
y="905.98718"
x="922.875"
sodipodi:role="line">AND</tspan></text>
<text
- sodipodi:linespacing="125%"
id="text4160-3"
y="922.48621"
x="939.82056"
- style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#00afdc;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:#00afdc;fill-opacity:1;stroke:none"
xml:space="preserve"><tspan
- style="font-size:21px;font-weight:bold;letter-spacing:-0.99000007px;-inkscape-font-specification:Sans Bold"
+ style="font-weight:bold;font-size:21px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';letter-spacing:-0.99px"
id="tspan4164-3"
y="922.48621"
x="939.82056"
sodipodi:role="line">OR</tspan></text>
</g>
</g>
</g>
<g
- sodipodi:insensitive="true"
inkscape:label="Segmentation Utilities"
id="layer1"
inkscape:groupmode="layer">
<g
inkscape:export-ydpi="90.470001"
inkscape:export-xdpi="90.470001"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\SegmentationUtilities_48x48.png"
- transform="translate(-57.851365,-13.716184)"
+ transform="translate(295.12591,-1018.5162)"
id="g4285">
<g
transform="translate(-43.125,-15.75)"
id="g3213"
style="display:inline">
<rect
y="1058.4187"
x="47.999092"
height="5.4800777"
width="5.4800777"
id="rect3005-74"
style="fill:#999999;fill-opacity:1;stroke:none" />
<rect
y="1058.4187"
x="54.03791"
height="5.4800777"
width="5.4800777"
id="rect3005-1-0"
style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
<rect
y="1058.4187"
x="60.076729"
height="5.4800777"
width="5.4800777"
id="rect3005-7-9"
style="fill:#d499a6;fill-opacity:1;stroke:none" />
<rect
y="1058.4187"
x="66.115547"
height="5.4800777"
width="5.4800777"
id="rect3005-4-48"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="1058.4187"
x="72.154366"
height="5.4800777"
width="5.4800777"
id="rect3005-4-9-8"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="1064.5543"
x="54.03791"
height="5.4800777"
width="5.4800777"
id="rect3005-4-4-2"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="1064.5543"
x="66.115547"
height="5.4800777"
width="5.4800777"
id="rect3005-4-8-4"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="1064.5543"
x="72.154366"
height="5.4800777"
width="5.4800777"
id="rect3005-4-82-5"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="1064.5543"
x="78.193184"
height="5.4800777"
width="5.4800777"
id="rect3005-4-45-5"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="1070.6899"
x="54.03791"
height="5.4800777"
width="5.4800777"
id="rect3005-4-5-1"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="1070.6899"
x="60.076729"
height="5.4800777"
width="5.4800777"
id="rect3005-4-1-7"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="1070.6899"
x="72.154366"
height="5.4800777"
width="5.4800777"
id="rect3005-4-7-1"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="1070.6899"
x="78.193184"
height="5.4800777"
width="5.4800777"
id="rect3005-4-11-1"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="1076.8256"
x="66.115547"
height="5.4800777"
width="5.4800777"
id="rect3005-4-52-5"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="1076.8256"
x="72.154366"
height="5.4800777"
width="5.4800777"
id="rect3005-4-76-2"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="1076.8256"
x="78.193184"
height="5.4800777"
width="5.4800777"
id="rect3005-4-14-7"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
<rect
y="1070.6899"
x="47.999092"
height="5.4800777"
width="5.4800777"
id="rect3005-1-2-6"
style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
<rect
y="1070.6899"
x="90.270821"
height="5.4800777"
width="5.4800777"
id="rect3005-1-3-1"
style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
<rect
y="1076.8256"
x="90.270821"
height="5.4800777"
width="5.4800777"
id="rect3005-1-22-4"
style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
<rect
y="1064.5543"
x="84.232002"
height="5.4800777"
width="5.4800777"
id="rect3005-1-1-2"
style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
<rect
y="1058.4187"
x="84.232002"
height="5.4800777"
width="5.4800777"
id="rect3005-1-6-3"
style="fill:#b3b3b3;fill-opacity:1;stroke:none" />
<rect
y="1064.5543"
x="60.076729"
height="5.4800777"
width="5.4800777"
id="rect3005-1-8-2"
style="fill:#e1d0da;fill-opacity:1;stroke:none" />
<rect
y="1070.6899"
x="66.115547"
height="5.4800777"
width="5.4800777"
id="rect3005-1-8-7-2"
style="fill:#e1d0da;fill-opacity:1;stroke:none" />
<rect
y="1076.8256"
x="60.076729"
height="5.4800777"
width="5.4800777"
id="rect3005-1-8-6-1"
style="fill:#e1d0da;fill-opacity:1;stroke:none" />
<rect
y="1058.4187"
x="78.193184"
height="5.4800777"
width="5.4800777"
id="rect3005-1-8-1-6"
style="fill:#e1d0da;fill-opacity:1;stroke:none" />
<rect
y="1064.5543"
x="47.999092"
height="5.4800777"
width="5.4800777"
id="rect3005-8-8"
style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
<rect
y="1076.8256"
x="47.999092"
height="5.4800777"
width="5.4800777"
id="rect3005-8-2-5"
style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
<rect
y="1070.6899"
x="84.232002"
height="5.4800777"
width="5.4800777"
id="rect3005-8-7-7"
style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
<rect
y="1058.4187"
x="90.270821"
height="5.4800777"
width="5.4800777"
id="rect3005-8-9-6"
style="fill:#4d4d4d;fill-opacity:1;stroke:none" />
<rect
y="1076.8256"
x="54.03791"
height="5.4800777"
width="5.4800777"
id="rect3005-8-5-1"
style="fill:#cf98a3;fill-opacity:1;stroke:none" />
<rect
y="1076.8256"
x="84.232002"
height="5.4800777"
width="5.4800777"
id="rect3005-8-5-3-8"
style="fill:#cf98a3;fill-opacity:1;stroke:none" />
<rect
y="1064.5543"
x="90.270821"
height="5.4800777"
width="5.4800777"
id="rect3005-2-9"
style="fill:#999999;fill-opacity:1;stroke:none" />
</g>
<g
transform="translate(-0.23863636,4.2386364)"
id="g4280">
<path
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0"
id="path4052"
d="m 34.24319,1031.4717 8.747098,-8.5733 -0.05204,-2.9235 5.054758,-5.2082 4.085023,3.7843 -5.303659,5.7251 -2.41443,0.092 -8.683803,8.8731 z"
- style="fill:url(#radialGradient4324);fill-opacity:1;stroke:#797b7b;stroke-width:0.69999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ style="fill:url(#radialGradient4324);fill-opacity:1;stroke:#797b7b;stroke-width:0.7;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="ssccss"
inkscape:connector-curvature="0"
id="path4050"
d="m 19.70767,1047.6527 c -1.846592,-1.8185 -1.043705,-4.3471 0.596591,-6.2643 1.640296,-1.9172 10.738637,-10.5596 10.738637,-10.5596 l 4.892045,5.1307 c 0,0 -8.378356,9.6654 -9.545454,11.1562 -1.54887,1.9785 -4.835227,2.3555 -6.681819,0.537 z"
- style="fill:url(#linearGradient4326);fill-opacity:1;stroke:#50718c;stroke-width:0.69999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ style="fill:url(#linearGradient4326);fill-opacity:1;stroke:#50718c;stroke-width:0.7;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
- style="fill:url(#radialGradient4328);fill-opacity:1;stroke:#797b7b;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="fill:url(#radialGradient4328);fill-opacity:1;stroke:#797b7b;stroke-width:0.7;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 46.855397,1040.1669 c 4.488776,4.7242 0.778133,8.5155 -4.125,3.5 -4.903132,-5.0155 -13.375,-14.125 -13.375,-14.125 -10.133107,1.6369 -10.676747,-2.9634 -10.541192,-6.8466 0.08814,-2.525 2.091948,-2.329 2.791193,0.2216 1.904554,6.9471 11.320358,-1.2275 4.374999,-4.25 -1.946512,-0.8471 -2.069672,-2.6063 0,-2.625 3.838646,-0.035 7.576804,0.7182 7.75,10 0,0 8.636225,9.4008 13.125,14.125 z"
id="path3280"
inkscape:connector-curvature="0"
sodipodi:nodetypes="zzcsssscz" />
</g>
</g>
</g>
<g
- sodipodi:insensitive="true"
inkscape:label="Segmentation"
id="layer4"
inkscape:groupmode="layer">
<g
inkscape:export-ydpi="90.470001"
inkscape:export-xdpi="90.470001"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\segmentation.png"
- transform="translate(43.909091,9.0681818)"
+ transform="translate(400.35317,-996.36923)"
id="g4075">
<g
style="display:inline"
id="g3213-9"
transform="translate(-198.35226,-38.284079)">
<rect
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-74-4"
width="5.4800777"
height="5.4800777"
x="47.999092"
y="1058.4187" />
<rect
style="fill:#b3b3b3;fill-opacity:1;stroke:none"
id="rect3005-1-0-8"
width="5.4800777"
height="5.4800777"
x="54.03791"
y="1058.4187" />
<rect
style="fill:#d499a6;fill-opacity:1;stroke:none"
id="rect3005-7-9-8"
width="5.4800777"
height="5.4800777"
x="60.076729"
y="1058.4187" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-48-2"
width="5.4800777"
height="5.4800777"
x="66.115547"
y="1058.4187" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-9-8-4"
width="5.4800777"
height="5.4800777"
x="72.154366"
y="1058.4187" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-4-2-5"
width="5.4800777"
height="5.4800777"
x="54.03791"
y="1064.5543" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-8-4-5"
width="5.4800777"
height="5.4800777"
x="66.115547"
y="1064.5543" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-82-5-1"
width="5.4800777"
height="5.4800777"
x="72.154366"
y="1064.5543" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-45-5-7"
width="5.4800777"
height="5.4800777"
x="78.193184"
y="1064.5543" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-5-1-1"
width="5.4800777"
height="5.4800777"
x="54.03791"
y="1070.6899" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-1-7-1"
width="5.4800777"
height="5.4800777"
x="60.076729"
y="1070.6899" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-7-1-5"
width="5.4800777"
height="5.4800777"
x="72.154366"
y="1070.6899" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-11-1-2"
width="5.4800777"
height="5.4800777"
x="78.193184"
y="1070.6899" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-52-5-7"
width="5.4800777"
height="5.4800777"
x="66.115547"
y="1076.8256" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-76-2-6"
width="5.4800777"
height="5.4800777"
x="72.154366"
y="1076.8256" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-14-7-1"
width="5.4800777"
height="5.4800777"
x="78.193184"
y="1076.8256" />
<rect
style="fill:#b3b3b3;fill-opacity:1;stroke:none"
id="rect3005-1-2-6-4"
width="5.4800777"
height="5.4800777"
x="47.999092"
y="1070.6899" />
<rect
style="fill:#b3b3b3;fill-opacity:1;stroke:none"
id="rect3005-1-3-1-2"
width="5.4800777"
height="5.4800777"
x="90.270821"
y="1070.6899" />
<rect
style="fill:#b3b3b3;fill-opacity:1;stroke:none"
id="rect3005-1-22-4-3"
width="5.4800777"
height="5.4800777"
x="90.270821"
y="1076.8256" />
<rect
style="fill:#b3b3b3;fill-opacity:1;stroke:none"
id="rect3005-1-1-2-2"
width="5.4800777"
height="5.4800777"
x="84.232002"
y="1064.5543" />
<rect
style="fill:#b3b3b3;fill-opacity:1;stroke:none"
id="rect3005-1-6-3-2"
width="5.4800777"
height="5.4800777"
x="84.232002"
y="1058.4187" />
<rect
style="fill:#e1d0da;fill-opacity:1;stroke:none"
id="rect3005-1-8-2-1"
width="5.4800777"
height="5.4800777"
x="60.076729"
y="1064.5543" />
<rect
style="fill:#e1d0da;fill-opacity:1;stroke:none"
id="rect3005-1-8-7-2-6"
width="5.4800777"
height="5.4800777"
x="66.115547"
y="1070.6899" />
<rect
style="fill:#e1d0da;fill-opacity:1;stroke:none"
id="rect3005-1-8-6-1-8"
width="5.4800777"
height="5.4800777"
x="60.076729"
y="1076.8256" />
<rect
style="fill:#e1d0da;fill-opacity:1;stroke:none"
id="rect3005-1-8-1-6-5"
width="5.4800777"
height="5.4800777"
x="78.193184"
y="1058.4187" />
<rect
style="fill:#4d4d4d;fill-opacity:1;stroke:none"
id="rect3005-8-8-7"
width="5.4800777"
height="5.4800777"
x="47.999092"
y="1064.5543" />
<rect
style="fill:#4d4d4d;fill-opacity:1;stroke:none"
id="rect3005-8-2-5-6"
width="5.4800777"
height="5.4800777"
x="47.999092"
y="1076.8256" />
<rect
style="fill:#4d4d4d;fill-opacity:1;stroke:none"
id="rect3005-8-7-7-1"
width="5.4800777"
height="5.4800777"
x="84.232002"
y="1070.6899" />
<rect
style="fill:#4d4d4d;fill-opacity:1;stroke:none"
id="rect3005-8-9-6-8"
width="5.4800777"
height="5.4800777"
x="90.270821"
y="1058.4187" />
<rect
style="fill:#cf98a3;fill-opacity:1;stroke:none"
id="rect3005-8-5-1-9"
width="5.4800777"
height="5.4800777"
x="54.03791"
y="1076.8256" />
<rect
style="fill:#cf98a3;fill-opacity:1;stroke:none"
id="rect3005-8-5-3-8-2"
width="5.4800777"
height="5.4800777"
x="84.232002"
y="1076.8256" />
<rect
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-2-9-7"
width="5.4800777"
height="5.4800777"
x="90.270821"
y="1064.5543" />
</g>
<g
inkscape:label="Add"
id="layer3"
transform="matrix(1.7960075,0,0,1.7960075,-176.17936,-805.38135)"
- style="stroke-width:0.89086491;stroke-miterlimit:4;stroke-dasharray:none">
+ style="stroke-width:0.890865;stroke-miterlimit:4;stroke-dasharray:none">
<g
transform="translate(-178.3781,580.55863)"
id="g4949"
- style="stroke-width:0.89086491;stroke-miterlimit:4;stroke-dasharray:none">
+ style="stroke-width:0.890865;stroke-miterlimit:4;stroke-dasharray:none">
<g
id="g3125"
- style="stroke-width:0.89086491;stroke-miterlimit:4;stroke-dasharray:none">
+ style="stroke-width:0.890865;stroke-miterlimit:4;stroke-dasharray:none">
<path
sodipodi:nodetypes="aaaaa"
inkscape:connector-curvature="0"
id="path3004"
d="m 204.375,433.36218 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:1.67037165;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ style="fill:none;stroke:#00adff;stroke-width:1.67037;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"
d="m 199.875,443.36218 c 6.84332,-2.29563 3.78655,-5.82877 5.625,-8.75"
- style="fill:none;stroke:#00adff;stroke-width:1.67037165;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
+ style="fill:none;stroke:#00adff;stroke-width:1.67037;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <circle
transform="matrix(1.7077889,0.59262847,-0.4791464,1.3807655,-23.440489,-258.11706)"
- 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"
style="fill:#00adff;fill-opacity:1;stroke:none"
- sodipodi:type="arc" />
+ cx="245.375"
+ cy="396.11218"
+ r="1.625" />
</g>
</g>
</g>
</g>
</g>
<g
inkscape:label="Image Masking"
id="layer5"
- inkscape:groupmode="layer"
- sodipodi:insensitive="true">
+ inkscape:groupmode="layer">
<g
- transform="translate(-1.1869722,0.39565741)"
+ transform="translate(360.56647,-1005.3995)"
inkscape:export-ydpi="89.07"
inkscape:export-xdpi="89.07"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\ImageMasking_48x48.png"
id="g4347">
<g
id="g4297"
style="fill:#d2d2d2;fill-opacity:1">
<rect
y="1029.8"
x="-160.56647"
height="5.4800777"
width="5.4800777"
id="rect3005-74-4-6"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1029.8"
x="-154.39507"
height="5.4800777"
width="5.4800777"
id="rect3005-1-0-8-0"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1035.9357"
x="-154.39507"
height="5.4800777"
width="5.4800777"
id="rect3005-4-4-2-5-7"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1042.0714"
x="-154.39507"
height="5.4800777"
width="5.4800777"
id="rect3005-4-5-1-1-2"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1042.0714"
x="-148.09108"
height="5.4800777"
width="5.4800777"
id="rect3005-4-1-7-1-0"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1042.0714"
x="-136.01344"
height="5.4800777"
width="5.4800777"
id="rect3005-4-7-1-5-0"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1042.0714"
x="-129.97462"
height="5.4800777"
width="5.4800777"
id="rect3005-4-11-1-2-1"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1048.2069"
x="-142.05226"
height="5.4800777"
width="5.4800777"
id="rect3005-4-52-5-7-4"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1048.2069"
x="-136.01344"
height="5.4800777"
width="5.4800777"
id="rect3005-4-76-2-6-6"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1048.2069"
x="-129.97462"
height="5.4800777"
width="5.4800777"
id="rect3005-4-14-7-1-0"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1042.0714"
x="-160.56647"
height="5.4800777"
width="5.4800777"
id="rect3005-1-2-6-4-7"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1042.0714"
x="-117.54343"
height="5.4800777"
width="5.4800777"
id="rect3005-1-3-1-2-1"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1048.2069"
x="-117.54343"
height="5.4800777"
width="5.4800777"
id="rect3005-1-22-4-3-7"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1035.9357"
x="-123.71484"
height="5.4800777"
width="5.4800777"
id="rect3005-1-1-2-2-7"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1029.8"
x="-123.71484"
height="5.4800777"
width="5.4800777"
id="rect3005-1-6-3-2-7"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1042.0714"
x="-142.05226"
height="5.4800777"
width="5.4800777"
id="rect3005-1-8-7-2-6-3"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1048.2069"
x="-148.09108"
height="5.4800777"
width="5.4800777"
id="rect3005-1-8-6-1-8-3"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1035.9357"
x="-160.56647"
height="5.4800777"
width="5.4800777"
id="rect3005-8-8-7-9"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1048.2069"
x="-160.56647"
height="5.4800777"
width="5.4800777"
id="rect3005-8-2-5-6-9"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1042.0714"
x="-123.71484"
height="5.4800777"
width="5.4800777"
id="rect3005-8-7-7-1-8"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1029.8"
x="-117.54343"
height="5.4800777"
width="5.4800777"
id="rect3005-8-9-6-8-1"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1048.2069"
x="-154.39507"
height="5.4800777"
width="5.4800777"
id="rect3005-8-5-1-9-8"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1048.2069"
x="-123.71484"
height="5.4800777"
width="5.4800777"
id="rect3005-8-5-3-8-2-2"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="1035.9357"
x="-117.54343"
height="5.4800777"
width="5.4800777"
id="rect3005-2-9-7-6"
style="fill:#d2d2d2;fill-opacity:1;stroke:none" />
<rect
y="-1029.2865"
x="112.06335"
height="5.4800777"
width="5.4800777"
id="rect3005-74-4-6-0"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1029.2865"
x="118.23475"
height="5.4800777"
width="5.4800777"
id="rect3005-1-0-8-0-3"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1023.1509"
x="118.23475"
height="5.4800777"
width="5.4800777"
id="rect3005-4-4-2-5-7-2"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1017.0153"
x="118.23475"
height="5.4800777"
width="5.4800777"
id="rect3005-4-5-1-1-2-4"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1017.0153"
x="124.49454"
height="5.4800777"
width="5.4800777"
id="rect3005-4-1-7-1-0-7"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1017.0153"
x="136.57217"
height="5.4800777"
width="5.4800777"
id="rect3005-4-7-1-5-0-8"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1017.0153"
x="142.61099"
height="5.4800777"
width="5.4800777"
id="rect3005-4-11-1-2-1-3"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1010.8796"
x="130.53336"
height="5.4800777"
width="5.4800777"
id="rect3005-4-52-5-7-4-5"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1010.8796"
x="136.57217"
height="5.4800777"
width="5.4800777"
id="rect3005-4-76-2-6-6-1"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1010.8796"
x="142.61099"
height="5.4800777"
width="5.4800777"
id="rect3005-4-14-7-1-0-2"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1017.0153"
x="112.06335"
height="5.4800777"
width="5.4800777"
id="rect3005-1-2-6-4-7-0"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1017.0153"
x="155.08638"
height="5.4800777"
width="5.4800777"
id="rect3005-1-3-1-2-1-1"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1010.8796"
x="155.08638"
height="5.4800777"
width="5.4800777"
id="rect3005-1-22-4-3-7-6"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1023.1509"
x="148.91498"
height="5.4800777"
width="5.4800777"
id="rect3005-1-1-2-2-7-4"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1029.2865"
x="148.91498"
height="5.4800777"
width="5.4800777"
id="rect3005-1-6-3-2-7-0"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1017.0153"
x="130.53336"
height="5.4800777"
width="5.4800777"
id="rect3005-1-8-7-2-6-3-1"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1010.8796"
x="124.49454"
height="5.4800777"
width="5.4800777"
id="rect3005-1-8-6-1-8-3-8"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1023.1509"
x="112.06335"
height="5.4800777"
width="5.4800777"
id="rect3005-8-8-7-9-8"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1010.8796"
x="112.06335"
height="5.4800777"
width="5.4800777"
id="rect3005-8-2-5-6-9-4"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1017.0153"
x="148.91498"
height="5.4800777"
width="5.4800777"
id="rect3005-8-7-7-1-8-1"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1029.2865"
x="155.08638"
height="5.4800777"
width="5.4800777"
id="rect3005-8-9-6-8-1-4"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1010.8796"
x="118.23475"
height="5.4800777"
width="5.4800777"
id="rect3005-8-5-1-9-8-3"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1010.8796"
x="148.91498"
height="5.4800777"
width="5.4800777"
id="rect3005-8-5-3-8-2-2-9"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
<rect
y="-1023.1509"
x="155.08638"
height="5.4800777"
width="5.4800777"
id="rect3005-2-9-7-6-8"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
- transform="scale(-1,-1)" />
+ transform="scale(-1)" />
</g>
<g
id="g4275"
transform="translate(0.29674306,42.186971)">
<g
id="g3485">
<rect
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-7-9-8-1"
width="5.4800777"
height="5.4800777"
x="-148.09108"
y="987.79999" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-48-2-6"
width="5.4800777"
height="5.4800777"
x="-142.05226"
y="987.79999" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-9-8-4-5"
width="5.4800777"
height="5.4800777"
x="-136.01344"
y="987.79999" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-8-4-5-5"
width="5.4800777"
height="5.4800777"
x="-142.05226"
y="993.93573" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-82-5-1-4"
width="5.4800777"
height="5.4800777"
x="-136.01344"
y="993.93573" />
<rect
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-4-45-5-7-1"
width="5.4800777"
height="5.4800777"
x="-129.97462"
y="993.93573" />
<rect
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-1-8-2-1-7"
width="5.4800777"
height="5.4800777"
x="-148.09108"
y="993.93573" />
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-1-8-1-6-5-5"
width="5.4800777"
height="5.4800777"
x="-129.97462"
y="987.79999" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-7-9-8-1-8"
width="5.4800777"
height="5.4800777"
x="124.49454"
y="-987.2865" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-48-2-6-0"
width="5.4800777"
height="5.4800777"
x="130.53336"
y="-987.2865" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-9-8-4-5-1"
width="5.4800777"
height="5.4800777"
x="136.57217"
y="-987.2865" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-8-4-5-5-5"
width="5.4800777"
height="5.4800777"
x="130.53336"
y="-981.15088" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-82-5-1-4-0"
width="5.4800777"
height="5.4800777"
x="136.57217"
y="-981.15088" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-4-45-5-7-1-9"
width="5.4800777"
height="5.4800777"
x="142.61099"
y="-981.15088" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-1-8-2-1-7-6"
width="5.4800777"
height="5.4800777"
x="124.49454"
y="-981.15088" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-1-8-1-6-5-5-9"
width="5.4800777"
height="5.4800777"
x="142.61099"
y="-987.2865" />
</g>
<g
transform="translate(-5.2457258,-39.238029)"
id="g4271">
<rect
y="1013.8058"
x="-143.93018"
height="25.713068"
width="25.59375"
id="rect4533-7"
- style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:1;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+ style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:1;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="1013.8058"
x="-143.93018"
height="25.713068"
width="25.59375"
id="rect4533"
- style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:1;stroke-opacity:1;stroke-dasharray:5, 2.5;stroke-dashoffset:0" />
+ style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:1;stroke-dasharray:5, 2.5;stroke-dashoffset:0;stroke-opacity:1" />
</g>
</g>
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer10"
inkscape:label="Surface To Image">
<g
- transform="translate(-1.185093,57.81894)"
+ transform="translate(560.81645,-1005.3995)"
inkscape:export-ydpi="89.07"
inkscape:export-xdpi="89.07"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\ImageMasking_48x48.png"
id="g4347-1">
<g
id="g4297-7"
- style="fill:#d2d2d2;fill-opacity:1"
- sodipodi:insensitive="true">
+ style="fill:#d2d2d2;fill-opacity:1">
<g
id="g4874"
transform="matrix(-1,0,0,1,-273.1298,0)"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\SurfaceToImage_48x48.png"
inkscape:export-xdpi="89.07"
inkscape:export-ydpi="89.07">
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-5-1-1-2-4-8"
width="5.4800777"
height="5.4800777"
x="118.45951"
y="-1017.0087" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-1-7-1-0-7-9"
width="5.4800777"
height="5.4800777"
x="124.60566"
y="-1017.0087" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-7-1-5-0-8-2"
width="5.4800777"
height="5.4800777"
x="136.89793"
y="-1017.0087" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-11-1-2-1-3-7"
width="5.4800777"
height="5.4800777"
x="143.04408"
y="-1017.0087" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-2-6-4-7-0-3"
width="5.4800777"
height="5.4800777"
x="112.31335"
y="-1017.0087" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-1-3-1-2-1-1-1"
width="5.4800777"
height="5.4800777"
x="155.33638"
y="-1017.0087" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-8-7-2-6-3-1-4"
width="5.4800777"
height="5.4800777"
x="130.7518"
y="-1017.0087" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-8-7-7-1-8-1-8"
width="5.4800777"
height="5.4800777"
x="149.19023"
y="-1017.0087" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-52-5-7-4-5-9"
width="5.4800777"
height="5.4800777"
x="130.7518"
y="-1010.8796" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-4-76-2-6-6-1-5"
width="5.4800777"
height="5.4800777"
x="136.89793"
y="-1010.8796" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-4-14-7-1-0-2-4"
width="5.4800777"
height="5.4800777"
x="143.04408"
y="-1010.8796" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-1-22-4-3-7-6-2"
width="5.4800777"
height="5.4800777"
x="155.33638"
y="-1010.8796" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-8-6-1-8-3-8-1"
width="5.4800777"
height="5.4800777"
x="124.60566"
y="-1010.8796" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-8-2-5-6-9-4-3"
width="5.4800777"
height="5.4800777"
x="112.31335"
y="-1010.8796" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-8-5-1-9-8-3-4"
width="5.4800777"
height="5.4800777"
x="118.45951"
y="-1010.8796" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-8-5-3-8-2-2-9-2"
width="5.4800777"
height="5.4800777"
x="149.19023"
y="-1010.8796" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-5-1-1-2-4-8-6"
width="5.4800777"
height="5.4800777"
x="118.45951"
y="-1023.1378" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-1-7-1-0-7-9-1"
width="5.4800777"
height="5.4800777"
x="124.60566"
y="-1023.1378" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-7-1-5-0-8-2-3"
width="5.4800777"
height="5.4800777"
x="136.89793"
y="-1023.1378" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-11-1-2-1-3-7-8"
width="5.4800777"
height="5.4800777"
x="143.04408"
y="-1023.1378" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-2-6-4-7-0-3-9"
width="5.4800777"
height="5.4800777"
x="112.31335"
y="-1023.1378" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-1-3-1-2-1-1-1-3"
width="5.4800777"
height="5.4800777"
x="155.33638"
y="-1023.1378" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-8-7-2-6-3-1-4-4"
width="5.4800777"
height="5.4800777"
x="130.7518"
y="-1023.1378" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-8-7-7-1-8-1-8-4"
width="5.4800777"
height="5.4800777"
x="149.19023"
y="-1023.1378" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-5-1-1-2-4-8-0"
width="5.4800777"
height="5.4800777"
x="118.45951"
y="-1029.2668" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-1-7-1-0-7-9-6"
width="5.4800777"
height="5.4800777"
x="124.60566"
y="-1029.2668" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-7-1-5-0-8-2-6"
width="5.4800777"
height="5.4800777"
x="136.89793"
y="-1029.2668" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-11-1-2-1-3-7-1"
width="5.4800777"
height="5.4800777"
x="143.04408"
y="-1029.2668" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-2-6-4-7-0-3-8"
width="5.4800777"
height="5.4800777"
x="112.31335"
y="-1029.2668" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-1-3-1-2-1-1-1-4"
width="5.4800777"
height="5.4800777"
x="155.33638"
y="-1029.2668" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-8-7-2-6-3-1-4-9"
width="5.4800777"
height="5.4800777"
x="130.7518"
y="-1029.2668" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-8-7-7-1-8-1-8-6"
width="5.4800777"
height="5.4800777"
x="149.19023"
y="-1029.2668" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-5-1-1-2-4-8-7"
width="5.4800777"
height="5.4800777"
x="118.45951"
y="-1035.396" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-1-7-1-0-7-9-8"
width="5.4800777"
height="5.4800777"
x="124.60566"
y="-1035.396" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-7-1-5-0-8-2-8"
width="5.4800777"
height="5.4800777"
x="136.89793"
y="-1035.396" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-11-1-2-1-3-7-2"
width="5.4800777"
height="5.4800777"
x="143.04408"
y="-1035.396" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-2-6-4-7-0-3-91"
width="5.4800777"
height="5.4800777"
x="112.31335"
y="-1035.396" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-1-3-1-2-1-1-1-35"
width="5.4800777"
height="5.4800777"
x="155.33638"
y="-1035.396" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-8-7-2-6-3-1-4-98"
width="5.4800777"
height="5.4800777"
x="130.7518"
y="-1035.396" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-8-7-7-1-8-1-8-40"
width="5.4800777"
height="5.4800777"
x="149.19023"
y="-1035.396" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-5-1-1-2-4-8-63"
width="5.4800777"
height="5.4800777"
x="118.45951"
y="-1041.525" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-1-7-1-0-7-9-61"
width="5.4800777"
height="5.4800777"
x="124.60566"
y="-1041.525" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-7-1-5-0-8-2-5"
width="5.4800777"
height="5.4800777"
x="136.89793"
y="-1041.525" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-11-1-2-1-3-7-4"
width="5.4800777"
height="5.4800777"
x="143.04408"
y="-1041.525" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-2-6-4-7-0-3-2"
width="5.4800777"
height="5.4800777"
x="112.31335"
y="-1041.525" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-1-3-1-2-1-1-1-0"
width="5.4800777"
height="5.4800777"
x="155.33638"
y="-1041.525" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-8-7-2-6-3-1-4-97"
width="5.4800777"
height="5.4800777"
x="130.7518"
y="-1041.525" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-8-7-7-1-8-1-8-3"
width="5.4800777"
height="5.4800777"
x="149.19023"
y="-1041.525" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-5-1-1-2-4-8-60"
width="5.4800777"
height="5.4800777"
x="118.45951"
y="-1047.6542" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-1-7-1-0-7-9-16"
width="5.4800777"
height="5.4800777"
x="124.60566"
y="-1047.6542" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-7-1-5-0-8-2-57"
width="5.4800777"
height="5.4800777"
x="136.89793"
y="-1047.6542" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3005-4-11-1-2-1-3-7-5"
width="5.4800777"
height="5.4800777"
x="143.04408"
y="-1047.6542" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-2-6-4-7-0-3-4"
width="5.4800777"
height="5.4800777"
x="112.31335"
y="-1047.6542" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-1-3-1-2-1-1-1-1"
width="5.4800777"
height="5.4800777"
x="155.33638"
y="-1047.6542" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-8-7-2-6-3-1-4-2"
width="5.4800777"
height="5.4800777"
x="130.7518"
y="-1047.6542" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-8-7-7-1-8-1-8-0"
width="5.4800777"
height="5.4800777"
x="149.19023"
y="-1047.6542" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-5-1-1-2-4-8-4"
width="5.4800777"
height="5.4800777"
x="118.45951"
y="-1053.7833" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-4-1-7-1-0-7-9-60"
width="5.4800777"
height="5.4800777"
x="124.60566"
y="-1053.7833" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-4-7-1-5-0-8-2-7"
width="5.4800777"
height="5.4800777"
x="136.89793"
y="-1053.7833" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-4-11-1-2-1-3-7-17"
width="5.4800777"
height="5.4800777"
x="143.04408"
y="-1053.7833" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-2-6-4-7-0-3-7"
width="5.4800777"
height="5.4800777"
x="112.31335"
y="-1053.7833" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-1-3-1-2-1-1-1-7"
width="5.4800777"
height="5.4800777"
x="155.33638"
y="-1053.7833" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#d2d2d2;fill-opacity:1;stroke:none"
id="rect3005-1-8-7-2-6-3-1-4-7"
width="5.4800777"
height="5.4800777"
x="130.7518"
y="-1053.7833" />
<rect
- transform="scale(-1,-1)"
+ transform="scale(-1)"
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3005-8-7-7-1-8-1-8-33"
width="5.4800777"
height="5.4800777"
x="149.19023"
y="-1053.7833" />
<g
- style="fill:#000000;fill-opacity:0;stroke:#00adff;stroke-width:2.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="fill:#000000;fill-opacity:0;stroke:#00adff;stroke-width:2.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="g4869">
<path
- style="fill:#000000;fill-opacity:0;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="fill:#000000;fill-opacity:0;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -137.625,1069.3622 12,4.875 5.875,13 -5.75,13.625 -12,4.75 z"
id="path4863"
inkscape:connector-curvature="0"
transform="translate(1.185093,-57.81894)" />
<path
- style="fill:#000000;fill-opacity:0;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="fill:#000000;fill-opacity:0;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -125.625,1074.3622 -12.125,13.125"
id="path4865"
inkscape:connector-curvature="0"
transform="translate(1.185093,-57.81894)" />
<path
- style="fill:#000000;fill-opacity:0;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="fill:#000000;fill-opacity:0;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -120.25,1087.2372 -17.25,0.125 12,13.375"
id="path4867"
inkscape:connector-curvature="0"
transform="translate(1.185093,-57.81894)" />
</g>
</g>
</g>
<g
id="g4275-7"
transform="translate(0.29674306,42.186971)" />
</g>
</g>
<g
inkscape:label="Morphological Operations"
id="layer7"
- inkscape:groupmode="layer"
- sodipodi:insensitive="true">
+ inkscape:groupmode="layer">
<g
inkscape:export-ydpi="90.470001"
inkscape:export-xdpi="90.470001"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\MorphologicalOperations_48x48.png"
- transform="translate(-109.1111,123.0997)"
+ transform="translate(259.11232,-881.79596)"
id="g4984">
<path
- style="fill:#bf264a;fill-opacity:0.32978725;stroke:#c0264b;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.71808543;stroke-dasharray:none"
+ style="fill:#bf264a;fill-opacity:0.329787;stroke:#c0264b;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.718085"
d="m -108.14089,906.34775 c -4.69686,-21.37148 26.795676,-29.59419 38.148906,-20.16936 10.11729,8.39884 12.92174,37.39802 -3.99393,42.87973 -4.33136,1.40362 -6.5528,-4.31454 -9.77824,-9.2112 -2.95178,-4.48121 -9.83544,-5.58262 -14.18533,-8.41713 -3.117706,-2.03159 -9.282166,-0.94484 -10.191406,-5.08204 z"
id="path4970"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sassss" />
<path
- style="fill:#bf264a;fill-opacity:0.37765958;stroke:#c0264a;stroke-width:0.99999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.70744683;stroke-dasharray:none"
+ style="fill:#bf264a;fill-opacity:0.37766;stroke:#c0264a;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.707447"
d="m -103.44466,903.12804 c -2.30997,-13.73691 22.213165,-19.81546 30.682509,-13.32011 7.988188,6.12632 10.465752,30.47133 -3.478182,32.94222 -3.259751,0.57764 -4.279427,-9.59405 -7.577458,-9.59621 -4.475588,-0.004 -11.530283,-7.45772 -12.794727,-7.01813 -2.777961,0.96576 -6.292672,0.20031 -6.832142,-3.00777 z"
id="path4968"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssssss" />
<path
- style="fill:#bf264a;fill-opacity:0.48404254;stroke:#c0264b;stroke-width:1.29999983;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="fill:#bf264a;fill-opacity:0.484043;stroke:#c0264b;stroke-width:1.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -97.037608,899.72564 c 1.053535,-7.83495 17.4303,-12.31753 23.208337,-5.65907 4.665289,5.37616 5.313738,21.46326 -2.279392,23.53663 -2.84013,0.77552 -2.97128,-10.19566 -5.284037,-8.61723 -1.784315,1.21777 -6.79947,-1.84848 -8.910347,-5.01601 -1.604756,-2.40805 -7.573523,1.99493 -6.734561,-4.24432 z"
id="path4955"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sassss" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="g7924"
- inkscape:label="Closing"
- sodipodi:insensitive="true">
+ inkscape:label="Closing">
<g
id="g7926"
- transform="translate(-103.6111,307.0997)"
+ transform="translate(258.6131,-732.30554)"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\Closing_48x48.png"
inkscape:export-xdpi="91.980354"
inkscape:export-ydpi="91.980354">
<path
style="fill:#bf264a;fill-opacity:1;stroke:none"
d="m -186.71875,1189.4062 c -13.09987,0.1051 -28.40712,8.6705 -25.03125,24.0313 0.90924,4.1372 7.06979,3.0623 10.1875,5.0937 4.34989,2.8347 11.23572,3.9251 14.1875,8.4063 3.22544,4.8967 5.44989,10.6225 9.78125,9.2187 16.91567,-5.4816 14.11729,-34.476 4,-42.875 -3.1931,-2.6506 -7.99897,-3.9159 -13.125,-3.875 z m -7.21875,5.5626 c 5.77684,0 10.46875,4.6919 10.46875,10.4687 0,5.7768 -4.69191,10.4375 -10.46875,10.4375 -5.77684,0 -10.46875,-4.6607 -10.46875,-10.4375 0,-5.7768 4.69191,-10.4687 10.46875,-10.4687 z m 16.65625,18.4062 c 2.87653,0 5.1875,2.3422 5.1875,5.2188 0,2.8765 -2.31097,5.1874 -5.1875,5.1874 -2.87653,0 -5.21875,-2.3109 -5.21875,-5.1874 0,-2.8766 2.34222,-5.2188 5.21875,-5.2188 z"
transform="translate(103.6111,-307.0997)"
id="path7928"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cscscccssssssssss" />
<g
id="g7976"
- transform="translate(-0.74999999,1.1344127e-8)"
+ transform="translate(-0.74999999)"
style="stroke:#00adff;stroke-opacity:1">
<path
inkscape:connector-curvature="0"
id="path7956"
d="m -77.8889,917.01249 10.5,-10.5"
- 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" />
<path
inkscape:connector-curvature="0"
id="path7956-5"
d="m -67.3889,917.01249 -10.5,-10.5"
- 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" />
</g>
</g>
</g>
<g
inkscape:label="Fill Holes"
id="g4366"
- inkscape:groupmode="layer"
- sodipodi:insensitive="true">
+ inkscape:groupmode="layer">
<g
inkscape:export-ydpi="91.980324"
inkscape:export-xdpi="91.980324"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\FillHoles_48x48.png"
- transform="translate(-97.954246,422.71166)"
+ transform="translate(258.6131,-632.30554)"
id="g4368">
<path
sodipodi:nodetypes="cscscccssssssssss"
inkscape:connector-curvature="0"
id="path4370"
transform="translate(103.6111,-307.0997)"
d="m -186.71875,1189.4062 c -13.09987,0.1051 -28.40712,8.6705 -25.03125,24.0313 0.90924,4.1372 7.06979,3.0623 10.1875,5.0937 4.34989,2.8347 11.23572,3.9251 14.1875,8.4063 3.22544,4.8967 5.44989,10.6225 9.78125,9.2187 16.91567,-5.4816 14.11729,-34.476 4,-42.875 -3.1931,-2.6506 -7.99897,-3.9159 -13.125,-3.875 z m -7.21875,5.5626 c 5.77684,0 10.46875,4.6919 10.46875,10.4687 0,5.7768 -4.69191,10.4375 -10.46875,10.4375 -5.77684,0 -10.46875,-4.6607 -10.46875,-10.4375 0,-5.7768 4.69191,-10.4687 10.46875,-10.4687 z m 16.65625,18.4062 c 2.87653,0 5.1875,2.3422 5.1875,5.2188 0,2.8765 -2.31097,5.1874 -5.1875,5.1874 -2.87653,0 -5.21875,-2.3109 -5.21875,-5.1874 0,-2.8766 2.34222,-5.2188 5.21875,-5.2188 z"
style="fill:#bf264a;fill-opacity:1;stroke:none" />
<g
- transform="translate(-0.74999999,1.1344127e-8)"
+ transform="translate(-0.74999999)"
id="g4372"
- style="stroke:#00adff;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none">
+ style="stroke:#00adff;stroke-width:2;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"
+ 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 -77.8889,917.01249 10.5,-10.5"
id="path4374"
inkscape:connector-curvature="0" />
<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 -67.3889,917.01249 -10.5,-10.5"
id="path4376"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(1.6788225,0,0,1.6788225,31.811662,-632.3363)"
id="g4372-4"
- style="stroke:#00adff;stroke-width:1.19131112;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none">
+ style="stroke:#00adff;stroke-width:1.19131;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
<path
- style="fill:none;stroke:#00adff;stroke-width:1.19131112;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="fill:none;stroke:#00adff;stroke-width:1.19131;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -77.8889,917.01249 10.5,-10.5"
id="path4374-0"
inkscape:connector-curvature="0" />
<path
- style="fill:none;stroke:#00adff;stroke-width:1.19131112;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="fill:none;stroke:#00adff;stroke-width:1.19131;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -67.3889,917.01249 -10.5,-10.5"
id="path4376-9"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
<g
inkscape:label="Opening"
id="g3324"
- inkscape:groupmode="layer"
- sodipodi:insensitive="true">
+ inkscape:groupmode="layer">
<g
inkscape:export-ydpi="90.470001"
inkscape:export-xdpi="90.470001"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\Opening_48x48.png"
- transform="translate(-100.8611,360.5997)"
+ transform="translate(257.18712,-684.10243)"
id="g3326">
<path
sodipodi:nodetypes="cscscccssssssssss"
inkscape:connector-curvature="0"
id="path3328"
d="m -81.185775,884.6034 c -13.09987,0.1051 -28.407115,8.6705 -25.031245,24.0313 0.90924,4.1372 7.069785,3.0623 10.187495,5.0937 4.34989,2.8347 11.23572,3.9251 14.1875,8.4063 3.22544,4.8967 5.44989,10.6225 9.78125,9.2187 16.91567,-5.4816 14.11729,-34.476 4,-42.875 -3.1931,-2.6506 -7.99897,-3.9159 -13.125,-3.875 z m -7.21875,5.5626 c 5.77684,0 10.46875,4.6919 10.46875,10.4687 0,5.7768 -4.69191,10.4375 -10.46875,10.4375 -5.77684,0 -10.46875,-4.6607 -10.46875,-10.4375 0,-5.7768 4.69191,-10.4687 10.46875,-10.4687 z m 16.65625,18.4062 c 2.87653,0 5.1875,2.3422 5.1875,5.2188 0,2.8765 -2.31097,5.1874 -5.1875,5.1874 -2.87653,0 -5.21875,-2.3109 -5.21875,-5.1874 0,-2.8766 2.34222,-5.2188 5.21875,-5.2188 z"
- style="fill:#bf264a;fill-opacity:0.32978725;stroke:#c0264b;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:none"
+ style="fill:#bf264a;fill-opacity:0.329787;stroke:#c0264b;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\Opening_48x48.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:#bf264a;fill-opacity:1;stroke:none"
d="m -81.2014,884.5878 c -3.0869,0.0248 -6.28505,0.5575 -9.34375,1.5 -0.18346,1.4226 -0.09951,2.9722 0.15625,4.25 0.63395,-0.1201 1.30015,-0.1875 1.96875,-0.1875 5.77684,0 10.46875,4.692 10.46875,10.4688 0,1.1769 -0.20351,2.3206 -0.5625,3.375 2.00945,2.1884 3.47913,3.4015 5.8125,4.6562 0.30872,-0.0572 0.61253,-0.0625 0.9375,-0.0625 2.87653,0 5.1875,2.3109 5.1875,5.1875 0,2.8765 -2.31097,5.1875 -5.1875,5.1875 -2.87653,0 -5.21875,-2.311 -5.21875,-5.1875 0,-0.0738 -0.0031,-0.1457 0,-0.2188 -1.65474,-2.0148 -3.24164,-3.2089 -5.53125,-4.2812 -1.67813,1.139 -3.72408,1.8125 -5.90625,1.8125 -0.51403,0 -1.00569,-0.0537 -1.5,-0.125 -1.02993,1.8634 -1.12047,3.6519 -0.59375,5.4375 3.36987,1.4176 6.79365,2.8911 8.65625,5.7188 3.22544,4.8967 5.44989,10.6225 9.78125,9.2187 16.91567,-5.4816 14.11729,-34.476 4,-42.875 -3.1931,-2.6506 -7.99897,-3.9159 -13.125,-3.875 z m -17.9375,5.75 c -5.48357,4.0778 -8.8624,10.2337 -7.09375,18.2813 0.65259,2.9694 4.0037,3.2936 7,3.9687 1.67262,-1.2671 3.13022,-3.098 3.625,-4.375 -2.02329,-1.9014 -3.28125,-4.5957 -3.28125,-7.5937 0,-2.763 1.06042,-5.2843 2.8125,-7.1563 -0.68689,-1.4325 -1.75339,-2.4391 -3.0625,-3.125 z"
id="path3328-1"
inkscape:connector-curvature="0"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\Opening_48x48.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="g3224"
- inkscape:label="Erode"
- sodipodi:insensitive="true">
+ inkscape:label="Erode">
<g
id="g3226"
- transform="translate(-105.47406,184.64955)"
+ transform="translate(258.61477,-832.31297)"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\Erode_48x48.png"
inkscape:export-xdpi="91.564468"
inkscape:export-ydpi="91.564468">
<g
id="g6644">
<path
- style="fill:#bf264a;fill-opacity:0.32978725;stroke:none"
+ style="fill:#bf264a;fill-opacity:0.329787;stroke:none"
d="m -108.14089,906.34775 c -4.69686,-21.37148 26.795676,-29.59419 38.148906,-20.16936 10.11729,8.39884 12.92174,37.39802 -3.99393,42.87973 -4.33136,1.40362 -6.5528,-4.31454 -9.77824,-9.2112 -2.95178,-4.48121 -9.83544,-5.58262 -14.18533,-8.41713 -3.117706,-2.03159 -9.282166,-0.94484 -10.191406,-5.08204 z"
id="path3228"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sassss" />
<path
style="fill:#bf264a;fill-opacity:1;stroke:none"
d="m -99.015895,899.4289 c 1.053535,-7.83495 18.783308,-11.07876 25.186624,-5.36233 5.880037,5.24929 5.313738,21.46326 -2.279392,23.53663 -2.84013,0.77552 -2.58498,-3.13574 -4.294893,-5.35306 -1.88323,-2.44206 -7.039508,-5.76376 -12.075607,-6.79646 -2.834789,-0.58131 -7.375694,0.21447 -6.536732,-6.02478 z"
id="path3232"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sassss" />
</g>
<g
id="g7858-6"
transform="matrix(0.61016652,0,0,0.61016652,-48.204939,369.60786)"
style="stroke:#00adff;stroke-opacity:1">
<path
- style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#TriangleInMUm);marker-end:none"
+ style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#TriangleInMUm);marker-end:none"
d="m -76.049605,896.6784 8.71058,-8.78881"
id="path6666-1"
inkscape:connector-curvature="0" />
<path
- style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#TriangleInMUF);marker-end:none"
+ style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#TriangleInMUF);marker-end:none"
d="m -76.070477,907.99147 8.7888,8.71058"
id="path6666-3-89"
inkscape:connector-curvature="0" />
<path
- style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#TriangleInMUl);marker-end:none"
+ style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#TriangleInMUl);marker-end:none"
d="m -86.796124,896.67838 -8.71058,-8.7888"
id="path6666-6-2"
inkscape:connector-curvature="0" />
<path
- style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#TriangleInMUX);marker-end:none"
+ style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#TriangleInMUX);marker-end:none"
d="m -86.775251,907.99147 -8.7888,8.71058"
id="path6666-3-8-7"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
<g
inkscape:label="Dilate"
id="g6654"
- inkscape:groupmode="layer"
- sodipodi:insensitive="true">
+ inkscape:groupmode="layer">
<g
inkscape:export-ydpi="91.761711"
inkscape:export-xdpi="91.761711"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\Dilate_48x48.png"
- transform="translate(-104.76695,244.40007)"
+ transform="translate(258.61477,-782.31297)"
id="g6656">
<g
id="g6658">
<path
sodipodi:nodetypes="sassss"
inkscape:connector-curvature="0"
id="path6660"
d="m -108.14089,906.34775 c -4.69686,-21.37148 26.795676,-29.59419 38.148906,-20.16936 10.11729,8.39884 12.92174,37.39802 -3.99393,42.87973 -4.33136,1.40362 -6.5528,-4.31454 -9.77824,-9.2112 -2.95178,-4.48121 -9.83544,-5.58262 -14.18533,-8.41713 -3.117706,-2.03159 -9.282166,-0.94484 -10.191406,-5.08204 z"
- style="fill:#bf264a;fill-opacity:0.32978725;stroke:none" />
+ style="fill:#bf264a;fill-opacity:0.329787;stroke:none" />
<path
sodipodi:nodetypes="sassss"
inkscape:connector-curvature="0"
id="path6662"
d="m -99.015895,899.4289 c 1.053535,-7.83495 18.783308,-11.07876 25.186624,-5.36233 5.880037,5.24929 5.313738,21.46326 -2.279392,23.53663 -2.84013,0.77552 -2.58498,-3.13574 -4.294893,-5.35306 -1.88323,-2.44206 -7.039508,-5.76376 -12.075607,-6.79646 -2.834789,-0.58131 -7.375694,0.21447 -6.536732,-6.02478 z"
style="fill:#bf264a;fill-opacity:1;stroke:none" />
<g
id="g7858"
transform="matrix(0.59392347,0,0,0.59392347,-49.465386,384.04206)"
style="stroke:#00adff;stroke-opacity:1">
<path
- style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#TriangleInMUe);marker-end:none"
+ style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#TriangleInMUe);marker-end:none"
d="m -70.775752,891.36279 -8.710577,8.78881"
id="path6666"
inkscape:connector-curvature="0" />
<path
- style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#TriangleInMUt);marker-end:none"
+ style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#TriangleInMUt);marker-end:none"
d="m -70.754879,913.26532 -8.7888,-8.71058"
id="path6666-3"
inkscape:connector-curvature="0" />
<path
- style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#TriangleInMUg);marker-end:none"
+ style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#TriangleInMUg);marker-end:none"
d="m -92.069976,891.36279 8.71058,8.7888"
id="path6666-6"
inkscape:connector-curvature="0" />
<path
- style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#TriangleInMUZ);marker-end:none"
+ style="fill:#00adff;fill-opacity:1;stroke:#00adff;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#TriangleInMUZ);marker-end:none"
d="m -92.090849,913.26532 8.7888,-8.71058"
id="path6666-3-8"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
</g>
<g
- sodipodi:insensitive="true"
inkscape:label="Boolean Difference"
id="layer6"
inkscape:groupmode="layer">
<g
inkscape:export-ydpi="89.07"
inkscape:export-xdpi="89.07"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\BooleanDifference_48x48.png"
id="g3986-1"
- transform="translate(-54.972257,-38.513914)">
+ transform="translate(331.70808,-1043.928)">
<g
transform="translate(-6.7261759,37.587454)"
id="g4014">
<rect
y="1022.3405"
x="-274.9819"
height="32"
width="32"
id="rect3982-7"
style="fill:#bf264b;fill-opacity:1;stroke:none" />
- <path
+ <circle
transform="matrix(0.86040482,0,0,0.86040482,47.623043,174.07452)"
- d="m -318.89986,985.89172 c 0,10.27023 -8.32567,18.59588 -18.5959,18.59588 -10.27023,0 -18.5959,-8.32565 -18.5959,-18.59588 0,-10.27023 8.32567,-18.59589 18.5959,-18.59589 10.27023,0 18.5959,8.32566 18.5959,18.59589 z"
- sodipodi:ry="18.595898"
- sodipodi:rx="18.595898"
- sodipodi:cy="985.89172"
- sodipodi:cx="-337.49576"
id="path3984-4"
style="fill:#999999;fill-opacity:1;stroke:none"
- sodipodi:type="arc" />
+ cx="-337.49576"
+ cy="985.89172"
+ r="18.595898" />
</g>
</g>
</g>
<g
- sodipodi:insensitive="true"
inkscape:label="Boolean Union"
id="layer8"
inkscape:groupmode="layer">
<g
inkscape:export-ydpi="89.07"
inkscape:export-xdpi="89.07"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\BooleanUnion_48x48.png"
style="fill:#bf264b;fill-opacity:1"
- transform="translate(-3.5367938,-1.3221171)"
+ transform="translate(374.9819,-1006.3405)"
id="g3986">
<rect
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="rect3982"
width="32"
height="32"
x="-274.9819"
y="1022.3405" />
- <path
- sodipodi:type="arc"
+ <circle
style="fill:#bf264b;fill-opacity:1;stroke:none"
id="path3984"
- sodipodi:cx="-337.49576"
- sodipodi:cy="985.89172"
- sodipodi:rx="18.595898"
- sodipodi:ry="18.595898"
- d="m -318.89986,985.89172 c 0,10.27023 -8.32567,18.59588 -18.5959,18.59588 -10.27023,0 -18.5959,-8.32565 -18.5959,-18.59588 0,-10.27023 8.32567,-18.59589 18.5959,-18.59589 10.27023,0 18.5959,8.32566 18.5959,18.59589 z"
- transform="matrix(0.86040482,0,0,0.86040482,47.623043,174.07452)" />
+ transform="matrix(0.86040482,0,0,0.86040482,47.623043,174.07452)"
+ cx="-337.49576"
+ cy="985.89172"
+ r="18.595898" />
</g>
</g>
<g
- sodipodi:insensitive="true"
inkscape:label="Boolean Intersection"
id="layer9"
inkscape:groupmode="layer">
<g
- transform="translate(-121.4427,-2.9047467)"
+ transform="translate(275.5264,-1007.4014)"
id="g3986-0">
<g
inkscape:export-ydpi="89.07"
inkscape:export-xdpi="89.07"
inkscape:export-filename="D:\MITK\Plugins\org.mitk.gui.qt.segmentation\resources\BooleanIntersection_48x48.png"
transform="translate(-18.744726,34.691732)"
id="g4069">
<rect
style="fill:#999999;fill-opacity:1;stroke:none"
id="rect3982-2"
width="32"
height="32"
x="-256.78168"
y="988.70966" />
- <path
- sodipodi:type="arc"
+ <circle
style="fill:#999999;fill-opacity:1;stroke:none"
id="path3984-45"
- sodipodi:cx="-337.49576"
- sodipodi:cy="985.89172"
- sodipodi:rx="18.595898"
- sodipodi:ry="18.595898"
- d="m -318.89986,985.89172 c 0,10.27023 -8.32567,18.59588 -18.5959,18.59588 -10.27023,0 -18.5959,-8.32565 -18.5959,-18.59588 0,-10.27023 8.32567,-18.59589 18.5959,-18.59589 10.27023,0 18.5959,8.32566 18.5959,18.59589 z"
- transform="matrix(0.86040482,0,0,0.86040482,65.823282,140.44364)" />
+ transform="matrix(0.86040482,0,0,0.86040482,65.823282,140.44364)"
+ cx="-337.49576"
+ cy="985.89172"
+ r="18.595898" />
<path
style="fill:#bf264b;fill-opacity:1;stroke:none"
- d="m -240.53168,988.70966 c 0,8.7535 7.02819,15.86694 15.75,15.99994 l 0,-15.99994 -15.75,0 z"
+ d="m -240.53168,988.70966 c 0,8.7535 7.02819,15.86694 15.75,15.99994 v -15.99994 z"
id="rect3982-9"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer11"
+ inkscape:label="Conversion"
+ inkscape:export-filename="Extract_48x48.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96">
+ <g
+ id="g34619"
+ inkscape:export-filename="Extract_48x48.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96">
+ <g
+ id="g8428"
+ transform="translate(400,-400)">
+ <rect
+ style="fill:#808080;fill-opacity:0;stroke:none;stroke-width:0.822952"
+ id="rect1111"
+ width="48"
+ height="48"
+ x="50"
+ y="400"
+ inkscape:export-filename="Extract_48x48.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96" />
+ <g
+ id="g7838"
+ transform="translate(0,4)">
+ <g
+ id="g7760"
+ transform="matrix(1.3333333,0,0,1.3333333,-16.166665,-136.83332)"
+ style="stroke:none;stroke-opacity:1">
+ <rect
+ style="fill:#999999;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2382"
+ width="24"
+ height="24"
+ x="50"
+ y="400" />
+ <circle
+ style="fill:#bf264a;fill-opacity:1;stroke:none;stroke-width:0.981133;stroke-opacity:1"
+ id="path2436"
+ cx="57.5"
+ cy="407.5"
+ r="6.5" />
+ <rect
+ style="fill:#00adff;fill-opacity:1;stroke:none;stroke-width:0.869565;stroke-opacity:1"
+ id="rect2490"
+ width="10"
+ height="10"
+ x="63"
+ y="413" />
+ </g>
+ <g
+ id="g7822"
+ transform="matrix(1.0958904,0,0,1.0958904,-9.6164374,-39.847279)"
+ style="stroke-width:0.68437501;stroke-dasharray:none;filter:url(#filter34631)">
+ <rect
+ style="fill:#d2d2d2;fill-opacity:1;stroke:#000000;stroke-width:0.68437501;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2382-7"
+ width="18"
+ height="18"
+ x="79"
+ y="401" />
+ <circle
+ style="fill:#bf264a;fill-opacity:1;stroke:none;stroke-width:0.68437501;stroke-dasharray:none"
+ id="path2436-3"
+ cx="84.5"
+ cy="406.5"
+ r="4.5" />
+ </g>
+ <g
+ id="g7826"
+ transform="matrix(1.0958904,0,0,1.0958904,-9.6164374,-39.113438)"
+ style="stroke:#000000;stroke-width:0.68437501;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter27856)">
+ <rect
+ style="fill:#d2d2d2;fill-opacity:1;stroke:#000000;stroke-width:0.68437501;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2382-7-7"
+ width="18"
+ height="18"
+ x="79"
+ y="421" />
+ <rect
+ style="fill:#00adff;fill-opacity:1;stroke:none;stroke-width:0.68437501;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2490-4"
+ width="8"
+ height="8"
+ x="88"
+ y="430" />
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
</svg>
diff --git a/Plugins/org.mitk.gui.qt.segmentation/resources/SegmentationUtilities.qrc b/Plugins/org.mitk.gui.qt.segmentation/resources/SegmentationUtilities.qrc
index bc86bfb60d..5bfd4e3c2a 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/resources/SegmentationUtilities.qrc
+++ b/Plugins/org.mitk.gui.qt.segmentation/resources/SegmentationUtilities.qrc
@@ -1,10 +1,11 @@
<RCC>
<qresource prefix="/SegmentationUtilities">
<file>BooleanOperations_48x48.png</file>
<file>ContourModelSetToImage_48x48.png</file>
+ <file>Extract_48x48.png</file>
<file>ImageMasking_48x48.png</file>
<file>MorphologicalOperations_48x48.png</file>
<file>SurfaceToImage_48x48.png</file>
<file>segmentation_utilities.svg</file>
</qresource>
</RCC>
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.cpp
index dc1c7ff7e0..73023101b5 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.cpp
@@ -1,356 +1,366 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkSegmentAnythingPreferencePage.h"
#include <mitkCoreServices.h>
#include <mitkIPreferencesService.h>
#include <mitkIPreferences.h>
#include <mitkProcessExecutor.h>
#include <itkCommand.h>
#include <QmitkSegmentAnythingToolGUI.h>
#include <QFileDialog>
#include <QmitkStyleManager.h>
#include <QDir>
#include <QDirIterator>
namespace
{
mitk::IPreferences* GetPreferences()
{
auto* preferencesService = mitk::CoreServices::GetPreferencesService();
return preferencesService->GetSystemPreferences()->Node("org.mitk.views.segmentation");
}
}
QmitkSegmentAnythingPreferencePage::QmitkSegmentAnythingPreferencePage()
: m_Ui(new Ui::QmitkSegmentAnythingPreferencePage),
m_Control(nullptr){}
QmitkSegmentAnythingPreferencePage::~QmitkSegmentAnythingPreferencePage(){}
void QmitkSegmentAnythingPreferencePage::Init(berry::IWorkbench::Pointer){}
void QmitkSegmentAnythingPreferencePage::CreateQtControl(QWidget* parent)
{
m_Control = new QWidget(parent);
m_Ui->setupUi(m_Control);
m_Ui->samModelTipLabel->hide();
#ifndef _WIN32
m_Ui->sysPythonComboBox->addItem("/usr/bin");
#endif
this->AutoParsePythonPaths();
m_Ui->timeoutEdit->setValidator(new QIntValidator(0, 1000, this));
m_Ui->sysPythonComboBox->addItem("Select...");
m_Ui->sysPythonComboBox->setCurrentIndex(0);
connect(m_Ui->installSAMButton, SIGNAL(clicked()), this, SLOT(OnInstallBtnClicked()));
connect(m_Ui->clearSAMButton, SIGNAL(clicked()), this, SLOT(OnClearInstall()));
connect(m_Ui->sysPythonComboBox,
QOverload<int>::of(&QComboBox::activated),
[=](int index) { OnSystemPythonChanged(m_Ui->sysPythonComboBox->itemText(index)); });
QIcon deleteIcon =
QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/edit-delete.svg"));
m_Ui->clearSAMButton->setIcon(deleteIcon);
const QString storageDir = m_Installer.GetVirtualEnvPath();
bool isInstalled = QmitkSegmentAnythingToolGUI::IsSAMInstalled(storageDir);
QString welcomeText;
if (isInstalled)
{
m_PythonPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(storageDir).first;
m_Installer.SetVirtualEnvPath(m_PythonPath);
welcomeText += " Segment Anything tool & MedSAM is already found installed.";
m_Ui->installSAMButton->setEnabled(false);
}
else
{
welcomeText += " Segment Anything tool & MedSAM not installed. Please click on \"Install SAM with MedSAM\" above. \
The installation will create a new virtual environment using the System Python selected above.";
m_Ui->installSAMButton->setEnabled(true);
}
this->WriteStatusMessage(welcomeText);
m_Ui->samModelTypeComboBox->addItems(VALID_MODELS);
m_Ui->gpuComboBox->addItem(CPU_ID);
this->SetGPUInfo();
this->Update();
}
QWidget* QmitkSegmentAnythingPreferencePage::GetQtControl() const
{
return m_Control;
}
bool QmitkSegmentAnythingPreferencePage::PerformOk()
{
auto* prefs = GetPreferences();
prefs->Put("sam parent path", m_Installer.STORAGE_DIR.toStdString());
prefs->Put("sam python path", m_PythonPath.toStdString());
prefs->Put("sam modeltype", m_Ui->samModelTypeComboBox->currentText().toStdString());
prefs->PutInt("sam gpuid", FetchSelectedGPUFromUI());
prefs->PutInt("sam timeout", std::stoi(m_Ui->timeoutEdit->text().toStdString()));
return true;
}
void QmitkSegmentAnythingPreferencePage::PerformCancel(){}
void QmitkSegmentAnythingPreferencePage::Update()
{
auto* prefs = GetPreferences();
m_Ui->samModelTypeComboBox->setCurrentText(QString::fromStdString(prefs->Get("sam modeltype", "vit_b")));
m_Ui->timeoutEdit->setText(QString::number(prefs->GetInt("sam timeout", 300)));
int gpuId = prefs->GetInt("sam gpuid", -1);
if (gpuId == -1)
{
m_Ui->gpuComboBox->setCurrentText(CPU_ID);
}
else if (m_GpuLoader.GetGPUCount() == 0)
{
m_Ui->gpuComboBox->setCurrentText(QString::number(gpuId));
}
else
{
std::vector<QmitkGPUSpec> specs = m_GpuLoader.GetAllGPUSpecs();
QmitkGPUSpec gpuSpec = specs[gpuId];
m_Ui->gpuComboBox->setCurrentText(QString::number(gpuSpec.id) + ": " + gpuSpec.name + " (" + gpuSpec.memory + ")");
}
}
std::pair<QString, QString> QmitkSegmentAnythingPreferencePage::OnSystemPythonChanged(const QString &pyEnv)
{
std::pair<QString, QString> pyPath;
if (pyEnv == QString("Select..."))
{
QString path = QFileDialog::getExistingDirectory(m_Ui->sysPythonComboBox->parentWidget(), "Python Path", "dir");
if (!path.isEmpty())
{
this->OnSystemPythonChanged(path); // recall same function for new path validation
bool oldState = m_Ui->sysPythonComboBox->blockSignals(true); // block signal firing while inserting item
m_Ui->sysPythonComboBox->insertItem(0, path);
m_Ui->sysPythonComboBox->setCurrentIndex(0);
m_Ui->sysPythonComboBox->blockSignals(oldState); // unblock signal firing after inserting item. Remove this after Qt6 migration
}
}
else
{
QString uiPyPath = this->GetPythonPathFromUI(pyEnv);
pyPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(uiPyPath);
}
return pyPath;
}
QString QmitkSegmentAnythingPreferencePage::GetPythonPathFromUI(const QString &pyUI) const
{
QString fullPath = pyUI;
if (-1 != fullPath.indexOf(")"))
{
fullPath = fullPath.mid(fullPath.indexOf(")") + 2);
}
return fullPath.simplified();
}
void QmitkSegmentAnythingPreferencePage::AutoParsePythonPaths()
{
QString homeDir = QDir::homePath();
std::vector<QString> searchDirs;
#ifdef _WIN32
searchDirs.push_back(QString("C:") + QDir::separator() + QString("ProgramData") + QDir::separator() +
QString("anaconda3"));
#else
// Add search locations for possible standard python paths here
searchDirs.push_back(homeDir + QDir::separator() + "anaconda3");
searchDirs.push_back(homeDir + QDir::separator() + "miniconda3");
searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "miniconda3");
searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "anaconda3");
#endif
for (QString searchDir : searchDirs)
{
if (searchDir.endsWith("anaconda3", Qt::CaseInsensitive))
{
if (QDir(searchDir).exists())
{
m_Ui->sysPythonComboBox->addItem("(base): " + searchDir);
searchDir.append((QDir::separator() + QString("envs")));
}
}
for (QDirIterator subIt(searchDir, QDir::AllDirs, QDirIterator::NoIteratorFlags); subIt.hasNext();)
{
subIt.next();
QString envName = subIt.fileName();
if (!envName.startsWith('.')) // Filter out irrelevent hidden folders, if any.
{
m_Ui->sysPythonComboBox->addItem("(" + envName + "): " + subIt.filePath());
}
}
}
}
void QmitkSegmentAnythingPreferencePage::SetGPUInfo()
{
std::vector<QmitkGPUSpec> specs = m_GpuLoader.GetAllGPUSpecs();
for (const QmitkGPUSpec &gpuSpec : specs)
{
m_Ui->gpuComboBox->addItem(QString::number(gpuSpec.id) + ": " + gpuSpec.name + " (" + gpuSpec.memory + ")");
}
if (specs.empty())
{
m_Ui->gpuComboBox->setCurrentIndex(m_Ui->gpuComboBox->findText("cpu"));
}
else
{
m_Ui->gpuComboBox->setCurrentIndex(m_Ui->gpuComboBox->count()-1);
}
}
int QmitkSegmentAnythingPreferencePage::FetchSelectedGPUFromUI() const
{
QString gpuInfo = m_Ui->gpuComboBox->currentText();
if ("cpu" == gpuInfo)
{
return -1;
}
else if(m_GpuLoader.GetGPUCount() == 0)
{
return static_cast<int>(gpuInfo.toInt());
}
else
{
QString gpuId = gpuInfo.split(":", Qt::SkipEmptyParts).first();
return static_cast<int>(gpuId.toInt());
}
}
void QmitkSegmentAnythingPreferencePage::OnInstallBtnClicked()
{
this->OnClearInstall(); // Clear any installation before
const auto [path, version] = OnSystemPythonChanged(m_Ui->sysPythonComboBox->currentText());
if (path.isEmpty())
{
this->WriteErrorMessage("<b>ERROR: </b>Couldn't find compatible Python.");
return;
}
+ if (!QmitkSetupVirtualEnvUtil::IsVenvInstalled(path))
+ {
+ this->WriteErrorMessage("venv module not found for the selected python to create a new virtual "
+ "environment. Please install venv or select another compatibile python");
+ return;
+ }
//check if python 3.12 and ask for confirmation
if (version.startsWith("3.13") &&
QMessageBox::No == QMessageBox::question(nullptr,
"Installing Segment Anything",
QString("WARNING: This is an unsupported version of Python that may not work. "
"We recommend using a supported Python version between 3.9 and 3.12.\n\n"
"Continue anyway?"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No))
{
return;
}
this->WriteStatusMessage("<b>STATUS: </b>Installing SAM & MedSAM...");
m_Ui->installSAMButton->setEnabled(false);
m_Installer.SetSystemPythonPath(path);
bool isInstalled = false;
if (m_Installer.SetupVirtualEnv(m_Installer.VENV_NAME))
{
isInstalled = QmitkSegmentAnythingToolGUI::IsSAMInstalled(m_Installer.GetVirtualEnvPath());
}
if (isInstalled)
{
m_PythonPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(m_Installer.GetVirtualEnvPath()).first;
this->WriteStatusMessage("<b>STATUS: </b>Successfully installed SAM & MedSAM.");
}
else
{
this->WriteErrorMessage("<b>ERROR: </b>Couldn't install SAM & MedSAM.");
m_Ui->installSAMButton->setEnabled(true);
}
}
void QmitkSegmentAnythingPreferencePage::OnClearInstall()
{
QDir folderPath(m_Installer.GetVirtualEnvPath());
bool isDeleted = folderPath.removeRecursively();
if (isDeleted)
{
this->WriteStatusMessage("Deleted SAM installation.");
m_Ui->installSAMButton->setEnabled(true);
m_PythonPath.clear();
}
else
{
MITK_ERROR << "The virtual environment couldn't be removed. Please check if you have the required access "
"privileges or, some other process is accessing the folders.";
}
}
void QmitkSegmentAnythingPreferencePage::WriteStatusMessage(const QString &message)
{
m_Ui->samInstallStatusLabel->setText(message);
m_Ui->samInstallStatusLabel->setStyleSheet("font-weight: bold; color: white");
qApp->processEvents();
}
void QmitkSegmentAnythingPreferencePage::WriteErrorMessage(const QString &message)
{
m_Ui->samInstallStatusLabel->setText(message);
m_Ui->samInstallStatusLabel->setStyleSheet("font-weight: bold; color: red");
qApp->processEvents();
}
QString QmitkSAMInstaller::GetVirtualEnvPath()
{
return STORAGE_DIR + VENV_NAME;
}
bool QmitkSAMInstaller::SetupVirtualEnv(const QString &venvName)
{
if (GetSystemPythonPath().isEmpty())
{
return false;
}
+ if (!QmitkSetupVirtualEnvUtil::IsVenvInstalled(GetSystemPythonPath()))
+ {
+ return false;
+ }
QDir folderPath(GetBaseDir());
folderPath.mkdir(venvName);
if (!folderPath.cd(venvName))
{
return false; // Check if directory creation was successful.
}
mitk::ProcessExecutor::ArgumentListType args;
auto spExec = mitk::ProcessExecutor::New();
auto spCommand = itk::CStyleCommand::New();
spCommand->SetCallback(&PrintProcessEvent);
spExec->AddObserver(mitk::ExternalProcessOutputEvent(), spCommand);
args.push_back("-m");
args.push_back("venv");
args.push_back(venvName.toStdString());
#ifdef _WIN32
QString pythonFile = GetSystemPythonPath() + QDir::separator() + "python.exe";
QString pythonExeFolder = "Scripts";
#else
QString pythonFile = GetSystemPythonPath() + QDir::separator() + "python3";
QString pythonExeFolder = "bin";
#endif
spExec->Execute(GetBaseDir().toStdString(), pythonFile.toStdString(), args); // Setup local virtual environment
if (folderPath.cd(pythonExeFolder))
{
this->SetPythonPath(folderPath.absolutePath());
this->SetPipPath(folderPath.absolutePath());
this->InstallPytorch();
for (auto &package : PACKAGES)
{
this->PipInstall(package.toStdString(), &PrintProcessEvent);
}
std::string pythonCode; // python syntax to check if torch is installed with CUDA.
pythonCode.append("import torch;");
pythonCode.append("print('Pytorch was installed with CUDA') if torch.cuda.is_available() else print('PyTorch was "
"installed WITHOUT CUDA');");
this->ExecutePython(pythonCode, &PrintProcessEvent);
return true;
}
return false;
}
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp
index 7ed6b1f9a2..e3f2571e73 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp
@@ -1,156 +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.
============================================================================*/
#include "QmitkSegmentationPreferencePage.h"
#include <mitkBaseApplication.h>
#include <mitkCoreServices.h>
#include <mitkIPreferencesService.h>
#include <mitkIPreferences.h>
#include <QFileDialog>
#include <ui_QmitkSegmentationPreferencePageControls.h>
namespace
{
mitk::IPreferences* GetPreferences()
{
auto* preferencesService = mitk::CoreServices::GetPreferencesService();
return preferencesService->GetSystemPreferences()->Node("org.mitk.views.segmentation");
}
}
QmitkSegmentationPreferencePage::QmitkSegmentationPreferencePage()
: m_Ui(new Ui::QmitkSegmentationPreferencePageControls),
m_Control(nullptr),
m_Initializing(false)
{
}
QmitkSegmentationPreferencePage::~QmitkSegmentationPreferencePage()
{
}
void QmitkSegmentationPreferencePage::Init(berry::IWorkbench::Pointer)
{
}
void QmitkSegmentationPreferencePage::CreateQtControl(QWidget* parent)
{
m_Initializing = true;
m_Control = new QWidget(parent);
m_Ui->setupUi(m_Control);
connect(m_Ui->labelSetPresetToolButton, SIGNAL(clicked()), this, SLOT(OnLabelSetPresetButtonClicked()));
connect(m_Ui->suggestionsToolButton, SIGNAL(clicked()), this, SLOT(OnSuggestionsButtonClicked()));
this->Update();
m_Initializing = false;
}
QWidget* QmitkSegmentationPreferencePage::GetQtControl() const
{
return m_Control;
}
bool QmitkSegmentationPreferencePage::PerformOk()
{
auto* prefs = GetPreferences();
prefs->PutBool("compact view", m_Ui->compactViewCheckBox->isChecked());
prefs->PutBool("draw outline", m_Ui->outlineRadioButton->isChecked());
prefs->PutBool("selection mode", m_Ui->selectionModeCheckBox->isChecked());
prefs->Put("label set preset", m_Ui->labelSetPresetLineEdit->text().toStdString());
prefs->PutBool("default label naming", m_Ui->defaultNameRadioButton->isChecked());
prefs->Put("label suggestions", m_Ui->suggestionsLineEdit->text().toStdString());
prefs->PutBool("replace standard suggestions", m_Ui->replaceStandardSuggestionsCheckBox->isChecked());
prefs->PutBool("suggest once", m_Ui->suggestOnceCheckBox->isChecked());
+ prefs->PutBool("monailabel allow all models", m_Ui->allowAllModelsCheckBox->isChecked());
return true;
}
void QmitkSegmentationPreferencePage::PerformCancel()
{
}
void QmitkSegmentationPreferencePage::Update()
{
auto* prefs = GetPreferences();
m_Ui->compactViewCheckBox->setChecked(prefs->GetBool("compact view", false));
if (prefs->GetBool("draw outline", true))
{
m_Ui->outlineRadioButton->setChecked(true);
}
else
{
m_Ui->overlayRadioButton->setChecked(true);
}
m_Ui->selectionModeCheckBox->setChecked(prefs->GetBool("selection mode", false));
auto labelSetPreset = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABELSET_PRESET.toStdString(), "");
bool isOverriddenByCmdLineArg = !labelSetPreset.empty();
if (!isOverriddenByCmdLineArg)
labelSetPreset = prefs->Get("label set preset", "");
m_Ui->labelSetPresetLineEdit->setDisabled(isOverriddenByCmdLineArg);
m_Ui->labelSetPresetToolButton->setDisabled(isOverriddenByCmdLineArg);
m_Ui->labelSetPresetCmdLineArgLabel->setVisible(isOverriddenByCmdLineArg);
m_Ui->labelSetPresetLineEdit->setText(QString::fromStdString(labelSetPreset));
if (prefs->GetBool("default label naming", true))
{
m_Ui->defaultNameRadioButton->setChecked(true);
}
else
{
m_Ui->askForNameRadioButton->setChecked(true);
}
auto labelSuggestions = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString(), "");
isOverriddenByCmdLineArg = !labelSuggestions.empty();
if (!isOverriddenByCmdLineArg)
labelSuggestions = prefs->Get("label suggestions", "");
m_Ui->defaultNameRadioButton->setDisabled(isOverriddenByCmdLineArg);
m_Ui->askForNameRadioButton->setDisabled(isOverriddenByCmdLineArg);
m_Ui->suggestionsLineEdit->setDisabled(isOverriddenByCmdLineArg);
m_Ui->suggestionsToolButton->setDisabled(isOverriddenByCmdLineArg);
m_Ui->suggestionsCmdLineArgLabel->setVisible(isOverriddenByCmdLineArg);
m_Ui->suggestionsLineEdit->setText(QString::fromStdString(labelSuggestions));
m_Ui->replaceStandardSuggestionsCheckBox->setChecked(prefs->GetBool("replace standard suggestions", true));
m_Ui->suggestOnceCheckBox->setChecked(prefs->GetBool("suggest once", true));
+
+ m_Ui->allowAllModelsCheckBox->setChecked(prefs->GetBool("monailabel allow all models", true));
}
void QmitkSegmentationPreferencePage::OnLabelSetPresetButtonClicked()
{
const auto filename = QFileDialog::getOpenFileName(m_Control, QStringLiteral("Load Label Set Preset"), QString(), QStringLiteral("Label set preset (*.lsetp)"));
if (!filename.isEmpty())
m_Ui->labelSetPresetLineEdit->setText(filename);
}
void QmitkSegmentationPreferencePage::OnSuggestionsButtonClicked()
{
const auto filename = QFileDialog::getOpenFileName(m_Control, QStringLiteral("Load Label Suggestions"), QString(), QStringLiteral("Label suggestions (*.json)"));
if (!filename.isEmpty())
m_Ui->suggestionsLineEdit->setText(filename);
}
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui
index 419f143f68..f0ddeafdb7 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui
@@ -1,233 +1,253 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmitkSegmentationPreferencePageControls</class>
<widget class="QWidget" name="QmitkSegmentationPreferencePageControls">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>656</width>
<height>779</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="compactViewLabel">
<property name="text">
<string>Compact view</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="compactViewCheckBox">
<property name="text">
<string>Hide tool button texts and increase icon size</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="displayLabel">
<property name="text">
<string>2D display</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="displayOptionsLayout">
<item>
<widget class="QRadioButton" name="outlineRadioButton">
<property name="text">
<string>Draw as outline</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">displayButtonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="overlayRadioButton">
<property name="text">
<string>Draw as transparent overlay</string>
</property>
<attribute name="buttonGroup">
<string notr="true">displayButtonGroup</string>
</attribute>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="selectionModeLabel">
<property name="text">
<string>Data node selection mode</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="selectionModeCheckBox">
<property name="toolTip">
<string>If checked the segmentation plugin ensures that only the selected segmentation and the reference image are visible at one time.</string>
</property>
<property name="text">
<string>Show only selected nodes</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelSetPresetLabel">
<property name="text">
<string>Default label set preset</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLineEdit" name="labelSetPresetLineEdit">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QToolButton" name="labelSetPresetToolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="labelSetPresetCmdLineArgLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#ff0000;&quot;&gt;The default label set preset is currently overridden by the &lt;/span&gt;&lt;span style=&quot; font-family:'Courier New'; color:#ff0000;&quot;&gt;Segmentation.labelSetPreset&lt;/span&gt;&lt;span style=&quot; color:#ff0000;&quot;&gt; command-line argument.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="labelCreationLabel">
<property name="text">
<string>Label creation</string>
</property>
</widget>
</item>
<item row="4" column="1">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QRadioButton" name="defaultNameRadioButton">
<property name="text">
<string>Assign default name and color</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">labelCreationButtonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="askForNameRadioButton">
<property name="text">
<string>Ask for name and color</string>
</property>
<attribute name="buttonGroup">
<string notr="true">labelCreationButtonGroup</string>
</attribute>
</widget>
</item>
</layout>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelSuggestions">
<property name="text">
<string>Label suggestions</string>
</property>
</widget>
</item>
<item row="5" column="1">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLineEdit" name="suggestionsLineEdit">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QToolButton" name="suggestionsToolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="suggestionsCmdLineArgLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#ff0000;&quot;&gt;Suggestions are currently enforced by the &lt;/span&gt;&lt;span style=&quot; font-family:'Courier New'; color:#ff0000;&quot;&gt;Segmentation.labelSuggestions&lt;/span&gt;&lt;span style=&quot; color:#ff0000;&quot;&gt; command-line argument.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="replaceStandardSuggestionsCheckBox">
<property name="text">
<string>Replace standard organ suggestions</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="suggestOnceCheckBox">
<property name="text">
<string>Suggest once per segmentation</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
+ <item row="8" column="0">
+ <widget class="QLabel" name="monaiLabel">
+ <property name="text">
+ <string>MONAILabel</string>
+ </property>
+ </widget>
+ </item>
<item row="8" column="1">
+ <widget class="QCheckBox" name="allowAllModelsCheckBox">
+ <property name="toolTip">
+ <string>If checked, all models are visible.</string>
+ </property>
+ <property name="text">
+ <string>Allow all models</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="1">
<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>
<resources/>
<connections/>
<buttongroups>
<buttongroup name="labelCreationButtonGroup"/>
<buttongroup name="displayButtonGroup"/>
</buttongroups>
</ui>
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationUtilitiesView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationUtilitiesView.cpp
index 64449df9fd..3e26d95e63 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationUtilitiesView.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationUtilitiesView.cpp
@@ -1,68 +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.
============================================================================*/
#include "QmitkSegmentationUtilitiesView.h"
#include <QmitkBooleanOperationsWidget.h>
#include <QmitkImageMaskingWidget.h>
#include <QmitkMorphologicalOperationsWidget.h>
#include <QmitkConvertToMultiLabelSegmentationWidget.h>
#include <QmitkExtractFromMultiLabelSegmentationWidget.h>
QmitkSegmentationUtilitiesView::QmitkSegmentationUtilitiesView()
: m_BooleanOperationsWidget(nullptr),
m_ImageMaskingWidget(nullptr),
m_MorphologicalOperationsWidget(nullptr),
m_ConvertToSegWidget(nullptr),
m_ExtractFromSegWidget(nullptr)
{
}
QmitkSegmentationUtilitiesView::~QmitkSegmentationUtilitiesView()
{
}
void QmitkSegmentationUtilitiesView::CreateQtPartControl(QWidget* parent)
{
m_Controls.setupUi(parent);
auto dataStorage = this->GetDataStorage();
m_BooleanOperationsWidget = new QmitkBooleanOperationsWidget(dataStorage, parent);
m_ImageMaskingWidget = new QmitkImageMaskingWidget(dataStorage, parent);
m_MorphologicalOperationsWidget = new QmitkMorphologicalOperationsWidget(dataStorage, parent);
m_ConvertToSegWidget = new QmitkConvertToMultiLabelSegmentationWidget(dataStorage, parent);
m_ExtractFromSegWidget = new QmitkExtractFromMultiLabelSegmentationWidget(dataStorage, parent);
this->AddUtilityWidget(m_BooleanOperationsWidget, QIcon(":/SegmentationUtilities/BooleanOperations_48x48.png"), "Boolean Operations");
this->AddUtilityWidget(m_ImageMaskingWidget, QIcon(":/SegmentationUtilities/ImageMasking_48x48.png"), "Image Masking");
this->AddUtilityWidget(m_MorphologicalOperationsWidget, QIcon(":/SegmentationUtilities/MorphologicalOperations_48x48.png"), "Morphological Operations");
this->AddUtilityWidget(m_ConvertToSegWidget, QIcon(":/SegmentationUtilities/SurfaceToImage_48x48.png"), "Convert to Segmentation");
- this->AddUtilityWidget(m_ExtractFromSegWidget, QIcon(":/SegmentationUtilities/SurfaceToImage_48x48.png"), "Extract from Segmentation");
+ this->AddUtilityWidget(m_ExtractFromSegWidget, QIcon(":/SegmentationUtilities/Extract_48x48.png"), "Extract from Segmentation");
}
void QmitkSegmentationUtilitiesView::AddUtilityWidget(QWidget* widget, const QIcon& icon, const QString& text)
{
m_Controls.toolBox->addItem(widget, icon, text);
}
void QmitkSegmentationUtilitiesView::SetFocus()
{
m_Controls.toolBox->setFocus();
}
void QmitkSegmentationUtilitiesView::RenderWindowPartActivated(mitk::IRenderWindowPart*)
{
}
void QmitkSegmentationUtilitiesView::RenderWindowPartDeactivated(mitk::IRenderWindowPart*)
{
}
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 f57666d0d8..2db71f1e9b 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp
@@ -1,1088 +1,1090 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 <mitkBaseApplication.h>
#include <mitkBaseRendererHelper.h>
#include <mitkCameraController.h>
#include <mitkLabelSetImage.h>
#include <mitkLabelSetImageHelper.h>
#include <mitkMultiLabelIOHelper.h>
#include <mitkManualPlacementAnnotationRenderer.h>
#include <mitkNodePredicateSubGeometry.h>
#include <mitkSegmentationObjectFactory.h>
#include <mitkSegTool2D.h>
#include <mitkStatusBar.h>
#include <mitkToolManagerProvider.h>
#include <mitkVtkResliceInterpolationProperty.h>
#include <mitkWorkbenchUtil.h>
#include <mitkIPreferences.h>
#include <mitkMultiLabelPredicateHelper.h>
// Qmitk
#include <QmitkRenderWindow.h>
#include <QmitkStaticDynamicSegmentationDialog.h>
#include <QmitkNewSegmentationDialog.h>
#include <QmitkMultiLabelManager.h>
// us
#include <usModuleResource.h>
#include <usModuleResourceStream.h>
// Qt
#include <QMessageBox>
#include <QShortcut>
#include <QDir>
// vtk
#include <vtkQImageToImageSource.h>
#include <regex>
namespace
{
QList<QmitkRenderWindow*> Get2DWindows(const QList<QmitkRenderWindow*> allWindows)
{
QList<QmitkRenderWindow*> all2DWindows;
for (auto* window : allWindows)
{
if (window->GetRenderer()->GetMapperID() == mitk::BaseRenderer::Standard2D)
{
all2DWindows.append(window);
}
}
return all2DWindows;
}
}
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_DrawOutline(true)
, m_SelectionMode(false)
, m_MouseCursorSet(false)
, m_DefaultLabelNaming(true)
, m_SelectionChangeIsAlreadyBeingHandled(false)
{
m_SegmentationPredicate = mitk::GetMultiLabelSegmentationPredicate();
m_ReferencePredicate = mitk::GetSegmentationReferenceImagePredicate();
}
QmitkSegmentationView::~QmitkSegmentationView()
{
+ m_RenderWindowPart = this->GetRenderWindowPart();
+
if (nullptr != m_Controls)
{
// deactivate all tools
m_ToolManager->ActivateTool(-1);
// removing all observers from working data
for (NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter)
{
(*dataIter).first->GetProperty("visible")->RemoveObserver((*dataIter).second);
}
m_WorkingDataObserverTags.clear();
this->RemoveObserversFromWorkingImage();
// removing all observers from reference data
for (NodeTagMapType::iterator dataIter = m_ReferenceDataObserverTags.begin(); dataIter != m_ReferenceDataObserverTags.end(); ++dataIter)
{
(*dataIter).first->GetProperty("visible")->RemoveObserver((*dataIter).second);
}
m_ReferenceDataObserverTags.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);
}
m_ToolManager->ActiveToolChanged -=
mitk::MessageDelegate<Self>(this, &Self::ActiveToolChanged);
delete m_Controls;
}
/**********************************************************************/
/* private Q_SLOTS */
/**********************************************************************/
void QmitkSegmentationView::OnReferenceSelectionChanged(QList<mitk::DataNode::Pointer>)
{
this->OnAnySelectionChanged();
}
void QmitkSegmentationView::OnSegmentationSelectionChanged(QList<mitk::DataNode::Pointer>)
{
this->OnAnySelectionChanged();
}
void QmitkSegmentationView::OnAnySelectionChanged()
{
// When only a segmentation has been selected and the method is then called by a reference image selection,
// the already selected segmentation may not match the geometry predicate of the new reference image anymore.
// This will trigger a recursive call of this method further below. While it would be resolved gracefully, we
// can spare the extra call with an early-out. The original call of this method will handle the segmentation
// selection change afterwards anyway.
if (m_SelectionChangeIsAlreadyBeingHandled)
return;
auto selectedReferenceNode = m_Controls->referenceNodeSelector->GetSelectedNode();
bool referenceNodeChanged = false;
m_ToolManager->ActivateTool(-1);
if (m_ReferenceNode != selectedReferenceNode)
{
referenceNodeChanged = true;
// Remove visibility observer for the current reference node
if (m_ReferenceDataObserverTags.find(m_ReferenceNode) != m_ReferenceDataObserverTags.end())
{
m_ReferenceNode->GetProperty("visible")->RemoveObserver(m_ReferenceDataObserverTags[m_ReferenceNode]);
m_ReferenceDataObserverTags.erase(m_ReferenceNode);
}
// Set new reference node
m_ReferenceNode = selectedReferenceNode;
m_ToolManager->SetReferenceData(m_ReferenceNode);
// Prepare for a potential recursive call when changing node predicates of the working node selector
m_SelectionChangeIsAlreadyBeingHandled = true;
if (m_ReferenceNode.IsNull())
{
// Without a reference image, allow all segmentations to be selected
m_Controls->workingNodeSelector->SetNodePredicate(m_SegmentationPredicate);
m_SelectionChangeIsAlreadyBeingHandled = false;
}
else
{
// With a reference image, only allow segmentations that fit the geometry of the reference image to be selected.
m_Controls->workingNodeSelector->SetNodePredicate(mitk::GetMultiLabelSegmentationPredicate(m_ReferenceNode->GetData()->GetGeometry()));
m_SelectionChangeIsAlreadyBeingHandled = false;
this->ApplySelectionModeOnReferenceNode();
// Add visibility observer for the new reference node
auto command = itk::SimpleMemberCommand<Self>::New();
command->SetCallbackFunction(this, &Self::ValidateSelectionInput);
m_ReferenceDataObserverTags[m_ReferenceNode] =
m_ReferenceNode->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command);
}
}
auto selectedWorkingNode = m_Controls->workingNodeSelector->GetSelectedNode();
bool workingNodeChanged = false;
if (m_WorkingNode != selectedWorkingNode)
{
workingNodeChanged = true;
this->RemoveObserversFromWorkingImage();
// Remove visibility observer for the current working node
if (m_WorkingDataObserverTags.find(m_WorkingNode) != m_WorkingDataObserverTags.end())
{
m_WorkingNode->GetProperty("visible")->RemoveObserver(m_WorkingDataObserverTags[m_WorkingNode]);
m_WorkingDataObserverTags.erase(m_WorkingNode);
}
// Set new working node
m_WorkingNode = selectedWorkingNode;
m_ToolManager->SetWorkingData(m_WorkingNode);
if (m_WorkingNode.IsNotNull())
{
this->ApplySelectionModeOnWorkingNode();
// Add visibility observer for the new segmentation node
auto command = itk::SimpleMemberCommand<Self>::New();
command->SetCallbackFunction(this, &Self::ValidateSelectionInput);
m_WorkingDataObserverTags[m_WorkingNode] =
m_WorkingNode->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command);
this->AddObserversToWorkingImage();
}
}
// Reset camera if any selection changed but only if both reference node and working node are set
if ((referenceNodeChanged || workingNodeChanged) && (m_ReferenceNode.IsNotNull() && m_WorkingNode.IsNotNull()))
{
if (nullptr != m_RenderWindowPart)
{
m_RenderWindowPart->InitializeViews(m_ReferenceNode->GetData()->GetTimeGeometry(), false);
}
}
this->UpdateGUI();
}
void QmitkSegmentationView::OnLabelAdded(mitk::LabelSetImage::LabelValueType)
{
this->ValidateSelectionInput();
}
void QmitkSegmentationView::OnLabelRemoved(mitk::LabelSetImage::LabelValueType)
{
this->ValidateSelectionInput();
}
void QmitkSegmentationView::OnGroupRemoved(mitk::LabelSetImage::GroupIndexType)
{
this->ValidateSelectionInput();
}
mitk::LabelSetImage* QmitkSegmentationView::GetWorkingImage()
{
if (m_WorkingNode.IsNull())
return nullptr;
return dynamic_cast<mitk::LabelSetImage*>(m_WorkingNode->GetData());
}
void QmitkSegmentationView::AddObserversToWorkingImage()
{
auto* workingImage = this->GetWorkingImage();
if (workingImage != nullptr)
{
auto& widget = *this;
m_LabelAddedObserver.Reset(workingImage, mitk::LabelAddedEvent(), [&widget](const itk::EventObject& event)
{
auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&event);
widget.OnLabelAdded(labelEvent->GetLabelValue());
});
m_LabelRemovedObserver.Reset(workingImage, mitk::LabelRemovedEvent(), [&widget](const itk::EventObject& event)
{
auto labelEvent = dynamic_cast<const mitk::AnyLabelEvent*>(&event);
widget.OnLabelRemoved(labelEvent->GetLabelValue());
});
m_GroupRemovedObserver.Reset(workingImage, mitk::GroupRemovedEvent(), [&widget](const itk::EventObject& event)
{
auto groupEvent = dynamic_cast<const mitk::AnyGroupEvent*>(&event);
widget.OnGroupRemoved(groupEvent->GetGroupID());
});
}
}
void QmitkSegmentationView::RemoveObserversFromWorkingImage()
{
m_LabelAddedObserver.Reset();
m_LabelRemovedObserver.Reset();
m_GroupRemovedObserver.Reset();
}
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()
{
if (m_WorkingNode.IsNull())
{
return;
}
auto workingImage = dynamic_cast<mitk::LabelSetImage*>(m_WorkingNode->GetData());
if (nullptr == workingImage)
{
return;
}
this->WaitCursorOn();
auto labels = workingImage->GetLabelValuesByGroup(workingImage->GetActiveLayer());
auto it = std::find(labels.begin(), labels.end(), workingImage->GetActiveLabel()->GetValue());
if (it != labels.end())
++it;
if (it == labels.end())
{
it = labels.begin();
}
workingImage->SetActiveLabel(*it);
m_Controls->multiLabelWidget->SetSelectedLabel(*it);
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*>(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");
return;
}
auto segTemplateImage = referenceImage;
if (referenceImage->GetDimension() > 3)
{
QmitkStaticDynamicSegmentationDialog dialog(m_Parent);
dialog.SetReferenceImage(referenceImage.GetPointer());
dialog.exec();
segTemplateImage = dialog.GetSegmentationTemplate();
}
mitk::DataNode::Pointer newSegmentationNode;
try
{
this->WaitCursorOn();
newSegmentationNode = mitk::LabelSetImageHelper::CreateNewSegmentationNode(m_ReferenceNode, segTemplateImage);
this->WaitCursorOff();
}
catch (mitk::Exception& e)
{
this->WaitCursorOff();
MITK_ERROR << "Exception caught: " << e.GetDescription();
QMessageBox::warning(m_Parent, "New segmentation", "Could not create a new segmentation.");
return;
}
auto newLabelSetImage = dynamic_cast<mitk::LabelSetImage*>(newSegmentationNode->GetData());
if (nullptr == newLabelSetImage)
{
// something went wrong
return;
}
const auto labelSetPreset = this->GetDefaultLabelSetPreset();
if (labelSetPreset.empty() || !mitk::MultiLabelIOHelper::LoadLabelSetImagePreset(labelSetPreset, newLabelSetImage))
{
auto newLabel = mitk::LabelSetImageHelper::CreateNewLabel(newLabelSetImage);
if (!m_DefaultLabelNaming)
QmitkNewSegmentationDialog::DoRenameLabel(newLabel, nullptr, m_Parent);
newLabelSetImage->AddLabel(newLabel, newLabelSetImage->GetActiveLayer());
}
if (!this->GetDataStorage()->Exists(newSegmentationNode))
{
this->GetDataStorage()->Add(newSegmentationNode, m_ReferenceNode);
}
if (m_ToolManager->GetWorkingData(0))
{
m_ToolManager->GetWorkingData(0)->SetSelected(false);
}
newSegmentationNode->SetSelected(true);
m_Controls->workingNodeSelector->SetCurrentSelectedNode(newSegmentationNode);
}
std::string QmitkSegmentationView::GetDefaultLabelSetPreset() const
{
auto labelSetPreset = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABELSET_PRESET.toStdString(), "");
if (labelSetPreset.empty())
labelSetPreset = m_LabelSetPresetPreference.toStdString();
return labelSetPreset;
}
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 (nullptr == manualSegmentationTool)
{
continue;
}
manualSegmentationTool->SetShowMarkerNodes(state);
}
}
void QmitkSegmentationView::OnCurrentLabelSelectionChanged(QmitkMultiLabelManager::LabelValueVectorType labels)
{
auto segmentation = this->GetCurrentSegmentation();
const auto labelValue = labels.front();
if (nullptr == segmentation->GetActiveLabel() || labelValue != segmentation->GetActiveLabel()->GetValue())
{
segmentation->SetActiveLabel(labelValue);
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
m_Controls->slicesInterpolator->SetActiveLabelValue(labelValue);
}
void QmitkSegmentationView::OnGoToLabel(mitk::LabelSetImage::LabelValueType /*label*/, const mitk::Point3D& pos)
{
if (m_RenderWindowPart)
{
m_RenderWindowPart->SetSelectedPosition(pos);
}
}
-void QmitkSegmentationView::OnLabelRenameRequested(mitk::Label* label, bool rename) const
+void QmitkSegmentationView::OnLabelRenameRequested(mitk::Label* label, bool rename, bool& canceled) const
{
auto segmentation = this->GetCurrentSegmentation();
if (rename)
{
- QmitkNewSegmentationDialog::DoRenameLabel(label, segmentation, this->m_Parent, QmitkNewSegmentationDialog::Mode::RenameLabel);
+ canceled = !QmitkNewSegmentationDialog::DoRenameLabel(label, segmentation, this->m_Parent, QmitkNewSegmentationDialog::Mode::RenameLabel);
return;
}
- QmitkNewSegmentationDialog::DoRenameLabel(label, nullptr, this->m_Parent, QmitkNewSegmentationDialog::Mode::NewLabel);
+ canceled = !QmitkNewSegmentationDialog::DoRenameLabel(label, nullptr, this->m_Parent, QmitkNewSegmentationDialog::Mode::NewLabel);
}
mitk::LabelSetImage* QmitkSegmentationView::GetCurrentSegmentation() const
{
auto workingNode = m_Controls->workingNodeSelector->GetSelectedNode();
if (workingNode.IsNull()) mitkThrow() << "Segmentation view is in an invalid state. Working node is null, but a label selection change has been triggered.";
auto segmentation = dynamic_cast<mitk::LabelSetImage*>(workingNode->GetData());
if (nullptr == segmentation) mitkThrow() << "Segmentation view is in an invalid state. Working node contains no segmentation, but a label selection change has been triggered.";
return segmentation;
}
/**********************************************************************/
/* private */
/**********************************************************************/
void QmitkSegmentationView::CreateQtPartControl(QWidget* parent)
{
m_Parent = parent;
m_Controls = new Ui::QmitkSegmentationViewControls;
m_Controls->setupUi(parent);
// *------------------------
// * SHORTCUTS
// *------------------------
QShortcut* visibilityShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_H), parent);
connect(visibilityShortcut, &QShortcut::activated, this, &Self::OnVisibilityShortcutActivated);
QShortcut* labelToggleShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_N), parent);
connect(labelToggleShortcut, &QShortcut::activated, this, &Self::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, &Self::OnReferenceSelectionChanged);
connect(m_Controls->workingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged,
this, &Self::OnSegmentationSelectionChanged);
// *------------------------
// * TOOLMANAGER
// *------------------------
m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager();
m_ToolManager->SetDataStorage(*(this->GetDataStorage()));
m_ToolManager->InitializeTools();
- QString segTools2D = tr("Add Subtract Lasso Fill Erase Close Paint Wipe 'Region Growing' 'Live Wire' 'Segment Anything', 'MedSAM'");
- QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Region Growing 3D' Picking GrowCut TotalSegmentator");
+ QString segTools2D = tr("Add Subtract Lasso Fill Erase Close Paint Wipe 'Region Growing' 'Live Wire' 'Segment Anything' 'MedSAM' 'MONAI Label 2D'");
+ QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Region Growing 3D' Picking GrowCut TotalSegmentator 'MONAI Label 3D'");
#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()));
}
}
// 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());
connect(m_Controls->toolSelectionBox2D, &QmitkToolSelectionBox::ToolSelected,
this, &Self::OnManualTool2DSelected);
// 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->slicesInterpolator->SetDataStorage(this->GetDataStorage());
// create general signal / slot connections
connect(m_Controls->newSegmentationButton, &QToolButton::clicked, this, &Self::OnNewSegmentation);
connect(m_Controls->slicesInterpolator, &QmitkSlicesInterpolator::SignalShowMarkerNodes, this, &Self::OnShowMarkerNodes);
connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::CurrentSelectionChanged, this, &Self::OnCurrentLabelSelectionChanged);
connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::GoToLabel, this, &Self::OnGoToLabel);
connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::LabelRenameRequested, this, &Self::OnLabelRenameRequested);
auto command = itk::SimpleMemberCommand<Self>::New();
command->SetCallbackFunction(this, &Self::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->referenceNodeSelector->SetAutoSelectNewNodes(true);
m_Controls->workingNodeSelector->SetAutoSelectNewNodes(true);
this->UpdateGUI();
}
void QmitkSegmentationView::ActiveToolChanged()
{
if (nullptr == m_RenderWindowPart)
{
return;
}
mitk::TimeGeometry* interactionReferenceGeometry = nullptr;
auto activeTool = m_ToolManager->GetActiveTool();
if (nullptr != activeTool && m_ReferenceNode.IsNotNull())
{
mitk::Image::ConstPointer referenceImage = dynamic_cast<mitk::Image *>(m_ReferenceNode->GetData());
if (referenceImage.IsNotNull())
{
// tool activated, reference image available: set reference geometry
interactionReferenceGeometry = m_ReferenceNode->GetData()->GetTimeGeometry();
}
}
// set the interaction reference geometry for the render window part (might be nullptr)
m_RenderWindowPart->SetInteractionReferenceGeometry(interactionReferenceGeometry);
}
void QmitkSegmentationView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart)
{
if (m_RenderWindowPart != renderWindowPart)
{
m_RenderWindowPart = renderWindowPart;
}
if (nullptr != m_Parent)
{
m_Parent->setEnabled(true);
}
if (nullptr == m_Controls)
{
return;
}
if (nullptr != m_RenderWindowPart)
{
auto all2DWindows = Get2DWindows(m_RenderWindowPart->GetQmitkRenderWindows().values());
m_Controls->slicesInterpolator->Initialize(m_ToolManager, all2DWindows);
if (!m_RenderWindowPart->HasCoupledRenderWindows())
{
// react if the active tool changed, only if a render window part with decoupled render windows is used
m_ToolManager->ActiveToolChanged +=
mitk::MessageDelegate<Self>(this, &Self::ActiveToolChanged);
}
}
}
void QmitkSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/)
{
m_RenderWindowPart = nullptr;
if (nullptr != m_Parent)
{
m_Parent->setEnabled(false);
}
// remove message-connection to make sure no message is processed if no render window part is available
m_ToolManager->ActiveToolChanged -=
mitk::MessageDelegate<Self>(this, &Self::ActiveToolChanged);
m_Controls->slicesInterpolator->Uninitialize();
}
void QmitkSegmentationView::RenderWindowPartInputChanged(mitk::IRenderWindowPart* /*renderWindowPart*/)
{
if (nullptr == m_RenderWindowPart)
{
return;
}
m_Controls->slicesInterpolator->Uninitialize();
auto all2DWindows = Get2DWindows(m_RenderWindowPart->GetQmitkRenderWindows().values());
m_Controls->slicesInterpolator->Initialize(m_ToolManager, all2DWindows);
}
void QmitkSegmentationView::OnPreferencesChanged(const mitk::IPreferences* prefs)
{
auto labelSuggestions = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString(), "");
m_DefaultLabelNaming = labelSuggestions.empty()
? prefs->GetBool("default label naming", true)
: false; // No default label naming when label suggestions are enforced via command-line argument
if (nullptr != m_Controls)
{
m_Controls->multiLabelWidget->SetDefaultLabelNaming(m_DefaultLabelNaming);
bool compactView = prefs->GetBool("compact view", false);
int numberOfColumns = compactView ? 6 : 4;
m_Controls->toolSelectionBox2D->SetLayoutColumns(numberOfColumns);
m_Controls->toolSelectionBox2D->SetShowNames(!compactView);
m_Controls->toolSelectionBox3D->SetLayoutColumns(numberOfColumns);
m_Controls->toolSelectionBox3D->SetShowNames(!compactView);
}
m_DrawOutline = prefs->GetBool("draw outline", true);
m_SelectionMode = prefs->GetBool("selection mode", false);
m_LabelSetPresetPreference = QString::fromStdString(prefs->Get("label set preset", ""));
this->ApplyDisplayOptions();
this->ApplySelectionMode();
}
void QmitkSegmentationView::NodeAdded(const mitk::DataNode* node)
{
if (m_SegmentationPredicate->CheckNode(node))
this->ApplyDisplayOptions(const_cast<mitk::DataNode*>(node));
this->ApplySelectionMode();
}
void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node)
{
if (!m_SegmentationPredicate->CheckNode(node))
{
return;
}
// 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;
auto image = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
mitk::SurfaceInterpolationController::GetInstance()->RemoveInterpolationSession(image);
}
void QmitkSegmentationView::ApplyDisplayOptions()
{
if (nullptr == m_Parent)
{
return;
}
if (nullptr == m_Controls)
{
return; // might happen on initialization (preferences loaded)
}
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 labelSetImage = dynamic_cast<mitk::LabelSetImage*>(node->GetData());
if (nullptr == labelSetImage)
{
return;
}
// the 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
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkSegmentationView::ApplySelectionMode()
{
if (!m_SelectionMode)
return;
this->ApplySelectionModeOnReferenceNode();
this->ApplySelectionModeOnWorkingNode();
}
void QmitkSegmentationView::ApplySelectionModeOnReferenceNode()
{
this->ApplySelectionMode(m_ReferenceNode, m_ReferencePredicate);
}
void QmitkSegmentationView::ApplySelectionModeOnWorkingNode()
{
this->ApplySelectionMode(m_WorkingNode, m_SegmentationPredicate);
}
void QmitkSegmentationView::ApplySelectionMode(mitk::DataNode* node, mitk::NodePredicateBase* predicate)
{
if (!m_SelectionMode || node == nullptr || predicate == nullptr)
return;
auto nodes = this->GetDataStorage()->GetSubset(predicate);
for (auto iter = nodes->begin(); iter != nodes->end(); ++iter)
(*iter)->SetVisibility(*iter == node);
}
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* 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,
threeDRenderWindow->GetRenderer()))
{
selectedRenderWindow = threeDRenderWindow;
}
// make node visible
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);
selectedRenderWindow->GetRenderer()->GetCameraController()->Fit();
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
}
void QmitkSegmentationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList<mitk::DataNode::Pointer>& nodes)
{
if (0 == nodes.size())
{
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->newSegmentationButton->setEnabled(false);
if (hasReferenceNode)
{
m_Controls->newSegmentationButton->setEnabled(true);
}
if (hasWorkingNode && hasReferenceNode)
{
int layer = -1;
referenceNode->GetIntProperty("layer", layer);
workingNode->SetIntProperty("layer", layer + 1);
}
this->ValidateSelectionInput();
}
void QmitkSegmentationView::ValidateSelectionInput()
{
auto referenceNode = m_Controls->referenceNodeSelector->GetSelectedNode();
auto workingNode = m_Controls->workingNodeSelector->GetSelectedNode();
bool hasReferenceNode = referenceNode.IsNotNull();
bool hasWorkingNode = workingNode.IsNotNull();
bool hasBothNodes = hasReferenceNode && hasWorkingNode;
QString warning;
bool toolSelectionBoxesEnabled = hasReferenceNode && hasWorkingNode;
unsigned int numberOfLabels = 0;
m_Controls->multiLabelWidget->setEnabled(hasWorkingNode);
m_Controls->toolSelectionBox2D->setEnabled(hasBothNodes);
m_Controls->toolSelectionBox3D->setEnabled(hasBothNodes);
m_Controls->slicesInterpolator->setEnabled(false);
m_Controls->interpolatorWarningLabel->hide();
if (hasReferenceNode)
{
if (nullptr != m_RenderWindowPart && m_RenderWindowPart->HasCoupledRenderWindows() && !referenceNode->IsVisible(nullptr))
{
warning += tr("The selected reference image is currently not visible!");
toolSelectionBoxesEnabled = false;
}
}
if (hasWorkingNode)
{
if (nullptr != m_RenderWindowPart && m_RenderWindowPart->HasCoupledRenderWindows() && !workingNode->IsVisible(nullptr))
{
warning += (!warning.isEmpty() ? "<br>" : "") + tr("The selected segmentation is currently not visible!");
toolSelectionBoxesEnabled = false;
}
m_ToolManager->SetReferenceData(referenceNode);
m_ToolManager->SetWorkingData(workingNode);
m_Controls->multiLabelWidget->setEnabled(true);
m_Controls->toolSelectionBox2D->setEnabled(true);
m_Controls->toolSelectionBox3D->setEnabled(true);
auto labelSetImage = dynamic_cast<mitk::LabelSetImage *>(workingNode->GetData());
numberOfLabels = labelSetImage->GetTotalNumberOfLabels();
if (numberOfLabels > 0)
m_Controls->slicesInterpolator->setEnabled(true);
m_Controls->multiLabelWidget->SetMultiLabelNode(workingNode);
if (!m_Controls->multiLabelWidget->GetSelectedLabels().empty())
{
m_Controls->slicesInterpolator->SetActiveLabelValue(m_Controls->multiLabelWidget->GetSelectedLabels().front());
}
}
else
{
m_Controls->multiLabelWidget->SetMultiLabelNode(nullptr);
}
toolSelectionBoxesEnabled &= numberOfLabels > 0;
// Here we need to check whether the geometry of the selected segmentation image (working image geometry)
// is aligned with the geometry of the 3D render window.
// It is not allowed to use a geometry different from the working image geometry for segmenting.
// We only need to this if the tool selection box would be enabled without this check.
// Additionally this check only has to be performed for render window parts with coupled render windows.
// For different render window parts the user is given the option to reinitialize each render window individually
// (see QmitkRenderWindow::ShowOverlayMessage).
if (toolSelectionBoxesEnabled && nullptr != m_RenderWindowPart && m_RenderWindowPart->HasCoupledRenderWindows())
{
const mitk::BaseGeometry* workingNodeGeometry = workingNode->GetData()->GetGeometry();
const mitk::BaseGeometry* renderWindowGeometry =
m_RenderWindowPart->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D();
if (nullptr != workingNodeGeometry && nullptr != renderWindowGeometry)
{
if (!mitk::Equal(*workingNodeGeometry->GetBoundingBox(), *renderWindowGeometry->GetBoundingBox(), mitk::eps, true))
{
warning += (!warning.isEmpty() ? "<br>" : "") + tr("Please reinitialize the selected segmentation image!");
toolSelectionBoxesEnabled = false;
}
}
}
m_Controls->toolSelectionBox2D->setEnabled(toolSelectionBoxesEnabled);
m_Controls->toolSelectionBox3D->setEnabled(toolSelectionBoxesEnabled);
this->UpdateWarningLabel(warning);
m_ToolManager->SetReferenceData(referenceNode);
m_ToolManager->SetWorkingData(workingNode);
}
void QmitkSegmentationView::UpdateWarningLabel(QString text)
{
if (text.isEmpty())
{
m_Controls->selectionWarningLabel->hide();
}
else
{
m_Controls->selectionWarningLabel->setText("<font color=\"red\">" + text + "</font>");
m_Controls->selectionWarningLabel->show();
}
}
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 1144bf3f21..b0abd74aa1 100644
--- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h
+++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h
@@ -1,180 +1,180 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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_QmitkSegmentationViewControls.h"
#include <QmitkAbstractView.h>
#include <mitkITKEventObserverGuard.h>
#include <mitkIRenderWindowPartListener.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 properties 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 creating new ones, 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 OnNewSegmentation();
void OnManualTool2DSelected(int id);
void OnShowMarkerNodes(bool);
void OnCurrentLabelSelectionChanged(QmitkMultiLabelManager::LabelValueVectorType labels);
void OnGoToLabel(mitk::LabelSetImage::LabelValueType label, const mitk::Point3D&);
- void OnLabelRenameRequested(mitk::Label* label, bool rename) const;
+ void OnLabelRenameRequested(mitk::Label* label, bool rename, bool& canceled) const;
void OnLabelAdded(mitk::LabelSetImage::LabelValueType labelValue);
void OnLabelRemoved(mitk::LabelSetImage::LabelValueType labelValue);
void OnGroupRemoved(mitk::LabelSetImage::GroupIndexType groupIndex);
private:
using Self = QmitkSegmentationView;
mitk::LabelSetImage* GetWorkingImage();
void AddObserversToWorkingImage();
void RemoveObserversFromWorkingImage();
void CreateQtPartControl(QWidget* parent) override;
void SetFocus() override {}
/**
* @brief Enable or disable the SegmentationInteractor.
*
* The active tool is retrieved from the tool manager.
* If the active tool is valid, the SegmentationInteractor is enabled
* to listen to 'SegmentationInteractionEvent's.
*/
void ActiveToolChanged();
void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override;
void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) override;
void RenderWindowPartInputChanged(mitk::IRenderWindowPart* renderWindowPart) override;
void OnPreferencesChanged(const mitk::IPreferences* prefs) override;
void NodeAdded(const mitk::DataNode* node) override;
void NodeRemoved(const mitk::DataNode* node) override;
void OnAnySelectionChanged();
// 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 ApplySelectionMode();
void ApplySelectionModeOnReferenceNode();
void ApplySelectionModeOnWorkingNode();
void ApplySelectionMode(mitk::DataNode* node, mitk::NodePredicateBase* predicate);
// 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 ValidateSelectionInput();
void UpdateWarningLabel(QString text);
std::string GetDefaultLabelSetPreset() const;
mitk::LabelSetImage* GetCurrentSegmentation() const;
QWidget* m_Parent;
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_ReferenceDataObserverTags;
unsigned int m_RenderingManagerObserverTag;
mitk::NodePredicateBase::Pointer m_ReferencePredicate;
mitk::NodePredicateBase::Pointer m_SegmentationPredicate;
bool m_DrawOutline;
bool m_SelectionMode;
bool m_MouseCursorSet;
QString m_LabelSetPresetPreference;
bool m_DefaultLabelNaming;
bool m_SelectionChangeIsAlreadyBeingHandled;
mitk::ITKEventObserverGuard m_LabelAddedObserver;
mitk::ITKEventObserverGuard m_LabelRemovedObserver;
mitk::ITKEventObserverGuard m_GroupRemovedObserver;
};
#endif
diff --git a/Plugins/org.mitk.gui.qt.stdmultiwidgeteditor/src/QmitkStdMultiWidgetEditor.cpp b/Plugins/org.mitk.gui.qt.stdmultiwidgeteditor/src/QmitkStdMultiWidgetEditor.cpp
index ae9296d22f..2f6219a446 100644
--- a/Plugins/org.mitk.gui.qt.stdmultiwidgeteditor/src/QmitkStdMultiWidgetEditor.cpp
+++ b/Plugins/org.mitk.gui.qt.stdmultiwidgeteditor/src/QmitkStdMultiWidgetEditor.cpp
@@ -1,398 +1,394 @@
/*============================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center (DKFZ)
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 "QmitkStdMultiWidgetEditor.h"
#include <berryUIException.h>
#include <berryIWorkbenchPage.h>
#include <mitkCoreServices.h>
#include <mitkIPreferencesService.h>
#include <mitkIPreferences.h>
#include <mitkColorProperty.h>
#include <mitkNodePredicateNot.h>
#include <mitkNodePredicateProperty.h>
#include <mitkDataStorageEditorInput.h>
#include <mitkIDataStorageService.h>
// mitk qt widgets module
#include <QmitkInteractionSchemeToolBar.h>
#include <QmitkLevelWindowWidget.h>
#include <QmitkRenderWindowWidget.h>
#include <QmitkStdMultiWidget.h>
// mitk gui qt common plugin
#include <QmitkMultiWidgetDecorationManager.h>
const QString QmitkStdMultiWidgetEditor::EDITOR_ID = "org.mitk.editors.stdmultiwidget";
struct QmitkStdMultiWidgetEditor::Impl final
{
Impl();
~Impl() = default;
QmitkInteractionSchemeToolBar* m_InteractionSchemeToolBar;
QmitkLevelWindowWidget* m_LevelWindowWidget;
-
- std::unique_ptr<QmitkMultiWidgetDecorationManager> m_MultiWidgetDecorationManager;
};
QmitkStdMultiWidgetEditor::Impl::Impl()
: m_InteractionSchemeToolBar(nullptr)
, m_LevelWindowWidget(nullptr)
{
// nothing here
}
//////////////////////////////////////////////////////////////////////////
// QmitkStdMultiWidgetEditor
//////////////////////////////////////////////////////////////////////////
QmitkStdMultiWidgetEditor::QmitkStdMultiWidgetEditor()
: QmitkAbstractMultiWidgetEditor()
, m_Impl(std::make_unique<Impl>())
{
// nothing here
}
QmitkStdMultiWidgetEditor::~QmitkStdMultiWidgetEditor()
{
GetSite()->GetPage()->RemovePartListener(this);
}
berry::IPartListener::Events::Types QmitkStdMultiWidgetEditor::GetPartEventTypes() const
{
return Events::CLOSED | Events::OPENED | Events::HIDDEN | Events::VISIBLE;
}
void QmitkStdMultiWidgetEditor::PartClosed(const berry::IWorkbenchPartReference::Pointer& partRef)
{
if (partRef->GetId() == QmitkStdMultiWidgetEditor::EDITOR_ID)
{
const auto& multiWidget = dynamic_cast<QmitkStdMultiWidget*>(GetMultiWidget());
if (nullptr != multiWidget)
{
multiWidget->RemovePlanesFromDataStorage();
multiWidget->ActivateMenuWidget(false);
}
}
}
void QmitkStdMultiWidgetEditor::PartOpened(const berry::IWorkbenchPartReference::Pointer& partRef)
{
if (partRef->GetId() == QmitkStdMultiWidgetEditor::EDITOR_ID)
{
const auto& multiWidget = dynamic_cast<QmitkStdMultiWidget*>(GetMultiWidget());
if (nullptr != multiWidget)
{
multiWidget->AddPlanesToDataStorage();
multiWidget->ActivateMenuWidget(true);
}
}
}
void QmitkStdMultiWidgetEditor::PartHidden(const berry::IWorkbenchPartReference::Pointer& partRef)
{
if (partRef->GetId() == QmitkStdMultiWidgetEditor::EDITOR_ID)
{
const auto& multiWidget = dynamic_cast<QmitkStdMultiWidget*>(GetMultiWidget());
if (nullptr != multiWidget)
{
multiWidget->ActivateMenuWidget(false);
}
}
}
void QmitkStdMultiWidgetEditor::PartVisible(const berry::IWorkbenchPartReference::Pointer& partRef)
{
if (partRef->GetId() == QmitkStdMultiWidgetEditor::EDITOR_ID)
{
const auto& multiWidget = dynamic_cast<QmitkStdMultiWidget*>(GetMultiWidget());
if (nullptr != multiWidget)
{
multiWidget->ActivateMenuWidget(true);
}
}
}
QmitkLevelWindowWidget* QmitkStdMultiWidgetEditor::GetLevelWindowWidget() const
{
return m_Impl->m_LevelWindowWidget;
}
void QmitkStdMultiWidgetEditor::EnableSlicingPlanes(bool enable)
{
const auto& multiWidget = dynamic_cast<QmitkStdMultiWidget*>(GetMultiWidget());
if (nullptr == multiWidget)
{
return;
}
multiWidget->SetWidgetPlanesVisibility(enable);
}
bool QmitkStdMultiWidgetEditor::IsSlicingPlanesEnabled() const
{
const auto& multiWidget = dynamic_cast<QmitkStdMultiWidget*>(GetMultiWidget());
if (nullptr == multiWidget)
{
return false;
}
mitk::DataNode::Pointer node = multiWidget->GetWidgetPlane1();
if (node.IsNotNull())
{
bool visible = false;
node->GetVisibility(visible, nullptr);
return visible;
}
else
{
return false;
}
}
void QmitkStdMultiWidgetEditor::OnInteractionSchemeChanged(mitk::InteractionSchemeSwitcher::InteractionScheme scheme)
{
const auto& multiWidget = GetMultiWidget();
if (nullptr == multiWidget)
{
return;
}
if (mitk::InteractionSchemeSwitcher::PACSStandard == scheme)
{
m_Impl->m_InteractionSchemeToolBar->setVisible(true);
}
else
{
m_Impl->m_InteractionSchemeToolBar->setVisible(false);
}
QmitkAbstractMultiWidgetEditor::OnInteractionSchemeChanged(scheme);
}
void QmitkStdMultiWidgetEditor::ShowLevelWindowWidget(bool show)
{
if (show)
{
m_Impl->m_LevelWindowWidget->disconnect(this);
m_Impl->m_LevelWindowWidget->SetDataStorage(GetDataStorage());
m_Impl->m_LevelWindowWidget->show();
}
else
{
m_Impl->m_LevelWindowWidget->disconnect(this);
m_Impl->m_LevelWindowWidget->hide();
}
}
void QmitkStdMultiWidgetEditor::SetFocus()
{
const auto& multiWidget = GetMultiWidget();
if (nullptr != multiWidget)
{
multiWidget->setFocus();
}
}
void QmitkStdMultiWidgetEditor::CreateQtPartControl(QWidget* parent)
{
QHBoxLayout* layout = new QHBoxLayout(parent);
layout->setContentsMargins(0, 0, 0, 0);
auto* preferences = this->GetPreferences();
auto multiWidget = GetMultiWidget();
if (nullptr == multiWidget)
{
multiWidget = new QmitkStdMultiWidget(parent);
// create left toolbar: interaction scheme toolbar to switch how the render window navigation behaves (in PACS mode)
if (nullptr == m_Impl->m_InteractionSchemeToolBar)
{
m_Impl->m_InteractionSchemeToolBar = new QmitkInteractionSchemeToolBar(parent);
layout->addWidget(m_Impl->m_InteractionSchemeToolBar);
}
m_Impl->m_InteractionSchemeToolBar->SetInteractionEventHandler(multiWidget->GetInteractionEventHandler());
multiWidget->SetDataStorage(GetDataStorage());
multiWidget->InitializeMultiWidget();
SetMultiWidget(multiWidget);
}
layout->addWidget(multiWidget);
// create level window slider on the right side
if (nullptr == m_Impl->m_LevelWindowWidget)
{
m_Impl->m_LevelWindowWidget = new QmitkLevelWindowWidget(parent);
m_Impl->m_LevelWindowWidget->setObjectName(QString::fromUtf8("levelWindowWidget"));
QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(m_Impl->m_LevelWindowWidget->sizePolicy().hasHeightForWidth());
m_Impl->m_LevelWindowWidget->setSizePolicy(sizePolicy);
m_Impl->m_LevelWindowWidget->setMaximumWidth(50);
}
layout->addWidget(m_Impl->m_LevelWindowWidget);
- m_Impl->m_MultiWidgetDecorationManager = std::make_unique<QmitkMultiWidgetDecorationManager>(multiWidget);
-
GetSite()->GetPage()->AddPartListener(this);
InitializePreferences(preferences);
OnPreferencesChanged(preferences);
}
void QmitkStdMultiWidgetEditor::OnPreferencesChanged(const mitk::IPreferences* preferences)
{
const auto& multiWidget = dynamic_cast<QmitkStdMultiWidget*>(GetMultiWidget());
if (nullptr == multiWidget)
{
return;
}
// change and apply decoration preferences
GetPreferenceDecorations(preferences);
- m_Impl->m_MultiWidgetDecorationManager->DecorationPreferencesChanged(preferences);
+ this->GetDecorationManager()->DecorationPreferencesChanged(preferences);
QmitkAbstractMultiWidget::RenderWindowWidgetMap renderWindowWidgets = multiWidget->GetRenderWindowWidgets();
int i = 0;
for (const auto& renderWindowWidget : renderWindowWidgets)
{
auto decorationColor = renderWindowWidget.second->GetDecorationColor();
multiWidget->SetDecorationColor(i, decorationColor);
++i;
}
int crosshairGapSize = preferences->GetInt("crosshair gap size", 32);
multiWidget->SetCrosshairGap(crosshairGapSize);
// zooming and panning preferences
bool constrainedZooming = preferences->GetBool("Use constrained zooming and panning", true);
mitk::RenderingManager::GetInstance()->SetConstrainedPanningZooming(constrainedZooming);
// mouse modes switcher toolbar
bool PACSInteractionScheme = preferences->GetBool("PACS like mouse interaction", false);
OnInteractionSchemeChanged(PACSInteractionScheme ?
mitk::InteractionSchemeSwitcher::PACSStandard :
mitk::InteractionSchemeSwitcher::MITKStandard);
// level window setting
bool showLevelWindowWidget = preferences->GetBool("Show level/window widget", true);
ShowLevelWindowWidget(showLevelWindowWidget);
mitk::RenderingManager::GetInstance()->RequestUpdateAll();
}
void QmitkStdMultiWidgetEditor::InitializePreferences(mitk::IPreferences * preferences)
{
auto multiWidget = this->GetMultiWidget();
if (nullptr == multiWidget)
return;
this->GetPreferenceDecorations(preferences); // Override if preferences are defined
for (const auto& renderWindowWidget : multiWidget->GetRenderWindowWidgets())
{
auto widgetName = renderWindowWidget.second->GetWidgetName().toStdString();
auto gradientBackgroundColors = renderWindowWidget.second->GetGradientBackgroundColors();
preferences->Put(widgetName + " first background color", this->MitkColorToHex(gradientBackgroundColors.first));
preferences->Put(widgetName + " second background color", this->MitkColorToHex(gradientBackgroundColors.second));
auto decorationColor = renderWindowWidget.second->GetDecorationColor();
preferences->Put(widgetName + " decoration color", this->MitkColorToHex(decorationColor));
auto cornerAnnotation = renderWindowWidget.second->GetCornerAnnotationText();
preferences->Put(widgetName + " corner annotation", cornerAnnotation);
}
}
void QmitkStdMultiWidgetEditor::GetPreferenceDecorations(const mitk::IPreferences * preferences)
{
auto multiWidget = dynamic_cast<QmitkStdMultiWidget*>(GetMultiWidget());
if (nullptr == multiWidget)
return;
auto hexBlack = "#000000";
auto gradientBlack = "#191919";
auto gradientGray = "#7F7F7F";
auto renderWindowWidgets = multiWidget->GetRenderWindowWidgets();
int i = 0;
for (const auto& renderWindowWidget : renderWindowWidgets)
{
auto widgetName = renderWindowWidget.second->GetWidgetName().toStdString();
if (mitk::BaseRenderer::Standard3D == mitk::BaseRenderer::GetInstance(renderWindowWidget.second->GetRenderWindow()->GetVtkRenderWindow())->GetMapperID())
{
auto upper = preferences->Get(widgetName + " first background color", gradientBlack);
auto lower = preferences->Get(widgetName + " second background color", gradientGray);
renderWindowWidget.second->SetGradientBackgroundColors(HexColorToMitkColor(upper), HexColorToMitkColor(lower));
}
else
{
auto upper = preferences->Get(widgetName + " first background color", hexBlack);
auto lower = preferences->Get(widgetName + " second background color", hexBlack);
renderWindowWidget.second->SetGradientBackgroundColors(HexColorToMitkColor(upper), HexColorToMitkColor(lower));
}
auto defaultDecorationColor = multiWidget->GetDecorationColor(i);
auto decorationColor = preferences->Get(widgetName + " decoration color", MitkColorToHex(defaultDecorationColor));
renderWindowWidget.second->SetDecorationColor(HexColorToMitkColor(decorationColor));
auto defaultCornerAnnotation = renderWindowWidget.second->GetCornerAnnotationText();
auto cornerAnnotation = preferences->Get(widgetName + " corner annotation", defaultCornerAnnotation);
renderWindowWidget.second->SetCornerAnnotationText(cornerAnnotation);
++i;
}
}
mitk::Color QmitkStdMultiWidgetEditor::HexColorToMitkColor(const std::string& hexColor)
{
QColor qColor(hexColor.c_str());
mitk::Color returnColor;
float colorMax = 255.0f;
if (hexColor.empty()) // default value
{
returnColor[0] = 1.0;
returnColor[1] = 1.0;
returnColor[2] = 1.0;
MITK_ERROR << "Using default color for unknown hex color " << hexColor;
}
else
{
returnColor[0] = qColor.red() / colorMax;
returnColor[1] = qColor.green() / colorMax;
returnColor[2] = qColor.blue() / colorMax;
}
return returnColor;
}
std::string QmitkStdMultiWidgetEditor::MitkColorToHex(const mitk::Color& color)
{
QColor returnColor;
float colorMax = 255.0f;
returnColor.setRed(static_cast<int>(color[0] * colorMax + 0.5));
returnColor.setGreen(static_cast<int>(color[1] * colorMax + 0.5));
returnColor.setBlue(static_cast<int>(color[2] * colorMax + 0.5));
return returnColor.name().toStdString();
}
diff --git a/SuperBuild.cmake b/SuperBuild.cmake
index 512fa28389..250dfe8795 100644
--- a/SuperBuild.cmake
+++ b/SuperBuild.cmake
@@ -1,504 +1,499 @@
#-----------------------------------------------------------------------------
# Convenient macro allowing to download a file
#-----------------------------------------------------------------------------
if(NOT MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL)
set(MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL https://www.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")
if(Qt6_DIR)
set(_install_rpath_linkflag "${_install_rpath_linkflag}:${Qt6_DIR}/../..")
endif()
set(_install_rpath_linkflag "${_install_rpath_linkflag}'")
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")
if(Qt6_DIR)
set(_install_rpath "${_install_rpath}:${Qt6_DIR}/../..")
endif()
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_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_Qt6
MITK_USE_SYSTEM_Boost
MITK_USE_BLUEBERRY
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()
# 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_CONFIGURATION_TYPES:STRING=${CMAKE_CONFIGURATION_TYPES}"
"-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}
-DQt6_DIR:PATH=${Qt6_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
)
diff --git a/Utilities/qtsingleapplication/qtlockedfile.cpp b/Utilities/qtsingleapplication/qtlockedfile.cpp
index 8e45624f42..92f67014ad 100644
--- a/Utilities/qtsingleapplication/qtlockedfile.cpp
+++ b/Utilities/qtsingleapplication/qtlockedfile.cpp
@@ -1,192 +1,192 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Solutions component.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Digia Plc and its Subsidiary(-ies) 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 COPYRIGHT
** OWNER 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."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qtlockedfile.h"
/*!
\class QtLockedFile
\brief The QtLockedFile class extends QFile with advisory locking
functions.
A file may be locked in read or write mode. Multiple instances of
\e QtLockedFile, created in multiple processes running on the same
machine, may have a file locked in read mode. Exactly one instance
may have it locked in write mode. A read and a write lock cannot
exist simultaneously on the same file.
The file locks are advisory. This means that nothing prevents
another process from manipulating a locked file using QFile or
file system functions offered by the OS. Serialization is only
guaranteed if all processes that access the file use
QLockedFile. Also, while holding a lock on a file, a process
must not open the same file again (through any API), or locks
can be unexpectedly lost.
The lock provided by an instance of \e QtLockedFile is released
whenever the program terminates. This is true even when the
program crashes and no destructors are called.
*/
/*! \enum QtLockedFile::LockMode
This enum describes the available lock modes.
\value ReadLock A read lock.
\value WriteLock A write lock.
\value NoLock Neither a read lock nor a write lock.
*/
/*!
Constructs an unlocked \e QtLockedFile object. This constructor
behaves in the same way as \e QFile::QFile().
\sa QFile::QFile()
*/
QtLockedFile::QtLockedFile() : QFile()
{
#ifdef Q_OS_WIN
wmutex = 0;
rmutex = 0;
#endif
m_lock_mode = NoLock;
}
/*!
Constructs an unlocked QtLockedFile object with file \a name. This
constructor behaves in the same way as \e QFile::QFile(const
QString&).
\sa QFile::QFile()
*/
QtLockedFile::QtLockedFile(const QString &name) : QFile(name)
{
#ifdef Q_OS_WIN
wmutex = 0;
rmutex = 0;
#endif
m_lock_mode = NoLock;
}
/*!
Opens the file in OpenMode \a mode.
This is identical to QFile::open(), with the one exception that the
Truncate mode flag is disallowed. Truncation would conflict with the
advisory file locking, since the file would be modified before the
write lock is obtained. If truncation is required, use resize(0)
after obtaining the write lock.
Returns true if successful; otherwise false.
\sa QFile::open(), QFile::resize()
*/
bool QtLockedFile::open(OpenMode mode)
{
if (mode & QIODevice::Truncate)
{
qWarning("QtLockedFile::open(): Truncate mode not allowed.");
return false;
}
return QFile::open(mode);
}
/*!
Returns \e true if this object has a in read or write lock;
otherwise returns \e false.
\sa lockMode()
*/
bool QtLockedFile::isLocked() const
{
return m_lock_mode != NoLock;
}
/*!
Returns the type of lock currently held by this object, or \e
QtLockedFile::NoLock.
\sa isLocked()
*/
QtLockedFile::LockMode QtLockedFile::lockMode() const
{
return m_lock_mode;
}
/*!
\fn bool QtLockedFile::lock(LockMode mode, bool block = true)
Obtains a lock of type \a mode. The file must be opened before it
can be locked.
If \a block is true, this function will block until the lock is
- aquired. If \a block is false, this function returns \e false
- immediately if the lock cannot be aquired.
+ acquired. If \a block is false, this function returns \e false
+ immediately if the lock cannot be acquired.
If this object already has a lock of type \a mode, this function
returns \e true immediately. If this object has a lock of a
different type than \a mode, the lock is first released and then a
new lock is obtained.
This function returns \e true if, after it executes, the file is
locked by this object, and \e false otherwise.
\sa unlock(), isLocked(), lockMode()
*/
/*!
\fn bool QtLockedFile::unlock()
Releases a lock.
If the object has no lock, this function returns immediately.
This function returns \e true if, after it executes, the file is
not locked by this object, and \e false otherwise.
\sa lock(), isLocked(), lockMode()
*/
/*!
\fn QtLockedFile::~QtLockedFile()
Destroys the \e QtLockedFile object. If any locks were held, they
are released.
*/

File Metadata

Mime Type
application/octet-stream
Expires
Wed, Sep 18, 8:51 PM (1 d, 23 h)
Storage Engine
chunks
Storage Format
Chunks
Storage Handle
Nh7Kj7Dj3Nn7
Default Alt Text
(6 MB)

Event Timeline