diff --git a/Applications/PluginGenerator/ProjectTemplate/SuperBuild.cmake b/Applications/PluginGenerator/ProjectTemplate/SuperBuild.cmake index 13ce6ef7e9..27398deae3 100644 --- a/Applications/PluginGenerator/ProjectTemplate/SuperBuild.cmake +++ b/Applications/PluginGenerator/ProjectTemplate/SuperBuild.cmake @@ -1,231 +1,228 @@ #----------------------------------------------------------------------------- # ExternalProjects #----------------------------------------------------------------------------- set(external_projects MITK ) set(EXTERNAL_MITK_DIR "${MITK_DIR}" CACHE PATH "Path to MITK build directory") mark_as_advanced(EXTERNAL_MITK_DIR) if(EXTERNAL_MITK_DIR) set(MITK_DIR ${EXTERNAL_MITK_DIR}) endif() -# Look for git early on, if needed -if(NOT MITK_DIR AND MITK_USE_CTK AND NOT CTK_DIR) - find_package(Git REQUIRED) -endif() +find_package(Git REQUIRED) #----------------------------------------------------------------------------- # External project settings #----------------------------------------------------------------------------- include(ExternalProject) set(ep_base "${CMAKE_BINARY_DIR}/CMakeExternals") set_property(DIRECTORY PROPERTY EP_BASE ${ep_base}) set(ep_install_dir "${CMAKE_BINARY_DIR}/CMakeExternals/Install") set(ep_suffix "-cmake") set(ep_build_shared_libs ON) set(ep_build_testing OFF) # 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() # Use this value where semi-colons are needed in ep_add args: set(sep "^^") ## if(MSVC_VERSION) set(ep_common_C_FLAGS "${CMAKE_C_FLAGS} /bigobj /MP") set(ep_common_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj /MP") endif() set(ep_common_args -DBUILD_TESTING:BOOL=${ep_build_testing} -DCMAKE_INSTALL_PREFIX:PATH=${ep_install_dir} -DBUILD_SHARED_LIBS:BOOL=${ep_build_shared_libs} -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} # 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} ) set(ep_common_cache_args ) set(ep_common_cache_default_args "-DCMAKE_PREFIX_PATH:PATH=${CMAKE_PREFIX_PATH}" "-DCMAKE_INCLUDE_PATH:PATH=${CMAKE_INCLUDE_PATH}" "-DCMAKE_LIBRARY_PATH:PATH=${CMAKE_LIBRARY_PATH}" ) # Include external projects foreach(p ${external_projects}) include(CMakeExternals/${p}.cmake) endforeach() #----------------------------------------------------------------------------- # Set superbuild boolean args #----------------------------------------------------------------------------- set(my_cmake_boolean_args WITH_COVERAGE BUILD_TESTING ${MY_PROJECT_NAME}_BUILD_ALL_PLUGINS ) #----------------------------------------------------------------------------- # Create the final variable containing superbuild boolean args #----------------------------------------------------------------------------- set(my_superbuild_boolean_args) foreach(my_cmake_arg ${my_cmake_boolean_args}) list(APPEND my_superbuild_boolean_args -D${my_cmake_arg}:BOOL=${${my_cmake_arg}}) endforeach() #----------------------------------------------------------------------------- # Project Utilities #----------------------------------------------------------------------------- set(proj ${MY_PROJECT_NAME}-Utilities) ExternalProject_Add(${proj} DOWNLOAD_COMMAND "" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" DEPENDS # Mandatory dependencies ${MITK_DEPENDS} # Optional dependencies ) #----------------------------------------------------------------------------- # Additional Project CXX/C Flags #----------------------------------------------------------------------------- set(${MY_PROJECT_NAME}_ADDITIONAL_C_FLAGS "" CACHE STRING "Additional C Flags for ${MY_PROJECT_NAME}") set(${MY_PROJECT_NAME}_ADDITIONAL_C_FLAGS_RELEASE "" CACHE STRING "Additional Release C Flags for ${MY_PROJECT_NAME}") set(${MY_PROJECT_NAME}_ADDITIONAL_C_FLAGS_DEBUG "" CACHE STRING "Additional Debug C Flags for ${MY_PROJECT_NAME}") mark_as_advanced(${MY_PROJECT_NAME}_ADDITIONAL_C_FLAGS ${MY_PROJECT_NAME}_ADDITIONAL_C_FLAGS_DEBUG ${MY_PROJECT_NAME}_ADDITIONAL_C_FLAGS_RELEASE) set(${MY_PROJECT_NAME}_ADDITIONAL_CXX_FLAGS "" CACHE STRING "Additional CXX Flags for ${MY_PROJECT_NAME}") set(${MY_PROJECT_NAME}_ADDITIONAL_CXX_FLAGS_RELEASE "" CACHE STRING "Additional Release CXX Flags for ${MY_PROJECT_NAME}") set(${MY_PROJECT_NAME}_ADDITIONAL_CXX_FLAGS_DEBUG "" CACHE STRING "Additional Debug CXX Flags for ${MY_PROJECT_NAME}") mark_as_advanced(${MY_PROJECT_NAME}_ADDITIONAL_CXX_FLAGS ${MY_PROJECT_NAME}_ADDITIONAL_CXX_FLAGS_DEBUG ${MY_PROJECT_NAME}_ADDITIONAL_CXX_FLAGS_RELEASE) set(${MY_PROJECT_NAME}_ADDITIONAL_EXE_LINKER_FLAGS "" CACHE STRING "Additional exe linker flags for ${MY_PROJECT_NAME}") set(${MY_PROJECT_NAME}_ADDITIONAL_SHARED_LINKER_FLAGS "" CACHE STRING "Additional shared linker flags for ${MY_PROJECT_NAME}") set(${MY_PROJECT_NAME}_ADDITIONAL_MODULE_LINKER_FLAGS "" CACHE STRING "Additional module linker flags for ${MY_PROJECT_NAME}") mark_as_advanced(${MY_PROJECT_NAME}_ADDITIONAL_EXE_LINKER_FLAGS ${MY_PROJECT_NAME}_ADDITIONAL_SHARED_LINKER_FLAGS ${MY_PROJECT_NAME}_ADDITIONAL_MODULE_LINKER_FLAGS) #----------------------------------------------------------------------------- # Project Configure #----------------------------------------------------------------------------- set(proj ${MY_PROJECT_NAME}-Configure) ExternalProject_Add(${proj} DOWNLOAD_COMMAND "" CMAKE_GENERATOR ${gen} CMAKE_CACHE_ARGS # --------------- Build options ---------------- -DBUILD_TESTING:BOOL=${ep_build_testing} -DCMAKE_INSTALL_PREFIX:PATH=${ep_install_dir} -DBUILD_SHARED_LIBS:BOOL=${ep_build_shared_libs} -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} "-DCMAKE_PREFIX_PATH:PATH=${CMAKE_PREFIX_PATH}" "-DCMAKE_INCLUDE_PATH:PATH=${CMAKE_INCLUDE_PATH}" "-DCMAKE_LIBRARY_PATH:PATH=${CMAKE_LIBRARY_PATH}" # --------------- Compile options ---------------- -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} ${${MY_PROJECT_NAME}_ADDITIONAL_C_FLAGS}" "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} ${${MY_PROJECT_NAME}_ADDITIONAL_CXX_FLAGS}" # debug flags "-DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG} ${${MY_PROJECT_NAME}_ADDITIONAL_CXX_FLAGS_DEBUG}" "-DCMAKE_C_FLAGS_DEBUG:STRING=${CMAKE_C_FLAGS_DEBUG} ${${MY_PROJECT_NAME}_ADDITIONAL_C_FLAGS_DEBUG}" # release flags "-DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE} ${${MY_PROJECT_NAME}_ADDITIONAL_CXX_FLAGS_RELEASE}" "-DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELEASE} ${${MY_PROJECT_NAME}_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} ${${MY_PROJECT_NAME}_ADDITIONAL_EXE_LINKER_FLAGS}" "-DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS} ${${MY_PROJECT_NAME}_ADDITIONAL_SHARED_LINKER_FLAGS}" "-DCMAKE_MODULE_LINKER_FLAGS:STRING=${CMAKE_MODULE_LINKER_FLAGS} ${${MY_PROJECT_NAME}_ADDITIONAL_MODULE_LINKER_FLAGS}" # ------------- Boolean build options -------------- ${my_superbuild_boolean_args} -D${MY_PROJECT_NAME}_USE_SUPERBUILD:BOOL=OFF -D${MY_PROJECT_NAME}_CONFIGURED_VIA_SUPERBUILD:BOOL=ON -DCTEST_USE_LAUNCHERS:BOOL=${CTEST_USE_LAUNCHERS} # ----------------- Miscellaneous --------------- -D${MY_PROJECT_NAME}_SUPERBUILD_BINARY_DIR:PATH=${PROJECT_BINARY_DIR} -DMITK_DIR:PATH=${MITK_DIR} -DITK_DIR:PATH=${ITK_DIR} -DVTK_DIR:PATH=${VTK_DIR} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} BINARY_DIR ${CMAKE_BINARY_DIR}/${MY_PROJECT_NAME}-build BUILD_COMMAND "" INSTALL_COMMAND "" DEPENDS ${MY_PROJECT_NAME}-Utilities ) #----------------------------------------------------------------------------- # Project #----------------------------------------------------------------------------- if(CMAKE_GENERATOR MATCHES ".*Makefiles.*") set(_build_cmd "$(MAKE)") else() set(_build_cmd ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/${MY_PROJECT_NAME}-build --config ${CMAKE_CFG_INTDIR}) endif() # The variable SUPERBUILD_EXCLUDE_${MY_PROJECT_NAME}BUILD_TARGET should be set when submitting to a dashboard if(NOT DEFINED SUPERBUILD_EXCLUDE_${MY_PROJECT_NAME}BUILD_TARGET OR NOT SUPERBUILD_EXCLUDE_${MY_PROJECT_NAME}BUILD_TARGET) set(_target_all_option "ALL") else() set(_target_all_option "") endif() add_custom_target(${MY_PROJECT_NAME}-build ${_target_all_option} COMMAND ${_build_cmd} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${MY_PROJECT_NAME}-build DEPENDS ${MY_PROJECT_NAME}-Configure ) #----------------------------------------------------------------------------- # Custom target allowing to drive the build of the project itself #----------------------------------------------------------------------------- add_custom_target(${MY_PROJECT_NAME} COMMAND ${_build_cmd} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${MY_PROJECT_NAME}-build ) diff --git a/CMake/FindGit.cmake b/CMake/FindGit.cmake deleted file mode 100644 index 5bc9acf228..0000000000 --- a/CMake/FindGit.cmake +++ /dev/null @@ -1,81 +0,0 @@ -# -# FindGit -# - -set(git_candidates git eg) - -if(WIN32) - list(APPEND git_candidates eg.cmd) - if(NOT MSYS) - # We don't append git.cmd, since it does not correctly return - # exit codes, see http://code.google.com/p/msysgit/issues/detail?id=428 - list(APPEND git_candidates git.cmd) - endif() -endif() - -find_program(GIT_EXECUTABLE ${git_candidates} - PATHS - "C:/Program Files/Git/bin" - "C:/Program Files (x86)/Git/bin" - DOC "git command line client") -mark_as_advanced(GIT_EXECUTABLE) - -if(GIT_EXECUTABLE) - - macro(GIT_IS_REPO dir result_var) - # check if ${dir} is a proper Git repository - execute_process(COMMAND ${GIT_EXECUTABLE} rev-list -n 1 HEAD - WORKING_DIRECTORY ${dir} - RESULT_VARIABLE GIT_error - OUTPUT_QUIET - ERROR_QUIET) - if(GIT_error EQUAL 0) - set(${result_var} 1) - else() - set(${result_var} 0) - endif() - endmacro() - - macro(GIT_WC_INFO dir prefix) - execute_process(COMMAND ${GIT_EXECUTABLE} rev-list -n 1 HEAD - WORKING_DIRECTORY ${dir} - ERROR_VARIABLE GIT_error - OUTPUT_VARIABLE ${prefix}_WC_REVISION_HASH - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT ${GIT_error} EQUAL 0) - message(SEND_ERROR "Command \"${GIT_EXECUTBALE} rev-list -n 1 HEAD\" in directory ${dir} failed with output:\n${GIT_error}") - else() - execute_process(COMMAND ${GIT_EXECUTABLE} name-rev ${${prefix}_WC_REVISION_HASH} - WORKING_DIRECTORY ${dir} - OUTPUT_VARIABLE ${prefix}_WC_REVISION_NAME - OUTPUT_STRIP_TRAILING_WHITESPACE) - endif() - - set(${prefix}_WC_GITSVN 0) - # In case, git-svn is used, attempt to extract svn info - execute_process(COMMAND ${GIT_EXECUTABLE} svn info - WORKING_DIRECTORY ${dir} - ERROR_VARIABLE git_svn_info_error - OUTPUT_VARIABLE ${prefix}_WC_INFO - RESULT_VARIABLE git_svn_info_result - OUTPUT_STRIP_TRAILING_WHITESPACE) - - if(${git_svn_info_result} EQUAL 0) - set(${prefix}_WC_GITSVN 1) - string(REGEX REPLACE "^(.*\n)?URL: ([^\n]+).*" - "\\2" ${prefix}_WC_URL "${${prefix}_WC_INFO}") - string(REGEX REPLACE "^(.*\n)?Revision: ([^\n]+).*" - "\\2" ${prefix}_WC_REVISION "${${prefix}_WC_INFO}") - string(REGEX REPLACE "^(.*\n)?Last Changed Author: ([^\n]+).*" - "\\2" ${prefix}_WC_LAST_CHANGED_AUTHOR "${${prefix}_WC_INFO}") - string(REGEX REPLACE "^(.*\n)?Last Changed Rev: ([^\n]+).*" - "\\2" ${prefix}_WC_LAST_CHANGED_REV "${${prefix}_WC_INFO}") - string(REGEX REPLACE "^(.*\n)?Last Changed Date: ([^\n]+).*" - "\\2" ${prefix}_WC_LAST_CHANGED_DATE "${${prefix}_WC_INFO}") - endif() - - endmacro() -endif() - -include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Git DEFAULT_MSG GIT_EXECUTABLE) diff --git a/CMake/MITKDashboardScript.cmake b/CMake/MITKDashboardScript.cmake index cad483cf01..6049d0e184 100644 --- a/CMake/MITKDashboardScript.cmake +++ b/CMake/MITKDashboardScript.cmake @@ -1,178 +1,196 @@ #[============================================================================[ Configuration ]============================================================================] set(CTEST_CMAKE_GENERATOR # "Unix Makefiles" "Visual Studio 16 2019" ) set(CTEST_CMAKE_GENERATOR_PLATFORM # "Win32" "x64" ) set(CTEST_BUILD_CONFIGURATION # "Debug" "Release" # "MinSizeRel" # "RelWithDebInfo" ) set(CTEST_DASHBOARD_MODEL # "Continuous" "Experimental" # "Nightly" ) # If empty: Default site name is the name of this computer set(CTEST_SITE "" ) -# If empty: Default build name is based on OS, compiler, -# CTEST_BUILD_CONFIGURATION, and MITK_BUILD_CONFIGURATION +# If empty: Default build name is automatically generated based on OS, +# compiler, CTEST_BUILD_CONFIGURATION, MITK_BUILD_CONFIGURATION, +# and MITK_BUILD_NAME_SUFFIX set(CTEST_BUILD_NAME "" ) set(CTEST_BUILD_FLAGS # "-j8" ) +# Only appended to automatically generated CTEST_BUILD_NAME +set(MITK_BUILD_NAME_SUFFIX + "" +) + +set(MITK_CLEAN_SOURCE_DIR + OFF +) + +set(MITK_CLEAN_BINARY_DIR + OFF +) + # If empty: Default URL is CTEST_SUBMIT_URL (CTestConfig.cmake) set(MITK_SUBMIT_URL "https://cdash.mitk.org/submit.php?project=MITK" ) +set(MITK_AUTH_TOKEN + "" +) + set(MITK_BUILD_CONFIGURATION # "All" "WorkbenchRelease" # "Minimal" ) set(MITK_BUILD_DOCUMENTATION OFF ) set(MITK_SUPERBUILD_OPTIONS # "MITK_WHITELIST:STRING=Minimal" "Qt5_DIR:PATH=C:/Qt/5.12.9/msvc2017_64/lib/cmake/Qt5" "MITK_AUTOCLEAN_EXTERNAL_PROJECTS:BOOL=ON" "MITK_FAST_TESTING:BOOL=ON" "MITK_XVFB_TESTING:BOOL=OFF" ) set(MITK_BUILD_OPTIONS "" ) set(MITK_REPOSITORY "https://phabricator.mitk.org/source/mitk.git" ) # Branch name, tag, or commit hash set(MITK_BRANCH "develop" # "master" ) # Three list items per MITK extension: set(MITK_EXTENSIONS # "MITK-ProjectTemplate" # "https://phabricator.mitk.org/source/mitk-projecttemplate.git" # "master" # "MITK Diffusion" # "https://phabricator.mitk.org/source/mitk-diffusion.git" # "master" ) #[============================================================================[ Helper functions ]============================================================================] function(get_temp_directory) set(temp_directory "${CMAKE_CURRENT_SOURCE_DIR}/temp") if(NOT EXISTS "${temp_directory}") file(MAKE_DIRECTORY "${temp_directory}") elseif(NOT IS_DIRECTORY "${temp_directory}") message(FATAL_ERROR "Temporary directory location blocked by file \"${temp_directory}\"") endif() set(${ARGV0} "${temp_directory}" PARENT_SCOPE) endfunction() function(download_file) cmake_parse_arguments(PARSE_ARGV 0 ARG "QUIET" "URL;OUTPUT_FILE;ERROR_CODE" "") get_filename_component(output_dir "${ARG_OUTPUT_FILE}" DIRECTORY) get_filename_component(output_filename "${ARG_OUTPUT_FILE}" NAME) if(NOT output_dir) set(output_dir "${CMAKE_CURRENT_SOURCE_DIR}") endif() set(output_file "${output_dir}/${output_filename}") if(NOT ARG_QUIET) message("Download file\n from: ${ARG_URL}\n to: ${output_file}") endif() get_temp_directory(temp_dir) string(RANDOM random_filename) while(EXISTS "${temp_dir}/${random_filename}") string(RANDOM random_filename) endwhile() set(temp_output_file "${temp_dir}/${random_filename}") file(DOWNLOAD "${ARG_URL}" "${temp_output_file}" STATUS status) list(GET status 0 error_code) list(GET status 1 error_message) set(error_message "${error_message} (${error_code})") if(NOT 0 EQUAL error_code) if(EXISTS "${temp_output_file}") file(REMOVE "${temp_output_file}") endif() if(NOT ARG_ERROR_CODE) message(FATAL_ERROR "${error_message}") elseif(NOT ARG_QUIET) message(" error: ${error_message}") endif() else() file(RENAME "${temp_output_file}" "${output_file}") endif() if(ARG_ERROR_CODE) set(${ARG_ERROR_CODE} ${error_code} PARENT_SCOPE) endif() endfunction() #[============================================================================[ Download and include actual script ]============================================================================] include("MITKDashboardScript.local.cmake" OPTIONAL RESULT_VARIABLE found) if(NOT found) get_temp_directory(temp_dir) set(script "${temp_dir}/MITKDashboardScript.cmake") download_file( URL "https://raw.githubusercontent.com/MITK/MITK/${MITK_BRANCH}/CMake/MITKDashboardScript.download.cmake" OUTPUT_FILE "${script}" ) include("${script}") endif() diff --git a/CMake/MITKDashboardScript.download.cmake b/CMake/MITKDashboardScript.download.cmake index 3ed3a6d16e..7ff24c8582 100644 --- a/CMake/MITKDashboardScript.download.cmake +++ b/CMake/MITKDashboardScript.download.cmake @@ -1,653 +1,698 @@ #[============================================================================[ Helper functions ]============================================================================] function(get_macos_codename) set(macos_codename "") set(software_license_file "\ /System/Library/CoreServices/Setup Assistant.app\ /Contents/Resources/en.lproj/OSXSoftwareLicense.rtf") if(EXISTS "${software_license_file}") file(READ "${software_license_file}" software_license) if(software_license MATCHES "SOFTWARE LICENSE AGREEMENT FOR macOS ([^\n]+)") set(macos_codename "${CMAKE_MATCH_1}") endif() endif() set(${ARGV0} ${macos_codename} PARENT_SCOPE) endfunction() function(get_macos_name) set(macos_name "macOS") get_macos_codename(macos_codename) if(macos_codename) set(macos_name "${macos_name} ${macos_codename}") endif() execute_process( RESULT_VARIABLE error_code OUTPUT_VARIABLE version OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND "sw_vers" "-productVersion") if(0 EQUAL error_code AND version) set(macos_name "${macos_name} ${version}") endif() set(${ARGV0} ${macos_name} PARENT_SCOPE) endfunction() function(get_linux_name) set(linux_name "Linux") set(os_release_file "/etc/os-release") if(EXISTS "${os_release_file}") file(STRINGS "${os_release_file}" os_release) set(name "") set(version_id "") foreach(line ${os_release}) if(NOT name AND line MATCHES "^NAME=[\"]?([^\"]+)") set(name "${CMAKE_MATCH_1}") continue() endif() if(NOT version_id AND line MATCHES "^VERSION_ID=\"?([^\"]+)") set(version_id "${CMAKE_MATCH_1}") continue() endif() if(name AND version_id) break() endif() endforeach() if(name) set(linux_name "${name}") endif() if(version_id) set(linux_name "${linux_name} ${version_id}") endif() endif() set(${ARGV0} ${linux_name} PARENT_SCOPE) endfunction() function(get_windows_name) set(windows_name "Windows") execute_process( RESULT_VARIABLE error_code OUTPUT_VARIABLE version OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND "wmic" "os" "get" "Version" "-value") if(0 EQUAL error_code AND version) if(version MATCHES "Version=([0-9]+)\\.([0-9]+)") set(windows_name "${windows_name} ${CMAKE_MATCH_1}") if(NOT 0 EQUAL CMAKE_MATCH_2) set(windows_name "${windows_name}.${CMAKE_MATCH_2}") endif() endif() endif() set(${ARGV0} ${windows_name} PARENT_SCOPE) endfunction() function(get_os_name) set(os_name "") if(APPLE) get_macos_name(os_name) elseif(UNIX) get_linux_name(os_name) elseif(WIN32) get_windows_name(os_name) endif() set(${ARGV0} ${os_name} PARENT_SCOPE) endfunction() function(download_string) cmake_parse_arguments(PARSE_ARGV 0 ARG "" "URL;OUTPUT_VARIABLE" "") get_temp_directory(temp_dir) string(RANDOM random_filename) while(EXISTS "${temp_dir}/${random_filename}") string(RANDOM random_filename) endwhile() set(temp_output_file "${temp_dir}/${random_filename}") download_file( URL ${ARG_URL} OUTPUT_FILE ${temp_output_file} ERROR_CODE error_code) set(output "") if(EXISTS "${temp_output_file}") file(READ "${temp_output_file}" output) file(REMOVE "${temp_output_file}") endif() set(${ARG_OUTPUT_VARIABLE} ${output} PARENT_SCOPE) endfunction() function(find_vswhere) get_temp_directory(temp_dir) set(latest_vswhere "${temp_dir}/vswhere.exe") set(vswhere "vswhere.exe-NOTFOUND") if(EXISTS "${latest_vswhere}") set(vswhere "${latest_vswhere}") else() set(program_files_x86 "PROGRAMFILES(X86)") set(program_files_x86 "$ENV{${program_files_x86}}") set(system_vswhere "${program_files_x86}/Microsoft Visual Studio/Installer/vswhere.exe") if(EXISTS "${system_vswhere}") set(vswhere "${system_vswhere}") else() download_string( URL "https://api.github.com/repos/microsoft/vswhere/releases/latest" OUTPUT_VARIABLE latest_vswhere_json) if(latest_vswhere_json MATCHES "\"browser_download_url\": *\"([^\"]*)\"") download_file( URL "${CMAKE_MATCH_1}" OUTPUT_FILE "${latest_vswhere}" ERROR_CODE error_code) if (0 EQUAL error_code) set(vswhere "${latest_vswhere}") endif() endif() endif() endif() set(${ARGV0} "${vswhere}" PARENT_SCOPE) endfunction() function(vswhere) cmake_parse_arguments(PARSE_ARGV 0 ARG "" "PROPERTY;OUTPUT_VARIABLE" "") set(property_value "") if(CTEST_CMAKE_GENERATOR MATCHES "^Visual Studio ([0-9]+)") set(major_version "${CMAKE_MATCH_1}") math(EXPR next_major_version "${major_version} + 1") find_vswhere(vswhere) if (vswhere) execute_process( OUTPUT_VARIABLE property_value OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND ${vswhere} "-requires" "Microsoft.VisualStudio.Workload.NativeDesktop" "-version" "[${major_version},${next_major_version})" "-property" "${ARG_PROPERTY}" "-format" "value") endif() endif() set(${ARG_OUTPUT_VARIABLE} ${property_value} PARENT_SCOPE) endfunction() function(get_visual_studio_name) set(visual_studio_name "Visual Studio") if(CTEST_CMAKE_GENERATOR MATCHES "^Visual Studio [0-9]+ ([0-9]+)") set(product_line_version "${CMAKE_MATCH_1}") set(visual_studio_name "${visual_studio_name} ${product_line_version}") vswhere(PROPERTY "catalog_productDisplayVersion" OUTPUT_VARIABLE product_display_version) if(product_display_version) set(visual_studio_name "${visual_studio_name} ${product_display_version}") endif() endif() set(${ARGV0} ${visual_studio_name} PARENT_SCOPE) endfunction() function(get_compiler_name) set(compiler_name "") if(CTEST_CMAKE_GENERATOR MATCHES "^Visual Studio") get_visual_studio_name(compiler_name) else() get_temp_directory(temp_dir) file(WRITE "${temp_dir}/src/CMakeLists.txt" "\ project(probe C) message(STATUS \"CMAKE_C_COMPILER_ID=\\\"\${CMAKE_C_COMPILER_ID}\\\"\") message(STATUS \"CMAKE_C_COMPILER_VERSION=\\\"\${CMAKE_C_COMPILER_VERSION}\\\"\")") file(REMOVE_RECURSE "${temp_dir}/build") unset(options) foreach(option ${MITK_SUPERBUILD_OPTIONS}) list(APPEND options "-D" "${option}") endforeach() execute_process( RESULT_VARIABLE error_code OUTPUT_VARIABLE compiler ERROR_QUIET COMMAND ${CMAKE_COMMAND} "-S" "${temp_dir}/src" "-B" "${temp_dir}/build" "-G" "${CTEST_CMAKE_GENERATOR}" ${options}) if(0 EQUAL error_code) if(compiler MATCHES "CMAKE_C_COMPILER_ID=\"([^\"]*)\"") set(compiler_name "${CMAKE_MATCH_1}") if(compiler MATCHES "CMAKE_C_COMPILER_VERSION=\"([^\"]*)\"") set(version "${CMAKE_MATCH_1}") if(version MATCHES "^([0-9]+\\.[0-9]+\\.[0-9]+)") set(version "${CMAKE_MATCH_1}") endif() string(REGEX REPLACE "\\.0$" "" version "${version}") set(compiler_name "${compiler_name} ${version}") endif() endif() endif() endif() set(${ARGV0} ${compiler_name} PARENT_SCOPE) endfunction() function(get_default_build_name) get_os_name(default_build_name) get_compiler_name(compiler_name) if(compiler_name) set(default_build_name "${default_build_name} ${compiler_name}") endif() set(default_build_name "${default_build_name} ${CTEST_BUILD_CONFIGURATION}") if(MITK_BUILD_CONFIGURATION) set(default_build_name "${default_build_name} ${MITK_BUILD_CONFIGURATION}") endif() + if(MITK_BUILD_NAME_SUFFIX) + set(default_build_name "${default_build_name} ${MITK_BUILD_NAME_SUFFIX}") + endif() + set(${ARGV0} ${default_build_name} PARENT_SCOPE) endfunction() function(git) cmake_parse_arguments(PARSE_ARGV 0 ARG "" "WORKING_DIRECTORY;OUTPUT_VARIABLE;RESULT_VARIABLE" "") if(NOT ARG_WORKING_DIRECTORY) set(ARG_WORKING_DIRECTORY "${CTEST_SOURCE_DIRECTORY}") endif() get_filename_component(ARG_WORKING_DIRECTORY "${ARG_WORKING_DIRECTORY}" ABSOLUTE) execute_process( WORKING_DIRECTORY "${ARG_WORKING_DIRECTORY}" RESULT_VARIABLE error_code OUTPUT_VARIABLE output OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND "${CTEST_GIT_COMMAND}" ${ARG_UNPARSED_ARGUMENTS}) if(ARG_OUTPUT_VARIABLE) set(${ARG_OUTPUT_VARIABLE} ${output} PARENT_SCOPE) endif() if(ARG_RESULT_VARIABLE) set(${ARG_RESULT_VARIABLE} ${error_code} PARENT_SCOPE) endif() endfunction() function(git_log) cmake_parse_arguments(PARSE_ARGV 0 ARG "" "WORKING_DIRECTORY;COMMIT_HASH;COMMITTER;SUBJECT" "") if(NOT ARG_WORKING_DIRECTORY) set(ARG_WORKING_DIRECTORY "${CTEST_SOURCE_DIRECTORY}") endif() get_filename_component(ARG_WORKING_DIRECTORY "${ARG_WORKING_DIRECTORY}" ABSOLUTE) set(args WORKING_DIRECTORY "${ARG_WORKING_DIRECTORY}" "log" "-1") if(ARG_COMMIT_HASH) unset(commit_hash) git(OUTPUT_VARIABLE commit_hash ${args} "--pretty=%h" ) if(commit_hash) set(${ARG_COMMIT_HASH} ${commit_hash} PARENT_SCOPE) endif() endif() if(ARG_COMMITTER) unset(committer) git(OUTPUT_VARIABLE committer ${args} "--pretty=%cn" ) if(committer) set(${ARG_COMMITTER} ${committer} PARENT_SCOPE) endif() endif() if(ARG_SUBJECT) unset(subject) git(OUTPUT_VARIABLE subject ${args} "--pretty=%s" ) if(subject) set(${ARG_SUBJECT} ${subject} PARENT_SCOPE) endif() endif() endfunction() +function(rm) + execute_process(COMMAND ${CMAKE_COMMAND} "-E" "rm" "-rf" ${ARGN}) +endfunction() + function(submit) cmake_parse_arguments(PARSE_ARGV 0 ARG "" "" "PARTS") if(MITK_SUBMIT_URL) set(submit_url "SUBMIT_URL" "${MITK_SUBMIT_URL}") else() unset(submit_url) endif() - ctest_submit(${submit_url} RETRY_COUNT 3 RETRY_DELAY 5 PARTS ${ARG_PARTS}) + if(MITK_AUTH_TOKEN) + set(http_header HTTPHEADER "Authorization: Bearer ${MITK_AUTH_TOKEN}") + else() + unset(http_header) + endif() + + ctest_submit(${submit_url} ${http_header} RETRY_COUNT 3 RETRY_DELAY 5 PARTS ${ARG_PARTS}) endfunction() #[============================================================================[ Actual script ]============================================================================] cmake_minimum_required(VERSION 3.15 FATAL_ERROR) find_program(CTEST_GIT_COMMAND git) if(NOT CTEST_GIT_COMMAND) message(FATAL_ERROR "Git not found") endif() if(NOT CTEST_CMAKE_GENERATOR MATCHES "^Visual Studio") unset(CTEST_CMAKE_GENERATOR_PLATFORM) endif() if(CTEST_CMAKE_GENERATOR MATCHES "^(Unix Makefiles|Ninja)$") set(CTEST_USE_LAUNCHERS ON) endif() if(NOT CTEST_SITE) unset(CTEST_SITE) site_name(CTEST_SITE) endif() if(NOT CTEST_BUILD_NAME) get_default_build_name(CTEST_BUILD_NAME) endif() set(CTEST_SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/MITK") set(CTEST_BINARY_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build") set(indent " ") message("CTest settings") if(MITK_SUBMIT_URL) message("${indent}Submit URL: ${MITK_SUBMIT_URL}") endif() message("\ ${indent}Dashboard model: ${CTEST_DASHBOARD_MODEL} ${indent}Build name: ${CTEST_BUILD_NAME} ${indent}Site: ${CTEST_SITE}") +if(MITK_CLEAN_SOURCE_DIR OR MITK_CLEAN_BINARY_DIR) + message("Clean") + + if(MITK_CLEAN_SOURCE_DIR) + set(clean_dir "${CMAKE_CURRENT_SOURCE_DIR}/src") + message("${indent}Source directory: ${clean_dir}") + rm("${clean_dir}") + endif() + + if(MITK_CLEAN_BINARY_DIR) + set(clean_dir "${CMAKE_CURRENT_SOURCE_DIR}/build") + message("${indent}Binary directory: ${clean_dir}") + rm("${clean_dir}") + endif() +endif() + message("MITK repository") if(NOT EXISTS "${CTEST_SOURCE_DIRECTORY}") message("\ ${indent}Clone repository: ${MITK_REPOSITORY} ${indent}Branch: ${MITK_BRANCH}") set(CTEST_CHECKOUT_COMMAND "\"${CTEST_GIT_COMMAND}\" clone --branch=${MITK_BRANCH} -- ${MITK_REPOSITORY} MITK") ctest_start(${CTEST_DASHBOARD_MODEL}) else() git_log(COMMIT_HASH old_revision COMMITTER committer SUBJECT subject) message("\ ${indent}Update repository: ${CTEST_SOURCE_DIRECTORY} ${indent}Branch: ${MITK_BRANCH} ${indent}Old revision: ${old_revision} (committed by ${committer}) ${indent}${indent}${subject}") ctest_start(${CTEST_DASHBOARD_MODEL}) set(CTEST_UPDATE_OPTIONS "origin ${MITK_BRANCH}") ctest_update(RETURN_VALUE return_value) if(-1 EQUAL return_value) return() endif() git_log(COMMIT_HASH new_revision) if(new_revision STREQUAL old_revision) message("${indent}New revision: up-to-date") else() git_log(COMMITTER committer SUBJECT subject) message("\ ${indent}New revision: ${new_revision} (committed by ${committer}) ${indent}${indent}${subject}") endif() endif() unset(extension_dirs) if(MITK_EXTENSIONS) list(LENGTH MITK_EXTENSIONS mitk_extensions_size) math(EXPR max_index "${mitk_extensions_size} - 1") foreach(name_index RANGE 0 ${max_index} 3) list(GET MITK_EXTENSIONS ${name_index} name) math(EXPR url_index "${name_index} + 1") list(GET MITK_EXTENSIONS ${url_index} url) math(EXPR branch_index "${name_index} + 2") list(GET MITK_EXTENSIONS ${branch_index} branch) message("${name} repository") set(extension_dir "${CMAKE_CURRENT_SOURCE_DIR}/src/${name}") list(APPEND extension_dirs "${extension_dir}") if(NOT EXISTS "${extension_dir}") message("\ ${indent}Clone repository: ${url} ${indent}Branch: ${branch}") git("clone" "--quiet" "--branch=${branch}" "${url}" "${name}" WORKING_DIRECTORY "${extension_dir}/..") else() git_log(COMMIT_HASH old_revision COMMITTER committer SUBJECT subject WORKING_DIRECTORY "${extension_dir}") message("\ ${indent}Update repository: ${extension_dir} ${indent}Branch: ${branch} ${indent}Old revision: ${old_revision} (committed by ${committer}) ${indent}${indent}${subject}") git("fetch" "--quiet" "origin" "${branch}" WORKING_DIRECTORY "${extension_dir}") git("diff" "--quiet" "HEAD" "FETCH_HEAD" WORKING_DIRECTORY "${extension_dir}" RESULT_VARIABLE error_code) if(NOT error_code EQUAL 0) git("reset" "--quiet" "--hard" "FETCH_HEAD" WORKING_DIRECTORY "${extension_dir}") endif() git_log(COMMIT_HASH new_revision WORKING_DIRECTORY "${extension_dir}") if(new_revision STREQUAL old_revision) message("${indent}New revision: up-to-date") else() git_log(COMMITTER committer SUBJECT subject WORKING_DIRECTORY "${extension_dir}") message("\ ${indent}New revision: ${new_revision} (committed by ${committer}) ${indent}${indent}${subject}") endif() endif() endforeach() endif() submit(PARTS Update) set(options "-D" "MITK_CTEST_SCRIPT_MODE:STRING=${CTEST_DASHBOARD_MODEL}" # "-D" "SUPERBUILD_EXCLUDE_MITKBUILD_TARGET:BOOL=TRUE" ) if(extension_dirs) string (REPLACE ";" "\\\;" extension_dirs "${extension_dirs}") list(APPEND options "-D" "MITK_EXTENSION_DIRS:STRING=${extension_dirs}") endif() if(MITK_BUILD_CONFIGURATION) list(APPEND options "-D" "MITK_BUILD_CONFIGURATION:STRING=${MITK_BUILD_CONFIGURATION}") endif() +if(MITK_BUILD_OPTIONS) + get_temp_directory(temp_dir) + set(mitk_initial_cache_file "${temp_dir}/MITKInitialCache.cmake") + file(WRITE "${mitk_initial_cache_file}" "") + foreach(mitk_option ${MITK_BUILD_OPTIONS}) + if(mitk_option MATCHES "^([^:]+):([^=]+)=(.*)") + set(var "${CMAKE_MATCH_1}") + set(type "${CMAKE_MATCH_2}") + set(value "${CMAKE_MATCH_3}") + file(APPEND "${mitk_initial_cache_file}" "set(${var} \"${value}\" CACHE ${type} \"\" FORCE)\n") + endif() + endforeach() + list(APPEND options "-D" "MITK_INITIAL_CACHE_FILE:FILEPATH=${mitk_initial_cache_file}") +endif() + foreach(option ${MITK_SUPERBUILD_OPTIONS}) list(APPEND options "-D" "${option}") endforeach() ctest_configure(OPTIONS "${options}" RETURN_VALUE return_value) submit(PARTS Configure) if(NOT 0 EQUAL return_value) submit(PARTS Done) return() endif() include("${CTEST_BINARY_DIRECTORY}/SuperBuildTargets.cmake") if(NOT SUPERBUILD_TARGETS) submit(PARTS Done) message(FATAL_ERROR "SUPERBUILD_TARGETS variable not set in SuperBuildTargets.cmake") else() set(mitk_targets MITK-Data MITK-Utilities MITK-Configure MITK-build ) list(LENGTH SUPERBUILD_TARGETS n) list(LENGTH mitk_targets num_mitk_targets) math(EXPR n "${n} + ${num_mitk_targets}") set(i 1) unset(append) foreach(target ${SUPERBUILD_TARGETS} ${mitk_targets}) message("MITK superbuild - [${i}/${n}] Build ${target}") if(NOT target IN_LIST mitk_targets) list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION ".*") set(pop_warning_exception TRUE) else() set(pop_warning_exception FALSE) endif() ctest_build(TARGET ${target} NUMBER_ERRORS num_build_errors NUMBER_WARNINGS num_build_warnings RETURN_VALUE return_value ${append}) if(pop_warning_exception) list(POP_BACK CTEST_CUSTOM_WARNING_EXCEPTION) endif() submit(PARTS Build) if(0 LESS num_build_warnings) message("${indent}${num_build_warnings} warning(s)") endif() if(NOT (0 EQUAL return_value AND 0 EQUAL num_build_errors)) submit(PARTS Done) message("${indent}${num_build_errors} error(s)") return() else() message("${indent}${target} was built successfully") endif() if(NOT append) set(append APPEND) endif() math(EXPR i "${i} + 1") endforeach() endif() if(MITK_BUILD_DOCUMENTATION) message("MITK build - Build documentation") list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION ".*") ctest_build(TARGET doc BUILD "${CTEST_BINARY_DIRECTORY}/MITK-build" NUMBER_ERRORS num_doc_errors NUMBER_WARNINGS num_doc_warnings RETURN_VALUE return_value APPEND ) list(POP_BACK CTEST_CUSTOM_WARNING_EXCEPTION) submit(PARTS Build) if(0 LESS num_doc_warnings) message("${indent}${num_doc_warnings} warning(s)") endif() if(NOT (0 EQUAL return_value AND 0 EQUAL num_doc_errors)) submit(PARTS Done) message("${indent}${num_doc_errors} error(s)") return() else() message("${indent}Documentation was built successfully") endif() endif() message("Run unit tests...") set(CTEST_CONFIGURATION_TYPE "${CTEST_BUILD_CONFIGURATION}") ctest_test( BUILD "${CTEST_BINARY_DIRECTORY}/MITK-build" PARALLEL_LEVEL 4) submit(PARTS Test Done) message("Done") diff --git a/CMake/mitkFunctionGetVersion.cmake b/CMake/mitkFunctionGetVersion.cmake index 72e181e002..dcefea4623 100644 --- a/CMake/mitkFunctionGetVersion.cmake +++ b/CMake/mitkFunctionGetVersion.cmake @@ -1,83 +1,57 @@ #! \brief Extract version information from a local working copy #! #! If the source_dir variable points to a git repository, this function #! extracts the current revision hash and branch/tag name. -#! -#! If the source_dir variable points to a subversion repository, this -#! function extracts the current svn revision. # #! The information is provided in #!
    -#!
  • ${prefix}_REVISION_ID The git hash or svn revision value +#!
  • ${prefix}_REVISION_ID The git hash +#!
  • ${prefix}_REVISION_SHORTID The short git hash (8 digits) #!
  • ${prefix}_REVISION_NAME The git branch/tag name or empty -#!
  • ${prefix}_WC_TYPE The working copy type, one of "local", "git", or "svn" +#!
  • ${prefix}_WC_TYPE The working copy type, one of "local", or "git" #!
#! #! \param source_dir The directory containing a working copy #! \param prefix A prefix to prepend to the variables containing #! the extracted information. #! function(mitkFunctionGetVersion source_dir prefix) if(NOT prefix) message(FATAL_ERROR "prefix argument not specified") endif() # initialize variables set(_wc_type "local") set(_wc_id "") set(_wc_name "") + execute_process(COMMAND ${GIT_EXECUTABLE} rev-list -1 HEAD + WORKING_DIRECTORY "${source_dir}" + RESULT_VARIABLE _result_var + OUTPUT_VARIABLE ${prefix}_WC_REVISION_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET) - find_package(Git) - if(GIT_FOUND) - - GIT_IS_REPO(${source_dir} _is_git_repo) - if(_is_git_repo) - set(_wc_type "git") - GIT_WC_INFO(${source_dir} ${prefix}) - - set(_wc_id ${${prefix}_WC_REVISION_HASH}) - - string(REPLACE " " ";" hash_name ${${prefix}_WC_REVISION_NAME}) - list(GET hash_name 1 name) - if(name) - set(_wc_name ${name}) - endif() - endif() - endif() - - # test for svn working copy - if(_wc_type STREQUAL "local") + if(NOT _result_var) + set(_wc_type "git") - find_package(Subversion) - if(Subversion_FOUND) - execute_process(COMMAND ${Subversion_SVN_EXECUTABLE} info - WORKING_DIRECTORY ${source_dir} - RESULT_VARIABLE _subversion_result - OUTPUT_QUIET - ERROR_QUIET) - - if(NOT _subversion_result) - set(_wc_type svn) - Subversion_WC_INFO(${source_dir} ${prefix}) - set(_wc_id ${${prefix}_WC_REVISION}) - endif() - endif() + execute_process(COMMAND ${GIT_EXECUTABLE} name-rev --name-only ${${prefix}_WC_REVISION_HASH} + WORKING_DIRECTORY "${source_dir}" + OUTPUT_VARIABLE ${prefix}_WC_REVISION_NAME + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(_wc_id ${${prefix}_WC_REVISION_HASH}) + set(_wc_name ${${prefix}_WC_REVISION_NAME}) endif() set(${prefix}_WC_TYPE ${_wc_type} PARENT_SCOPE) set(${prefix}_REVISION_ID ${_wc_id} PARENT_SCOPE) set(_shortid ${_wc_id}) if(_wc_type STREQUAL "git") string(SUBSTRING ${_shortid} 0 8 _shortid) endif() set(${prefix}_REVISION_SHORTID ${_shortid} PARENT_SCOPE) set(${prefix}_REVISION_NAME ${_wc_name} PARENT_SCOPE) - # For backwards compatibility - set(${prefix}_WC_REVISION_HASH ${_wc_id} PARENT_SCOPE) - set(${prefix}_WC_REVISION_NAME ${_wc_name} PARENT_SCOPE) - endfunction() diff --git a/CMake/mitkFunctionGetVersionDescription.cmake b/CMake/mitkFunctionGetVersionDescription.cmake index 80c55c39c7..ade15db69f 100644 --- a/CMake/mitkFunctionGetVersionDescription.cmake +++ b/CMake/mitkFunctionGetVersionDescription.cmake @@ -1,71 +1,71 @@ #! \brief Extract the version description from a local working copy #! #! If the given repository is a git repository, the functions runs the #! git rev-parse --exact-match HEAD command #! #! Information provided is stored in ${prefix}_REVISION_DESC an is #! \ul #! \li The exact tag if the HEAD of the source-tree has a tag #! \li the 'git describe' output, which is -<#Commits>-g #! \lu #! In case the working copy contains local changes, the ${prefix}_REVISION_DESC strings will contain #! a suffix [local changes]. #! #! The revision description can be overridden by a ${prefix}_CUSTOM_REVISION_DESC variable. #! #! \param source_dir The directory containing a working copy #! \param prefix A prefix to prepent to the variables containing #! the extracted information #! function(mitkFunctionGetVersionDescription source_dir prefix) if(NOT prefix) message(FATAL_ERROR "prefix argument not specified") endif() if(${prefix}_CUSTOM_REVISION_DESC) set(_wc_description ${${prefix}_CUSTOM_REVISION_DESC}) else() # initialize variable set(_wc_description "unknown_version") set(_dirty_repo_str "-local_changes") - find_package(Git) + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --is-inside-work-tree + WORKING_DIRECTORY "${source_dir}" + RESULT_VARIABLE _result_var + OUTPUT_QUIET + ERROR_QUIET) + if(NOT _result_var) + execute_process(COMMAND ${GIT_EXECUTABLE} describe --exact-match --dirty=${_dirty_repo_str} + WORKING_DIRECTORY ${source_dir} + OUTPUT_VARIABLE _project_git_tagname + RESULT_VARIABLE _proper_version + ERROR_VARIABLE _error_msg) - if(GIT_FOUND) - GIT_IS_REPO(${source_dir} _is_git_repo) - if(_is_git_repo) - execute_process(COMMAND ${GIT_EXECUTABLE} describe --exact-match --dirty=${_dirty_repo_str} + if(_proper_version EQUAL 0) + set(_wc_description ${_project_git_tagname}) + else() + # the execution failed, i.e. the HEAD has no tag, + # for fallback string: execute again but without the --exact-match + execute_process(COMMAND ${GIT_EXECUTABLE} describe --dirty=${_dirty_repo_str} WORKING_DIRECTORY ${source_dir} - OUTPUT_VARIABLE _project_git_tagname + OUTPUT_VARIABLE _wc_description RESULT_VARIABLE _proper_version ERROR_VARIABLE _error_msg) - if(_proper_version EQUAL 0) - set(_wc_description ${_project_git_tagname}) - else() - # the execution failed, i.e. the HEAD has no tag, - # for fallback string: execute again but without the --exact-match - execute_process(COMMAND ${GIT_EXECUTABLE} describe --dirty=${_dirty_repo_str} + if(NOT _proper_version EQUAL 0) + # last fallback, i.e. working copy is a shallow clone, at least use + # commit hash + execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --dirty=${_dirty_repo_str} WORKING_DIRECTORY ${source_dir} - OUTPUT_VARIABLE _wc_description - RESULT_VARIABLE _proper_version - ERROR_VARIABLE _error_msg) - - if(NOT _proper_version EQUAL 0) - # last fallback, i.e. working copy is a shallow clone, at least use - # commit hash - execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --dirty=${_dirty_repo_str} - WORKING_DIRECTORY ${source_dir} - OUTPUT_VARIABLE _wc_description) - endif() + OUTPUT_VARIABLE _wc_description) endif() - # remove newline at and of the string - string(STRIP "${_wc_description}" _wc_description) endif() + # remove newline at and of the string + string(STRIP "${_wc_description}" _wc_description) endif() endif() set(${prefix}_REVISION_DESC ${_wc_description} PARENT_SCOPE) endfunction() diff --git a/CMake/mitkSetupCPack.cmake b/CMake/mitkSetupCPack.cmake index 77fb3ae8d1..1663a79fb3 100644 --- a/CMake/mitkSetupCPack.cmake +++ b/CMake/mitkSetupCPack.cmake @@ -1,142 +1,142 @@ # # First, set the generator variable # if(NOT CPACK_GENERATOR) if(WIN32) find_program(NSIS_MAKENSIS NAMES makensis PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS] DOC "Where is makensis.exe located" ) if(NOT NSIS_MAKENSIS) set(CPACK_GENERATOR ZIP) else() set(CPACK_GENERATOR "NSIS;ZIP") endif(NOT NSIS_MAKENSIS) else() if(APPLE) set(CPACK_GENERATOR DragNDrop) else() set(CPACK_GENERATOR TGZ) endif() endif() endif(NOT CPACK_GENERATOR) # Set Redistributable information for windows if(${CMAKE_SYSTEM_NAME} MATCHES Windows) include(mitkFunctionGetMSVCVersion) mitkFunctionGetMSVCVersion() set(CPACK_VISUAL_STUDIO_VERSION_MAJOR "${VISUAL_STUDIO_VERSION_MAJOR}") set(CPACK_VISUAL_STUDIO_PRODUCT_NAME "${VISUAL_STUDIO_PRODUCT_NAME}") set(CPACK_LIBRARY_ARCHITECTURE "${CMAKE_LIBRARY_ARCHITECTURE}") # Visual Studio 2017 already comes with redistributable installers. # Try to find the right one. set(vswhere "$ENV{PROGRAMFILES\(X86\)}\\Microsoft Visual Studio\\Installer\\vswhere.exe") if(EXISTS ${vswhere}) execute_process(COMMAND ${vswhere} -latest -property installationPath OUTPUT_VARIABLE installationPath OUTPUT_STRIP_TRAILING_WHITESPACE) file(TO_CMAKE_PATH "${installationPath}" installationPath) set(redistPath "${installationPath}/VC/Redist/MSVC") file(GLOB redistPath "${installationPath}/VC/Redist/MSVC/*") list(LENGTH redistPath length) if(length EQUAL 1) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(redistPath "${redistPath}/vc_redist.x64.exe") else() set(redistPath "${redistPath}/vc_redist.x86.exe") endif() if(EXISTS ${redistPath}) set(CMAKE_${CPACK_VISUAL_STUDIO_PRODUCT_NAME}_REDISTRIBUTABLE ${redistPath} CACHE FILEPATH "Path to the appropriate Microsoft Visual Studio Redistributable") endif() endif() endif() if(NOT DEFINED CMAKE_${CPACK_VISUAL_STUDIO_PRODUCT_NAME}_REDISTRIBUTABLE) set(CMAKE_${CPACK_VISUAL_STUDIO_PRODUCT_NAME}_REDISTRIBUTABLE "" CACHE FILEPATH "Path to the appropriate Microsoft Visual Studio Redistributable") endif() endif() if(EXISTS ${CMAKE_${CPACK_VISUAL_STUDIO_PRODUCT_NAME}_REDISTRIBUTABLE} ) install(PROGRAMS ${CMAKE_${CPACK_VISUAL_STUDIO_PRODUCT_NAME}_REDISTRIBUTABLE} DESTINATION thirdpartyinstallers) get_filename_component(CPACK_REDISTRIBUTABLE_FILE_NAME ${CMAKE_${CPACK_VISUAL_STUDIO_PRODUCT_NAME}_REDISTRIBUTABLE} NAME ) endif() # On windows set default install directory appropriately for 32 and 64 bit # installers if not already set if(WIN32 AND NOT CPACK_NSIS_INSTALL_ROOT) if(CMAKE_CL_64) set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") else() set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES") endif() endif() # By default, do not warn when built on machines using only VS Express if(NOT DEFINED CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS) set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS ON) endif() # include required mfc libraries include(InstallRequiredSystemLibraries) set(CPACK_PACKAGE_NAME "MITK") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The Medical Imaging Interaction Toolkit") set(CPACK_PACKAGE_VENDOR "German Cancer Research Center (DKFZ)") set(CPACK_PACKAGE_DESCRIPTION_FILE "${MITK_SOURCE_DIR}/LICENSE") set(CPACK_RESOURCE_FILE_LICENSE "${MITK_SOURCE_DIR}/LICENSE") -set(CPACK_PACKAGE_VERSION_MAJOR ${MITK_REVISION_DESC}) +string(REPLACE "/" "_" CPACK_PACKAGE_VERSION_MAJOR "${MITK_REVISION_DESC}") # tell cpack to strip all debug symbols from all files set(CPACK_STRIP_FILES ON) # set version if(NOT CPACK_PACKAGE_VERSION_MAJOR) set(CPACK_PACKAGE_VERSION_MAJOR ${MITK_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${MITK_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${MITK_VERSION_PATCH}) set(CPACK_PACKAGE_VERSION "${MITK_VERSION_MAJOR}.${MITK_VERSION_MINOR}.${MITK_VERSION_PATCH}") else() set(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}) endif() # determine possible system specific extension set(CPACK_PACKAGE_ARCH "unkown-architecture") if(${CMAKE_SYSTEM_NAME} MATCHES Windows) if(CMAKE_CL_64) set(CPACK_PACKAGE_ARCH "windows-x86_64") elseif(WIN32) set(CPACK_PACKAGE_ARCH "windows-x86") endif() endif() if(${CMAKE_SYSTEM_NAME} MATCHES Linux) if(${CMAKE_SYSTEM_PROCESSOR} MATCHES i686) set(CPACK_PACKAGE_ARCH "linux-x86") elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES x86_64) if(${CMAKE_CXX_FLAGS} MATCHES " -m32 ") set(CPACK_PACKAGE_ARCH "linux-x86") else() set(CPACK_PACKAGE_ARCH "linux-x86_64") endif() else() set(CPACK_PACKAGE_ARCH "linux") endif() endif() if(${CMAKE_SYSTEM_NAME} MATCHES Darwin) set(CPACK_PACKAGE_ARCH "macos-x86_64") endif() set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_ARCH}") diff --git a/CMakeExternals/CTK.cmake b/CMakeExternals/CTK.cmake index 9324832e57..095ab60876 100644 --- a/CMakeExternals/CTK.cmake +++ b/CMakeExternals/CTK.cmake @@ -1,101 +1,100 @@ #----------------------------------------------------------------------------- # CTK #----------------------------------------------------------------------------- if(MITK_USE_CTK) # Sanity checks if(DEFINED CTK_DIR AND NOT EXISTS ${CTK_DIR}) message(FATAL_ERROR "CTK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj CTK) set(proj_DEPENDENCIES DCMTK) set(CTK_DEPENDS ${proj}) if(NOT DEFINED CTK_DIR) set(revision_tag "78341aba") set(ctk_optional_cache_args ) if(MITK_USE_Python3) list(APPEND ctk_optional_cache_args -DCTK_LIB_Scripting/Python/Widgets:BOOL=ON -DCTK_ENABLE_Python_Wrapping:BOOL=OFF -DCTK_APP_ctkSimplePythonShell:BOOL=OFF "-DPYTHON_INCLUDE_DIR:PATH=${Python3_INCLUDE_DIRS}" "-DPYTHON_LIBRARY:FILEPATH=${Python3_LIBRARY_RELEASE}" ) else() list(APPEND ctk_optional_cache_args -DCTK_LIB_Scripting/Python/Widgets:BOOL=OFF -DCTK_ENABLE_Python_Wrapping:BOOL=OFF -DCTK_APP_ctkSimplePythonShell:BOOL=OFF -DDCMTK_CMAKE_DEBUG_POSTFIX:STRING=d ) endif() if(CTEST_USE_LAUNCHERS) list(APPEND ctk_optional_cache_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() 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() mitk_query_custom_ep_vars() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} GIT_REPOSITORY https://github.com/commontk/CTK GIT_TAG ${revision_tag} UPDATE_COMMAND "" INSTALL_COMMAND "" CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${ctk_optional_cache_args} # The CTK PluginFramework cannot cope with # a non-empty CMAKE_DEBUG_POSTFIX for the plugin # libraries yet. -DCMAKE_DEBUG_POSTFIX:STRING= -DCTK_QT_VERSION:STRING=5 -DQt5_DIR=${Qt5_DIR} - -DGit_EXECUTABLE:FILEPATH=${GIT_EXECUTABLE} -DGIT_EXECUTABLE:FILEPATH=${GIT_EXECUTABLE} -DCTK_BUILD_QTDESIGNER_PLUGINS:BOOL=ON -DCTK_LIB_CommandLineModules/Backend/LocalProcess:BOOL=ON -DCTK_LIB_CommandLineModules/Frontend/QtGui:BOOL=ON -DCTK_LIB_PluginFramework:BOOL=ON -DCTK_LIB_DICOM/Widgets:BOOL=ON -DCTK_LIB_XNAT/Core:BOOL=ON -DCTK_PLUGIN_org.commontk.eventadmin:BOOL=ON -DCTK_PLUGIN_org.commontk.configadmin:BOOL=ON -DCTK_USE_GIT_PROTOCOL:BOOL=OFF -DDCMTK_DIR:PATH=${DCMTK_DIR} -DPythonQt_URL:STRING=${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/PythonQt_fae23012.tar.gz ${${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(CTK_DIR ${binary_dir}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 504c27f89e..7634f1a83e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1413 +1,1415 @@ set(MITK_CMAKE_MINIMUM_REQUIRED_VERSION 3.14.5) cmake_minimum_required(VERSION ${MITK_CMAKE_MINIMUM_REQUIRED_VERSION}) #----------------------------------------------------------------------------- # See https://cmake.org/cmake/help/v3.14/manual/cmake-policies.7.html for details #----------------------------------------------------------------------------- set(project_policies ) foreach(policy ${project_policies}) if(POLICY ${policy}) cmake_policy(SET ${policy} NEW) endif() endforeach() #----------------------------------------------------------------------------- # 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 2018.04.99) include_directories(SYSTEM ${MITK_SUPERBUILD_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # MITK Extension Feature #----------------------------------------------------------------------------- set(MITK_EXTENSION_DIRS "" CACHE STRING "") set(MITK_DIR_PLUS_EXTENSION_DIRS ${MITK_SOURCE_DIR} ${MITK_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_EXTENSION_DIRS}) set(MITK_CMAKE_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMake) get_filename_component(MITK_CMAKE_EXTENSION_DIR ${MITK_CMAKE_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_CMAKE_EXTENSION_DIR}) list(APPEND CMAKE_MODULE_PATH ${MITK_CMAKE_EXTENSION_DIR}) endif() endforeach() #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- # Standard CMake macros include(FeatureSummary) include(CTest) include(CMakeParseArguments) include(FindPackageHandleStandardArgs) # MITK macros include(mitkFunctionGetGccVersion) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkMacroEmptyExternalProject) include(mitkFunctionEnableBuildConfiguration) include(mitkFunctionWhitelists) include(mitkFunctionAddExternalProject) include(mitkFunctionAddLibrarySearchPaths) SUPPRESS_VC_DEPRECATED_WARNINGS() #----------------------------------------------------------------------------- # Set a default build type if none was specified #----------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() #----------------------------------------------------------------------------- # Check miminum macOS version #----------------------------------------------------------------------------- # The minimum supported macOS version is 10.13. If you use a version less than 10.13, there is no guarantee that the build still works. if(APPLE) exec_program(sw_vers ARGS -productVersion OUTPUT_VARIABLE macos_version) if (macos_version VERSION_LESS "10.13") message(WARNING "Detected macOS version \"${macos_version}\" is not supported anymore. Minimum required macOS version is at least 10.13.") endif() if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.13) message(WARNING "Detected macOS deployment target \"${CMAKE_OSX_DEPLOYMENT_TARGET}\" is not supported anymore. Minimum required macOS version is at least 10.13.") endif() endif() #----------------------------------------------------------------------------- # Check miminum compiler versions #----------------------------------------------------------------------------- if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # require at least gcc 4.9 as provided by ppa:ubuntu-toolchain-r/test for Ubuntu 14.04 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) message(FATAL_ERROR "GCC version must be at least 4.9 If you are using Ubuntu 14.04, you can easily install gcc and g++ 4.9 (or any later version available) in addition to your version ${CMAKE_CXX_COMPILER_VERSION}: sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install gcc-4.9 g++-4.9 Make sure to explicitly specify these compilers when configuring MITK: CMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc-4.9 CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++-4.9 For more information on the proposed PPA see the Toolchain Updates section of https://wiki.ubuntu.com/ToolChain.") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # require at least clang 3.4 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4) message(FATAL_ERROR "Clang version must be at least 3.4") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") # require at least clang 5.0 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) message(FATAL_ERROR "Apple Clang version must be at least 5.0") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # require at least Visual Studio 2017 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10) message(FATAL_ERROR "Microsoft Visual Studio 2017 or newer required") endif() else() message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang (Linux or Apple), GCC and MSVC.") endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) else() set(GCC_VERSION 0) endif() set(MITK_CXX_STANDARD 14) set(CMAKE_CXX_EXTENSIONS 0) set(CMAKE_CXX_STANDARD ${MITK_CXX_STANDARD}) set(CMAKE_CXX_STANDARD_REQUIRED 1) # This is necessary to avoid problems with compile feature checks. # CMAKE_CXX_STANDARD seems to only set the -std=c++14 flag for targets. # However, compile flag checks also need to be done with -std=c++14. # The MITK_CXX14_FLAG variable is also used for external projects # build during the MITK super-build. mitkFunctionCheckCompilerFlags("-std=c++14" MITK_CXX14_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) option(MITK_ENABLE_PIC_READER "Enable support for reading the DKFZ pic file format." ON) mark_as_advanced( MITK_XVFB_TESTING MITK_FAST_TESTING MITK_BUILD_ALL_APPS MITK_ENABLE_PIC_READER ) #----------------------------------------------------------------------------- # 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_EXTENSION_DIRS}) set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMakeExternals) get_filename_component(MITK_CMAKE_EXTERNALS_EXTENSION_DIR ${MITK_CMAKE_EXTERNALS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake) include(${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake) endif() endforeach() # ----------------------------------------- # Other MITK_USE_* options not related to # external projects build via the # MITK superbuild option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) option(MITK_USE_OpenCL "Use OpenCL GPU-Computing library" OFF) option(MITK_USE_OpenMP "Use OpenMP" OFF) option(MITK_USE_Python3 "Use Python 3" OFF) #----------------------------------------------------------------------------- # Build configurations #----------------------------------------------------------------------------- set(_buildConfigs "Custom") file(GLOB _buildConfigFiles CMake/BuildConfigurations/*.cmake) foreach(_buildConfigFile ${_buildConfigFiles}) get_filename_component(_buildConfigFile ${_buildConfigFile} NAME_WE) list(APPEND _buildConfigs ${_buildConfigFile}) endforeach() foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) file(GLOB _extBuildConfigFiles ${MITK_EXTENSION_DIR}/CMake/BuildConfigurations/*.cmake) foreach(_extBuildConfigFile ${_extBuildConfigFiles}) get_filename_component(_extBuildConfigFile ${_extBuildConfigFile} NAME_WE) list(APPEND _buildConfigs ${_extBuildConfigFile}) endforeach() list(REMOVE_DUPLICATES _buildConfigs) endforeach() set(MITK_BUILD_CONFIGURATION "Custom" CACHE STRING "Use pre-defined MITK configurations") set_property(CACHE MITK_BUILD_CONFIGURATION PROPERTY STRINGS ${_buildConfigs}) mitkFunctionEnableBuildConfiguration() mitkFunctionCreateWhitelistPaths(MITK) mitkFunctionFindWhitelists(MITK) # ----------------------------------------- # Qt version related variables option(MITK_USE_Qt5 "Use Qt 5 library" ON) if(MITK_USE_Qt5) set(MITK_QT5_MINIMUM_VERSION 5.12) set(MITK_QT5_COMPONENTS Concurrent OpenGL PrintSupport Script Sql Svg Widgets Xml XmlPatterns WebEngineWidgets UiTools Help LinguistTools) if(APPLE) list(APPEND MITK_QT5_COMPONENTS DBus) elseif(UNIX) list(APPEND MITK_QT5_COMPONENTS X11Extras) endif() # Hint at default install locations of Qt if(NOT Qt5_DIR) if(MSVC) set(_dir_candidates "C:/Qt") if(CMAKE_GENERATOR MATCHES "^Visual Studio [0-9]+ ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") elseif(CMAKE_GENERATOR MATCHES "Ninja") include(mitkFunctionGetMSVCVersion) mitkFunctionGetMSVCVersion() if(VISUAL_STUDIO_PRODUCT_NAME MATCHES "^Visual Studio ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") endif() endif() if(_compilers MATCHES "[0-9]+") if (CMAKE_MATCH_0 EQUAL 2019) list(APPEND _compilers "msvc2017") # Binary compatible to 2019 endif() endif() else() set(_dir_candidates ~/Qt) if(APPLE) set(_compilers clang) else() list(APPEND _dir_candidates /opt/Qt) set(_compilers gcc) endif() endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) foreach(_compiler ${_compilers}) list(APPEND _compilers64 "${_compiler}_64") endforeach() set(_compilers ${_compilers64}) endif() foreach(_dir_candidate ${_dir_candidates}) get_filename_component(_dir_candidate ${_dir_candidate} REALPATH) foreach(_compiler ${_compilers}) set(_glob_expression "${_dir_candidate}/5.*/${_compiler}") file(GLOB _hints ${_glob_expression}) list(SORT _hints) list(APPEND MITK_QT5_HINTS ${_hints}) endforeach() endforeach() endif() find_package(Qt5 ${MITK_QT5_MINIMUM_VERSION} COMPONENTS ${MITK_QT5_COMPONENTS} REQUIRED HINTS ${MITK_QT5_HINTS}) if(${Qt5_VERSION} VERSION_GREATER_EQUAL 5.13) message(WARNING "Qt version ${Qt5_VERSION_MAJOR}.${Qt5_VERSION_MINOR} is not yet supported. We recommend using version 5.12.x.") endif() endif() # ----------------------------------------- # Custom dependency logic 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) find_package(OpenSSL QUIET) if(NOT OpenSSL_FOUND) set(openssl_message "Could not find OpenSSL (dependency of C++ REST SDK).\n") if(UNIX) if(APPLE) set(openssl_message "${openssl_message}Please install it using your favorite package management " "system (i.e. Homebrew or MacPorts).\n") else() set(openssl_message "${openssl_message}Please install the dev package of OpenSSL (i.e. libssl-dev).\n") endif() else() set(openssl_message "${openssl_message}Please install Win32 OpenSSL:\n" " https://slproweb.com/products/Win32OpenSSL.html\n") endif() set(openssl_message "${openssl_message}If it still cannot be found, you can hint CMake to find OpenSSL by " "adding/setting the OPENSSL_ROOT_DIR variable to the root directory of an " "OpenSSL installation. Make sure to clear variables of partly found " "versions of OpenSSL before, or they will be mixed up.") message(FATAL_ERROR ${openssl_message}) endif() list(APPEND MITK_USE_Boost_LIBRARIES date_time regex system) if(UNIX) list(APPEND MITK_USE_Boost_LIBRARIES atomic chrono filesystem random thread) endif() list(REMOVE_DUPLICATES MITK_USE_Boost_LIBRARIES) set(MITK_USE_Boost_LIBRARIES ${MITK_USE_Boost_LIBRARIES} CACHE STRING "A semi-colon separated list of required Boost libraries" FORCE) endif() if(MITK_USE_Python3) set(MITK_USE_ZLIB ON CACHE BOOL "" FORCE) if(APPLE AND CMAKE_FRAMEWORK_PATH AND CMAKE_FRAMEWORK_PATH MATCHES "python3\\.?([0-9]+)") find_package(Python3 3.${CMAKE_MATCH_1} EXACT REQUIRED COMPONENTS Interpreter Development NumPy) else() find_package(Python3 REQUIRED COMPONENTS Interpreter Development NumPy) endif() if(WIN32) string(REPLACE "\\" "/" Python3_STDARCH "${Python3_STDARCH}") string(REPLACE "\\" "/" Python3_STDLIB "${Python3_STDLIB}") string(REPLACE "\\" "/" Python3_SITELIB "${Python3_SITELIB}") endif() endif() if(BUILD_TESTING AND NOT MITK_USE_CppUnit) message("> Forcing MITK_USE_CppUnit to ON because BUILD_TESTING=ON") set(MITK_USE_CppUnit ON CACHE BOOL "Use CppUnit for unit tests" FORCE) endif() if(MITK_USE_BLUEBERRY) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) mark_as_advanced(MITK_BUILD_ALL_PLUGINS) if(NOT MITK_USE_CTK) message("> Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY") set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE) endif() endif() #----------------------------------------------------------------------------- # Pixel type multiplexing #----------------------------------------------------------------------------- # Customize the default pixel types for multiplex macros set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") mark_as_advanced(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES MITK_ACCESSBYITK_DIMENSIONS ) # consistency checks if(NOT MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES) set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES) set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES) set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) string(REPLACE "," ";" _integral_types ${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES}) string(REPLACE "," ";" _floating_types ${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES}) foreach(_scalar_type ${_integral_types} ${_floating_types}) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}itk::VariableLengthVector<${_scalar_type}>,") endforeach() string(LENGTH "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" _length) math(EXPR _length "${_length} - 1") string(SUBSTRING "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" 0 ${_length} MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES ${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES} CACHE STRING "List of vector pixel types used in AccessByItk and InstantiateAccessFunction macros for itk::VectorImage types" FORCE) endif() if(NOT MITK_ACCESSBYITK_DIMENSIONS) set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") endif() +find_package(Git REQUIRED) + #----------------------------------------------------------------------------- # Superbuild script #----------------------------------------------------------------------------- if(MITK_USE_SUPERBUILD) include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake") # Print configuration summary message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL) return() endif() #***************************************************************************** #**************************** END OF SUPERBUILD **************************** #***************************************************************************** #----------------------------------------------------------------------------- # Organize MITK targets in folders #----------------------------------------------------------------------------- set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(MITK_ROOT_FOLDER "MITK" CACHE STRING "") mark_as_advanced(MITK_ROOT_FOLDER) #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(WriteBasicConfigVersionFile) include(CheckCXXSourceCompiles) include(GenerateExportHeader) include(mitkFunctionAddCustomModuleTest) include(mitkFunctionCheckModuleDependencies) include(mitkFunctionCompileSnippets) include(mitkFunctionConfigureVisualStudioUserProjectFile) include(mitkFunctionConvertXPSchema) include(mitkFunctionCreateBlueBerryApplication) include(mitkFunctionCreateCommandLineApp) include(mitkFunctionCreateModule) include(mitkFunctionCreatePlugin) include(mitkFunctionCreateProvisioningFile) include(mitkFunctionGetLibrarySearchPaths) include(mitkFunctionGetVersion) include(mitkFunctionGetVersionDescription) include(mitkFunctionInstallAutoLoadModules) include(mitkFunctionInstallCTKPlugin) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionInstallThirdPartyCTKPlugins) include(mitkFunctionOrganizeSources) include(mitkFunctionUseModules) if( ${MITK_USE_MatchPoint} ) include(mitkFunctionCreateMatchPointDeployedAlgorithm) endif() include(mitkMacroConfigureItkPixelTypes) include(mitkMacroCreateExecutable) include(mitkMacroCreateModuleTests) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetLinuxDistribution) include(mitkMacroGetPMDPlatformString) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroMultiplexPicType) # Deprecated include(mitkMacroCreateCTKPlugin) #----------------------------------------------------------------------------- # Global CMake variables #----------------------------------------------------------------------------- # Required and enabled C++14 features for all MITK code. # These are added as PUBLIC compile features to all MITK modules. set(MITK_CXX_FEATURES cxx_auto_type cxx_decltype cxx_enum_forward_declarations cxx_extended_friend_declarations cxx_extern_templates cxx_final cxx_lambdas cxx_local_type_template_args cxx_long_long_type cxx_nullptr cxx_override cxx_range_for cxx_right_angle_brackets cxx_rvalue_references cxx_static_assert cxx_strong_enums cxx_template_template_parameters cxx_trailing_return_types cxx_variadic_macros ) if(NOT DEFINED CMAKE_DEBUG_POSTFIX) # We can't do this yet because the CTK Plugin Framework # cannot cope with a postfix yet. #set(CMAKE_DEBUG_POSTFIX d) endif() #----------------------------------------------------------------------------- # Output directories. #----------------------------------------------------------------------------- set(_default_LIBRARY_output_dir lib) set(_default_RUNTIME_output_dir bin) set(_default_ARCHIVE_output_dir lib) foreach(type LIBRARY RUNTIME ARCHIVE) # Make sure the directory exists if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY AND NOT EXISTS ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) message("Creating directory MITK_CMAKE_${type}_OUTPUT_DIRECTORY: ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") file(MAKE_DIRECTORY "${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") endif() if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) else() set(CMAKE_${type}_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${_default_${type}_output_dir}) set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY}) endif() set(CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY} CACHE INTERNAL "Output directory for ${type} files.") mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY) endforeach() #----------------------------------------------------------------------------- # Set MITK specific options and variables (NOT available during superbuild) #----------------------------------------------------------------------------- # 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) # TODO: check if necessary option(USE_ITKZLIB "Use the ITK zlib for pic compression." ON) mark_as_advanced(USE_ITKZLIB) if(NOT MITK_FAST_TESTING) if(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) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) set(MITK_APPS "") include(${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) list(GET target_info_list 0 app_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} Mitk${app_name}) endif() endforeach() endif() endforeach() endif() #----------------------------------------------------------------------------- # Set coverage Flags #----------------------------------------------------------------------------- if(WITH_COVERAGE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG") set(COVERAGE_CXX_FLAGS ${coverage_flags}) set(COVERAGE_C_FLAGS ${coverage_flags}) endif() endif() #----------------------------------------------------------------------------- # MITK C/CXX Flags #----------------------------------------------------------------------------- set(MITK_C_FLAGS "${COVERAGE_C_FLAGS}") set(MITK_C_FLAGS_DEBUG ) set(MITK_C_FLAGS_RELEASE ) set(MITK_CXX_FLAGS "${COVERAGE_CXX_FLAGS} ${MITK_CXX14_FLAG}") set(MITK_CXX_FLAGS_DEBUG ) set(MITK_CXX_FLAGS_RELEASE ) set(MITK_EXE_LINKER_FLAGS ) set(MITK_SHARED_LINKER_FLAGS ) if(WIN32) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DPOCO_NO_UNWINDOWS -DWIN32_LEAN_AND_MEAN -DNOMINMAX") mitkFunctionCheckCompilerFlags("/wd4005" MITK_CXX_FLAGS) # warning C4005: macro redefinition mitkFunctionCheckCompilerFlags("/wd4231" MITK_CXX_FLAGS) # warning C4231: nonstandard extension used : 'extern' before template explicit instantiation # the following line should be removed after fixing bug 17637 mitkFunctionCheckCompilerFlags("/wd4316" MITK_CXX_FLAGS) # warning C4316: object alignment on heap mitkFunctionCheckCompilerFlags("/wd4180" MITK_CXX_FLAGS) # warning C4180: qualifier applied to function type has no meaning mitkFunctionCheckCompilerFlags("/wd4251" MITK_CXX_FLAGS) # warning C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' endif() if(APPLE) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DGL_SILENCE_DEPRECATION") # Apple deprecated OpenGL in macOS 10.14 endif() if(NOT MSVC_VERSION) foreach(_flag -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align -Wwrite-strings -Wno-error=gnu -Wno-error=unknown-pragmas # The strict-overflow warning is generated by ITK template code -Wno-error=strict-overflow -Woverloaded-virtual -Wstrict-null-sentinel #-Wold-style-cast #-Wsign-promo -Wno-error=deprecated-copy -Wno-array-bounds -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_EXTENSION_DIRS}) set(MITK_PACKAGE_DEPENDS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMake/PackageDepends) get_filename_component(MITK_PACKAGE_DEPENDS_EXTENSION_DIR ${MITK_PACKAGE_DEPENDS_EXTENSION_DIR} ABSOLUTE) 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.68 1.68.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 # Config.cmake files pointed at by _DIR variables. # Otherwise, existing Find.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.cmake instead of config #files. set(_temp_EP_${_package}_dir ${${_package}_DIR}) endif(DEFINED ${_package}_DIR) find_package(${_package} QUIET CONFIG) string(TOUPPER "${_package}" _package_uc) if(NOT (${_package}_FOUND OR ${_package_uc}_FOUND)) if(DEFINED _temp_EP_${_package}_dir) set(${_package}_DIR ${_temp_EP_${_package}_dir} CACHE PATH "externaly set dir of the package ${_package}" FORCE) endif(DEFINED _temp_EP_${_package}_dir) find_package(${_package} REQUIRED) endif() endif() endif() endforeach() # Ensure that the MITK CMake module path comes first set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR} ${CMAKE_MODULE_PATH} ) if(MITK_USE_DCMTK) # 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. 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() if(MITK_USE_DCMQI) # Due to the preferred CONFIG mode in find_package calls above, # the DCMQIConfig.cmake file is read, which does not provide useful # package information. We explictly need MODULE mode to find DCMQI. # Help our FindDCMQI.cmake script find our super-build DCMQI set(DCMQI_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) find_package(DCMQI REQUIRED) endif() link_directories(${Boost_LIBRARY_DIRS}) if(MITK_USE_OpenIGTLink) link_directories(${OpenIGTLink_LIBRARY_DIRS}) endif() if(MITK_USE_OpenCL) find_package(OpenCL REQUIRED) endif() if(MITK_USE_OpenMP) find_package(OpenMP REQUIRED COMPONENTS CXX) else() find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) elseif(APPLE AND OpenMP_libomp_LIBRARY AND NOT OpenMP_CXX_LIB_NAMES) set(OpenMP_CXX_LIB_NAMES libomp CACHE STRING "" FORCE) get_filename_component(openmp_lib_dir "${OpenMP_libomp_LIBRARY}" DIRECTORY) set(openmp_include_dir "${openmp_lib_dir}/../include") if(EXISTS "${openmp_include_dir}") get_filename_component(openmp_include_dir "${openmp_include_dir}" REALPATH) set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${openmp_include_dir}" CACHE STRING "" FORCE) find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) endif() endif() endif() endif() # Qt support if(MITK_USE_Qt5) find_package(Qt5Core ${MITK_QT5_MINIMUM_VERSION} REQUIRED) # at least Core required get_target_property(_qmake_exec Qt5::qmake LOCATION) execute_process(COMMAND ${_qmake_exec} -query QT_INSTALL_BINS RESULT_VARIABLE _result OUTPUT_VARIABLE QT_BINARY_DIR ERROR_VARIABLE _error ) string(STRIP "${QT_BINARY_DIR}" QT_BINARY_DIR) if(_result OR NOT EXISTS "${QT_BINARY_DIR}") message(FATAL_ERROR "Could not determine Qt binary directory: ${_result} ${QT_BINARY_DIR} ${_error}") endif() find_program(QT_HELPGENERATOR_EXECUTABLE NAMES qhelpgenerator qhelpgenerator-qt5 qhelpgenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_COLLECTIONGENERATOR_EXECUTABLE NAMES qcollectiongenerator qcollectiongenerator-qt5 qcollectiongenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_ASSISTANT_EXECUTABLE NAMES assistant assistant-qt5 assistant5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_XMLPATTERNS_EXECUTABLE NAMES xmlpatterns PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) mark_as_advanced(QT_HELPGENERATOR_EXECUTABLE QT_COLLECTIONGENERATOR_EXECUTABLE QT_ASSISTANT_EXECUTABLE QT_XMLPATTERNS_EXECUTABLE ) if(MITK_USE_BLUEBERRY) option(BLUEBERRY_USE_QT_HELP "Enable support for integrating plugin documentation into Qt Help" ${DOXYGEN_FOUND}) mark_as_advanced(BLUEBERRY_USE_QT_HELP) # Sanity checks for in-application BlueBerry plug-in help generation if(BLUEBERRY_USE_QT_HELP) set(_force_blueberry_use_qt_help_to_off 0) if(NOT DOXYGEN_FOUND) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen was not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(DOXYGEN_FOUND AND DOXYGEN_VERSION VERSION_LESS 1.8.7) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_HELPGENERATOR_EXECUTABLE) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because QT_HELPGENERATOR_EXECUTABLE is empty.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT MITK_USE_Qt5) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because MITK_USE_Qt5 is OFF.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_XMLPATTERNS_EXECUTABLE) message("You have enabled Qt Help support, but QT_XMLPATTERNS_EXECUTABLE is empty") set(_force_blueberry_use_qt_help_to_off 1) endif() if(_force_blueberry_use_qt_help_to_off) set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating plugin documentation into Qt Help" FORCE) endif() endif() if(BLUEBERRY_QT_HELP_REQUIRED AND NOT BLUEBERRY_USE_QT_HELP) message(FATAL_ERROR "BLUEBERRY_USE_QT_HELP is required to be set to ON") endif() endif() endif() #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- if(BUILD_TESTING) #[[ See T27701 # Initial cache for ProjectTemplate and PluginGenerator tests configure_file( CMake/mitkTestInitialCache.txt.in ${MITK_BINARY_DIR}/mitkTestInitialCache.txt @ONLY )]] # Configuration for the CMake-generated test driver set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ") 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_EXTENSION_DIRS}) get_filename_component(MITK_ROOT_FOLDER ${MITK_EXTENSION_DIR} NAME) set(MITK_MODULES_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Modules) get_filename_component(MITK_MODULES_EXTENSION_DIR ${MITK_MODULES_EXTENSION_DIR} ABSOLUTE) 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) set(BLUEBERRY_XPDOC_OUTPUT_DIR "${MITK_DOXYGEN_OUTPUT_DIR}/html/extension-points/html/") execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${BLUEBERRY_XPDOC_OUTPUT_DIR}) 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_EXTENSION_DIRS}) set(MITK_PLUGINS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Plugins) get_filename_component(MITK_PLUGINS_EXTENSION_DIR ${MITK_PLUGINS_EXTENSION_DIR} ABSOLUTE) 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) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) set(MITK_APPS "") include(${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) if(${option_name}) list(APPEND mitk_apps_fullpath "${MITK_APPLICATIONS_EXTENSION_DIR}/${directory_name}^^${option_name}") endif() endforeach() endif() endforeach() if (mitk_plugins_fullpath) ctkMacroSetupPlugins(${mitk_plugins_fullpath} BUILD_OPTION_PREFIX MITK_BUILD_ APPS ${mitk_apps_fullpath} BUILD_ALL ${MITK_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) endif() set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE}) else() file(REMOVE ${MITK_PLUGIN_USE_FILE}) set(MITK_PLUGIN_USE_FILE ) endif() endif() #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- if(DOXYGEN_FOUND) add_subdirectory(Documentation) endif() #----------------------------------------------------------------------------- # Installation #----------------------------------------------------------------------------- # set MITK cpack variables # These are the default variables, which can be overwritten ( see below ) include(mitkSetupCPack) set(use_default_config ON) set(ALL_MITK_APPS "") set(activated_apps_no 0) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Applications) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) 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_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Applications) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_APPLICATIONS_EXTENSION_DIR}/CMakeLists.txt) add_subdirectory(${MITK_APPLICATIONS_EXTENSION_DIR} Applications) endif() endforeach() #----------------------------------------------------------------------------- # MITK Examples #----------------------------------------------------------------------------- if(MITK_BUILD_EXAMPLES) # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Examples) endif() #----------------------------------------------------------------------------- # Print configuration summary #----------------------------------------------------------------------------- message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL ) diff --git a/Documentation/Doxygen/2-UserManual/MITKPluginGeneralManualsList.dox b/Documentation/Doxygen/2-UserManual/MITKPluginGeneralManualsList.dox index 0ec050c887..3d89a0f0dd 100644 --- a/Documentation/Doxygen/2-UserManual/MITKPluginGeneralManualsList.dox +++ b/Documentation/Doxygen/2-UserManual/MITKPluginGeneralManualsList.dox @@ -1,32 +1,33 @@ /** \page PluginListGeneralPage List of General Purpose Plugins \li \subpage org_mitk_views_basicimageprocessing \li \subpage org_mitk_views_datamanager \li \subpage org_mitk_views_properties \li \subpage org_mitk_editors_dicombrowser - \li \subpage org_mitk_gui_qt_dicominspector + \li \subpage org_mitk_views_dicominspector \li \subpage org_mitk_views_imagecropper \li \subpage org_mitk_views_imagenavigator \li \subpage org_blueberry_views_logview \li \subpage org_mitk_gui_qt_matchpoint_algorithm_batch \li \subpage org_mitk_views_matchpoint_algorithm_browser \li \subpage org_mitk_views_matchpoint_algorithm_control \li \subpage org_mitk_views_matchpoint_evaluator \li \subpage org_mitk_views_matchpoint_framereg \li \subpage org_mitk_views_matchpoint_manipulator \li \subpage org_mitk_views_matchpoint_mapper \li \subpage org_mitk_views_matchpoint_visualizer \li \subpage org_mitk_gui_qt_measurementtoolbox \li \subpage org_mitk_views_moviemaker \li \subpage org_mitk_views_multilabelsegmentation \li \subpage org_mitk_views_pointsetinteraction - \li \subpage org_mitk_gui_qt_python - \li \subpage org_mitk_gui_qt_remeshing + \li \subpage org_mitk_views_python + \li \subpage org_mitk_views_qt_remeshing \li \subpage org_mitk_views_screenshotmaker \li \subpage org_mitk_views_segmentation + \li \subpage org_mitk_views_deformableclippingplane \li \subpage org_mitk_gui_qt_flow_segmentation - \li \subpage org_mitk_gui_qt_viewnavigator + \li \subpage org_mitk_views_viewnavigatorview \li \subpage org_mitk_views_volumevisualization */ diff --git a/Documentation/Doxygen/2-UserManual/MITKPluginManualsList.dox b/Documentation/Doxygen/2-UserManual/MITKPluginManualsList.dox index c4d4470da7..be3c4b4319 100644 --- a/Documentation/Doxygen/2-UserManual/MITKPluginManualsList.dox +++ b/Documentation/Doxygen/2-UserManual/MITKPluginManualsList.dox @@ -1,84 +1,83 @@ /** \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. - \subpage PluginListGeneralPage
    -
  • \ref org_mitk_views_basicimageprocessing
  • -
  • \ref org_mitk_views_datamanager
  • -
  • \ref org_mitk_editors_dicombrowser
  • -
  • \ref org_mitk_gui_qt_dicominspector
  • -
  • \ref org_mitk_gui_qt_flowapplication
  • -
  • \ref org_mitk_views_imagecropper
  • -
  • \ref org_mitk_views_imagenavigator
  • -
  • \ref org_blueberry_views_logview
  • -
  • \ref org_mitk_views_matchpoint_algorithm_browser
  • -
  • \ref org_mitk_views_matchpoint_algorithm_control
  • -
  • \ref org_mitk_views_matchpoint_evaluator
  • -
  • \ref org_mitk_views_matchpoint_framereg
  • -
  • \ref org_mitk_views_matchpoint_manipulator
  • -
  • \ref org_mitk_views_matchpoint_mapper
  • -
  • \ref org_mitk_views_matchpoint_visualizer
  • -
  • \ref org_mitk_gui_qt_measurementtoolbox
  • -
  • \ref org_mitk_views_moviemaker
  • -
  • \ref org_mitk_views_multilabelsegmentation
  • -
  • \ref org_mitk_views_pointsetinteraction
  • -
  • \ref org_mitk_gui_qt_python
  • -
  • \ref org_mitk_gui_qt_remeshing
  • -
  • \ref org_mitk_views_screenshotmaker
  • -
  • \ref org_mitk_views_segmentation
  • -
  • \ref org_mitk_gui_qt_flow_segmentation
  • -
  • \ref org_mitk_gui_qt_viewnavigator
  • -
  • \ref org_mitk_views_volumevisualization
  • -
  • \ref org_mitk_views_properties
  • +
  • \ref org_mitk_views_basicimageprocessing
  • +
  • \ref org_mitk_views_datamanager
  • +
  • \ref org_mitk_editors_dicombrowser
  • +
  • \ref org_mitk_views_dicominspector
  • +
  • \ref org_mitk_views_imagecropper
  • +
  • \ref org_mitk_views_imagenavigator
  • +
  • \ref org_blueberry_views_logview
  • +
  • \ref org_mitk_views_matchpoint_algorithm_browser
  • +
  • \ref org_mitk_views_matchpoint_algorithm_control
  • +
  • \ref org_mitk_views_matchpoint_evaluator
  • +
  • \ref org_mitk_views_matchpoint_framereg
  • +
  • \ref org_mitk_views_matchpoint_manipulator
  • +
  • \ref org_mitk_views_matchpoint_mapper
  • +
  • \ref org_mitk_views_matchpoint_visualizer
  • +
  • \ref org_mitk_views_measurement
  • +
  • \ref org_mitk_views_imagestatistics
  • +
  • \ref org_mitk_views_moviemaker
  • +
  • \ref org_mitk_views_multilabelsegmentation
  • +
  • \ref org_mitk_views_pointsetinteraction
  • +
  • \ref org_mitk_views_python
  • +
  • \ref org_mitk_views_remeshing
  • +
  • \ref org_mitk_views_screenshotmaker
  • +
  • \ref org_mitk_views_segmentation
  • +
  • \ref org_mitk_views_deformableclippingplane
  • +
  • \ref org_mitk_views_viewnavigatorview
  • +
  • \ref org_mitk_views_volumevisualization
  • +
  • \ref org_mitk_views_properties
\subpage PluginListSpecificPage
    -
  • \ref org_mitk_gui_qt_aicpregistration
  • -
  • \ref org_mitk_gui_qt_cest
  • -
  • \ref org_mitk_gui_qt_classificationsegmentation
  • -
  • \ref org_mitk_views_cmdlinemodules
  • -
  • \ref org_mitk_views_pharmacokinetics_concentration_mri
  • -
  • \ref org_mitk_views_pharmacokinetics_mri
  • -
  • \ref org_mitk_gui_qt_pharmacokinetics_pet
  • -
  • \ref org_mitk_gui_qt_eventrecorder
  • -
  • \ref org_mitk_gui_qt_examples
  • -
  • \ref org_mitk_gui_qt_geometrytools
  • -
  • \ref org_mitk_gui_qt_igtexample
  • -
  • \ref org_mitk_gui_qt_igtlplugin
  • -
  • \ref org_mitk_gui_qt_igttracking
  • -
  • \ref org_mitk_gui_qt_igttrackingsemiautomaticmeasurement
  • -
  • \ref org_mitk_views_imagestatistics
  • -
  • \ref org_mitk_gui_qt_lasercontrol
  • -
  • \ref org_mitk_views_fit_demo
  • -
  • \ref org_mitk_gui_qt_fit_genericfitting
  • -
  • \ref org_mitk_views_fit_inspector
  • -
  • \ref org_mitkexamplesopencv
  • -
  • \ref org_mitk_gui_qt_overlaymanager
  • -
  • \ref org_mitk_gui_qt_mitkphenotyping
  • -
  • \ref org_mitk_gui_qt_photoacoustics_pausmotioncompensation
  • -
  • \ref org_mitk_example_gui_pcaexample
  • -
  • \ref org_mitk_gui_qt_preprocessing_resampling
  • -
  • \ref org_mitk_views_pharmacokinetics_curvedescriptor
  • -
  • \ref org_mitk_gui_qt_photoacoustics_imageprocessing
  • -
  • \ref org_mitk_gui_qt_pharmacokinetics_simulation
  • -
  • \ref org_mitk_gui_qt_pointsetinteractionmultispectrum
  • -
  • \ref org_mitk_gui_qt_renderwindowmanager
  • -
  • \ref org_mitk_gui_qt_photoacoustics_spectralunmixing
  • -
  • \ref org_mitk_gui_qt_spectrocamrecorder
  • -
  • \ref org_surfacematerialeditor
  • -
  • \ref org_toftutorial
  • -
  • \ref org_blueberry_ui_qt_objectinspector
  • -
  • \ref org_mitk_gui_qt_ultrasound
  • -
  • \ref org_mitk_gui_qt_igt_app_echotrack
  • -
  • \ref org_mitk_gui_qt_xnat
  • +
  • \ref org_mitk_gui_qt_flowapplication
  • +
  • \ref org_mitk_views_flow_control
  • +
  • \ref org_mitk_gui_qt_aicpregistration
  • +
  • \ref org_mitk_gui_qt_cest
  • +
  • \ref org_mitk_gui_qt_classificationsegmentation
  • +
  • \ref org_mitk_views_cmdlinemodules
  • +
  • \ref org_mitk_views_pharmacokinetics_concentration_mri
  • +
  • \ref org_mitk_views_pharmacokinetics_mri
  • +
  • \ref org_mitk_views_pharmacokinetics_pet
  • +
  • \ref org_mitk_gui_qt_eventrecorder
  • +
  • \ref org_mitk_gui_qt_examples
  • +
  • \ref org_mitk_gui_qt_geometrytools
  • +
  • \ref org_mitk_gui_qt_igtexample
  • +
  • \ref org_mitk_gui_qt_igtlplugin
  • +
  • \ref org_mitk_gui_qt_igttracking
  • +
  • \ref org_mitk_views_igttrackingsemiautomaticmeasurement
  • +
  • \ref org_mitk_gui_qt_lasercontrol
  • +
  • \ref org_mitk_views_fit_demo
  • +
  • \ref org_mitk_views_fit_genericfitting
  • +
  • \ref org_mitk_views_fit_inspector
  • +
  • \ref org_mitkexamplesopencv
  • +
  • \ref org_mitk_gui_qt_overlaymanager
  • +
  • \ref org_mitk_gui_qt_mitkphenotyping
  • +
  • \ref org_mitk_gui_qt_photoacoustics_pausmotioncompensation
  • +
  • \ref org_mitk_example_gui_pcaexample
  • +
  • \ref org_mitk_gui_qt_preprocessing_resampling
  • +
  • \ref org_mitk_views_pharmacokinetics_curvedescriptor
  • +
  • \ref org_mitk_views_photoacoustics_imageprocessing
  • +
  • \ref org_mitk_gui_qt_pharmacokinetics_simulation
  • +
  • \ref org_mitk_gui_qt_pointsetinteractionmultispectrum
  • +
  • \ref org_mitk_gui_qt_renderwindowmanager
  • +
  • \ref org_mitk_gui_qt_photoacoustics_spectralunmixing
  • +
  • \ref org_mitk_gui_qt_spectrocamrecorder
  • +
  • \ref org_surfacematerialeditor
  • +
  • \ref org_toftutorial
  • +
  • \ref org_blueberry_ui_qt_objectinspector
  • +
  • \ref org_mitk_gui_qt_ultrasound
  • +
  • \ref org_mitk_gui_qt_igt_app_echotrack
  • +
  • \ref org_mitk_gui_qt_xnat
- */ diff --git a/Documentation/Doxygen/2-UserManual/MITKPluginSpecificManualsList.dox b/Documentation/Doxygen/2-UserManual/MITKPluginSpecificManualsList.dox index c470916167..c30e4932d9 100644 --- a/Documentation/Doxygen/2-UserManual/MITKPluginSpecificManualsList.dox +++ b/Documentation/Doxygen/2-UserManual/MITKPluginSpecificManualsList.dox @@ -1,45 +1,46 @@ /** \page PluginListSpecificPage List of Application-specific Plugins \li \subpage org_mitk_gui_qt_aicpregistration \li \subpage org_mitk_gui_qt_cest \li \subpage org_mitk_gui_qt_classificationsegmentation \li \subpage org_mitk_gui_qt_flowapplication \li \subpage org_mitk_views_cmdlinemodules \li \subpage org_mitk_views_pharmacokinetics_concentration_mri \li \subpage org_mitk_views_pharmacokinetics_mri - \li \subpage org_mitk_gui_qt_pharmacokinetics_pet + \li \subpage org_mitk_views_pharmacokinetics_pet + \li \subpage org_mitk_views_pharmacokinetics_simulation \li \subpage org_mitk_gui_qt_eventrecorder \li \subpage org_mitk_gui_qt_examples \li \subpage org_mitk_gui_qt_geometrytools \li \subpage org_mitk_gui_qt_igtexample \li \subpage org_mitk_gui_qt_igtlplugin \li \subpage org_mitk_gui_qt_igttracking - \li \subpage org_mitk_gui_qt_igttrackingsemiautomaticmeasurement + \li \subpage org_mitk_views_igttrackingsemiautomaticmeasurement \li \subpage org_mitk_views_imagestatistics \li \subpage org_mitk_gui_qt_lasercontrol \li \subpage org_mitk_views_fit_demo - \li \subpage org_mitk_gui_qt_fit_genericfitting + \li \subpage org_mitk_views_fit_genericfitting \li \subpage org_mitk_views_fit_inspector \li \subpage org_mitkexamplesopencv \li \subpage org_mitk_gui_qt_overlaymanager \li \subpage org_mitk_gui_qt_mitkphenotyping \li \subpage org_mitk_gui_qt_photoacoustics_pausmotioncompensation \li \subpage org_mitk_example_gui_pcaexample \li \subpage org_mitk_gui_qt_preprocessing_resampling \li \subpage org_mitk_views_pharmacokinetics_curvedescriptor - \li \subpage org_mitk_gui_qt_photoacoustics_imageprocessing + \li \subpage org_mitk_views_photoacoustics_imageprocessing \li \subpage org_mitk_gui_qt_pharmacokinetics_simulation \li \subpage org_mitk_gui_qt_pointsetinteractionmultispectrum \li \subpage org_mitk_gui_qt_renderwindowmanager \li \subpage org_mitk_gui_qt_photoacoustics_spectralunmixing \li \subpage org_mitk_gui_qt_spectrocamrecorder \li \subpage org_surfacematerialeditor \li \subpage org_blueberry_ui_qt_objectinspector \li \subpage org_toftutorial \li \subpage org_mitk_gui_qt_ultrasound \li \subpage org_mitk_gui_qt_igt_app_echotrack \li \subpage org_mitk_gui_qt_xnat -*/ \ No newline at end of file +*/ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox index 7489ed7af3..bfddaa5cf1 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox @@ -1,30 +1,31 @@ /** \page Concepts MITK Concepts The following items describe some issues about MITK on a more abstract level. -# \subpage CodingPage "Coding Concepts" -# \ref CodingPageGeneral -# \ref CodingPageStyle -# \ref CodingPageMITKMacros -# \subpage MicroServices_Overview -# Data Concepts -# \subpage BasicDataTypesPage -# \subpage DataManagementPage -# \subpage ReaderWriterPage -# \subpage MitkImagePage -# \subpage PropertiesPage -# \subpage GeometryOverviewPage -# \subpage PipelineingConceptPage -# \subpage AnnotationPage -# \subpage PersistenceConceptPage + -# \subpage SelectionConceptPage -# \subpage QVTKRendering -# Interaction -# \subpage DataInteractionPage -# \subpage InteractionPage -# \subpage LoggingPage -# \subpage ExceptionPage -# \subpage ModularizationPage "Modularization Concept" -# \ref ModularizationPageOverview -# \ref ModularizationPageHowTo */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Selection.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Selection.dox new file mode 100644 index 0000000000..562426f2f2 --- /dev/null +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Selection.dox @@ -0,0 +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. +
+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. +
+ 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 data storage inspectors or node selection widgets. +
+ \imageMacro{selection_concept_class_diagram.png, "", 16} +
+ 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), 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), 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). + 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().GetPointer()); + connect(m_ModelViewSelectionConnector.get(), SIGNAL(CurrentSelectionChanged(QList)), m_SelectionServiceConnector.get(), SLOT(ChangeServiceSelection(QList))); + } + else + { + m_SelectionServiceConnector->RemoveAsSelectionProvider(); + disconnect(m_ModelViewSelectionConnector.get(), SIGNAL(CurrentSelectionChanged(QList)), m_SelectionServiceConnector.get(), SLOT(ChangeServiceSelection(QList))); + } + } + \endcode + +
+ \code + void QmitkDataStorageViewerTestView::SetAsSelectionListener(bool enable) + { + if (enable) + { + m_SelectionServiceConnector->AddPostSelectionListener(GetSite()->GetWorkbenchWindow()->GetSelectionService()); + connect(m_SelectionServiceConnector.get(), SIGNAL(ServiceSelectionChanged(QList)), m_ModelViewSelectionConnector.get(), SLOT(SetCurrentSelection(QList))); + } + else + { + m_SelectionServiceConnector->RemovePostSelectionListener(); + disconnect(m_SelectionServiceConnector.get(), SIGNAL(ServiceSelectionChanged(QList)), m_ModelViewSelectionConnector.get(), SLOT(SetCurrentSelection(QList))); + } + } + \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 Node Selection 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("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. + 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 << "

Invalid selection.

The number of selected nodes must be uneven! the current number is " << nodes.size() << ".

"; + 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"). +
+ See how the selection also changes in the inspectors if nodes are selected in the mitk::DataManager and the Set as selection listener-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/Concepts/images/selection/selection_concept_class_diagram.png b/Documentation/Doxygen/3-DeveloperManual/Concepts/images/selection/selection_concept_class_diagram.png new file mode 100644 index 0000000000..88c2d91cc7 Binary files /dev/null and b/Documentation/Doxygen/3-DeveloperManual/Concepts/images/selection/selection_concept_class_diagram.png differ diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/DirectoryStructure.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/DirectoryStructure.dox index c1d027e251..4dda6a0e2b 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/DirectoryStructure.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/DirectoryStructure.dox @@ -1,88 +1,70 @@ /** \page DirectoryStructurePage Directory Structure To avoid clutter and ease the administration of the source code the MITK source code is structured into several subdirectories according to their function. This document aims at helping you finding what you want within this structure. \section DirectoryStructurePageOverview Overview The top level directories are the following: \li \ref DirectoryStructurePageApplications -\li \ref DirectoryStructurePageBlueBerry -\li \ref DirectoryStructurePageBuild \li \ref DirectoryStructurePageCMake \li \ref DirectoryStructurePageCMakeExternals -\li \ref DirectoryStructurePageConfig -\li \ref DirectoryStructurePageCore \li \ref DirectoryStructurePageDocumentation \li \ref DirectoryStructurePageExamples +\li \ref DirectoryStructurePageLicenses \li \ref DirectoryStructurePageModules \li \ref DirectoryStructurePagePlugins +\li \ref DirectoryStructurePageTemplates \li \ref DirectoryStructurePageUtilities \li \ref DirectoryStructurePageWrapping see the corresponding section for more information. \section DirectoryStructurePageTopLevelDirs The Top Level Directories \subsection DirectoryStructurePageApplications Applications This directory contains the different applications. Each application has its own subfolder, including the necessary code, icons and other resources. For more information on applications see \ref ModularizationPageApplications or \ref ApplicationsPageApplications . -\subsection DirectoryStructurePageBlueBerry BlueBerry - -The BlueBerry directory contains all code related to the BlueBerry framework. This code can be used independently of MITK. - -\subsection DirectoryStructurePageBuild Build - -The Build directory contains tools for the generation of MITK Plugins, editing of StateMachines and similar tasks. They are usually their own executables which are not integrated within any applications from the Applications directory. - \subsection DirectoryStructurePageCMake CMake The %CMake directory contains %CMake files, macros and templates for the configuration of MITK as a project. \subsection DirectoryStructurePageCMakeExternals CMakeExternals This directory contains %CMake files for the configuration of other projects used by MITK. -\subsection DirectoryStructurePageConfig Config - -The Config directory contains configuration files. These provide presets and default values. - -\subsection DirectoryStructurePageCore Core - -The Core directory contains the basic MITK classes, which are used by most of the Plugins or Modules. They provide the basic MITK functionality and are subject to stricter quality controls than the rest of the code. - -\subsubsection DirectoryStructurePageCoreCode Code +\subsection DirectoryStructurePageDocumentation Documentation -This subdirectory contains the actual core code of MITK. It provides datastructures, rendering classes, interactors and many more. - -\subsubsection DirectoryStructurePageCoreDocumentation Documentation - -This subdirectory contains the documentation on the concepts MITK was designed upon. - -\subsection DirectoryStructurePageDocumentation Documenation - -The Documentation directory contains doxygen and other documentation as well as tutorials and examples. It includes basic end user manuals for the framework as well as developer documentation which does not depend on any additional Modules. +The Documentation directory contains doxygen and other documentation as well as tutorials and examples. It includes basic end user manuals for the framework as well as developer documentation which does not depend on any additional modules. \subsection DirectoryStructurePageExamples Examples This directory contains example code used by the documentation in the Documentation directory. +\subsection DirectoryStructurePageLicenses Licenses + +The Licenses directory contains various licenses used by MITK's dependencies. + \subsection DirectoryStructurePageModules Modules -This directory contains modules which extend the capabilities of MITK. For the concept behind modules see \ref ModularizationPageModules . +This directory contains modules, which extend the capabilities of MITK. For the concept behind modules see \ref ModularizationPageModules . \subsection DirectoryStructurePagePlugins Plugins This directory contains plugins, which provide specific solutions. For further information see \ref ModularizationPagePlugInBundles . +\subsection DirectoryStructurePageTemplates Templates + +This directory contains template files for recurring snippets like the copyright header for all source files. + \subsection DirectoryStructurePageUtilities Utilities -The Utilities directory contains self contained libraries, which are used by MITK, but not necessarily developed or maintained by the MITK team. +The Utilities directory contains self contained components, which are used by MITK, but not necessarily developed or maintained by the MITK team. \subsection DirectoryStructurePageWrapping Wrapping The Wrapping directory contains code necessary for the Python wrapping capabilities of MITK. */ diff --git a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp index 9f8a0533a9..6da6ebc0b1 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp @@ -1,507 +1,507 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkLevelWindow.h" #include "mitkImage.h" #include "mitkImageSliceSelector.h" #include "mitkImageStatisticsHolder.h" #include void mitk::LevelWindow::EnsureConsistency() { // Check if total range is ok { if (m_RangeMin > m_RangeMax) std::swap(m_RangeMin, m_RangeMax); if (m_RangeMin == m_RangeMax) m_RangeMin = m_RangeMax - 1; } // Check if current window is ok { if (m_LowerWindowBound > m_UpperWindowBound) std::swap(m_LowerWindowBound, m_UpperWindowBound); if (m_LowerWindowBound <= m_RangeMin) m_LowerWindowBound = m_RangeMin; if (m_UpperWindowBound <= m_RangeMin) m_UpperWindowBound = m_RangeMin + 1; if (m_LowerWindowBound >= m_RangeMax) m_LowerWindowBound = m_RangeMax - 1; if (m_UpperWindowBound >= m_RangeMax) m_UpperWindowBound = m_RangeMax; if (m_LowerWindowBound == m_UpperWindowBound) { m_UpperWindowBound += 0.5; m_LowerWindowBound -= 0.5; m_UpperWindowBound = std::min(m_UpperWindowBound, m_RangeMax); m_LowerWindowBound = std::max(m_LowerWindowBound, m_RangeMin); } } } mitk::LevelWindow::LevelWindow(mitk::ScalarType level, mitk::ScalarType window) : m_LowerWindowBound(level - window / 2.0), m_UpperWindowBound(level + window / 2.0), m_RangeMin(-2048.0), m_RangeMax(4096.0), m_DefaultLowerBound(-2048.0), m_DefaultUpperBound(4096.0), m_IsFloatingImage(false), m_Fixed(false) { SetDefaultLevelWindow(level, window); SetLevelWindow(level, window, true); } mitk::LevelWindow::LevelWindow(const mitk::LevelWindow &levWin) : m_LowerWindowBound(levWin.GetLowerWindowBound()), m_UpperWindowBound(levWin.GetUpperWindowBound()), m_RangeMin(levWin.GetRangeMin()), m_RangeMax(levWin.GetRangeMax()), m_DefaultLowerBound(levWin.GetDefaultLowerBound()), m_DefaultUpperBound(levWin.GetDefaultUpperBound()), m_IsFloatingImage(levWin.IsFloatingValues()), m_Fixed(levWin.GetFixed()) { } mitk::LevelWindow::~LevelWindow() { } mitk::ScalarType mitk::LevelWindow::GetLevel() const { return (m_UpperWindowBound - m_LowerWindowBound) / 2.0 + m_LowerWindowBound; } mitk::ScalarType mitk::LevelWindow::GetWindow() const { return (m_UpperWindowBound - m_LowerWindowBound); } mitk::ScalarType mitk::LevelWindow::GetDefaultLevel() const { return ((m_DefaultUpperBound + m_DefaultLowerBound) / 2.0); } mitk::ScalarType mitk::LevelWindow::GetDefaultWindow() const { return ((m_DefaultUpperBound - m_DefaultLowerBound)); } void mitk::LevelWindow::ResetDefaultLevelWindow() { SetLevelWindow(GetDefaultLevel(), GetDefaultWindow()); } mitk::ScalarType mitk::LevelWindow::GetLowerWindowBound() const { return m_LowerWindowBound; } mitk::ScalarType mitk::LevelWindow::GetUpperWindowBound() const { return m_UpperWindowBound; } void mitk::LevelWindow::SetDefaultLevelWindow(mitk::ScalarType level, mitk::ScalarType window) { SetDefaultBoundaries((level - (window / 2.0)), (level + (window / 2.0))); } void mitk::LevelWindow::SetLevelWindow(mitk::ScalarType level, mitk::ScalarType window, bool expandRangesIfNecessary) { SetWindowBounds((level - (window / 2.0)), (level + (window / 2.0)), expandRangesIfNecessary); } void mitk::LevelWindow::SetWindowBounds(mitk::ScalarType lowerBound, mitk::ScalarType upperBound, bool expandRangesIfNecessary) { if (IsFixed()) return; m_LowerWindowBound = lowerBound; m_UpperWindowBound = upperBound; if (expandRangesIfNecessary) { /* if caller is sure he wants exactly that level/window, we make sure the limits match */ if (m_LowerWindowBound > m_UpperWindowBound) std::swap(m_LowerWindowBound, m_UpperWindowBound); if (m_LowerWindowBound < m_RangeMin) { m_RangeMin = m_LowerWindowBound; } if (m_UpperWindowBound > m_RangeMax) { m_RangeMax = m_UpperWindowBound; } } EnsureConsistency(); } void mitk::LevelWindow::SetRangeMinMax(mitk::ScalarType min, mitk::ScalarType max) { if (IsFixed()) return; m_RangeMin = min; m_RangeMax = max; EnsureConsistency(); } void mitk::LevelWindow::SetDefaultBoundaries(mitk::ScalarType low, mitk::ScalarType up) { if (IsFixed()) return; m_DefaultLowerBound = low; m_DefaultUpperBound = up; // Check if default window is ok { if (m_DefaultLowerBound > m_DefaultUpperBound) std::swap(m_DefaultLowerBound, m_DefaultUpperBound); if (m_DefaultLowerBound == m_DefaultUpperBound) m_DefaultLowerBound--; } EnsureConsistency(); } void mitk::LevelWindow::SetToMaxWindowSize() { SetWindowBounds(m_RangeMin, m_RangeMax); } mitk::ScalarType mitk::LevelWindow::GetRangeMin() const { return m_RangeMin; } mitk::ScalarType mitk::LevelWindow::GetRangeMax() const { return m_RangeMax; } mitk::ScalarType mitk::LevelWindow::GetRange() const { return m_RangeMax - m_RangeMin; } mitk::ScalarType mitk::LevelWindow::GetDefaultUpperBound() const { return m_DefaultUpperBound; } mitk::ScalarType mitk::LevelWindow::GetDefaultLowerBound() const { return m_DefaultLowerBound; } void mitk::LevelWindow::ResetDefaultRangeMinMax() { SetRangeMinMax(m_DefaultLowerBound, m_DefaultUpperBound); } /*! This method initializes a mitk::LevelWindow from an mitk::Image. The algorithm is as follows: Default to taking the central image slice for quick analysis. Compute the smallest (minValue), second smallest (min2ndValue), second largest (max2ndValue), and largest (maxValue) data value by traversing the pixel values only once. In the same scan it also computes the count of minValue values and maxValue values. After that a basic histogram with specific information about the extrems is complete. If minValue == maxValue, the center slice is uniform and the above scan is repeated for the complete image, not just one slice Next, special cases of images with only 1, 2 or 3 distinct data values have hand assigned level window ranges. Next the level window is set relative to the inner range IR = lengthOf([min2ndValue, max2ndValue]) For count(minValue) > 20% the smallest values are frequent and should be distinct from the min2ndValue and larger values (minValue may be std:min, may signify something special) hence the lower end of the level window is set to min2ndValue - 0.5 * IR For count(minValue) <= 20% the smallest values are not so important and can blend with the next ones => min(level window) = min2ndValue And analog for max(level window): count(max2ndValue) > 20%: max(level window) = max2ndValue + 0.5 * IR count(max2ndValue) < 20%: max(level window) = max2ndValue In both 20%+ cases the level window bounds are clamped to the [minValue, maxValue] range In consequence the level window maximizes contrast with minimal amount of computation and does do useful things if the data contains std::min or std:max values or has only 1 or 2 or 3 data values. */ void mitk::LevelWindow::SetAuto(const mitk::Image *image, bool /*tryPicTags*/, bool guessByCentralSlice, unsigned selectedComponent) { if (IsFixed()) return; if (image == nullptr || !image->IsInitialized()) return; if (itk::ImageIOBase::IOComponentType::FLOAT == image->GetPixelType().GetComponentType() || itk::ImageIOBase::IOComponentType::DOUBLE == image->GetPixelType().GetComponentType()) { 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)->Update(); minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(0); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); for (unsigned int i = 1; i < image->GetDimension(3); ++i) { ScalarType minValueTemp = image->GetStatistics()->GetScalarValueMin(i, selectedComponent); if (minValue > minValueTemp) minValue = minValueTemp; ScalarType maxValueTemp = image->GetStatistics()->GetScalarValueMaxNoRecompute(i); if (maxValue < maxValueTemp) maxValue = maxValueTemp; ScalarType min2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(i); if (min2ndValue > min2ndValueTemp) min2ndValue = min2ndValueTemp; ScalarType max2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(i); if (max2ndValue > max2ndValueTemp) max2ndValue = max2ndValueTemp; } } // Fix for bug# 344 Level Window wird bei Eris Cut bildern nicht richtig gesetzt if (image->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR && image->GetPixelType().GetComponentType() == itk::ImageIOBase::INT && image->GetPixelType().GetBpe() >= 8) { // the windows compiler complains about ambiguos 'pow' call, therefore static casting to (double, int) if (minValue == -(pow((double)2.0, static_cast(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); - unsigned int numPixelsInDataset = image->GetDimensions()[0]; - for (unsigned int k = 0; k < image->GetDimension(); ++k) + size_t numPixelsInDataset = image->GetDimensions()[0]; + for (decltype(image->GetDimension()) k = 1; k < image->GetDimension(); ++k) numPixelsInDataset *= image->GetDimensions()[k]; - unsigned int minCount = image->GetStatistics()->GetCountOfMinValuedVoxelsNoRecompute(); - unsigned int maxCount = image->GetStatistics()->GetCountOfMaxValuedVoxelsNoRecompute(); - ScalarType minCountFraction = minCount / ScalarType(numPixelsInDataset); - ScalarType maxCountFraction = maxCount / ScalarType(numPixelsInDataset); + const auto minCount = image->GetStatistics()->GetCountOfMinValuedVoxelsNoRecompute(); + const auto maxCount = image->GetStatistics()->GetCountOfMaxValuedVoxelsNoRecompute(); + const auto minCountFraction = minCount / static_cast(numPixelsInDataset); + const auto maxCountFraction = maxCount / static_cast(numPixelsInDataset); //// binary image if (min2ndValue == maxValue) { // noop; full range is fine } //// triple value image, put middle value in center of gray level ramp else if (min2ndValue == max2ndValue) { ScalarType minDelta = std::min(min2ndValue - minValue, maxValue - min2ndValue); minValue = min2ndValue - minDelta; maxValue = min2ndValue + minDelta; } // now we can assume more than three distict scalar values else { ScalarType innerRange = max2ndValue - min2ndValue; if (minCountFraction > 0.2) //// lots of min values -> make different from rest, but not miles away { ScalarType halfInnerRangeGapMinValue = min2ndValue - innerRange / 2.0; minValue = std::max(minValue, halfInnerRangeGapMinValue); } else //// few min values -> focus on innerRange { minValue = min2ndValue; } if (maxCountFraction > 0.2) //// lots of max values -> make different from rest { ScalarType halfInnerRangeGapMaxValue = max2ndValue + innerRange / 2.0; maxValue = std::min(maxValue, halfInnerRangeGapMaxValue); } else //// few max values -> focus on innerRange { maxValue = max2ndValue; } } SetWindowBounds(minValue, maxValue); SetDefaultLevelWindow((maxValue - minValue) / 2 + minValue, maxValue - minValue); } void mitk::LevelWindow::SetToImageRange(const mitk::Image *image) { if (IsFixed()) return; if (image == nullptr || !image->IsInitialized()) return; ScalarType minValue = image->GetStatistics()->GetScalarValueMin(0); if (!std::isfinite(minValue)) { minValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); } ScalarType maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(0); if (!std::isfinite(maxValue)) { maxValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); } SetRangeMinMax(minValue, maxValue); SetDefaultBoundaries(minValue, maxValue); SetWindowBounds(minValue, maxValue); SetDefaultLevelWindow((maxValue - minValue) / 2 + minValue, maxValue - minValue); } void mitk::LevelWindow::SetFixed(bool fixed) { m_Fixed = fixed; } bool mitk::LevelWindow::GetFixed() const { return m_Fixed; } bool mitk::LevelWindow::IsFixed() const { return m_Fixed; } bool mitk::LevelWindow::IsFloatingValues() const { return m_IsFloatingImage; } void mitk::LevelWindow::SetFloatingValues(bool value) { m_IsFloatingImage = value; } bool mitk::LevelWindow::operator==(const mitk::LevelWindow &levWin) const { return mitk::Equal(this->m_RangeMin, levWin.m_RangeMin, mitk::sqrteps) && mitk::Equal(this->m_RangeMax, levWin.m_RangeMax, mitk::sqrteps) && mitk::Equal(this->m_DefaultLowerBound, levWin.m_DefaultLowerBound, mitk::sqrteps) && mitk::Equal(this->m_DefaultUpperBound, levWin.m_DefaultUpperBound, mitk::sqrteps) && mitk::Equal(this->m_LowerWindowBound, levWin.m_LowerWindowBound, mitk::sqrteps) && mitk::Equal(this->m_UpperWindowBound, levWin.m_UpperWindowBound, mitk::sqrteps) && m_Fixed == levWin.IsFixed() && m_IsFloatingImage == levWin.IsFloatingValues(); } bool mitk::LevelWindow::operator!=(const mitk::LevelWindow &levWin) const { return !((*this) == levWin); } mitk::LevelWindow &mitk::LevelWindow::operator=(const mitk::LevelWindow &levWin) { if (this == &levWin) { return *this; } else { m_RangeMin = levWin.GetRangeMin(); m_RangeMax = levWin.GetRangeMax(); m_LowerWindowBound = levWin.GetLowerWindowBound(); m_UpperWindowBound = levWin.GetUpperWindowBound(); m_DefaultLowerBound = levWin.GetDefaultLowerBound(); m_DefaultUpperBound = levWin.GetDefaultUpperBound(); m_Fixed = levWin.GetFixed(); m_IsFloatingImage = levWin.IsFloatingValues(); return *this; } } diff --git a/Modules/Multilabel/mitkLabelSetIOHelper.cpp b/Modules/Multilabel/mitkLabelSetIOHelper.cpp index 0106aa2fb0..1149e997f0 100644 --- a/Modules/Multilabel/mitkLabelSetIOHelper.cpp +++ b/Modules/Multilabel/mitkLabelSetIOHelper.cpp @@ -1,226 +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 "mitkLabelSetIOHelper.h" #include "mitkLabelSetImage.h" #include #include -bool mitk::LabelSetIOHelper::SaveLabelSetImagePreset(std::string &presetFilename, - mitk::LabelSetImage::Pointer &inputImage) +namespace { - if (presetFilename.find(".lsetp") == std::string::npos) + std::string EnsureExtension(const std::string& filename) { - presetFilename.append(".lsetp"); - } + const std::string extension = ".lsetp"; - auto *presetXmlDoc = new TiXmlDocument(); + if (filename.size() < extension.size() || std::string::npos == filename.find(extension, filename.size() - extension.size())) + return filename + extension; - auto *decl = new TiXmlDeclaration("1.0", "", ""); - presetXmlDoc->LinkEndChild(decl); + return filename; + } +} - auto *presetElement = new TiXmlElement("LabelSetImagePreset"); - presetElement->SetAttribute("layers", inputImage->GetNumberOfLayers()); +bool mitk::LabelSetIOHelper::SaveLabelSetImagePreset(const std::string &presetFilename, + mitk::LabelSetImage::Pointer &inputImage) +{ + const auto filename = EnsureExtension(presetFilename); + + TiXmlDocument xmlDocument; + xmlDocument.LinkEndChild(new TiXmlDeclaration("1.0", "", "")); - presetXmlDoc->LinkEndChild(presetElement); + auto *rootElement = new TiXmlElement("LabelSetImagePreset"); + rootElement->SetAttribute("layers", inputImage->GetNumberOfLayers()); + xmlDocument.LinkEndChild(rootElement); - for (unsigned int layerIdx = 0; layerIdx < inputImage->GetNumberOfLayers(); layerIdx++) + for (unsigned int layerIndex = 0; layerIndex < inputImage->GetNumberOfLayers(); layerIndex++) { auto *layerElement = new TiXmlElement("Layer"); - layerElement->SetAttribute("index", layerIdx); - layerElement->SetAttribute("labels", inputImage->GetNumberOfLabels(layerIdx)); + layerElement->SetAttribute("index", layerIndex); + layerElement->SetAttribute("labels", inputImage->GetNumberOfLabels(layerIndex)); + rootElement->LinkEndChild(layerElement); - presetElement->LinkEndChild(layerElement); - - for (unsigned int labelIdx = 0; labelIdx < inputImage->GetNumberOfLabels(layerIdx); labelIdx++) - { - TiXmlElement *labelAsXml = LabelSetIOHelper::GetLabelAsTiXmlElement(inputImage->GetLabel(labelIdx, layerIdx)); - layerElement->LinkEndChild(labelAsXml); - } + for (unsigned int labelIndex = 0; labelIndex < inputImage->GetNumberOfLabels(layerIndex); labelIndex++) + layerElement->LinkEndChild(LabelSetIOHelper::GetLabelAsTiXmlElement(inputImage->GetLabel(labelIndex, layerIndex))); } - bool wasSaved = presetXmlDoc->SaveFile(presetFilename); - delete presetXmlDoc; - - return wasSaved; + return xmlDocument.SaveFile(filename); } -void mitk::LabelSetIOHelper::LoadLabelSetImagePreset(std::string &presetFilename, +void mitk::LabelSetIOHelper::LoadLabelSetImagePreset(const std::string &presetFilename, mitk::LabelSetImage::Pointer &inputImage) { - if (presetFilename.find(".lsetp") == std::string::npos) - { - presetFilename.append(".lsetp"); - } + if (inputImage.IsNull()) + return; - std::unique_ptr presetXmlDoc(new TiXmlDocument()); + const auto filename = EnsureExtension(presetFilename); - bool ok = presetXmlDoc->LoadFile(presetFilename); - if (!ok) + TiXmlDocument xmlDocument; + + if(!xmlDocument.LoadFile(filename)) return; - TiXmlElement *presetElem = presetXmlDoc->FirstChildElement("LabelSetImagePreset"); - if (!presetElem) + auto *rootElement = xmlDocument.FirstChildElement("LabelSetImagePreset"); + + if (nullptr == rootElement) { - MITK_INFO << "No valid preset XML"; + MITK_WARN << "Not a valid LabelSet preset"; return; } - int numberOfLayers; - presetElem->QueryIntAttribute("layers", &numberOfLayers); + auto activeLayerBackup = inputImage->GetActiveLayer(); + + int numberOfLayers = 0; + rootElement->QueryIntAttribute("layers", &numberOfLayers); + + auto* layerElement = rootElement->FirstChildElement("Layer"); - for (int i = 0; i < numberOfLayers; i++) + if (nullptr == layerElement) { - TiXmlElement *layerElem = presetElem->FirstChildElement("Layer"); - int numberOfLabels; - layerElem->QueryIntAttribute("labels", &numberOfLabels); + MITK_WARN << "LabelSet preset does not contain any layers"; + return; + } - if (inputImage->GetLabelSet(i) == nullptr) + for (int layerIndex = 0; layerIndex < numberOfLayers; layerIndex++) + { + int numberOfLabels = 0; + layerElement->QueryIntAttribute("labels", &numberOfLabels); + + if (nullptr == inputImage->GetLabelSet(layerIndex)) + { inputImage->AddLayer(); + } + else + { + inputImage->SetActiveLayer(layerIndex); + } - TiXmlElement *labelElement = layerElem->FirstChildElement("Label"); - if (labelElement == nullptr) - break; - for (int j = 0; j < numberOfLabels; j++) + auto *labelElement = layerElement->FirstChildElement("Label"); + + if (nullptr == labelElement) + continue; + + for (int labelIndex = 0; labelIndex < numberOfLabels; labelIndex++) { - mitk::Label::Pointer label = mitk::LabelSetIOHelper::LoadLabelFromTiXmlDocument(labelElement); + auto label = mitk::LabelSetIOHelper::LoadLabelFromTiXmlDocument(labelElement); + const auto labelValue = label->GetValue(); - if (label->GetValue() == 0) - inputImage->SetExteriorLabel(label); - else - inputImage->GetLabelSet()->AddLabel(label); + if (0 != labelValue) + { + auto* labelSet = inputImage->GetLabelSet(layerIndex); + auto* alreadyExistingLabel = labelSet->GetLabel(labelValue); + + if (nullptr != alreadyExistingLabel) + { + // Override existing label with label from preset + alreadyExistingLabel->ConcatenatePropertyList(label); + labelSet->UpdateLookupTable(labelValue); + } + else + { + labelSet->AddLabel(label); + } + } labelElement = labelElement->NextSiblingElement("Label"); - if (labelElement == nullptr) - break; + + if (nullptr == labelElement) + continue; } + + layerElement = layerElement->NextSiblingElement("Layer"); + + if (nullptr == layerElement) + continue; } + + inputImage->SetActiveLayer(activeLayerBackup); } TiXmlElement *mitk::LabelSetIOHelper::GetLabelAsTiXmlElement(Label *label) { auto *labelElem = new TiXmlElement("Label"); // add XML contents const PropertyList::PropertyMap *propmap = label->GetMap(); for (auto iter = propmap->begin(); iter != propmap->end(); ++iter) { std::string key = iter->first; const BaseProperty *property = iter->second; TiXmlElement *element = PropertyToXmlElem(key, property); if (element) labelElem->LinkEndChild(element); } return labelElem; } mitk::Label::Pointer mitk::LabelSetIOHelper::LoadLabelFromTiXmlDocument(TiXmlElement *labelElem) { // reread TiXmlElement *propElem = labelElem->FirstChildElement("property"); std::string name; mitk::BaseProperty::Pointer prop; mitk::Label::Pointer label = mitk::Label::New(); while (propElem) { LabelSetIOHelper::PropertyFromXmlElem(name, prop, propElem); label->SetProperty(name, prop); propElem = propElem->NextSiblingElement("property"); } return label.GetPointer(); } TiXmlElement *mitk::LabelSetIOHelper::PropertyToXmlElem(const std::string &key, const BaseProperty *property) { auto *keyelement = new TiXmlElement("property"); keyelement->SetAttribute("key", key); keyelement->SetAttribute("type", property->GetNameOfClass()); // construct name of serializer class std::string serializername(property->GetNameOfClass()); serializername += "Serializer"; std::list allSerializers = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); if (allSerializers.size() < 1) MITK_ERROR << "No serializer found for " << property->GetNameOfClass() << ". Skipping object"; if (allSerializers.size() > 1) MITK_WARN << "Multiple serializers found for " << property->GetNameOfClass() << "Using arbitrarily the first one."; for (auto iter = allSerializers.begin(); iter != allSerializers.end(); ++iter) { if (auto *serializer = dynamic_cast(iter->GetPointer())) { serializer->SetProperty(property); try { TiXmlElement *valueelement = serializer->Serialize(); if (valueelement) keyelement->LinkEndChild(valueelement); } catch (std::exception &e) { MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what(); } break; } } return keyelement; } bool mitk::LabelSetIOHelper::PropertyFromXmlElem(std::string &key, mitk::BaseProperty::Pointer &prop, TiXmlElement *elem) { std::string type; elem->QueryStringAttribute("type", &type); elem->QueryStringAttribute("key", &key); // construct name of serializer class std::string serializername(type); serializername += "Serializer"; std::list allSerializers = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); if (allSerializers.size() < 1) MITK_ERROR << "No serializer found for " << type << ". Skipping object"; if (allSerializers.size() > 1) MITK_WARN << "Multiple deserializers found for " << type << "Using arbitrarily the first one."; for (auto iter = allSerializers.begin(); iter != allSerializers.end(); ++iter) { if (auto *serializer = dynamic_cast(iter->GetPointer())) { try { prop = serializer->Deserialize(elem->FirstChildElement()); } catch (std::exception &e) { MITK_ERROR << "Deserializer " << serializer->GetNameOfClass() << " failed: " << e.what(); return false; } break; } } if (prop.IsNull()) return false; return true; } diff --git a/Modules/Multilabel/mitkLabelSetIOHelper.h b/Modules/Multilabel/mitkLabelSetIOHelper.h index ff763d61fd..3800fda33a 100644 --- a/Modules/Multilabel/mitkLabelSetIOHelper.h +++ b/Modules/Multilabel/mitkLabelSetIOHelper.h @@ -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. ============================================================================*/ #ifndef __mitkLabelSetIOHelper_h #define __mitkLabelSetIOHelper_h #include #include class TiXmlElement; namespace mitk { class BaseProperty; class LabelSetImage; class Label; /** * @brief The LabelSetIOHelper 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 LabelSetIOHelper { 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(std::string &presetFilename, + static bool SaveLabelSetImagePreset(const std::string &presetFilename, itk::SmartPointer &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 */ - static void LoadLabelSetImagePreset(std::string &presetFilename, + static void LoadLabelSetImagePreset(const std::string &presetFilename, itk::SmartPointer &inputImage); /** * @brief Creates a mitk::Label from a TiXmlElement * @param labelElem the xml element from which a mitk::Label will be created * @return the created mitk::Label */ static itk::SmartPointer LoadLabelFromTiXmlDocument(TiXmlElement *labelElem); /** * @brief Creates a TiXmlElement from a mitk::Label * @param label the mitk::Label from which the xml element will be created * @return the created TiXmlElement */ static TiXmlElement *GetLabelAsTiXmlElement(Label *label); /** * @brief Since a mitk::Label is basically a mitk::PropertyList this function coverts the label's properties into * XML * @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 TiXmlElement */ static TiXmlElement *PropertyToXmlElem(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 * @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 PropertyFromXmlElem(std::string &key, itk::SmartPointer &prop, TiXmlElement *elem); private: LabelSetIOHelper(); }; } #endif // __mitkLabelSetIOHelper_h diff --git a/Modules/Pharmacokinetics/include/mitkConvertT2ConcentrationFunctor.h b/Modules/Pharmacokinetics/include/mitkConvertT2ConcentrationFunctor.h index 5cd540cb94..c3ac6b66ac 100644 --- a/Modules/Pharmacokinetics/include/mitkConvertT2ConcentrationFunctor.h +++ b/Modules/Pharmacokinetics/include/mitkConvertT2ConcentrationFunctor.h @@ -1,62 +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 MITKCONVERTT2CONCENTRATIONFUNCTOR #define MITKCONVERTT2CONCENTRATIONFUNCTOR #include "MitkPharmacokineticsExports.h" namespace mitk { template class MITKPHARMACOKINETICS_EXPORT ConvertT2ConcentrationFunctor { public: ConvertT2ConcentrationFunctor(): m_k(0.0), m_TE(0.0) {} ; ~ConvertT2ConcentrationFunctor() {}; void initialize(double factor, double TE) { this->m_k = factor; this->m_TE = TE; } bool operator!=( const ConvertT2ConcentrationFunctor & other)const { return !(*this == other); } bool operator==( const ConvertT2ConcentrationFunctor & other) const { return (this->m_k == other.m_k && this->m_TE == other.m_TE); } inline TOutputpixel operator()( const TInputPixel1 & value, const TInputPixel2 & baseline) { - TOutputpixel concentration(0); + double concentration = 0.0; if(value !=0 && baseline != 0) { - concentration = this->m_k / this->m_TE * log((double)(value- baseline)) ; + concentration = (-1.) * (this->m_k / this->m_TE) * log(static_cast(value) / baseline); + } - return concentration; + return static_cast(concentration); } private: double m_k; double m_TE; }; } #endif // MITKCONVERTT2CONCENTRATIONFUNCTOR diff --git a/Modules/PlanarFigure/src/DataManagement/mitkPlanarEllipse.cpp b/Modules/PlanarFigure/src/DataManagement/mitkPlanarEllipse.cpp index 6627a6c865..4c24cce19d 100644 --- a/Modules/PlanarFigure/src/DataManagement/mitkPlanarEllipse.cpp +++ b/Modules/PlanarFigure/src/DataManagement/mitkPlanarEllipse.cpp @@ -1,304 +1,304 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkPlanarEllipse.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" #include mitk::PlanarEllipse::PlanarEllipse() : FEATURE_ID_MAJOR_AXIS(Superclass::AddFeature("Major Axis", "mm")), FEATURE_ID_MINOR_AXIS(Superclass::AddFeature("Minor Axis", "mm")), FEATURE_ID_AREA(Superclass::AddFeature("Area", "mm2")), m_MinRadius(0), m_MaxRadius(100), m_MinMaxRadiusContraintsActive(false), m_TreatAsCircle(true) { // Ellipse has three control points this->ResetNumberOfControlPoints(4); this->SetNumberOfPolyLines(2); this->SetProperty("closed", mitk::BoolProperty::New(true)); } bool mitk::PlanarEllipse::SetControlPoint(unsigned int index, const Point2D &point, bool createIfDoesNotExist) { if (index == 0) // moving center point and control points accordingly { const Point2D ¢erPoint = GetControlPoint(0); Point2D boundaryPoint1 = GetControlPoint(1); Point2D boundaryPoint2 = GetControlPoint(2); Point2D boundaryPoint3 = GetControlPoint(3); const vnl_vector vec = (point.GetVnlVector() - centerPoint.GetVnlVector()); boundaryPoint1[0] += vec[0]; boundaryPoint1[1] += vec[1]; boundaryPoint2[0] += vec[0]; boundaryPoint2[1] += vec[1]; boundaryPoint3[0] += vec[0]; boundaryPoint3[1] += vec[1]; PlanarFigure::SetControlPoint(0, point, createIfDoesNotExist); PlanarFigure::SetControlPoint(1, boundaryPoint1, createIfDoesNotExist); PlanarFigure::SetControlPoint(2, boundaryPoint2, createIfDoesNotExist); PlanarFigure::SetControlPoint(3, boundaryPoint3, createIfDoesNotExist); return true; } else if (index < 3) { PlanarFigure::SetControlPoint(index, point, createIfDoesNotExist); int otherIndex = index + 1; if (otherIndex > 2) otherIndex = 1; - const Point2D ¢erPoint = GetControlPoint(0); + const Point2D centerPoint = GetControlPoint(0); Point2D otherPoint = GetControlPoint(otherIndex); Point2D point3 = GetControlPoint(3); const Vector2D vec1 = point - centerPoint; Vector2D vec2; if (index == 1 && m_TreatAsCircle) { const float x = vec1[0]; vec2[0] = vec1[1]; vec2[1] = x; if (index == 1) vec2[0] *= -1; else vec2[1] *= -1; otherPoint = centerPoint + vec2; PlanarFigure::SetControlPoint(otherIndex, otherPoint, createIfDoesNotExist); const float r = centerPoint.EuclideanDistanceTo(otherPoint); // adjust additional third control point const Point2D p3 = this->GetControlPoint(3); Vector2D vec3; vec3[0] = p3[0] - centerPoint[0]; vec3[1] = p3[1] - centerPoint[1]; if (vec3[0] != 0 || vec3[1] != 0) { vec3.Normalize(); vec3 *= r; } else { vec3[0] = r; vec3[1] = 0; } point3 = centerPoint + vec3; PlanarFigure::SetControlPoint(3, point3, createIfDoesNotExist); } else if (vec1.GetNorm() > 0) { const float r = centerPoint.EuclideanDistanceTo(otherPoint); const float x = vec1[0]; vec2[0] = vec1[1]; vec2[1] = x; if (index == 1) vec2[0] *= -1; else vec2[1] *= -1; vec2.Normalize(); vec2 *= r; if (vec2.GetNorm() > 0) { otherPoint = centerPoint + vec2; PlanarFigure::SetControlPoint(otherIndex, otherPoint, createIfDoesNotExist); } // adjust third control point Vector2D vec3 = point3 - centerPoint; vec3.Normalize(); const double r1 = centerPoint.EuclideanDistanceTo(GetControlPoint(1)); const double r2 = centerPoint.EuclideanDistanceTo(GetControlPoint(2)); const Point2D newPoint = centerPoint + vec3 * std::max(r1, r2); PlanarFigure::SetControlPoint(3, newPoint, createIfDoesNotExist); m_TreatAsCircle = false; } return true; } else if (index == 3) { const Point2D centerPoint = GetControlPoint(0); Vector2D vec3 = point - centerPoint; vec3.Normalize(); const double r1 = centerPoint.EuclideanDistanceTo(GetControlPoint(1)); const double r2 = centerPoint.EuclideanDistanceTo(GetControlPoint(2)); const Point2D newPoint = centerPoint + vec3 * std::max(r1, r2); PlanarFigure::SetControlPoint(index, newPoint, createIfDoesNotExist); m_TreatAsCircle = false; return true; } return false; } void mitk::PlanarEllipse::PlaceFigure(const mitk::Point2D &point) { PlanarFigure::PlaceFigure(point); m_SelectedControlPoint = 1; } mitk::Point2D mitk::PlanarEllipse::ApplyControlPointConstraints(unsigned int index, const Point2D &point) { return point; Point2D indexPoint; this->GetPlaneGeometry()->WorldToIndex(point, indexPoint); BoundingBox::BoundsArrayType bounds = this->GetPlaneGeometry()->GetBounds(); if (indexPoint[0] < bounds[0]) { indexPoint[0] = bounds[0]; } if (indexPoint[0] > bounds[1]) { indexPoint[0] = bounds[1]; } if (indexPoint[1] < bounds[2]) { indexPoint[1] = bounds[2]; } if (indexPoint[1] > bounds[3]) { indexPoint[1] = bounds[3]; } Point2D constrainedPoint; this->GetPlaneGeometry()->IndexToWorld(indexPoint, constrainedPoint); if (m_MinMaxRadiusContraintsActive) { if (index != 0) { const Point2D ¢erPoint = this->GetControlPoint(0); const double euclideanDinstanceFromCenterToPoint1 = centerPoint.EuclideanDistanceTo(point); Vector2D vectorProjectedPoint = point - centerPoint; vectorProjectedPoint.Normalize(); if (euclideanDinstanceFromCenterToPoint1 > m_MaxRadius) { vectorProjectedPoint *= m_MaxRadius; constrainedPoint = centerPoint; constrainedPoint += vectorProjectedPoint; } else if (euclideanDinstanceFromCenterToPoint1 < m_MinRadius) { vectorProjectedPoint *= m_MinRadius; constrainedPoint = centerPoint; constrainedPoint += vectorProjectedPoint; } } } return constrainedPoint; } void mitk::PlanarEllipse::GeneratePolyLine() { // clear the PolyLine-Contrainer, it will be reconstructed soon enough... this->ClearPolyLines(); const Point2D ¢erPoint = GetControlPoint(0); const Point2D &boundaryPoint1 = GetControlPoint(1); const Point2D &boundaryPoint2 = GetControlPoint(2); Vector2D dir = boundaryPoint1 - centerPoint; dir.Normalize(); vnl_matrix_fixed rot; // differentiate between clockwise and counterclockwise rotation int start = 0; int end = 64; if (dir[1] < 0) { dir[0] = -dir[0]; start = -32; end = 32; } // construct rotation matrix to align ellipse with control point vector rot[0][0] = dir[0]; rot[1][1] = rot[0][0]; rot[1][0] = sin(acos(rot[0][0])); rot[0][1] = -rot[1][0]; const double radius1 = centerPoint.EuclideanDistanceTo(boundaryPoint1); const double radius2 = centerPoint.EuclideanDistanceTo(boundaryPoint2); // Generate poly-line with 64 segments for (int t = start; t < end; ++t) { const double alpha = (double)t * vnl_math::pi / 32.0; // construct the new polyline point ... vnl_vector_fixed vec; vec[0] = radius1 * cos(alpha); vec[1] = radius2 * sin(alpha); vec = rot * vec; Point2D polyLinePoint; polyLinePoint[0] = centerPoint[0] + vec[0]; polyLinePoint[1] = centerPoint[1] + vec[1]; // ... and append it to the PolyLine. // No extending supported here, so we can set the index of the PolyLineElement to '0' this->AppendPointToPolyLine(0, polyLinePoint); } this->AppendPointToPolyLine(1, centerPoint); this->AppendPointToPolyLine(1, this->GetControlPoint(3)); } void mitk::PlanarEllipse::GenerateHelperPolyLine(double /*mmPerDisplayUnit*/, unsigned int /*displayHeight*/) { // A circle does not require a helper object } void mitk::PlanarEllipse::EvaluateFeaturesInternal() { const Point2D centerPoint = this->GetControlPoint(0); const auto longAxisLength = centerPoint.EuclideanDistanceTo(this->GetControlPoint(1)); const auto shortAxisLength = centerPoint.EuclideanDistanceTo(this->GetControlPoint(2)); this->SetQuantity(FEATURE_ID_MAJOR_AXIS, 2 * longAxisLength); this->SetQuantity(FEATURE_ID_MINOR_AXIS, 2 * shortAxisLength); this->SetQuantity(FEATURE_ID_AREA, longAxisLength * shortAxisLength * itk::Math::pi); } void mitk::PlanarEllipse::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); } bool mitk::PlanarEllipse::Equals(const mitk::PlanarFigure &other) const { const auto *otherEllipse = dynamic_cast(&other); if (otherEllipse) { if (this->m_TreatAsCircle != otherEllipse->m_TreatAsCircle) return false; return Superclass::Equals(other); } else { return false; } } diff --git a/Modules/RT/include/mitkDICOMRTMimeTypes.h b/Modules/RT/include/mitkDICOMRTMimeTypes.h index 37522ba7bf..08ec13a39a 100644 --- a/Modules/RT/include/mitkDICOMRTMimeTypes.h +++ b/Modules/RT/include/mitkDICOMRTMimeTypes.h @@ -1,79 +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 MITKDICOMRTMimeTypes_H #define MITKDICOMRTMimeTypes_H #include -#include #include #include #include #include namespace mitk { class MITKRT_EXPORT DICOMRTMimeTypes { public: class MITKRT_EXPORT RTDoseMimeType : public CustomMimeType { public: RTDoseMimeType(); bool AppliesTo(const std::string &path) const override; RTDoseMimeType* Clone() const override; }; class MITKRT_EXPORT RTStructMimeType : public CustomMimeType { public: RTStructMimeType(); bool AppliesTo(const std::string &path) const override; RTStructMimeType* Clone() const override; }; class MITKRT_EXPORT RTPlanMimeType : public CustomMimeType { public: RTPlanMimeType(); bool AppliesTo(const std::string &path) const override; RTPlanMimeType* Clone() const override; }; // Get all DicomRT Mime Types static std::array, 3> Get(); static RTDoseMimeType DICOMRT_DOSE_MIMETYPE(); static RTStructMimeType DICOMRT_STRUCT_MIMETYPE(); static RTPlanMimeType DICOMRT_PLAN_MIMETYPE(); static std::string DICOMRT_DOSE_MIMETYPE_NAME(); static std::string DICOMRT_STRUCT_MIMETYPE_NAME(); static std::string DICOMRT_PLAN_MIMETYPE_NAME(); static std::string DICOMRT_DOSE_MIMETYPE_DESCRIPTION(); static std::string DICOMRT_STRUCT_MIMETYPE_DESCRIPTION(); static std::string DICOMRT_PLAN_MIMETYPE_DESCRIPTION(); DICOMRTMimeTypes() = delete; DICOMRTMimeTypes(const DICOMRTMimeTypes&) = delete; - static mitk::IDICOMTagsOfInterest* GetDicomTagsOfInterestService(); static bool canReadByDicomFileReader(const std::string & path); static std::string GetModality(const std::string & path); }; } #endif // MITKDICOMRTMimeTypes_H diff --git a/Modules/RT/src/mitkDICOMRTMimeTypes.cpp b/Modules/RT/src/mitkDICOMRTMimeTypes.cpp index bbb3cc0260..d72e25afa4 100644 --- a/Modules/RT/src/mitkDICOMRTMimeTypes.cpp +++ b/Modules/RT/src/mitkDICOMRTMimeTypes.cpp @@ -1,252 +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 #include #include #include #include #include #include -#include -#include -#include -#include + namespace mitk { std::array, 3> DICOMRTMimeTypes::Get() { return { std::make_unique(), std::make_unique(), std::make_unique() }; } // 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) { + if (!canRead) + { return false; } - if (!canReadByDicomFileReader(path)) { - 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. + if (!itksys::SystemTools::FileExists(path.c_str())) + { + return canRead; } + // end fix for bug 18572 auto modality = GetModality(path); - - if (modality == "RTDOSE") { - return true; + if (modality == "RTDOSE") + { + return canReadByDicomFileReader(path); } - else { - return false; - } + + return false; } std::string DICOMRTMimeTypes::GetModality(const std::string & path) { - mitk::IDICOMTagsOfInterest* toiSrv = GetDicomTagsOfInterestService(); - - auto tagsOfInterest = toiSrv->GetTagsOfInterest(); - - DICOMTagPathList tagsOfInterestList; - for (const auto& tag : tagsOfInterest) { - tagsOfInterestList.push_back(tag.first); - } + const auto modalityTagPath = DICOMTagPath(0x0008, 0x0060); mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); scanner->SetInputFiles({ path }); - scanner->AddTagPaths(tagsOfInterestList); + scanner->AddTagPaths({ modalityTagPath }); scanner->Scan(); mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); std::string modality = ""; if (frames.empty()) return modality; - auto findings = frames.front()->GetTagValueAsString(DICOMTagPath(0x0008, 0x0060)); + 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(); - if (reader.IsNull()) { - return false; - } - else { - return true; - } + 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. + if (!itksys::SystemTools::FileExists(path.c_str())) + { + return canRead; + } + // end fix for bug 18572 + auto modality = GetModality(path); if (modality == "RTSTRUCT") { return true; } - else { - return false; - } + + 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. + if (!itksys::SystemTools::FileExists(path.c_str())) + { + return canRead; + } + // end fix for bug 18572 + auto modality = GetModality(path); - if (modality == "RTPLAN") { - return true; - } - else { - return false; - } + 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"; } -mitk::IDICOMTagsOfInterest* DICOMRTMimeTypes::GetDicomTagsOfInterestService() -{ - mitk::IDICOMTagsOfInterest* result = nullptr; - - std::vector > toiRegisters = us::GetModuleContext()->GetServiceReferences(); - if (!toiRegisters.empty()) - { - if (toiRegisters.size() > 1) - { - MITK_WARN << "Multiple DICOM tags of interest services found. Using just one."; - } - result = us::GetModuleContext()->GetService(toiRegisters.front()); - } - - return result; -} - } diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp b/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp index 9396da2a9f..b4220d6acb 100644 --- a/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp @@ -1,480 +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 "mitkFastMarchingTool.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkInteractionConst.h" #include "mitkRenderingManager.h" #include "itkOrImageFilter.h" #include "mitkImageTimeSelector.h" // us #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, FastMarchingTool, "FastMarching2D tool"); } mitk::FastMarchingTool::FastMarchingTool() : FeedbackContourTool("FastMarchingTool"), m_NeedUpdate(true), m_CurrentTimeStep(0), m_PositionEvent(nullptr), m_LowerThreshold(0), m_UpperThreshold(200), m_StoppingValue(100), m_Sigma(1.0), m_Alpha(-0.5), m_Beta(3.0) { } mitk::FastMarchingTool::~FastMarchingTool() { if (this->m_SmoothFilter.IsNotNull()) this->m_SmoothFilter->RemoveAllObservers(); if (this->m_SigmoidFilter.IsNotNull()) this->m_SigmoidFilter->RemoveAllObservers(); if (this->m_GradientMagnitudeFilter.IsNotNull()) this->m_GradientMagnitudeFilter->RemoveAllObservers(); if (this->m_FastMarchingFilter.IsNotNull()) this->m_FastMarchingFilter->RemoveAllObservers(); } void mitk::FastMarchingTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("ShiftSecondaryButtonPressed", OnAddPoint); CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnAddPoint); CONNECT_FUNCTION("DeletePoint", OnDelete); } const char **mitk::FastMarchingTool::GetXPM() const { return nullptr; // mitkFastMarchingTool_xpm; } us::ModuleResource mitk::FastMarchingTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("FastMarching_48x48.png"); return resource; } us::ModuleResource mitk::FastMarchingTool::GetCursorIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("FastMarching_Cursor_32x32.png"); return resource; } const char *mitk::FastMarchingTool::GetName() const { return "2D Fast Marching"; } void mitk::FastMarchingTool::BuildITKPipeline() { m_ReferenceImageSliceAsITK = InternalImageType::New(); m_ReferenceImageSlice = GetAffectedReferenceSlice(m_PositionEvent); CastToItkImage(m_ReferenceImageSlice, m_ReferenceImageSliceAsITK); m_ProgressCommand = mitk::ToolCommand::New(); m_SmoothFilter = SmoothingFilterType::New(); m_SmoothFilter->SetInput(m_ReferenceImageSliceAsITK); m_SmoothFilter->SetTimeStep(0.05); m_SmoothFilter->SetNumberOfIterations(2); m_SmoothFilter->SetConductanceParameter(9.0); m_GradientMagnitudeFilter = GradientFilterType::New(); m_GradientMagnitudeFilter->SetSigma(m_Sigma); m_SigmoidFilter = SigmoidFilterType::New(); m_SigmoidFilter->SetAlpha(m_Alpha); m_SigmoidFilter->SetBeta(m_Beta); m_SigmoidFilter->SetOutputMinimum(0.0); m_SigmoidFilter->SetOutputMaximum(1.0); m_FastMarchingFilter = FastMarchingFilterType::New(); m_FastMarchingFilter->SetStoppingValue(m_StoppingValue); m_ThresholdFilter = ThresholdingFilterType::New(); m_ThresholdFilter->SetLowerThreshold(m_LowerThreshold); m_ThresholdFilter->SetUpperThreshold(m_UpperThreshold); m_ThresholdFilter->SetOutsideValue(0); m_ThresholdFilter->SetInsideValue(1.0); m_SeedContainer = NodeContainer::New(); m_SeedContainer->Initialize(); m_FastMarchingFilter->SetTrialPoints(m_SeedContainer); if (this->m_SmoothFilter.IsNotNull()) this->m_SmoothFilter->RemoveAllObservers(); if (this->m_SigmoidFilter.IsNotNull()) this->m_SigmoidFilter->RemoveAllObservers(); if (this->m_GradientMagnitudeFilter.IsNotNull()) this->m_GradientMagnitudeFilter->RemoveAllObservers(); if (this->m_FastMarchingFilter.IsNotNull()) this->m_FastMarchingFilter->RemoveAllObservers(); m_SmoothFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_GradientMagnitudeFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_SigmoidFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_FastMarchingFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_SmoothFilter->SetInput(m_ReferenceImageSliceAsITK); m_GradientMagnitudeFilter->SetInput(m_SmoothFilter->GetOutput()); m_SigmoidFilter->SetInput(m_GradientMagnitudeFilter->GetOutput()); m_FastMarchingFilter->SetInput(m_SigmoidFilter->GetOutput()); m_ThresholdFilter->SetInput(m_FastMarchingFilter->GetOutput()); m_ReferenceImageSliceAsITK = InternalImageType::New(); } void mitk::FastMarchingTool::SetUpperThreshold(double value) { if (m_UpperThreshold != value) { m_UpperThreshold = value / 10.0; m_ThresholdFilter->SetUpperThreshold(m_UpperThreshold); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetLowerThreshold(double value) { if (m_LowerThreshold != value) { m_LowerThreshold = value / 10.0; m_ThresholdFilter->SetLowerThreshold(m_LowerThreshold); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetBeta(double value) { if (m_Beta != value) { m_Beta = value; m_SigmoidFilter->SetBeta(m_Beta); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetSigma(double value) { if (m_Sigma != value) { m_Sigma = value; m_GradientMagnitudeFilter->SetSigma(m_Sigma); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetAlpha(double value) { if (m_Alpha != value) { m_Alpha = value; m_SigmoidFilter->SetAlpha(m_Alpha); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetStoppingValue(double value) { if (m_StoppingValue != value) { m_StoppingValue = value; m_FastMarchingFilter->SetStoppingValue(m_StoppingValue); m_NeedUpdate = true; } } void mitk::FastMarchingTool::Activated() { Superclass::Activated(); m_ResultImageNode = mitk::DataNode::New(); m_ResultImageNode->SetName("FastMarching_Preview"); m_ResultImageNode->SetBoolProperty("helper object", true); m_ResultImageNode->SetColor(0.0, 1.0, 0.0); m_ResultImageNode->SetVisibility(true); m_ToolManager->GetDataStorage()->Add(this->m_ResultImageNode, m_ToolManager->GetReferenceData(0)); m_SeedsAsPointSet = mitk::PointSet::New(); m_SeedsAsPointSetNode = mitk::DataNode::New(); m_SeedsAsPointSetNode->SetData(m_SeedsAsPointSet); m_SeedsAsPointSetNode->SetName("Seeds_Preview"); m_SeedsAsPointSetNode->SetBoolProperty("helper object", true); m_SeedsAsPointSetNode->SetColor(0.0, 1.0, 0.0); m_SeedsAsPointSetNode->SetVisibility(true); m_ToolManager->GetDataStorage()->Add(this->m_SeedsAsPointSetNode, m_ToolManager->GetReferenceData(0)); this->Initialize(); } void mitk::FastMarchingTool::Deactivated() { m_ToolManager->GetDataStorage()->Remove(this->m_ResultImageNode); m_ToolManager->GetDataStorage()->Remove(this->m_SeedsAsPointSetNode); this->ClearSeeds(); m_ResultImageNode = nullptr; m_SeedsAsPointSetNode = nullptr; mitk::RenderingManager::GetInstance()->RequestUpdateAll(); Superclass::Deactivated(); } void mitk::FastMarchingTool::Initialize() { m_ReferenceImage = dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData()); if (m_ReferenceImage->GetTimeGeometry()->CountTimeSteps() > 1) { mitk::ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(m_ReferenceImage); timeSelector->SetTimeNr(m_CurrentTimeStep); timeSelector->UpdateLargestPossibleRegion(); m_ReferenceImage = timeSelector->GetOutput(); } m_NeedUpdate = true; } void mitk::FastMarchingTool::ConfirmSegmentation() { // combine preview image with current working segmentation if (dynamic_cast(m_ResultImageNode->GetData())) { // logical or combination of preview and segmentation slice - mitk::Image::Pointer workingImageSlice; - mitk::Image::Pointer workingImage = dynamic_cast(this->m_ToolManager->GetWorkingData(0)->GetData()); - workingImageSlice = GetAffectedImageSliceAs2DImage(m_WorkingPlane, workingImage, m_CurrentTimeStep); + Image::Pointer workingImageSlice; - mitk::Image::Pointer segmentationResult = mitk::Image::New(); + Image::Pointer workingImage = dynamic_cast(this->m_ToolManager->GetWorkingData(0)->GetData()); + TimePointType referenceImageTimePoint = m_ReferenceImage->GetTimeGeometry()->TimeStepToTimePoint(m_CurrentTimeStep); + TimeStepType workingImageTimeStep = workingImage->GetTimeGeometry()->TimePointToTimeStep(referenceImageTimePoint); + workingImageSlice = GetAffectedImageSliceAs2DImage(m_WorkingPlane, workingImage, workingImageTimeStep); + + Image::Pointer segmentationResult = Image::New(); bool isDeprecatedUnsignedCharSegmentation = (workingImage->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR); if (isDeprecatedUnsignedCharSegmentation) { typedef itk::Image OutputUCharImageType; OutputUCharImageType::Pointer workingImageSliceInITK = OutputUCharImageType::New(); CastToItkImage(workingImageSlice, workingImageSliceInITK); typedef itk::OrImageFilter OrImageFilterType; OrImageFilterType::Pointer orFilter = OrImageFilterType::New(); orFilter->SetInput1(m_ThresholdFilter->GetOutput()); orFilter->SetInput2(workingImageSliceInITK); orFilter->Update(); mitk::CastToMitkImage(orFilter->GetOutput(), segmentationResult); } else { OutputImageType::Pointer workingImageSliceInITK = OutputImageType::New(); CastToItkImage(workingImageSlice, workingImageSliceInITK); typedef itk::OrImageFilter OrImageFilterType; OrImageFilterType::Pointer orFilter = OrImageFilterType::New(); orFilter->SetInput(0, m_ThresholdFilter->GetOutput()); orFilter->SetInput(1, workingImageSliceInITK); orFilter->Update(); mitk::CastToMitkImage(orFilter->GetOutput(), segmentationResult); } segmentationResult->GetGeometry()->SetOrigin(workingImageSlice->GetGeometry()->GetOrigin()); segmentationResult->GetGeometry()->SetIndexToWorldTransform( workingImageSlice->GetGeometry()->GetIndexToWorldTransform()); // write to segmentation volume and hide preview image // again, current time step is not considered - this->WriteBackSegmentationResult(m_WorkingPlane, segmentationResult, m_CurrentTimeStep); + this->WriteBackSegmentationResult(m_WorkingPlane, segmentationResult, workingImageTimeStep); this->m_ResultImageNode->SetVisibility(false); this->ClearSeeds(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_ToolManager->ActivateTool(-1); } void mitk::FastMarchingTool::OnAddPoint(StateMachineAction *, InteractionEvent *interactionEvent) { // Add a new seed point for FastMarching algorithm auto *positionEvent = dynamic_cast(interactionEvent); // const PositionEvent* p = dynamic_cast(stateEvent->GetEvent()); if (positionEvent == nullptr) return; if (m_PositionEvent.IsNotNull()) m_PositionEvent = nullptr; m_PositionEvent = InteractionPositionEvent::New(positionEvent->GetSender(), positionEvent->GetPointerPositionOnScreen()); // if click was on another renderwindow or slice then reset pipeline and preview if ((m_LastEventSender != m_PositionEvent->GetSender()) || (m_LastEventSlice != m_PositionEvent->GetSender()->GetSlice())) { this->BuildITKPipeline(); this->ClearSeeds(); } m_LastEventSender = m_PositionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); m_WorkingPlane = positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone(); mitk::Point3D clickInIndex; m_ReferenceImageSlice->GetGeometry()->WorldToIndex(m_PositionEvent->GetPositionInWorld(), clickInIndex); itk::Index<2> seedPosition; seedPosition[0] = clickInIndex[0]; seedPosition[1] = clickInIndex[1]; NodeType node; const double seedValue = 0.0; node.SetValue(seedValue); node.SetIndex(seedPosition); this->m_SeedContainer->InsertElement(this->m_SeedContainer->Size(), node); m_FastMarchingFilter->Modified(); m_SeedsAsPointSet->InsertPoint(m_SeedsAsPointSet->GetSize(), m_PositionEvent->GetPositionInWorld()); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_NeedUpdate = true; this->Update(); m_ReadyMessage.Send(); } void mitk::FastMarchingTool::OnDelete(StateMachineAction *, InteractionEvent *) { // delete last seed point if (!(this->m_SeedContainer->empty())) { // delete last element of seeds container this->m_SeedContainer->pop_back(); m_FastMarchingFilter->Modified(); // delete last point in pointset - somehow ugly m_SeedsAsPointSet->GetPointSet()->GetPoints()->DeleteIndex(m_SeedsAsPointSet->GetSize() - 1); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_NeedUpdate = true; this->Update(); } } void mitk::FastMarchingTool::Update() { const unsigned int progress_steps = 20; // update FastMarching pipeline and show result if (m_NeedUpdate) { m_ProgressCommand->AddStepsToDo(progress_steps); CurrentlyBusy.Send(true); try { m_ThresholdFilter->Update(); } catch (itk::ExceptionObject &excep) { MITK_ERROR << "Exception caught: " << excep.GetDescription(); // progress by max step count, will force m_ProgressCommand->SetProgress(progress_steps); CurrentlyBusy.Send(false); std::string msg = excep.GetDescription(); ErrorMessage.Send(msg); return; } m_ProgressCommand->SetProgress(progress_steps); CurrentlyBusy.Send(false); // make output visible mitk::Image::Pointer result = mitk::Image::New(); CastToMitkImage(m_ThresholdFilter->GetOutput(), result); result->GetGeometry()->SetOrigin(m_ReferenceImageSlice->GetGeometry()->GetOrigin()); result->GetGeometry()->SetIndexToWorldTransform(m_ReferenceImageSlice->GetGeometry()->GetIndexToWorldTransform()); m_ResultImageNode->SetData(result); m_ResultImageNode->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void mitk::FastMarchingTool::ClearSeeds() { // clear seeds for FastMarching as well as the PointSet for visualization if (this->m_SeedContainer.IsNotNull()) this->m_SeedContainer->Initialize(); if (this->m_SeedsAsPointSet.IsNotNull()) { this->m_SeedsAsPointSet = mitk::PointSet::New(); this->m_SeedsAsPointSetNode->SetData(this->m_SeedsAsPointSet); m_SeedsAsPointSetNode->SetName("Seeds_Preview"); m_SeedsAsPointSetNode->SetBoolProperty("helper object", true); m_SeedsAsPointSetNode->SetColor(0.0, 1.0, 0.0); m_SeedsAsPointSetNode->SetVisibility(true); } if (this->m_FastMarchingFilter.IsNotNull()) m_FastMarchingFilter->Modified(); this->m_NeedUpdate = true; } void mitk::FastMarchingTool::Reset() { // clear all seeds and preview empty result this->ClearSeeds(); m_ResultImageNode->SetVisibility(false); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::FastMarchingTool::SetCurrentTimeStep(int t) { if (m_CurrentTimeStep != t) { m_CurrentTimeStep = t; this->Initialize(); } } diff --git a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp index a787010bb7..4dca280975 100644 --- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp @@ -1,586 +1,591 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, LiveWireTool2D, "LiveWire tool"); } mitk::LiveWireTool2D::LiveWireTool2D() : SegTool2D("LiveWireTool"), m_CreateAndUseDynamicCosts(false) { } mitk::LiveWireTool2D::~LiveWireTool2D() { this->ClearSegmentation(); } void mitk::LiveWireTool2D::RemoveHelperObjects() { auto dataStorage = m_ToolManager->GetDataStorage(); if (nullptr == dataStorage) return; for (const auto &editingContour : m_EditingContours) dataStorage->Remove(editingContour.first); for (const auto &workingContour : m_WorkingContours) dataStorage->Remove(workingContour.first); if (m_EditingContourNode.IsNotNull()) dataStorage->Remove(m_EditingContourNode); if (m_LiveWireContourNode.IsNotNull()) dataStorage->Remove(m_LiveWireContourNode); if (m_ContourNode.IsNotNull()) dataStorage->Remove(m_ContourNode); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::LiveWireTool2D::ReleaseHelperObjects() { this->RemoveHelperObjects(); m_EditingContours.clear(); m_WorkingContours.clear(); m_EditingContourNode = nullptr; m_EditingContour = nullptr; m_LiveWireContourNode = nullptr; m_LiveWireContour = nullptr; m_ContourNode = nullptr; m_Contour = nullptr; } void mitk::LiveWireTool2D::ReleaseInteractors() { this->EnableContourLiveWireInteraction(false); m_LiveWireInteractors.clear(); } void mitk::LiveWireTool2D::ConnectActionsAndFunctions() { CONNECT_CONDITION("CheckContourClosed", OnCheckPoint); CONNECT_FUNCTION("InitObject", OnInitLiveWire); CONNECT_FUNCTION("AddPoint", OnAddPoint); CONNECT_FUNCTION("CtrlAddPoint", OnAddPoint); CONNECT_FUNCTION("MovePoint", OnMouseMoveNoDynamicCosts); CONNECT_FUNCTION("FinishContour", OnFinish); CONNECT_FUNCTION("DeletePoint", OnLastSegmentDelete); CONNECT_FUNCTION("CtrlMovePoint", OnMouseMoved); } const char **mitk::LiveWireTool2D::GetXPM() const { return mitkLiveWireTool2D_xpm; } us::ModuleResource mitk::LiveWireTool2D::GetIconResource() const { return us::GetModuleContext()->GetModule()->GetResource("LiveWire_48x48.png"); } us::ModuleResource mitk::LiveWireTool2D::GetCursorIconResource() const { return us::GetModuleContext()->GetModule()->GetResource("LiveWire_Cursor_32x32.png"); } const char *mitk::LiveWireTool2D::GetName() const { return "Live Wire"; } void mitk::LiveWireTool2D::Activated() { Superclass::Activated(); this->ResetToStartState(); this->EnableContourLiveWireInteraction(true); } void mitk::LiveWireTool2D::Deactivated() { this->ConfirmSegmentation(); Superclass::Deactivated(); } void mitk::LiveWireTool2D::EnableContourLiveWireInteraction(bool on) { for (const auto &interactor : m_LiveWireInteractors) interactor->EnableInteraction(on); } void mitk::LiveWireTool2D::ConfirmSegmentation() { + auto referenceNode = m_ToolManager->GetReferenceData(0); auto workingNode = m_ToolManager->GetWorkingData(0); - if (nullptr == workingNode) + if (nullptr == referenceNode || nullptr == workingNode) return; + auto referenceImage = dynamic_cast(referenceNode->GetData()); auto workingImage = dynamic_cast(workingNode->GetData()); - if (nullptr == workingImage) + if (nullptr == referenceImage || nullptr == workingImage) return; std::vector sliceInfos; sliceInfos.reserve(m_WorkingContours.size()); for (const auto &workingContour : m_WorkingContours) { auto contour = dynamic_cast(workingContour.first->GetData()); if (nullptr == contour) continue; const auto numberOfTimeSteps = contour->GetTimeSteps(); for (std::remove_const_t t = 0; t < numberOfTimeSteps; ++t) { if (contour->IsEmptyTimeStep(t)) continue; - auto workingSlice = this->GetAffectedImageSliceAs2DImage(workingContour.second, workingImage, t); + TimePointType referenceImageTimePoint = referenceImage->GetTimeGeometry()->TimeStepToTimePoint(t); + TimeStepType workingImageTimeStep = workingImage->GetTimeGeometry()->TimePointToTimeStep(referenceImageTimePoint); + + auto workingSlice = this->GetAffectedImageSliceAs2DImage(workingContour.second, workingImage, workingImageTimeStep); auto projectedContour = ContourModelUtils::ProjectContourTo2DSlice(workingSlice, contour, true, false); - ContourModelUtils::FillContourInSlice(projectedContour, t, workingSlice, workingImage, 1); + ContourModelUtils::FillContourInSlice(projectedContour, referenceImageTimePoint, workingSlice, workingImage, 1); - sliceInfos.emplace_back(workingSlice, workingContour.second, t); + sliceInfos.emplace_back(workingSlice, workingContour.second, referenceImageTimePoint); this->WriteSliceToVolume(sliceInfos.back()); } } this->WriteBackSegmentationResult(sliceInfos, false); this->ClearSegmentation(); } void mitk::LiveWireTool2D::ClearSegmentation() { this->ReleaseHelperObjects(); this->ReleaseInteractors(); this->ResetToStartState(); } bool mitk::LiveWireTool2D::IsPositionEventInsideImageRegion(mitk::InteractionPositionEvent *positionEvent, mitk::BaseData *data) { bool isPositionEventInsideImageRegion = nullptr != data && data->GetGeometry()->IsInside(positionEvent->GetPositionInWorld()); if (!isPositionEventInsideImageRegion) MITK_WARN("LiveWireTool2D") << "PositionEvent is outside ImageRegion!"; return isPositionEventInsideImageRegion; } void mitk::LiveWireTool2D::OnInitLiveWire(StateMachineAction *, InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) return; auto workingDataNode = m_ToolManager->GetWorkingData(0); if (!IsPositionEventInsideImageRegion(positionEvent, workingDataNode->GetData())) { this->ResetToStartState(); return; } m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); auto t = positionEvent->GetSender()->GetTimeStep(); m_Contour = mitk::ContourModel::New(); m_Contour->Expand(t + 1); m_ContourNode = mitk::DataNode::New(); m_ContourNode->SetData(m_Contour); m_ContourNode->SetName("working contour node"); m_ContourNode->SetProperty("layer", IntProperty::New(100)); m_ContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); m_ContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_ContourNode->AddProperty("contour.color", ColorProperty::New(1.0f, 1.0f, 0.0f), nullptr, true); m_ContourNode->AddProperty("contour.points.color", ColorProperty::New(1.0f, 0.0f, 0.1f), nullptr, true); m_ContourNode->AddProperty("contour.controlpoints.show", BoolProperty::New(true), nullptr, true); m_LiveWireContour = mitk::ContourModel::New(); m_LiveWireContour->Expand(t + 1); m_LiveWireContourNode = mitk::DataNode::New(); m_LiveWireContourNode->SetData(m_LiveWireContour); m_LiveWireContourNode->SetName("active livewire node"); m_LiveWireContourNode->SetProperty("layer", IntProperty::New(101)); m_LiveWireContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); m_LiveWireContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_LiveWireContourNode->AddProperty("contour.color", ColorProperty::New(0.1f, 1.0f, 0.1f), nullptr, true); m_LiveWireContourNode->AddProperty("contour.width", mitk::FloatProperty::New(4.0f), nullptr, true); m_EditingContour = mitk::ContourModel::New(); m_EditingContour->Expand(t + 1); m_EditingContourNode = mitk::DataNode::New(); m_EditingContourNode->SetData(m_EditingContour); m_EditingContourNode->SetName("editing node"); m_EditingContourNode->SetProperty("layer", IntProperty::New(102)); m_EditingContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); m_EditingContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_EditingContourNode->AddProperty("contour.color", ColorProperty::New(0.1f, 1.0f, 0.1f), nullptr, true); m_EditingContourNode->AddProperty("contour.points.color", ColorProperty::New(0.0f, 0.0f, 1.0f), nullptr, true); m_EditingContourNode->AddProperty("contour.width", mitk::FloatProperty::New(4.0f), nullptr, true); auto dataStorage = m_ToolManager->GetDataStorage(); dataStorage->Add(m_ContourNode, workingDataNode); dataStorage->Add(m_LiveWireContourNode, workingDataNode); dataStorage->Add(m_EditingContourNode, workingDataNode); // Set current slice as input for ImageToLiveWireContourFilter m_WorkingSlice = this->GetAffectedReferenceSlice(positionEvent); auto origin = m_WorkingSlice->GetSlicedGeometry()->GetOrigin(); m_WorkingSlice->GetSlicedGeometry()->WorldToIndex(origin, origin); m_WorkingSlice->GetSlicedGeometry()->IndexToWorld(origin, origin); m_WorkingSlice->GetSlicedGeometry()->SetOrigin(origin); m_LiveWireFilter = ImageLiveWireContourModelFilter::New(); m_LiveWireFilter->SetInput(m_WorkingSlice); // Map click to pixel coordinates auto click = positionEvent->GetPositionInWorld(); itk::Index<3> idx; m_WorkingSlice->GetGeometry()->WorldToIndex(click, idx); // Get the pixel with the highest gradient in a 7x7 region itk::Index<3> indexWithHighestGradient; AccessFixedDimensionByItk_2(m_WorkingSlice, FindHighestGradientMagnitudeByITK, 2, idx, indexWithHighestGradient); click[0] = indexWithHighestGradient[0]; click[1] = indexWithHighestGradient[1]; click[2] = indexWithHighestGradient[2]; m_WorkingSlice->GetGeometry()->IndexToWorld(click, click); // Set initial start point m_Contour->AddVertex(click, true, t); m_LiveWireFilter->SetStartPoint(click); // Remember PlaneGeometry to determine if events were triggered in the same plane m_PlaneGeometry = interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry(); m_CreateAndUseDynamicCosts = true; mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::LiveWireTool2D::OnAddPoint(StateMachineAction *, InteractionEvent *interactionEvent) { // Complete LiveWire interaction for the last segment. Add current LiveWire contour to // the finished contour and reset to start a new segment and computation. auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) return; if (m_PlaneGeometry.IsNotNull()) { // Check if the point is in the correct slice if (m_PlaneGeometry->DistanceFromPlane(positionEvent->GetPositionInWorld()) > mitk::sqrteps) return; } auto t = static_cast(positionEvent->GetSender()->GetTimeStep()); // Add repulsive points to avoid getting the same path again std::for_each(m_LiveWireContour->IteratorBegin(), m_LiveWireContour->IteratorEnd(), [this](ContourElement::VertexType *vertex) { ImageLiveWireContourModelFilter::InternalImageType::IndexType idx; this->m_WorkingSlice->GetGeometry()->WorldToIndex(vertex->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint(idx); }); // Remove duplicate first vertex, it's already contained in m_Contour m_LiveWireContour->RemoveVertexAt(0, t); // Set last point as control point m_LiveWireContour->SetControlVertexAt(m_LiveWireContour->GetNumberOfVertices(t) - 1, t); // Merge contours m_Contour->Concatenate(m_LiveWireContour, t); // Clear the LiveWire contour and reset the corresponding DataNode m_LiveWireContour->Clear(t); // Set new start point m_LiveWireFilter->SetStartPoint(positionEvent->GetPositionInWorld()); if (m_CreateAndUseDynamicCosts) { // Use dynamic cost map for next update m_LiveWireFilter->CreateDynamicCostMap(m_Contour); m_LiveWireFilter->SetUseDynamicCostMap(true); } mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::LiveWireTool2D::OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent) { // Compute LiveWire segment from last control point to current mouse position auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) return; auto t = positionEvent->GetSender()->GetTimeStep(); m_LiveWireFilter->SetEndPoint(positionEvent->GetPositionInWorld()); m_LiveWireFilter->SetTimeStep(t); m_LiveWireFilter->Update(); m_LiveWireContour = this->m_LiveWireFilter->GetOutput(); m_LiveWireContourNode->SetData(this->m_LiveWireContour); RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::LiveWireTool2D::OnMouseMoveNoDynamicCosts(StateMachineAction *, InteractionEvent *interactionEvent) { m_LiveWireFilter->SetUseDynamicCostMap(false); this->OnMouseMoved(nullptr, interactionEvent); m_LiveWireFilter->SetUseDynamicCostMap(true); } bool mitk::LiveWireTool2D::OnCheckPoint(const InteractionEvent *interactionEvent) { // Check double click on first control point to finish the LiveWire tool auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) return false; auto t = static_cast(positionEvent->GetSender()->GetTimeStep()); mitk::Point3D click = positionEvent->GetPositionInWorld(); mitk::Point3D first = this->m_Contour->GetVertexAt(0, t)->Coordinates; return first.EuclideanDistanceTo(click) < 4.5; } void mitk::LiveWireTool2D::OnFinish(StateMachineAction *, InteractionEvent *interactionEvent) { // Finish LiveWire tool interaction auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) return; // Have to do that here so that the m_LastEventSender is set correctly mitk::SegTool2D::AddContourmarker(); auto t = static_cast(positionEvent->GetSender()->GetTimeStep()); // Remove last control point added by double click m_Contour->RemoveVertexAt(m_Contour->GetNumberOfVertices(t) - 1, t); // Save contour and corresponding plane geometry to list this->m_WorkingContours.emplace_back(std::make_pair(m_ContourNode, positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone())); this->m_EditingContours.emplace_back(std::make_pair(m_EditingContourNode, positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone())); m_LiveWireFilter->SetUseDynamicCostMap(false); this->FinishTool(); } void mitk::LiveWireTool2D::FinishTool() { auto numberOfTimesteps = static_cast(m_Contour->GetTimeGeometry()->CountTimeSteps()); for (int i = 0; i <= numberOfTimesteps; ++i) m_Contour->Close(i); m_ToolManager->GetDataStorage()->Remove(m_LiveWireContourNode); m_LiveWireContourNode = nullptr; m_LiveWireContour = nullptr; m_ContourInteractor = mitk::ContourModelLiveWireInteractor::New(); m_ContourInteractor->SetDataNode(m_ContourNode); m_ContourInteractor->LoadStateMachine("ContourModelModificationInteractor.xml", us::GetModuleContext()->GetModule()); m_ContourInteractor->SetEventConfig("ContourModelModificationConfig.xml", us::GetModuleContext()->GetModule()); m_ContourInteractor->SetWorkingImage(this->m_WorkingSlice); m_ContourInteractor->SetEditingContourModelNode(this->m_EditingContourNode); m_ContourNode->SetDataInteractor(m_ContourInteractor.GetPointer()); this->m_LiveWireInteractors.push_back(m_ContourInteractor); } void mitk::LiveWireTool2D::OnLastSegmentDelete(StateMachineAction *, InteractionEvent *interactionEvent) { int t = static_cast(interactionEvent->GetSender()->GetTimeStep()); // If last point of current contour will be removed go to start state and remove nodes if (m_Contour->GetNumberOfVertices(t) <= 1) { auto dataStorage = m_ToolManager->GetDataStorage(); dataStorage->Remove(m_LiveWireContourNode); dataStorage->Remove(m_ContourNode); dataStorage->Remove(m_EditingContourNode); m_LiveWireContour = mitk::ContourModel::New(); m_LiveWireContourNode->SetData(m_LiveWireContour); m_Contour = mitk::ContourModel::New(); m_ContourNode->SetData(m_Contour); this->ResetToStartState(); } else // Remove last segment from contour and reset LiveWire contour { m_LiveWireContour = mitk::ContourModel::New(); m_LiveWireContourNode->SetData(m_LiveWireContour); auto newContour = mitk::ContourModel::New(); newContour->Expand(m_Contour->GetTimeSteps()); auto begin = m_Contour->IteratorBegin(); // Iterate from last point to next active point auto newLast = m_Contour->IteratorBegin() + (m_Contour->GetNumberOfVertices() - 1); // Go at least one down if (newLast != begin) --newLast; // Search next active control point while (newLast != begin && !((*newLast)->IsControlPoint)) --newLast; // Set position of start point for LiveWire filter to coordinates of the new last point m_LiveWireFilter->SetStartPoint((*newLast)->Coordinates); auto it = m_Contour->IteratorBegin(); // Fll new Contour while (it <= newLast) { newContour->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, t); ++it; } newContour->SetClosed(m_Contour->IsClosed()); m_ContourNode->SetData(newContour); m_Contour = newContour; mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow()); } } template void mitk::LiveWireTool2D::FindHighestGradientMagnitudeByITK(itk::Image *inputImage, itk::Index<3> &index, itk::Index<3> &returnIndex) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; const auto MAX_X = inputImage->GetLargestPossibleRegion().GetSize()[0]; const auto MAX_Y = inputImage->GetLargestPossibleRegion().GetSize()[1]; returnIndex[0] = index[0]; returnIndex[1] = index[1]; returnIndex[2] = 0.0; double gradientMagnitude = 0.0; double maxGradientMagnitude = 0.0; // The size and thus the region of 7x7 is only used to calculate the gradient magnitude in that region, // not for searching the maximum value. // Maximum value in each direction for size typename InputImageType::SizeType size; size[0] = 7; size[1] = 7; // Minimum value in each direction for startRegion IndexType startRegion; startRegion[0] = index[0] - 3; startRegion[1] = index[1] - 3; if (startRegion[0] < 0) startRegion[0] = 0; if (startRegion[1] < 0) startRegion[1] = 0; if (MAX_X - index[0] < 7) startRegion[0] = MAX_X - 7; if (MAX_Y - index[1] < 7) startRegion[1] = MAX_Y - 7; index[0] = startRegion[0] + 3; index[1] = startRegion[1] + 3; typename InputImageType::RegionType region; region.SetSize(size); region.SetIndex(startRegion); typedef typename itk::GradientMagnitudeImageFilter GradientMagnitudeFilterType; typename GradientMagnitudeFilterType::Pointer gradientFilter = GradientMagnitudeFilterType::New(); gradientFilter->SetInput(inputImage); gradientFilter->GetOutput()->SetRequestedRegion(region); gradientFilter->Update(); typename InputImageType::Pointer gradientMagnitudeImage; gradientMagnitudeImage = gradientFilter->GetOutput(); IndexType currentIndex; currentIndex[0] = 0; currentIndex[1] = 0; // Search max (approximate) gradient magnitude for (int x = -1; x <= 1; ++x) { currentIndex[0] = index[0] + x; for (int y = -1; y <= 1; ++y) { currentIndex[1] = index[1] + y; gradientMagnitude = gradientMagnitudeImage->GetPixel(currentIndex); // Check for new max if (maxGradientMagnitude < gradientMagnitude) { maxGradientMagnitude = gradientMagnitude; returnIndex[0] = currentIndex[0]; returnIndex[1] = currentIndex[1]; returnIndex[2] = 0.0; } } currentIndex[1] = index[1]; } } diff --git a/Plugins/PluginList.cmake b/Plugins/PluginList.cmake index a0899b3787..0d16bd73bc 100644 --- a/Plugins/PluginList.cmake +++ b/Plugins/PluginList.cmake @@ -1,96 +1,97 @@ # Plug-ins must be ordered according to their dependencies set(MITK_PLUGINS org.blueberry.core.runtime:ON org.blueberry.core.expressions:OFF org.blueberry.core.commands:OFF org.blueberry.core.jobs:OFF org.blueberry.ui.qt:OFF org.blueberry.ui.qt.help:ON org.blueberry.ui.qt.log:ON org.blueberry.ui.qt.objectinspector:OFF org.mitk.core.services:ON org.mitk.gui.common:ON org.mitk.planarfigure:ON org.mitk.core.ext:OFF org.mitk.core.jobs:OFF org.mitk.gui.qt.application:ON org.mitk.gui.qt.ext:OFF org.mitk.gui.qt.extapplication:OFF org.mitk.gui.qt.mitkworkbench.intro:OFF org.mitk.gui.qt.common:ON org.mitk.gui.qt.stdmultiwidgeteditor:ON org.mitk.gui.qt.mxnmultiwidgeteditor:OFF org.mitk.gui.qt.common.legacy:OFF org.mitk.gui.qt.cmdlinemodules:OFF org.mitk.gui.qt.chartExample:OFF org.mitk.gui.qt.datamanager:ON org.mitk.gui.qt.datamanagerlight:OFF org.mitk.gui.qt.datastorageviewertest:OFF org.mitk.gui.qt.properties:ON org.mitk.gui.qt.basicimageprocessing:OFF org.mitk.gui.qt.dicombrowser:OFF org.mitk.gui.qt.dicominspector:OFF org.mitk.gui.qt.dosevisualization:OFF org.mitk.gui.qt.geometrytools:OFF org.mitk.gui.qt.igtexamples:OFF org.mitk.gui.qt.igttracking:OFF org.mitk.gui.qt.lasercontrol:OFF org.mitk.gui.qt.openigtlink:OFF org.mitk.gui.qt.imagecropper:OFF org.mitk.gui.qt.imagenavigator:ON org.mitk.gui.qt.viewnavigator:OFF org.mitk.gui.qt.materialeditor:OFF org.mitk.gui.qt.measurementtoolbox:OFF org.mitk.gui.qt.moviemaker:OFF org.mitk.gui.qt.pointsetinteraction:OFF org.mitk.gui.qt.pointsetinteractionmultispectrum:OFF org.mitk.gui.qt.python:OFF org.mitk.gui.qt.remeshing:OFF org.mitk.gui.qt.segmentation:OFF + org.mitk.gui.qt.deformableclippingplane:OFF org.mitk.gui.qt.aicpregistration:OFF org.mitk.gui.qt.renderwindowmanager:OFF org.mitk.gui.qt.semanticrelations:OFF org.mitk.gui.qt.toftutorial:OFF org.mitk.gui.qt.tofutil:OFF org.mitk.gui.qt.tubegraph:OFF org.mitk.gui.qt.ugvisualization:OFF org.mitk.gui.qt.photoacoustics.pausviewer:OFF org.mitk.gui.qt.photoacoustics.pausmotioncompensation:OFF org.mitk.gui.qt.photoacoustics.imageprocessing:OFF org.mitk.gui.qt.photoacoustics.simulation:OFF org.mitk.gui.qt.photoacoustics.spectralunmixing:OFF org.mitk.gui.qt.ultrasound:OFF org.mitk.gui.qt.volumevisualization:OFF org.mitk.gui.qt.eventrecorder:OFF org.mitk.gui.qt.xnat:OFF org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation:OFF org.mitk.gui.qt.spectrocamrecorder:OFF org.mitk.gui.qt.classificationsegmentation:OFF org.mitk.gui.qt.overlaymanager:OFF org.mitk.gui.qt.igt.app.hummelprotocolmeasurements:OFF org.mitk.gui.qt.multilabelsegmentation:OFF org.mitk.matchpoint.core.helper:OFF org.mitk.gui.qt.matchpoint.algorithm.browser:OFF org.mitk.gui.qt.matchpoint.algorithm.control:OFF org.mitk.gui.qt.matchpoint.mapper:OFF org.mitk.gui.qt.matchpoint.framereg:OFF org.mitk.gui.qt.matchpoint.visualizer:OFF org.mitk.gui.qt.matchpoint.evaluator:OFF org.mitk.gui.qt.matchpoint.manipulator:OFF org.mitk.gui.qt.preprocessing.resampling:OFF org.mitk.gui.qt.radiomics:OFF org.mitk.gui.qt.cest:OFF org.mitk.gui.qt.fit.demo:OFF org.mitk.gui.qt.fit.inspector:OFF org.mitk.gui.qt.fit.genericfitting:OFF org.mitk.gui.qt.pharmacokinetics.mri:OFF org.mitk.gui.qt.pharmacokinetics.pet:OFF org.mitk.gui.qt.pharmacokinetics.simulation:OFF org.mitk.gui.qt.pharmacokinetics.curvedescriptor:OFF org.mitk.gui.qt.pharmacokinetics.concentration.mri:OFF org.mitk.gui.qt.flowapplication:OFF org.mitk.gui.qt.flow.segmentation:OFF ) diff --git a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTNormalizeView.cpp b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTNormalizeView.cpp index 6ff6b43f96..7a9de6b733 100644 --- a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTNormalizeView.cpp +++ b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTNormalizeView.cpp @@ -1,110 +1,110 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkCESTNormalizeView.h" #include #include "mitkWorkbenchUtil.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateDataProperty.h" #include "mitkNodePredicateDataType.h" #include "QmitkDataStorageComboBoxWithSelectNone.h" #include #include "mitkCESTImageNormalizationFilter.h" #include "mitkCESTPropertyHelper.h" #include "mitkCESTImageDetectionHelper.h" -const std::string QmitkCESTNormalizeView::VIEW_ID = "org.mitk.gui.qt.cest.normalize"; +const std::string QmitkCESTNormalizeView::VIEW_ID = "org.mitk.views.cest.normalization"; void QmitkCESTNormalizeView::SetFocus() { m_Controls.btnNormalize->setFocus(); } void QmitkCESTNormalizeView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnNormalize->setEnabled(false); m_Controls.comboCESTImage->SetPredicate(this->m_IsCESTImagePredicate); m_Controls.comboCESTImage->SetDataStorage(this->GetDataStorage()); connect(m_Controls.btnNormalize, SIGNAL(clicked()), this, SLOT(OnNormalizeButtonClicked())); connect(m_Controls.comboCESTImage, SIGNAL(OnSelectionChanged(const mitk::DataNode *)), this, SLOT(UpdateGUIControls())); UpdateGUIControls(); } void QmitkCESTNormalizeView::UpdateGUIControls() { m_Controls.btnNormalize->setEnabled(m_Controls.comboCESTImage->GetSelectedNode().IsNotNull()); } void QmitkCESTNormalizeView::OnNormalizeButtonClicked() { auto selectedImageNode = m_Controls.comboCESTImage->GetSelectedNode(); if (!selectedImageNode) { MITK_ERROR << "Invalid system state. CEST selection is invalid. Selected node is null_ptr."; return; } auto selectedImage = dynamic_cast(selectedImageNode->GetData()); if (!selectedImageNode) { MITK_ERROR << "Invalid system state. CEST selection is invalid. Selected node is not an image."; return; } std::string offsetsStr = ""; bool hasOffsets = selectedImage->GetPropertyList()->GetStringProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str(), offsetsStr); if (!hasOffsets) { QMessageBox::information(nullptr, "CEST normalization", "Selected image was missing CEST offset information."); return; } if (!mitk::IsNotNormalizedCESTImage(selectedImage)) { QMessageBox::information(nullptr, "CEST normalization", "Selected image already seems to be normalized."); return; } if (selectedImage->GetDimension() == 4) { auto normalizationFilter = mitk::CESTImageNormalizationFilter::New(); normalizationFilter->SetInput(selectedImage); normalizationFilter->Update(); auto resultImage = normalizationFilter->GetOutput(); mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); dataNode->SetData(resultImage); std::string normalizedName = selectedImageNode->GetName() + "_normalized"; dataNode->SetName(normalizedName); this->GetDataStorage()->Add(dataNode); } } QmitkCESTNormalizeView::QmitkCESTNormalizeView() { auto isImage = mitk::NodePredicateDataType::New("Image"); this->m_IsCESTImagePredicate = mitk::NodePredicateAnd::New(isImage, mitk::CreateAnyCESTImageNodePredicate()).GetPointer(); } diff --git a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp index 2b8c2c96bf..5f913cf2a3 100644 --- a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp @@ -1,815 +1,815 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // itk #include "itksys/SystemTools.hxx" #include #include // Blueberry #include #include // Qmitk #include "QmitkCESTStatisticsView.h" // Qt #include #include // qwt #include // mitk #include #include #include #include #include #include #include #include #include #include #include #include #include // boost #include #include // stl #include #include #include #include #include #include namespace { template void GetSortPermutation(std::vector &out, const std::vector &determiningVector, Compare compare = std::less()) { out.resize(determiningVector.size()); std::iota(out.begin(), out.end(), 0); std::sort(out.begin(), out.end(), [&](unsigned i, unsigned j) { return compare(determiningVector[i], determiningVector[j]); }); } template void ApplyPermutation(const std::vector &order, std::vector &vectorToSort) { assert(order.size() == vectorToSort.size()); std::vector tempVector(vectorToSort.size()); for (unsigned i = 0; i < vectorToSort.size(); i++) { tempVector[i] = vectorToSort[order[i]]; } vectorToSort = tempVector; } template void ApplyPermutation(const std::vector &order, std::vector ¤tVector, std::vector &... remainingVectors) { ApplyPermutation(order, currentVector); ApplyPermutation(order, remainingVectors...); } template void SortVectors(const std::vector &orderDeterminingVector, Compare comparison, std::vector &... vectorsToBeSorted) { std::vector order; GetSortPermutation(order, orderDeterminingVector, comparison); ApplyPermutation(order, vectorsToBeSorted...); } } // namespace -const std::string QmitkCESTStatisticsView::VIEW_ID = "org.mitk.views.ceststatistics"; +const std::string QmitkCESTStatisticsView::VIEW_ID = "org.mitk.views.cest.statistics"; QmitkCESTStatisticsView::QmitkCESTStatisticsView(QObject * /*parent*/, const char * /*name*/) { this->m_CalculatorJob = new QmitkImageStatisticsCalculationJob(); m_currentSelectedPosition.Fill(0.0); m_currentSelectedTimePoint = 0.; m_CrosshairPointSet = mitk::PointSet::New(); } QmitkCESTStatisticsView::~QmitkCESTStatisticsView() { while (this->m_CalculatorJob->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } delete this->m_CalculatorJob; } void QmitkCESTStatisticsView::SetFocus() { m_Controls.threeDimToFourDimPushButton->setFocus(); } void QmitkCESTStatisticsView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); connect( m_Controls.threeDimToFourDimPushButton, SIGNAL(clicked()), this, SLOT(OnThreeDimToFourDimPushButtonClicked())); connect((QObject *)this->m_CalculatorJob, SIGNAL(finished()), this, SLOT(OnThreadedStatisticsCalculationEnds()), Qt::QueuedConnection); connect((QObject *)(this->m_Controls.fixedRangeCheckBox), SIGNAL(toggled(bool)), (QObject *)this, SLOT(OnFixedRangeCheckBoxToggled(bool))); connect((QObject *)(this->m_Controls.fixedRangeLowerDoubleSpinBox), SIGNAL(editingFinished()), (QObject *)this, SLOT(OnFixedRangeDoubleSpinBoxChanged())); connect((QObject *)(this->m_Controls.fixedRangeUpperDoubleSpinBox), SIGNAL(editingFinished()), (QObject *)this, SLOT(OnFixedRangeDoubleSpinBoxChanged())); m_Controls.threeDimToFourDimPushButton->setEnabled(false); m_Controls.widget_statistics->SetDataStorage(this->GetDataStorage()); this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } void QmitkCESTStatisticsView::RenderWindowPartActivated(mitk::IRenderWindowPart *renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart); } void QmitkCESTStatisticsView::RenderWindowPartDeactivated(mitk::IRenderWindowPart *renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void QmitkCESTStatisticsView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList &nodes) { if (nodes.empty()) { std::stringstream message; message << "Please select an image."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } // iterate all selected objects bool atLeastOneWasCESTImage = false; foreach (mitk::DataNode::Pointer node, nodes) { if (node.IsNull()) { continue; } if (dynamic_cast(node->GetData()) != nullptr) { m_Controls.labelWarning->setVisible(false); bool zSpectrumSet = SetZSpectrum(dynamic_cast( node->GetData()->GetProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str()).GetPointer())); atLeastOneWasCESTImage = atLeastOneWasCESTImage || zSpectrumSet; if (zSpectrumSet) { m_ZImage = dynamic_cast(node->GetData()); m_Controls.widget_statistics->SetImageNodes({node.GetPointer()}); } else { m_MaskImage = dynamic_cast(node->GetData()); m_Controls.widget_statistics->SetMaskNodes({node.GetPointer()}); } } if (dynamic_cast(node->GetData()) != nullptr) { m_MaskPlanarFigure = dynamic_cast(node->GetData()); m_Controls.widget_statistics->SetMaskNodes({node.GetPointer()}); } if (dynamic_cast(node->GetData()) != nullptr) { m_PointSet = dynamic_cast(node->GetData()); } } // We only want to offer normalization or timestep copying if one object is selected if (nodes.size() == 1) { if (dynamic_cast(nodes.front()->GetData())) { m_Controls.threeDimToFourDimPushButton->setDisabled(atLeastOneWasCESTImage); } else { m_Controls.threeDimToFourDimPushButton->setEnabled(false); std::stringstream message; message << "The selected node is not an image."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } this->Clear(); return; } // we always need a mask, either image or planar figure as well as an image for further processing if (nodes.size() != 2) { this->Clear(); return; } m_Controls.threeDimToFourDimPushButton->setEnabled(false); if (!atLeastOneWasCESTImage) { std::stringstream message; message << "None of the selected data nodes contains required CEST meta information"; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } bool bothAreImages = (m_ZImage.GetPointer() != nullptr) && (m_MaskImage.GetPointer() != nullptr); if (bothAreImages) { bool geometriesMatch = mitk::Equal(*(m_ZImage->GetTimeGeometry()), *(m_MaskImage->GetTimeGeometry()), mitk::eps, false); if (!geometriesMatch) { std::stringstream message; message << "The selected images have different geometries."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } } if (!this->DataSanityCheck()) { this->Clear(); return; } if (m_PointSet.IsNull()) { // initialize thread and trigger it this->m_CalculatorJob->SetIgnoreZeroValueVoxel(false); this->m_CalculatorJob->Initialize(m_ZImage.GetPointer(), m_MaskImage.GetPointer(), m_MaskPlanarFigure.GetPointer()); std::stringstream message; message << "Calculating statistics..."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); try { // Compute statistics this->m_CalculatorJob->start(); } catch (const mitk::Exception &e) { std::stringstream message; message << "" << e.GetDescription() << ""; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } catch (const std::runtime_error &e) { // In case of exception, print error message on GUI std::stringstream message; message << "" << e.what() << ""; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } catch (const std::exception &e) { MITK_ERROR << "Caught exception: " << e.what(); // In case of exception, print error message on GUI std::stringstream message; message << "Error! Unequal Dimensions of Image and Segmentation. No recompute possible "; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } while (this->m_CalculatorJob->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } } if (m_PointSet.IsNotNull()) { if (m_ZImage->GetDimension() == 4) { AccessFixedDimensionByItk(m_ZImage, PlotPointSet, 4); } else { MITK_WARN << "Expecting a 4D image."; } } } void QmitkCESTStatisticsView::OnThreadedStatisticsCalculationEnds() { this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::xBottom, "delta w"); this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::yLeft, "z"); if (this->m_CalculatorJob->GetStatisticsUpdateSuccessFlag()) { auto statistics = this->m_CalculatorJob->GetStatisticsData(); std::string statisticsNodeName = "CEST_statistics"; auto statisticsNode = mitk::CreateImageStatisticsNode(statistics, statisticsNodeName); auto imageRule = mitk::StatisticsToImageRelationRule::New(); imageRule->Connect(statistics, m_CalculatorJob->GetStatisticsImage()); if (m_CalculatorJob->GetMaskImage()) { auto maskRule = mitk::StatisticsToMaskRelationRule::New(); maskRule->Connect(statistics, m_CalculatorJob->GetMaskImage()); } else if (m_CalculatorJob->GetPlanarFigure()) { auto planarFigureRule = mitk::StatisticsToMaskRelationRule::New(); planarFigureRule->Connect(statistics, m_CalculatorJob->GetPlanarFigure()); } this->GetDataStorage()->Add(statisticsNode); QmitkPlotWidget::DataVector::size_type numberOfSpectra = this->m_zSpectrum.size(); QmitkPlotWidget::DataVector means(numberOfSpectra); QmitkPlotWidget::DataVector stdevs(numberOfSpectra); for (unsigned int index = 0; index < numberOfSpectra; ++index) { means[index] = statistics->GetStatisticsForTimeStep(index).GetValueConverted( mitk::ImageStatisticsConstants::MEAN()); stdevs[index] = statistics->GetStatisticsForTimeStep(index).GetValueConverted( mitk::ImageStatisticsConstants::STANDARDDEVIATION()); } QmitkPlotWidget::DataVector xValues = this->m_zSpectrum; RemoveMZeros(xValues, means, stdevs); ::SortVectors(xValues, std::less(), xValues, means, stdevs); unsigned int curveId = this->m_Controls.m_DataViewWidget->InsertCurve("Spectrum"); this->m_Controls.m_DataViewWidget->SetCurveData(curveId, xValues, means, stdevs, stdevs); this->m_Controls.m_DataViewWidget->SetErrorPen(curveId, QPen(Qt::blue)); QwtSymbol *blueSymbol = new QwtSymbol(QwtSymbol::Rect, QColor(Qt::blue), QColor(Qt::blue), QSize(8, 8)); this->m_Controls.m_DataViewWidget->SetCurveSymbol(curveId, blueSymbol); this->m_Controls.m_DataViewWidget->SetLegendAttribute(curveId, QwtPlotCurve::LegendShowSymbol); QwtLegend *legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); this->m_Controls.m_DataViewWidget->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.m_DataViewWidget->GetPlot() ->axisScaleEngine(QwtPlot::Axis::xBottom) ->setAttributes(QwtScaleEngine::Inverted); this->m_Controls.m_DataViewWidget->Replot(); m_Controls.labelWarning->setVisible(false); m_Controls.m_StatisticsGroupBox->setEnabled(true); m_Controls.m_StatisticsGroupBox->setEnabled(true); if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale( 2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } else { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, true); } } else { m_Controls.labelWarning->setText(m_CalculatorJob->GetLastErrorMessage().c_str()); m_Controls.labelWarning->setVisible(true); this->Clear(); } } void QmitkCESTStatisticsView::OnFixedRangeDoubleSpinBoxChanged() { if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale(2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } this->m_Controls.m_DataViewWidget->Replot(); } template void QmitkCESTStatisticsView::PlotPointSet(itk::Image *image) { this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::xBottom, "delta w"); this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::yLeft, "z"); QmitkPlotWidget::DataVector::size_type numberOfSpectra = this->m_zSpectrum.size(); mitk::PointSet::Pointer internalPointset; if (m_PointSet.IsNotNull()) { internalPointset = m_PointSet; } else { internalPointset = m_CrosshairPointSet; } if (internalPointset.IsNull()) { return; } if (!this->DataSanityCheck()) { m_Controls.labelWarning->setText("Data can not be plotted, internally inconsistent."); m_Controls.labelWarning->show(); return; } auto maxIndex = internalPointset->GetMaxId().Index(); for (std::size_t number = 0; number < maxIndex + 1; ++number) { mitk::PointSet::PointType point; if (!internalPointset->GetPointIfExists(number, &point)) { continue; } if (!this->m_ZImage->GetGeometry()->IsInside(point)) { continue; } itk::Index<3> itkIndex; this->m_ZImage->GetGeometry()->WorldToIndex(point, itkIndex); itk::Index itkIndexTime; itkIndexTime[0] = itkIndex[0]; itkIndexTime[1] = itkIndex[1]; itkIndexTime[2] = itkIndex[2]; QmitkPlotWidget::DataVector values(numberOfSpectra); for (std::size_t step = 0; step < numberOfSpectra; ++step) { if (VImageDimension == 4) { itkIndexTime[3] = step; } values[step] = image->GetPixel(itkIndexTime); } std::stringstream name; name << "Point " << number; // Qcolor enums go from 0 to 19, but 19 is transparent and 0,1 are for bitmaps // 3 is white and thus not visible QColor color(static_cast(number % 17 + 4)); QmitkPlotWidget::DataVector xValues = this->m_zSpectrum; RemoveMZeros(xValues, values); ::SortVectors(xValues, std::less(), xValues, values); unsigned int curveId = this->m_Controls.m_DataViewWidget->InsertCurve(name.str().c_str()); this->m_Controls.m_DataViewWidget->SetCurveData(curveId, xValues, values); this->m_Controls.m_DataViewWidget->SetCurvePen(curveId, QPen(color)); QwtSymbol *symbol = new QwtSymbol(QwtSymbol::Rect, color, color, QSize(8, 8)); this->m_Controls.m_DataViewWidget->SetCurveSymbol(curveId, symbol); this->m_Controls.m_DataViewWidget->SetLegendAttribute(curveId, QwtPlotCurve::LegendShowSymbol); } if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale(2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } else { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, true); } QwtLegend *legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); this->m_Controls.m_DataViewWidget->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.m_DataViewWidget->GetPlot() ->axisScaleEngine(QwtPlot::Axis::xBottom) ->setAttributes(QwtScaleEngine::Inverted); this->m_Controls.m_DataViewWidget->Replot(); m_Controls.labelWarning->setVisible(false); } void QmitkCESTStatisticsView::OnFixedRangeCheckBoxToggled(bool state) { this->m_Controls.fixedRangeLowerDoubleSpinBox->setEnabled(state); this->m_Controls.fixedRangeUpperDoubleSpinBox->setEnabled(state); } void QmitkCESTStatisticsView::RemoveMZeros(QmitkPlotWidget::DataVector &xValues, QmitkPlotWidget::DataVector &yValues) { QmitkPlotWidget::DataVector tempX; QmitkPlotWidget::DataVector tempY; for (std::size_t index = 0; index < xValues.size(); ++index) { if ((xValues.at(index) < -299) || (xValues.at(index)) > 299) { // do not include } else { tempX.push_back(xValues.at(index)); tempY.push_back(yValues.at(index)); } } xValues = tempX; yValues = tempY; } void QmitkCESTStatisticsView::RemoveMZeros(QmitkPlotWidget::DataVector &xValues, QmitkPlotWidget::DataVector &yValues, QmitkPlotWidget::DataVector &stdDevs) { QmitkPlotWidget::DataVector tempX; QmitkPlotWidget::DataVector tempY; QmitkPlotWidget::DataVector tempDevs; for (std::size_t index = 0; index < xValues.size(); ++index) { if ((xValues.at(index) < -299) || (xValues.at(index)) > 299) { // do not include } else { tempX.push_back(xValues.at(index)); tempY.push_back(yValues.at(index)); tempDevs.push_back(stdDevs.at(index)); } } xValues = tempX; yValues = tempY; stdDevs = tempDevs; } void QmitkCESTStatisticsView::OnThreeDimToFourDimPushButtonClicked() { QList nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataNode *node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return QMessageBox::information(nullptr, "CEST View", "Please load and select an image before starting image processing."); return; } // here we have a valid mitk::DataNode // a node itself is not very useful, we need its data item (the image) mitk::BaseData *data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image *image = dynamic_cast(data); if (image) { if (image->GetDimension() == 4) { AccessFixedDimensionByItk(image, CopyTimesteps, 4); } this->Clear(); } } } template void QmitkCESTStatisticsView::CopyTimesteps(itk::Image *image) { typedef itk::Image ImageType; // typedef itk::PasteImageFilter PasteImageFilterType; unsigned int numberOfTimesteps = image->GetLargestPossibleRegion().GetSize(3); typename ImageType::RegionType sourceRegion = image->GetLargestPossibleRegion(); sourceRegion.SetSize(3, 1); typename ImageType::RegionType targetRegion = image->GetLargestPossibleRegion(); targetRegion.SetSize(3, 1); for (unsigned int timestep = 1; timestep < numberOfTimesteps; ++timestep) { targetRegion.SetIndex(3, timestep); itk::ImageRegionConstIterator sourceIterator(image, sourceRegion); itk::ImageRegionIterator targetIterator(image, targetRegion); while (!sourceIterator.IsAtEnd()) { targetIterator.Set(sourceIterator.Get()); ++sourceIterator; ++targetIterator; } } } bool QmitkCESTStatisticsView::SetZSpectrum(mitk::StringProperty *zSpectrumProperty) { if (zSpectrumProperty == nullptr) { return false; } mitk::LocaleSwitch localeSwitch("C"); std::string zSpectrumString = zSpectrumProperty->GetValueAsString(); std::istringstream iss(zSpectrumString); std::vector zSpectra; std::copy( std::istream_iterator(iss), std::istream_iterator(), std::back_inserter(zSpectra)); m_zSpectrum.clear(); m_zSpectrum.resize(0); for (auto const &spectrumString : zSpectra) { m_zSpectrum.push_back(std::stod(spectrumString)); } return (m_zSpectrum.size() > 0); } bool QmitkCESTStatisticsView::DataSanityCheck() { QmitkPlotWidget::DataVector::size_type numberOfSpectra = m_zSpectrum.size(); // if we do not have a spectrum, the data can not be processed if (numberOfSpectra == 0) { return false; } // if we do not have CEST image data, the data can not be processed if (m_ZImage.IsNull()) { return false; } // if the CEST image data and the meta information do not match, the data can not be processed if (numberOfSpectra != m_ZImage->GetTimeSteps()) { MITK_INFO << "CEST meta information and number of volumes does not match."; return false; } // if we have neither a mask image, a point set nor a mask planar figure, we can not do statistics // statistics on the whole image would not make sense if (m_MaskImage.IsNull() && m_MaskPlanarFigure.IsNull() && m_PointSet.IsNull() && m_CrosshairPointSet->IsEmpty()) { return false; } // if we have a mask image and a mask planar figure, we can not do statistics // we do not know which one to use if (m_MaskImage.IsNotNull() && m_MaskPlanarFigure.IsNotNull()) { return false; } return true; } void QmitkCESTStatisticsView::Clear() { this->m_zSpectrum.clear(); this->m_zSpectrum.resize(0); this->m_ZImage = nullptr; this->m_MaskImage = nullptr; this->m_MaskPlanarFigure = nullptr; this->m_PointSet = nullptr; this->m_Controls.m_DataViewWidget->Clear(); this->m_Controls.m_StatisticsGroupBox->setEnabled(false); this->m_Controls.widget_statistics->SetImageNodes({}); this->m_Controls.widget_statistics->SetMaskNodes({}); } void QmitkCESTStatisticsView::OnSliceChanged() { mitk::Point3D currentSelectedPosition = this->GetRenderWindowPart()->GetSelectedPosition(nullptr); mitk::TimePointType currentSelectedTimePoint = this->GetRenderWindowPart()->GetSelectedTimePoint(); if (m_currentSelectedPosition != currentSelectedPosition || currentSelectedTimePoint != currentSelectedTimePoint) //|| 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_currentSelectedTimePoint = currentSelectedTimePoint; m_currentPositionTime.Modified(); m_CrosshairPointSet->Clear(); m_CrosshairPointSet->SetPoint(0, m_currentSelectedPosition); QList nodes = this->GetDataManagerSelection(); if (nodes.empty() || nodes.size() > 1) return; mitk::DataNode *node = nodes.front(); if (!node) { return; } if (dynamic_cast(node->GetData()) != nullptr) { m_Controls.labelWarning->setVisible(false); bool zSpectrumSet = SetZSpectrum(dynamic_cast( node->GetData()->GetProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str()).GetPointer())); if (zSpectrumSet) { m_ZImage = dynamic_cast(node->GetData()); } else { return; } } else { return; } this->m_Controls.m_DataViewWidget->Clear(); AccessFixedDimensionByItk(m_ZImage, PlotPointSet, 4); } } diff --git a/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/CommandLineModulesViewConstants.cpp b/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/CommandLineModulesViewConstants.cpp index 8b2532e75e..6b63d6c35e 100644 --- a/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/CommandLineModulesViewConstants.cpp +++ b/Plugins/org.mitk.gui.qt.cmdlinemodules/src/internal/CommandLineModulesViewConstants.cpp @@ -1,31 +1,31 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "CommandLineModulesViewConstants.h" -const QString CommandLineModulesViewConstants::VIEW_ID = "org.mitk.gui.qt.cmdlinemodules"; +const QString CommandLineModulesViewConstants::VIEW_ID = "org.mitk.views.cmdlinemodules"; const QString CommandLineModulesViewConstants::TEMPORARY_DIRECTORY_NODE_NAME = "temporary directory"; const QString CommandLineModulesViewConstants::OUTPUT_DIRECTORY_NODE_NAME = "output directory"; const QString CommandLineModulesViewConstants::MODULE_DIRECTORIES_NODE_NAME = "module directories"; const QString CommandLineModulesViewConstants::MODULE_FILES_NODE_NAME = "module files"; const QString CommandLineModulesViewConstants::DEBUG_OUTPUT_NODE_NAME = "debug output"; const QString CommandLineModulesViewConstants::LOAD_FROM_APPLICATION_DIR = "load from application dir"; const QString CommandLineModulesViewConstants::LOAD_FROM_HOME_DIR = "load from home dir"; const QString CommandLineModulesViewConstants::LOAD_FROM_CURRENT_DIR = "load from current dir"; const QString CommandLineModulesViewConstants::LOAD_FROM_AUTO_LOAD_DIR = "load from auto-load dir"; const QString CommandLineModulesViewConstants::LOAD_FROM_APPLICATION_DIR_CLI_MODULES = "load from application dir/cli-modules"; const QString CommandLineModulesViewConstants::LOAD_FROM_HOME_DIR_CLI_MODULES = "load from home dir/cli-modules"; const QString CommandLineModulesViewConstants::LOAD_FROM_CURRENT_DIR_CLI_MODULES = "load from current dir/cli-modules"; const QString CommandLineModulesViewConstants::XML_VALIDATION_MODE = "xml validation mode"; const QString CommandLineModulesViewConstants::XML_TIMEOUT_SECS = "xml time-out"; const QString CommandLineModulesViewConstants::MAX_CONCURRENT = "max concurrent processes"; const QString CommandLineModulesViewConstants::SHOW_ADVANCED_WIDGETS_NAME = "show advanced widgets"; diff --git a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager.dox b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager.dox index 08e636a260..78387972f7 100644 --- a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager.dox +++ b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager.dox @@ -1,109 +1,194 @@ /** -\page org_mitk_views_datamanager The DataManager +\page org_mitk_views_datamanager The Datamanager -\imageMacro{data-manager.svg,"Icon of the Data Manager",2.00} +\imageMacro{data-manager.svg,"Icon of the Data Manager", 2.00} \tableofcontents -\section QmitkDataManagerIntroduction Introduction +\section DataManagerIntroduction Introduction -The Datamanager is the central componenent to manage medical data like images, surfaces, etc.. -After loading one or more data into the Datamanager the data are shown in the four-view window, the so called Standard View. -The user can now start working on the data by just clicking into the standard view or by using the MITK-modules such as "Segmentation" or "Basic Image Processing". +The Datamanager is the central component to manage medical data like images, segmentation masks, registrations, surfaces, point sets, annotations, measurements, etc..
+After loading data into the Datamanager the data is shown in the four-view window, the so-called Standard Display. +The user can now start working on the data by interacting with the data inside the Standard Display, +by using the MITK-plugins such as Segmentation or Basic Image Processing or by using the context menu inside the data manager. -\imageMacro{QmitkDatamanager_Overview.png,"How MITK looks when started",16.00} +\section DataManagerLoadingData Loading Data +There are several ways of loading data into the Datamanager as so-called data nodes: -\section QmitkDataManagerLoading Loading Data - -There are three ways of loading data into the Datamanager as so called Data-Elements. - -The user can just drag and drop data into the Datamanager or directly into one of the four parts of the Standard View. -He can as well use the Open-Button in the right upper corner. Or he can use the standard "File->Open"-Dialog on the top. +
    +
  • drag and drop data (e.g. from a file explorer or desktop) into the Datamanager +
  • drag and drop data (e.g. from a file explorer or desktop) into one of the four windows of the Standard Display +
  • use the keyboard shortcut Ctrl + o +
  • use the Open File Button in the left upper corner and use the Open Dialog +
  • use File -> Open File... from the top menu +
-A lot of file-formats can be loaded into MITK, for example +A lot of file-formats can be loaded into MITK, for example:
    -
  • 2D-images/3D-volumes with or without several timesteps (*.dcm, *.ima, *.pic, ...) -
  • Surfaces (*.stl, *.vtk, ...) -
  • Pointsets (*.mps) -
  • ... +
  • 2D-images / 3D-volumes with or without several time steps (*.dcm, *.ima, *.pic, *.nrrd, ...) +
  • Surfaces (*.stl, *.vtk, ...) +
  • Point sets (*.mps) +
  • and many more
The user can also load a series of 2D images (e.g. image001.png, image002.png ...) to a MITK 3D volume. To do this, just drag and drop one of those 2D data files into the Datamanager by holding the ALT key. +Note: What really happens is that the data nodes are stored inside a "Data storage" and the Datamanager is just a visual representation of the data inside the "Data storage". +That's why the documentation sometimes uses the term "Data storage" instead of "Datamanager". -After loading one or more data into the Datamanager they appear as Data-Elements in a sorted list inside the Datamanager. -Data-Elements can also be sorted hierarchically as a parent-child-relation. -For example after using the Segmentation-Module on Data-Element1 the result is created as Data-Element2, which is a child of Data-Element1 (see Screenshot1). -The order can be changed by drag and drop. +\section DataManagerSavingData Saving Data -\imageMacro{QmitkDatamanager_ParentChild.png,"Screenshot1",9.61} +There are several ways of saving data from the Datamanager: -The listed Data-Elements are shown in the standard view. -Here the user can scale or rotate the medical objects or he can change the cutting planes of the object by just using the mouse inside this view. +
    +
  • use the keyboard shortcut Ctrl + s on a single data node +
  • use the keyboard shortcut Ctrl + s on multiple data nodes +
  • use the context menu right-click -> Save on a single data node +
  • use the context menu right-click -> Save on multiple data nodes +
  • use File -> Save... from the top menu on a single data node +
  • use File -> Save... from the top menu on multiple data nodes +
-\section QmitkDataManagerSaving Saving Data +\section DataManagerWorking Working with the Datamanager -There are two ways of saving data from the Datamanger. The user can either save the whole project with all Data-Elements by clicking on "File"->"Save Project" -or he can save single Data-Elements by right-clicking->"Save", directly on a Data-Element. -When saving the whole project, the sorting of Data-Elements is saved as well. By contrast the sorting is lost, when saving a single Data-Element. +After loading data into the Datamanager the data appears as data nodes in a sorted list inside the Datamanager. +The user can change the order of the data nodes manually by dragging one or multiple data nodes and dropping them at any position inside the Datamanager. +Data nodes can also be sorted hierarchically as a parent-child-relation. +For example after using the Segmentation-Plugin to create a segmentation on DataNode, the result is created as ChildNode, +which is a child of DataNode (see \ref ParentChild "Parent-Child-Relation" screenshot).
+A parent-child-relation can be changed by dragging a child of a data node and dropping it onto another data node. +For this the Property Allow changing of parent nodes needs to be enabled (see \ref DataManagerPreferences "Preferences"). -\section QmitkDataManagerProperties Working with the Datamanager +\anchor ParentChild +\imageMacro{QmitkDatamanager_ParentChild.png, "Parent-Child-Relation", 16.00} -\subsection QmitkDataManagerPropertiesList List of Data-Elements +\subsection DataManagerNodeVisibility Visibility of data nodes -The Data-Elements are listed in the Datamanager. -As described above the elements can be sorted hierarchically as a parent-child-relation. -For example after using the Segmentation-Module on Data-Element1 the result is created as Data-Element2, which is a child of Data-Element1 (see Screenshot1). -By drag and drop the sorting of Data-Elements and their hierarchical relation can be changed. +By default all loaded data nodes are visible in the Standard Display. The visibility is indicated by the checkbox in front of the data node name in the Datamanager. +If a data node is visible, the checkbox is filled - an empty checkbox indicates a hidden data node (see \ref ParentChild "Parent-Child-Relation" screenshot). +Hint: The data nodes are rendered on top of each other, such that the topmost visible data node is always displayed on top of other data nodes. By hiding +the topmost visible data node the next data node becomes the topmost visible data node and the hidden data node is not rendered anymore. +Note: "Visible" does not mean that the node is correctly displayed inside the render windows. The user might have to re-initialize a data node to have it correctly displayed. -\subsection QmitkDataManagerPropertiesVisibility Visibility of Data-Elements +\subsection DataManagerNodeSelection Selection of data nodes -By default all loaded Data-Elements are visible in the standard view. -The visibility can be changed by right-clicking on the Data-Element and then choosing "Toogle visibility". -The box in front of the Data-Element in the Datamanager shows the visibility. -A green-filled box means a visible Data-Element, an empty box means an invisible Data-Element (see Screenshot1). +Some MITK-plugins contain widgets to control the data nodes that are used for plugin-specific data processing. These widgets can be configured such that they listen to +the current selection of the Datamanager. Having such a plugin active each selection change of data nodes inside the data manager will change the respective selection of +the plugin. The data node selection widgets can have specific node predicates, which means that only specific data nodes can be controlled by the widgets. It might happen +that a selected data node will not be represented in such a selection widget or that the selection widget will be emptied. -\subsection QmitkDataManagerPropertiesRepresentation Representation of Data-Elements +\subsection DataManagerNodeRenaming Renaming data nodes -There are different types of representations how to show the Data-Element inside the standard view. By right-clicking on the Data-Element all options are listed (see Screenshot2 and Screenshot 3). +There are two ways of changing the name of a data node inside the Datamanager:
    -
  • An arbitrary color can be chosen -
  • The opacity can be changed with a slide control -
  • In case of images a texture interpolation can be switched on or off. The texture interpolation smoothes the image, so that no single pixels are visible anymore. -
  • In case of surfaces the surface representation can be changed between points, wireframe or surface. -
  • Global reinit updates all windows to contain all the current data: +
  • use the F2 key on a single data node +
  • double-click on a single data node +
- - The orientation of the worldgeometry, which basically defines the rendering space, is set to the standard coordinate system, i.e. [(0,0,1);(0,1,0);(0,0,1)] - - The size of the worldgeometry is calculated, so that it includes all loaded data (depends on size and position of your data) - - The spacing is set to the smallest existing spacing regarding your data +In both cases the new name can be accepted by hitting Enter or by clicking somewhere else. - Reinit updates a single data item and fits the windows to contain only this data item: +\section DataManagerContextMenu Context Menu - - The orientation of the worldgeometry, is aligned according to the orientation of the currently selected datanode - - The size of the worldgeometry is set to the size of the currently selected datanode - - The spacing is set to the spacing of the currently selected datanode +The Datamanager provides a context menu for each data node that can be opened by right-clicking on a data node. +An example of the context-menu can be seen in the \ref ContextMenu "Context menu" screenshot. +The context menu allows to quickly perform common actions on data nodes. These actions differ according to the data type. Some of these actions are described here. +For more actions see the respective modules and plugins (e.g. QmitkCreatePolygonModelAction inside the org_mitk_gui_qt_segmentation-plugin. +
    +
  • Global Reinit: Re-initializes the render windows to the common bounding box of all data nodes of the data storage that +
      +
    • have not set "includeInBoundingBox" to false +
    • are "visible". +
    + In this case it does not matter on which node this action is performed. +
  • Reinit: Re-initializes the render windows to the common bounding box of all selected data nodes of the data storage that +
      +
    • have not set "includeInBoundingBox" to false +
    • are "visible". +
    +
  • Save: see \ref DataManagerSavingData "Saving Data" section +
  • Remove: Removes all selected data nodes from the data storage. +
  • Show only selected nodes: Enables the visibility of all selected data nodes and hides all other data nodes. +
  • Toggle visibility: Shows / hides each selected data node according to each node's current visibility state. +
  • Show details: Opens a pop-up window with detailed information about each node, like data type, geometry, DICOM information, file path etc. +
  • Opacity: Sets the opacity via a slider for the rendering of the selected data node. +
  • Color: Opens a pop-up window that allows to pick an arbitrary color for the rendering of all selected data nodes. +
  • Colormap: Opens another submenu with a list of different colormaps that can be chosen for the rendering of all selected data nodes. +
  • Component: Sets the currently visible data component for the rendering of this particular component of the selected data node. +
  • Texture Interpolation: Smooths the data visualization for rendering of a selected data node. +
  • Surface Representation: Opens another submenu with the following entries: +
      +
    • Points: Visually represents a surface (a data type) as a set of points. +
    • Wireframe: Visually represents a surface (a data type) as a wireframe model. +
    • Surface: Visually represents a surface (a data type) as a solid surface. +
-\imageMacro{QmitkDatamanager_ImageProperties.png,"Screenshot2: Properties for images",10.56} -\imageMacro{QmitkDatamanager_SurfaceProperties.png,"Screenshot3: Properties for surfaces",11.01} +As the description of the actions showed, it is possible to open / use the context menu with a single data node or with a set of selected data nodes. +If the data types of multiple selected data nodes differ, the actions might not appear / work as expected. Also some actions are not available for a set of selected data nodes. +\anchor ContextMenu +\imageMacro{QmitkDatamanager_ContextMenu.png, "Context menu", 16.00} -\subsection QmitkDataManagerPropertiesPreferences Preferences +\section DataManagerPreferences Preferences -For the datamanager there are already some default hotkeys like the del-key for deleting a Data-Element. The whole list is seen in Screenshot4. -From here the Hotkeys can also be changed. The preference page is found in "Window"->"Preferences". +The MITK Workbench provides a preference page for specific plugins. The preference page provided for the Datamanager can be seen in the \ref PreferencePage "Preference page" screenshot. +The user can open the preference page by +
    +
  • using the keyboard shortcut Ctrl + p +
  • using Window -> Preferences... from the top menu. +
+ +It allows to set the following preferences for the Datamanager, which define the behavior of the Datamanager: +
    +
  • Place new nodes on top: If enabled, newly added data nodes will be inserted at the top of the list of data nodes inside the Datamanager. + If disabled, newly added nodes will be inserted at the bottom of the list. +
  • Show helper objects: If enabled, data nodes that have set "helper object" to true will be displayed in the Datamanager. + If disabled, data nodes that have set "helper object" to true will not be visible in the Datamanager. +
  • Show nodes containing no data: If enabled, data nodes that have no underlying base data defined will be displayed in the Datamanager. + If disabled, data nodes that have no underlying base data defined will not be visible in the Datamanager. +
  • Use surface decimation: If enabled, a newly created surface will be decimated to reduce the number of triangles in the triangle mesh. + Such a surface can be created as a polygon model from a segmentation (see \ref DataManagerContextMenu "Context Menu"). + If disabled, the surface will have its original number of triangles in the triangle mesh. +
  • Allow changing of parent node: If enabled, the user can change the hierarchy of the data nodes manually by dragging one or multiple data nodes and + dropping them at any position inside the Datamanager (see \ref DataManagerWorking "Working with the Datamanager") for changing the order of the data nodes manually). +
-\imageMacro{QmitkDatamanager_Preferences.png,"Screenshot4",16.00} +\anchor PreferencePage +\imageMacro{QmitkDatamanager_PreferencePage.png, "Preference page", 16.00} -\section QmitkDataManagerPropertyList Property List +\section DataManagerHotkeys Hotkeys -The Property List displays all the properties the currently selected Data-Element has. Which properties these are depends on the Data-Element. Examples are opacity, shader, visibility. These properties can be changed by clicking on the appropriate field in the "value" column. +The MITK Workbench provides hotkeys for specific plugins. The hotkeys provided for the Datamanager can be seen in the \ref Hotkeys "Hotkeys" screenshot. +They allow to expedite common operations in relation to data nodes or the Datamanager. +The user can customize the hotkeys by accessing the preference page: +
    +
  • using the keyboard shortcut Ctrl + p +
  • useing Window -> Preferences... from the top menu +
-\imageMacro{QmitkDatamanager_PropertyList.png,"Screenshot5: Property List",7.85} +
    +
  • Delete selected nodes Removes all selected data nodes from the data storage. +
  • Global reinit Re-initializes the render windows to the common bounding box of all data nodes of the data storage that +
      +
    • have not set "includeInBoundingBox" to false +
    • are "visible" +
    + In this case it does not matter on which node this action is performed. +
  • Make all nodes invisible Hides all data nodes of the data storage. +
  • Reinit selected nodes Re-initializes the render windows to the common bounding box of all selected data nodes of the data storage that +
      +
    • have not set "includeInBoundingBox" to false +
    • are "visible" +
    +
  • Show node information Opens a pop-up window with detailed information about each node, like data type, geometry, DICOM information, file path etc. +
  • Toggle visibility of selected nodes: Shows / hides each selected data node according to each node's current visibility state. +
+\anchor Hotkeys +\imageMacro{QmitkDatamanager_Hotkeys.png, "Hotkeys", 16.00} */ diff --git a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_ContextMenu.png b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_ContextMenu.png new file mode 100644 index 0000000000..b09f73ca57 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_ContextMenu.png differ diff --git a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_Hotkeys.png b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_Hotkeys.png new file mode 100644 index 0000000000..231ff85929 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_Hotkeys.png differ diff --git a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_Icon.png b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_Icon.png deleted file mode 100644 index 6b35281c8c..0000000000 Binary files a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_Icon.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_ImageProperties.png b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_ImageProperties.png deleted file mode 100644 index 4ed17cad11..0000000000 Binary files a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_ImageProperties.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_Overview.png b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_Overview.png deleted file mode 100644 index 4c4caae3d1..0000000000 Binary files a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_Overview.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_ParentChild.png b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_ParentChild.png index 0f33a98e84..d0168e4043 100644 Binary files a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_ParentChild.png and b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_ParentChild.png differ diff --git a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_PreferencePage.png b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_PreferencePage.png new file mode 100644 index 0000000000..c9532fcbc0 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_PreferencePage.png differ diff --git a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_Preferences.png b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_Preferences.png deleted file mode 100644 index 92e5246d0b..0000000000 Binary files a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_Preferences.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_PropertyList.png b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_PropertyList.png deleted file mode 100644 index 4fee5d9454..0000000000 Binary files a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_PropertyList.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_SurfaceProperties.png b/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_SurfaceProperties.png deleted file mode 100644 index 9e98ca9da6..0000000000 Binary files a/Plugins/org.mitk.gui.qt.datamanager/documentation/UserManual/QmitkDatamanager_SurfaceProperties.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.deformableclippingplane/CMakeLists.txt b/Plugins/org.mitk.gui.qt.deformableclippingplane/CMakeLists.txt new file mode 100644 index 0000000000..8dd1c33292 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.deformableclippingplane/CMakeLists.txt @@ -0,0 +1,6 @@ +project(org_mitk_gui_qt_deformableclippingplane) + +mitk_create_plugin( + EXPORT_DIRECTIVE DEFORMABLECLIPPINGPLANE_EXPORT + EXPORTED_INCLUDE_SUFFIXES src +) diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane.dox b/Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane.dox similarity index 100% rename from Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane.dox rename to Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane.dox diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane_DataManager.png b/Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane_DataManager.png similarity index 100% rename from Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane_DataManager.png rename to Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane_DataManager.png diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane_Icon.png b/Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane_Icon.png similarity index 100% rename from Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane_Icon.png rename to Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane_Icon.png diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane_Interaction.png b/Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane_Interaction.png similarity index 100% rename from Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane_Interaction.png rename to Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane_Interaction.png diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane_Overview.png b/Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane_Overview.png similarity index 100% rename from Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane_Overview.png rename to Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane_Overview.png diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane_Overview2.png b/Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane_Overview2.png similarity index 100% rename from Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane_Overview2.png rename to Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane_Overview2.png diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane_UpdateVolumina.png b/Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane_UpdateVolumina.png similarity index 100% rename from Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkClippingPlane_UpdateVolumina.png rename to Plugins/org.mitk.gui.qt.deformableclippingplane/documentation/UserManual/QmitkClippingPlane_UpdateVolumina.png diff --git a/Plugins/org.mitk.gui.qt.deformableclippingplane/files.cmake b/Plugins/org.mitk.gui.qt.deformableclippingplane/files.cmake new file mode 100644 index 0000000000..d6fb308c8c --- /dev/null +++ b/Plugins/org.mitk.gui.qt.deformableclippingplane/files.cmake @@ -0,0 +1,21 @@ +set(CPP_FILES + src/internal/QmitkDeformableClippingPlaneView.cpp + src/internal/mitkPluginActivator.cpp +) + +set(MOC_H_FILES + src/internal/mitkPluginActivator.h + src/internal/QmitkDeformableClippingPlaneView.h +) + +set(UI_FILES + src/internal/QmitkDeformableClippingPlaneViewControls.ui +) + +set(CACHED_RESOURCE_FILES + resources/deformablePlane.png + resources/clipping_plane_translate_48x48.png + resources/clipping_plane_rotate48x48.png + resources/clipping_plane_deform48x48.png + plugin.xml +) diff --git a/Plugins/org.mitk.gui.qt.deformableclippingplane/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.deformableclippingplane/manifest_headers.cmake new file mode 100644 index 0000000000..92f1980c68 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.deformableclippingplane/manifest_headers.cmake @@ -0,0 +1,5 @@ +set(Plugin-Name "Clipping Plane") +set(Plugin-Version "1.0.0") +set(Plugin-Vendor "German Cancer Research Center (DKFZ)") +set(Plugin-ContactAddress "http://www.mitk.org") +set(Require-Plugin org.mitk.gui.qt.common org.mitk.gui.qt.datamanager) diff --git a/Plugins/org.mitk.gui.qt.deformableclippingplane/plugin.xml b/Plugins/org.mitk.gui.qt.deformableclippingplane/plugin.xml new file mode 100644 index 0000000000..09a9fd4c0c --- /dev/null +++ b/Plugins/org.mitk.gui.qt.deformableclippingplane/plugin.xml @@ -0,0 +1,20 @@ + + + + + Allow the clipping of a volume using a deformable plane. + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.segmentation/resources/clipping_plane_deform48x48.png b/Plugins/org.mitk.gui.qt.deformableclippingplane/resources/clipping_plane_deform48x48.png similarity index 100% rename from Plugins/org.mitk.gui.qt.segmentation/resources/clipping_plane_deform48x48.png rename to Plugins/org.mitk.gui.qt.deformableclippingplane/resources/clipping_plane_deform48x48.png diff --git a/Plugins/org.mitk.gui.qt.segmentation/resources/clipping_plane_rotate48x48.png b/Plugins/org.mitk.gui.qt.deformableclippingplane/resources/clipping_plane_rotate48x48.png similarity index 100% rename from Plugins/org.mitk.gui.qt.segmentation/resources/clipping_plane_rotate48x48.png rename to Plugins/org.mitk.gui.qt.deformableclippingplane/resources/clipping_plane_rotate48x48.png diff --git a/Plugins/org.mitk.gui.qt.segmentation/resources/clipping_plane_translate_48x48.png b/Plugins/org.mitk.gui.qt.deformableclippingplane/resources/clipping_plane_translate_48x48.png similarity index 100% rename from Plugins/org.mitk.gui.qt.segmentation/resources/clipping_plane_translate_48x48.png rename to Plugins/org.mitk.gui.qt.deformableclippingplane/resources/clipping_plane_translate_48x48.png diff --git a/Plugins/org.mitk.gui.qt.segmentation/resources/deformablePlane.png b/Plugins/org.mitk.gui.qt.deformableclippingplane/resources/deformablePlane.png similarity index 100% rename from Plugins/org.mitk.gui.qt.segmentation/resources/deformablePlane.png rename to Plugins/org.mitk.gui.qt.deformableclippingplane/resources/deformablePlane.png diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkDeformableClippingPlaneView.cpp b/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/QmitkDeformableClippingPlaneView.cpp similarity index 100% rename from Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkDeformableClippingPlaneView.cpp rename to Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/QmitkDeformableClippingPlaneView.cpp diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkDeformableClippingPlaneView.h b/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/QmitkDeformableClippingPlaneView.h similarity index 100% rename from Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkDeformableClippingPlaneView.h rename to Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/QmitkDeformableClippingPlaneView.h diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkDeformableClippingPlaneViewControls.ui b/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/QmitkDeformableClippingPlaneViewControls.ui similarity index 94% rename from Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkDeformableClippingPlaneViewControls.ui rename to Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/QmitkDeformableClippingPlaneViewControls.ui index 0130bd6153..f19a2edcc3 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkDeformableClippingPlaneViewControls.ui +++ b/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/QmitkDeformableClippingPlaneViewControls.ui @@ -1,431 +1,429 @@ QmitkDeformableClippingPlaneViewControls 0 0 300 600 0 0 Deformable surface Selected image 0 0 0 40 false false 0 0 Create new clipping plane Qt::RightToLeft ...with surface model true 0 0 0 0 16777215 16777215 Plane 0 0 QComboBox::AdjustToMinimumContentsLength true 50 false 0 0 20 50 Translation - :/org.mitk.gui.qt.segmentation/resources/clipping_plane_translate_48x48.png:/org.mitk.gui.qt.segmentation/resources/clipping_plane_translate_48x48.png + :/org.mitk.gui.qt.deformableclippingplane/resources/clipping_plane_translate_48x48.png:/org.mitk.gui.qt.deformableclippingplane/resources/clipping_plane_translate_48x48.png 32 32 true false false 0 0 20 50 Rotation - :/org.mitk.gui.qt.segmentation/resources/clipping_plane_rotate48x48.png:/org.mitk.gui.qt.segmentation/resources/clipping_plane_rotate48x48.png + :/org.mitk.gui.qt.deformableclippingplane/resources/clipping_plane_rotate48x48.png:/org.mitk.gui.qt.deformableclippingplane/resources/clipping_plane_rotate48x48.png 32 32 true false 0 0 20 50 Deformation - :/org.mitk.gui.qt.segmentation/resources/clipping_plane_deform48x48.png:/org.mitk.gui.qt.segmentation/resources/clipping_plane_deform48x48.png + :/org.mitk.gui.qt.deformableclippingplane/resources/clipping_plane_deform48x48.png:/org.mitk.gui.qt.deformableclippingplane/resources/clipping_plane_deform48x48.png 32 32 true false Qt::Vertical QSizePolicy::Preferred 20 20 0 0 Please select less or equal 6 clipping planes! 0 0 0 0 Referenced image 0 0 Clipping planes Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true 0 0 8 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Update Volumina Qt::Vertical QSizePolicy::Preferred 20 20 true 0 0 QmitkSingleNodeSelectionWidget QWidget
QmitkSingleNodeSelectionWidget.h
QmitkDataStorageComboBox QComboBox
QmitkDataStorageComboBox.h
- - - +
diff --git a/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/mitkPluginActivator.cpp new file mode 100644 index 0000000000..6fdaaa8cdd --- /dev/null +++ b/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/mitkPluginActivator.cpp @@ -0,0 +1,23 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkPluginActivator.h" +#include "QmitkDeformableClippingPlaneView.h" + +void mitk::PluginActivator::start(ctkPluginContext *context) +{ + BERRY_REGISTER_EXTENSION_CLASS(QmitkDeformableClippingPlaneView, context) +} + +void mitk::PluginActivator::stop(ctkPluginContext *) +{ +} diff --git a/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/mitkPluginActivator.h b/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/mitkPluginActivator.h new file mode 100644 index 0000000000..6a85b014de --- /dev/null +++ b/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/mitkPluginActivator.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 PluginActivator_h +#define PluginActivator_h + +#include + +namespace mitk +{ + class PluginActivator : public QObject, public ctkPluginActivator + { + Q_OBJECT + Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_deformableclippingplane") + Q_INTERFACES(ctkPluginActivator) + + public: + void start(ctkPluginContext *context) override; + void stop(ctkPluginContext *context) override; + }; +} + +#endif diff --git a/Plugins/org.mitk.gui.qt.dicominspector/documentation/UserManual/Manual.dox b/Plugins/org.mitk.gui.qt.dicominspector/documentation/UserManual/Manual.dox index f45e70d5e1..e1305a43b8 100644 --- a/Plugins/org.mitk.gui.qt.dicominspector/documentation/UserManual/Manual.dox +++ b/Plugins/org.mitk.gui.qt.dicominspector/documentation/UserManual/Manual.dox @@ -1,12 +1,12 @@ /** -\page org_mitk_gui_qt_dicominspector The DICOM Inspector +\page org_mitk_views_dicominspector The DICOM Inspector \imageMacro{ "inspector.png", "Icon of DICOM Inspector", 2} This is a simple view that displays all DICOM properties of the data of the currently selected node. DICOM properties are all properties that have a property name starting with "DICOM". The plugin will show the dicom values corresponding to the currently selected time point and z slice. E.G. the value of "acquesition time" will change with changing the time step of 3d+t image or the value of "slice position" will change with changing the current z slice of the image. */ diff --git a/Plugins/org.mitk.gui.qt.dicominspector/plugin.xml b/Plugins/org.mitk.gui.qt.dicominspector/plugin.xml index dde8b4b9d1..8da249251b 100644 --- a/Plugins/org.mitk.gui.qt.dicominspector/plugin.xml +++ b/Plugins/org.mitk.gui.qt.dicominspector/plugin.xml @@ -1,10 +1,10 @@ - diff --git a/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp b/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp index 8400fd4978..531f020f2b 100644 --- a/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp +++ b/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp @@ -1,376 +1,376 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include #include // mitk #include #include #include // Qt #include #include #include #include #include "QmitkDicomInspectorView.h" -const std::string QmitkDicomInspectorView::VIEW_ID = "org.mitk.gui.qt.dicominspector"; +const std::string QmitkDicomInspectorView::VIEW_ID = "org.mitk.views.dicominspector"; QmitkDicomInspectorView::ObserverInfo::ObserverInfo(mitk::SliceNavigationController* controller, int observerTag, const std::string& renderWindowName, mitk::IRenderWindowPart* part) : controller(controller), observerTag(observerTag), renderWindowName(renderWindowName), renderWindowPart(part) { } QmitkDicomInspectorView::QmitkDicomInspectorView() : m_RenderWindowPart(nullptr) , m_PendingSliceChangedEvent(false) , m_SelectedNode(nullptr) , m_SelectedTimePoint(0.) , m_CurrentSelectedZSlice(0) { m_SelectedPosition.Fill(0.0); } QmitkDicomInspectorView::~QmitkDicomInspectorView() { this->RemoveAllObservers(); } void QmitkDicomInspectorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_RenderWindowPart != renderWindowPart) { m_RenderWindowPart = renderWindowPart; if (!InitObservers()) { QMessageBox::information(nullptr, "Error", "Unable to set up the event observers. The " \ "plot will not be triggered on changing the crosshair, " \ "position or time step."); } } } void QmitkDicomInspectorView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) { m_RenderWindowPart = nullptr; this->RemoveAllObservers(renderWindowPart); } void QmitkDicomInspectorView::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Controls.singleSlot->SetDataStorage(GetDataStorage()); m_Controls.singleSlot->SetSelectionIsOptional(true); m_Controls.singleSlot->SetEmptyInfo(QString("Please select a data node")); m_Controls.singleSlot->SetPopUpTitel(QString("Select data node")); m_SelectionServiceConnector = std::make_unique(); SetAsSelectionListener(true); m_Controls.timePointValueLabel->setText(QString("")); m_Controls.sliceNumberValueLabel->setText(QString("")); connect(m_Controls.singleSlot, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkDicomInspectorView::OnCurrentSelectionChanged); mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart(); RenderWindowPartActivated(renderWindowPart); } bool QmitkDicomInspectorView::InitObservers() { bool result = true; typedef QHash WindowMapType; WindowMapType windowMap = m_RenderWindowPart->GetQmitkRenderWindows(); auto i = windowMap.begin(); while (i != windowMap.end()) { mitk::SliceNavigationController* sliceNavController = i.value()->GetSliceNavigationController(); if (sliceNavController) { auto cmdSliceEvent = itk::SimpleMemberCommand::New(); cmdSliceEvent->SetCallbackFunction(this, &QmitkDicomInspectorView::OnSliceChanged); int tag = sliceNavController->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0), cmdSliceEvent); m_ObserverMap.insert(std::make_pair(sliceNavController, ObserverInfo(sliceNavController, tag, i.key().toStdString(), m_RenderWindowPart))); auto cmdTimeEvent = itk::SimpleMemberCommand::New(); cmdTimeEvent->SetCallbackFunction(this, &QmitkDicomInspectorView::OnSliceChanged); tag = sliceNavController->AddObserver( mitk::SliceNavigationController::GeometryTimeEvent(nullptr, 0), cmdTimeEvent); m_ObserverMap.insert(std::make_pair(sliceNavController, ObserverInfo(sliceNavController, tag, i.key().toStdString(), m_RenderWindowPart))); auto cmdDelEvent = itk::MemberCommand::New(); cmdDelEvent->SetCallbackFunction(this, &QmitkDicomInspectorView::OnSliceNavigationControllerDeleted); tag = sliceNavController->AddObserver(itk::DeleteEvent(), cmdDelEvent); m_ObserverMap.insert(std::make_pair(sliceNavController, ObserverInfo(sliceNavController, tag, i.key().toStdString(), m_RenderWindowPart))); } ++i; result = result && sliceNavController; } return result; } void QmitkDicomInspectorView::RemoveObservers(const mitk::SliceNavigationController* deletedSlicer) { std::pair obsRange = m_ObserverMap.equal_range(deletedSlicer); for (ObserverMapType::const_iterator pos = obsRange.first; pos != obsRange.second; ++pos) { pos->second.controller->RemoveObserver(pos->second.observerTag); } m_ObserverMap.erase(deletedSlicer); } void QmitkDicomInspectorView::RemoveAllObservers(mitk::IRenderWindowPart* deletedPart) { for (ObserverMapType::const_iterator pos = m_ObserverMap.begin(); pos != m_ObserverMap.end();) { ObserverMapType::const_iterator delPos = pos++; if (nullptr == deletedPart || deletedPart == delPos->second.renderWindowPart) { delPos->second.controller->RemoveObserver(delPos->second.observerTag); m_ObserverMap.erase(delPos); } } } void QmitkDicomInspectorView::OnCurrentSelectionChanged(QList nodes) { if (nodes.empty() || nodes.front().IsNull()) { m_SelectedNode = nullptr; m_SelectedData = nullptr; UpdateData(); return; } if (nodes.front() != this->m_SelectedNode) { // node is selected, create DICOM tag table m_SelectedNode = nodes.front(); m_SelectedData = this->m_SelectedNode->GetData(); m_SelectedNodeTime.Modified(); UpdateData(); OnSliceChangedDelayed(); } } void QmitkDicomInspectorView::OnSliceChanged() { // Taken from QmitkStdMultiWidget::HandleCrosshairPositionEvent(). // Since there are always 3 events arriving (one for each render window) every time the slice // or time changes, the slot OnSliceChangedDelayed is triggered - and only if it hasn't been // triggered yet - so it is only executed once for every slice/time change. if (!m_PendingSliceChangedEvent) { m_PendingSliceChangedEvent = true; QTimer::singleShot(0, this, SLOT(OnSliceChangedDelayed())); } } void QmitkDicomInspectorView::OnSliceNavigationControllerDeleted(const itk::Object* sender, const itk::EventObject& /*e*/) { auto sendingSlicer = dynamic_cast(sender); this->RemoveObservers(sendingSlicer); } void QmitkDicomInspectorView::ValidateAndSetCurrentPosition() { mitk::Point3D currentSelectedPosition = GetRenderWindowPart()->GetSelectedPosition(nullptr); const auto currentSelectedTimePoint = GetRenderWindowPart()->GetSelectedTimePoint(); if (m_SelectedPosition != currentSelectedPosition || m_SelectedTimePoint != currentSelectedTimePoint || m_SelectedNodeTime > m_CurrentPositionTime) { // the current position has been changed, the selected node has been changed since // the last position validation or the current time position has been changed -> check position m_SelectedPosition = currentSelectedPosition; m_SelectedTimePoint = currentSelectedTimePoint; m_CurrentPositionTime.Modified(); m_ValidSelectedPosition = false; if (m_SelectedData.IsNull()) { return; } mitk::BaseGeometry::Pointer geometry = m_SelectedData->GetTimeGeometry()->GetGeometryForTimePoint(m_SelectedTimePoint); // check for invalid time step if (geometry.IsNull()) { geometry = m_SelectedData->GetTimeGeometry()->GetGeometryForTimeStep(0); } if (geometry.IsNull()) { return; } m_ValidSelectedPosition = geometry->IsInside(m_SelectedPosition); itk::Index<3> index; geometry->WorldToIndex(m_SelectedPosition, index); m_CurrentSelectedZSlice = index[2]; } } void QmitkDicomInspectorView::OnSliceChangedDelayed() { m_PendingSliceChangedEvent = false; ValidateAndSetCurrentPosition(); m_Controls.tableTags->setEnabled(m_ValidSelectedPosition); if (m_SelectedNode.IsNotNull()) { RenderTable(); } } void QmitkDicomInspectorView::RenderTable() { assert(nullptr != m_RenderWindowPart); const auto timeStep = (m_SelectedData.IsNull()) ? 0 : m_SelectedData->GetTimeGeometry()->TimePointToTimeStep(m_SelectedTimePoint); unsigned int rowIndex = 0; for (const auto& element : m_Tags) { QTableWidgetItem* newItem = new QTableWidgetItem(QString::fromStdString( element.second.prop->GetValue(timeStep, m_CurrentSelectedZSlice, true, true))); m_Controls.tableTags->setItem(rowIndex, 3, newItem); ++rowIndex; } UpdateLabels(); } void QmitkDicomInspectorView::UpdateData() { QStringList headers; m_Controls.tableTags->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); m_Tags.clear(); if (m_SelectedData.IsNotNull()) { for (const auto& element : *(m_SelectedData->GetPropertyList()->GetMap())) { if (element.first.find("DICOM") == 0) { std::istringstream stream(element.first); std::string token; std::getline(stream, token, '.'); //drop the DICOM suffix std::getline(stream, token, '.'); //group id unsigned long dcmgroup = std::stoul(token, nullptr, 16); std::getline(stream, token, '.'); //element id unsigned long dcmelement = std::stoul(token, nullptr, 16); TagInfo info(mitk::DICOMTag(dcmgroup, dcmelement), dynamic_cast(element.second.GetPointer())); m_Tags.insert(std::make_pair(element.first, info)); } } } m_Controls.tableTags->setRowCount(m_Tags.size()); unsigned int rowIndex = 0; for (const auto& element : m_Tags) { QTableWidgetItem* newItem = new QTableWidgetItem(QString::number(element.second.tag.GetGroup(), 16)); m_Controls.tableTags->setItem(rowIndex, 0, newItem); newItem = new QTableWidgetItem(QString::number(element.second.tag.GetElement(), 16)); m_Controls.tableTags->setItem(rowIndex, 1, newItem); newItem = new QTableWidgetItem(QString::fromStdString(element.second.tag.GetName())); m_Controls.tableTags->setItem(rowIndex, 2, newItem); newItem = new QTableWidgetItem(QString::fromStdString(element.second.prop->GetValue())); m_Controls.tableTags->setItem(rowIndex, 3, newItem); ++rowIndex; } UpdateLabels(); } void QmitkDicomInspectorView::UpdateLabels() { if (m_SelectedData.IsNull()) { m_Controls.timePointValueLabel->setText(QString("")); m_Controls.sliceNumberValueLabel->setText(QString("")); } else { const auto timeStep = m_SelectedData->GetTimeGeometry()->TimePointToTimeStep(m_SelectedTimePoint); if (m_ValidSelectedPosition) { m_Controls.timePointValueLabel->setText(QString::number(timeStep) + QStringLiteral("(")+ QString::number(m_SelectedTimePoint/1000.) + QStringLiteral(" [s])")); m_Controls.sliceNumberValueLabel->setText(QString::number(m_CurrentSelectedZSlice)); } else { m_Controls.timePointValueLabel->setText(QString("outside data geometry")); m_Controls.sliceNumberValueLabel->setText(QString("outside data geometry")); } } } void QmitkDicomInspectorView::SetAsSelectionListener(bool checked) { if (checked) { m_SelectionServiceConnector->AddPostSelectionListener(GetSite()->GetWorkbenchWindow()->GetSelectionService()); connect(m_SelectionServiceConnector.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.singleSlot, &QmitkSingleNodeSelectionWidget::SetCurrentSelection); } else { m_SelectionServiceConnector->RemovePostSelectionListener(); disconnect(m_SelectionServiceConnector.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.singleSlot, &QmitkSingleNodeSelectionWidget::SetCurrentSelection); } } diff --git a/Plugins/org.mitk.gui.qt.fit.demo/src/internal/FitGeneratorDemoView.cpp b/Plugins/org.mitk.gui.qt.fit.demo/src/internal/FitGeneratorDemoView.cpp index 899fbaee1a..321d361ff4 100644 --- a/Plugins/org.mitk.gui.qt.fit.demo/src/internal/FitGeneratorDemoView.cpp +++ b/Plugins/org.mitk.gui.qt.fit.demo/src/internal/FitGeneratorDemoView.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 "FitGeneratorDemoView.h" #include #include #include #include #include "mitkWorkbenchUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include -const std::string FitGeneratorDemoView::VIEW_ID = "org.mitk.gui.qt.fit.demo"; +const std::string FitGeneratorDemoView::VIEW_ID = "org.mitk.views.fit.demo"; typedef itk::Image FrameITKImageType; typedef itk::Image DynamicITKImageType; void FitGeneratorDemoView::SetFocus() { m_Controls.btnModelling->setFocus(); } void FitGeneratorDemoView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnModelling->setEnabled(false); connect(m_Controls.btnModelling, SIGNAL(clicked()), this, SLOT(OnModellingButtonClicked())); connect(m_Controls.btnGenerateTestData, SIGNAL(clicked()), this, SLOT(OnGenerateTestDataButtonClicked())); m_Controls.leFitName->setText(tr("demo")); } void FitGeneratorDemoView::OnModellingButtonClicked() { Generate(); } void FitGeneratorDemoView::OnGenerateTestDataButtonClicked() { mitk::Image::Pointer testImage = mitk::GenerateDynamicTestImageMITK(); mitk::DataNode::Pointer testNode = mitk::DataNode::New(); testNode->SetData(testImage); testNode->SetName("LinearModel_4DTestImage"); this->GetDataStorage()->Add(testNode); } void FitGeneratorDemoView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList& /*nodes*/) { QList dataNodes = this->GetDataManagerSelection(); m_selectedNode = nullptr; m_selectedImage = nullptr; m_selectedMaskNode = nullptr; m_selectedMask = nullptr; if (!dataNodes.empty()) { m_selectedNode = dataNodes[0]; mitk::Image* selectedImage = dynamic_cast(m_selectedNode->GetData()); if (selectedImage && selectedImage->GetDimension(3) > 1) { m_selectedImage = selectedImage; } if (dataNodes.size() > 1) { m_selectedMaskNode = dataNodes[1]; mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isLegacyMask = mitk::NodePredicateAnd::New(isImage, isBinary); mitk::NodePredicateOr::Pointer maskPredicate = mitk::NodePredicateOr::New(isLegacyMask, isLabelSet); mitk::Image* selectedMask = dynamic_cast(this->m_selectedMaskNode->GetData()); if (selectedMask && maskPredicate->CheckNode(m_selectedMaskNode)) { m_selectedMask = selectedMask; if (this->m_selectedMask->GetTimeSteps() > 1) { MITK_INFO << "Selected mask has multiple timesteps. Only use first timestep to mask model fit. Mask name: " << m_selectedMaskNode->GetName() ; mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedMask = maskedImageTimeSelector->GetOutput(); } } } } if (m_selectedImage) { m_Controls.lTimeseries->setText((m_selectedNode->GetName()).c_str()); } else { if (m_selectedNode.IsNull()) { m_Controls.lTimeseries->setText("None"); } else { m_Controls.lTimeseries->setText("Error. Selected node #1 is no 4D image!"); } } if (m_selectedMask) { m_Controls.lMask->setText((m_selectedMaskNode->GetName()).c_str()); } else { if (m_selectedMaskNode.IsNull()) { m_Controls.lMask->setText("None"); } else { m_Controls.lMask->setText("Error. Selected node #2 is no mask!"); } } m_Controls.btnModelling->setEnabled(m_selectedImage.IsNotNull()); } void FitGeneratorDemoView::Generate() { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); //Model configuration (static parameters) can be done now //Specify fitting strategy and evaluation parameters mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor = mitk::LevenbergMarquardtModelFitFunctor::New(); mitk::SumOfSquaredDifferencesFitCostFunction::Pointer evaluation = mitk::SumOfSquaredDifferencesFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("sum_diff^2", evaluation); //Parametrize fit generator mitk::LinearModelParameterizer::Pointer parameterizer = mitk::LinearModelParameterizer::New(); fitGenerator->SetModelParameterizer(parameterizer); fitGenerator->SetDynamicImage(m_selectedImage); fitGenerator->SetFitFunctor(fitFunctor); if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); } mitk::modelFit::ModelFitInfo::Pointer fitSession = mitk::modelFit::CreateFitInfoFromModelParameterizer(parameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), m_Controls.leFitName->text().toStdString()); ///////////////////////// //create job and put it into the thread pool ParameterFitBackgroundJob* pJob = new ParameterFitBackgroundJob(fitGenerator, fitSession, m_selectedNode); pJob->setAutoDelete(true); connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnJobFinished())); connect(pJob, SIGNAL(ResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), this, SLOT(OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(JobProgress(double)), this, SLOT(OnJobProgress(double))); connect(pJob, SIGNAL(JobStatusChanged(QString)), this, SLOT(OnJobStatusChanged(QString))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } FitGeneratorDemoView::FitGeneratorDemoView() { m_selectedImage = nullptr; m_selectedNode = nullptr; } void FitGeneratorDemoView::OnJobFinished() { this->m_Controls.textEdit->append(QString("Fitting finished")); }; void FitGeneratorDemoView::OnJobError(QString err) { MITK_ERROR << err.toStdString().c_str(); m_Controls.textEdit->append(QString("") + err + QString("")); }; void FitGeneratorDemoView::OnJobResultsAreAvailable( mitk::modelFit::ModelFitResultNodeVectorType results, const ParameterFitBackgroundJob* pJob) { //Store the resulting parameter fit image via convenience helper function in data storage //(handles the correct generation of the nodes and their properties) mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), results, pJob->GetParentNode()); }; void FitGeneratorDemoView::OnJobProgress(double progress) { QString report = QString("Progress. ") + QString::number(progress); this->m_Controls.textEdit->append(report); }; void FitGeneratorDemoView::OnJobStatusChanged(QString info) { this->m_Controls.textEdit->append(info); } diff --git a/Plugins/org.mitk.gui.qt.fit.genericfitting/documentation/UserManual/Manual.dox b/Plugins/org.mitk.gui.qt.fit.genericfitting/documentation/UserManual/Manual.dox index 063fdf0dd9..c90a30a4de 100644 --- a/Plugins/org.mitk.gui.qt.fit.genericfitting/documentation/UserManual/Manual.dox +++ b/Plugins/org.mitk.gui.qt.fit.genericfitting/documentation/UserManual/Manual.dox @@ -1,16 +1,16 @@ /** -\page org_mitk_gui_qt_fit_genericfitting The Model Fit Generic Fitting View +\page org_mitk_views_fit_genericfitting The Model Fit Generic Fitting View \imageMacro{fit_generic_doc.svg,"Icon of the Generic Fitting View",3.0} \tableofcontents \section FIT_GENERIC_Introduction Introduction This plug-in offers a generic fitting component for time resolved image data. \section FIT_GENERIC_Contact Contact information This plug-in is being developed by the SIDT group (Software development for Integrated Diagnostics and Therapy) at the German Cancer Research Center (DKFZ). If you have any questions, need support, find a bug or have a feature request, feel free to contact us at www.mitk.org. */ diff --git a/Plugins/org.mitk.gui.qt.fit.genericfitting/plugin.xml b/Plugins/org.mitk.gui.qt.fit.genericfitting/plugin.xml index c77936d932..258904cf04 100644 --- a/Plugins/org.mitk.gui.qt.fit.genericfitting/plugin.xml +++ b/Plugins/org.mitk.gui.qt.fit.genericfitting/plugin.xml @@ -1,12 +1,12 @@ - diff --git a/Plugins/org.mitk.gui.qt.fit.genericfitting/src/internal/GenericDataFittingView.cpp b/Plugins/org.mitk.gui.qt.fit.genericfitting/src/internal/GenericDataFittingView.cpp index 3a8dfb2d14..44a8d83a62 100644 --- a/Plugins/org.mitk.gui.qt.fit.genericfitting/src/internal/GenericDataFittingView.cpp +++ b/Plugins/org.mitk.gui.qt.fit.genericfitting/src/internal/GenericDataFittingView.cpp @@ -1,639 +1,639 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "GenericDataFittingView.h" #include "mitkWorkbenchUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Includes for image casting between ITK and MITK #include #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include #include #include -const std::string GenericDataFittingView::VIEW_ID = "org.mitk.gui.qt.fit.genericfitting"; +const std::string GenericDataFittingView::VIEW_ID = "org.mitk.views.fit.genericfitting"; void GenericDataFittingView::SetFocus() { m_Controls.btnModelling->setFocus(); } void GenericDataFittingView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnModelling->setEnabled(false); this->InitModelComboBox(); connect(m_Controls.btnModelling, SIGNAL(clicked()), this, SLOT(OnModellingButtonClicked())); connect(m_Controls.comboModel, SIGNAL(currentIndexChanged(int)), this, SLOT(OnModellSet(int))); connect(m_Controls.radioPixelBased, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); //Gerneric setting m_Controls.groupGeneric->hide(); m_Controls.labelFormulaInfo->hide(); connect(m_Controls.editFormula, SIGNAL(textChanged(const QString&)), this, SLOT(UpdateGUIControls())); connect(m_Controls.checkFormulaInfo, SIGNAL(toggled(bool)), m_Controls.labelFormulaInfo, SLOT(setVisible(bool))); connect(m_Controls.nrOfParams, SIGNAL(valueChanged(int)), this, SLOT(OnNrOfParamsChanged())); //Model fit configuration m_Controls.groupBox_FitConfiguration->hide(); m_Controls.checkBox_Constraints->setEnabled(false); m_Controls.constraintManager->setEnabled(false); m_Controls.initialValuesManager->setEnabled(false); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), m_Controls.initialValuesManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setVisible(bool))); UpdateGUIControls(); } void GenericDataFittingView::UpdateGUIControls() { m_Controls.lineFitName->setPlaceholderText(QString::fromStdString(this->GetDefaultFitName())); m_Controls.lineFitName->setEnabled(!m_FittingInProgress); m_Controls.checkBox_Constraints->setEnabled(m_modelConstraints.IsNotNull()); bool isGenericFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; m_Controls.groupGeneric->setVisible(isGenericFactory); m_Controls.groupBox_FitConfiguration->setVisible(m_selectedModelFactory); m_Controls.groupBox->setEnabled(!m_FittingInProgress); m_Controls.comboModel->setEnabled(!m_FittingInProgress); m_Controls.groupGeneric->setEnabled(!m_FittingInProgress); m_Controls.groupBox_FitConfiguration->setEnabled(!m_FittingInProgress); m_Controls.radioROIbased->setEnabled(m_selectedMask.IsNotNull()); m_Controls.btnModelling->setEnabled(m_selectedImage.IsNotNull() && m_selectedModelFactory.IsNotNull() && !m_FittingInProgress && CheckModelSettings()); } std::string GenericDataFittingView::GetFitName() const { std::string fitName = m_Controls.lineFitName->text().toStdString(); if (fitName.empty()) { fitName = m_Controls.lineFitName->placeholderText().toStdString(); } return fitName; } std::string GenericDataFittingView::GetDefaultFitName() const { std::string defaultName = "undefined model"; if (this->m_selectedModelFactory.IsNotNull()) { defaultName = this->m_selectedModelFactory->GetClassID(); } if (this->m_Controls.radioPixelBased->isChecked()) { defaultName += "_pixel"; } else { defaultName += "_roi"; } return defaultName; } void GenericDataFittingView::OnNrOfParamsChanged() { PrepareFitConfiguration(); UpdateGUIControls(); } void GenericDataFittingView::OnModellSet(int index) { m_selectedModelFactory = nullptr; if (index > 0) { if (static_cast(index) <= m_FactoryStack.size() ) { m_selectedModelFactory = m_FactoryStack[index - 1]; } else { MITK_WARN << "Invalid model index. Index outside of the factory stack. Factory stack size: "<< m_FactoryStack.size() << "; invalid index: "<< index; } } UpdateGUIControls(); } bool GenericDataFittingView::IsGenericParamFactorySelected() const { return dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; } void GenericDataFittingView::PrepareFitConfiguration() { if (m_selectedModelFactory) { mitk::ModelBase::ParameterNamesType paramNames = m_selectedModelFactory->GetParameterNames(); unsigned int nrOfPools = this->m_Controls.nrOfParams->value(); //init values if (this->IsGenericParamFactorySelected()) { mitk::modelFit::ModelFitInfo::Pointer fitInfo = mitk::modelFit::ModelFitInfo::New(); fitInfo->staticParamMap.Add(mitk::GenericParamModel::NAME_STATIC_PARAMETER_number, { static_cast(nrOfPools) }); auto parameterizer = m_selectedModelFactory->CreateParameterizer(fitInfo); paramNames = parameterizer->GetParameterNames(); m_Controls.initialValuesManager->setInitialValues(paramNames, parameterizer->GetDefaultInitialParameterization()); } else { m_Controls.initialValuesManager->setInitialValues(paramNames, this->m_selectedModelFactory->GetDefaultInitialParameterization()); } //constraints this->m_modelConstraints = dynamic_cast (m_selectedModelFactory->CreateDefaultConstraints().GetPointer()); if (this->m_modelConstraints.IsNull()) { this->m_modelConstraints = mitk::SimpleBarrierConstraintChecker::New(); } m_Controls.constraintManager->setChecker(this->m_modelConstraints, paramNames); } }; void GenericDataFittingView::OnModellingButtonClicked() { //check if all static parameters set if (m_selectedModelFactory.IsNotNull() && CheckModelSettings()) { mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr; mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr; bool isLinearFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isGenericFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isT2DecayFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; if (isLinearFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } else if (isGenericFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } else if (isT2DecayFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } //add other models with else if if (generator.IsNotNull() && fitSession.IsNotNull()) { m_FittingInProgress = true; UpdateGUIControls(); DoFit(fitSession, generator); } else { QMessageBox box; box.setText("Fitting error!"); box.setInformativeText("Could not establish fitting job. Error when setting ab generator, model parameterizer or session info."); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } else { QMessageBox box; box.setText("Static parameters for model are not set!"); box.setInformativeText("Some static parameters, that are needed for calculation are not set and equal to zero. Modeling not possible"); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } void GenericDataFittingView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList& selectedNodes) { m_selectedNode = nullptr; m_selectedImage = nullptr; m_selectedMaskNode = nullptr; m_selectedMask = nullptr; m_Controls.masklabel->setText("No (valid) mask selected."); m_Controls.timeserieslabel->setText("No (valid) series selected."); QList nodes = selectedNodes; mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isLegacyMask = mitk::NodePredicateAnd::New(isImage, isBinary); mitk::NodePredicateOr::Pointer maskPredicate = mitk::NodePredicateOr::New(isLegacyMask, isLabelSet); if (nodes.size() > 0 && isImage->CheckNode(nodes.front())) { this->m_selectedNode = nodes.front(); this->m_selectedImage = dynamic_cast(this->m_selectedNode->GetData()); m_Controls.timeserieslabel->setText((this->m_selectedNode->GetName()).c_str()); nodes.pop_front(); } if (nodes.size() > 0 && maskPredicate->CheckNode(nodes.front())) { this->m_selectedMaskNode = nodes.front(); this->m_selectedMask = dynamic_cast(this->m_selectedMaskNode->GetData()); if (this->m_selectedMask->GetTimeSteps() > 1) { MITK_INFO << "Selected mask has multiple timesteps. Only use first timestep to mask model fit. Mask name: " << m_selectedMaskNode->GetName(); mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedMask = maskedImageTimeSelector->GetOutput(); } m_Controls.masklabel->setText((this->m_selectedMaskNode->GetName()).c_str()); } if (m_selectedMask.IsNull()) { this->m_Controls.radioPixelBased->setChecked(true); } UpdateGUIControls(); } bool GenericDataFittingView::CheckModelSettings() const { bool ok = true; //check wether any model is set at all. Otherwise exit with false if (m_selectedModelFactory.IsNotNull()) { bool isGenericFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; if (isGenericFactory) { ok = !m_Controls.editFormula->text().isEmpty(); } } else { ok = false; } return ok; } void GenericDataFittingView::ConfigureInitialParametersOfParameterizer(mitk::ModelParameterizerBase* parameterizer) const { if (m_Controls.radioButton_StartParameters->isChecked()) { //use user defined initial parameters mitk::ValueBasedParameterizationDelegate::Pointer paramDelegate = mitk::ValueBasedParameterizationDelegate::New(); paramDelegate->SetInitialParameterization(m_Controls.initialValuesManager->getInitialValues()); parameterizer->SetInitialParameterizationDelegate(paramDelegate); } mitk::GenericParamModelParameterizer* genericParameterizer = dynamic_cast(parameterizer); if (genericParameterizer) { genericParameterizer->SetFunctionString(m_Controls.editFormula->text().toStdString()); } } template void GenericDataFittingView::GenerateModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); auto genericParameterizer = dynamic_cast(modelParameterizer.GetPointer()); if (genericParameterizer) { genericParameterizer->SetNumberOfParameters(this->m_Controls.nrOfParams->value()); } this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); roiUID = m_selectedMask->GetUID(); } fitGenerator->SetDynamicImage(this->m_selectedImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); } template void GenericDataFittingView::GenerateModelFit_ROIBased( mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); auto genericParameterizer = dynamic_cast(modelParameterizer.GetPointer()); if (genericParameterizer) { genericParameterizer->SetNumberOfParameters(this->m_Controls.nrOfParams->value()); } this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); signalGenerator->SetDynamicImage(m_selectedImage); 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(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(m_selectedImage)); generator = fitGenerator.GetPointer(); std::string roiUID = this->m_selectedMask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), 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); } void GenericDataFittingView::DoFit(const mitk::modelFit::ModelFitInfo* fitSession, mitk::ParameterFitImageGeneratorBase* generator) { QString message = "Fitting Data Set . . ."; m_Controls.infoBox->append(message); ///////////////////////// //create job and put it into the thread pool ParameterFitBackgroundJob* pJob = new ParameterFitBackgroundJob(generator, fitSession, this->m_selectedNode); pJob->setAutoDelete(true); connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnJobFinished())); connect(pJob, SIGNAL(ResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), this, SLOT(OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(JobProgress(double)), this, SLOT(OnJobProgress(double))); connect(pJob, SIGNAL(JobStatusChanged(QString)), this, SLOT(OnJobStatusChanged(QString))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } GenericDataFittingView::GenericDataFittingView() : m_FittingInProgress(false) { m_selectedImage = nullptr; m_selectedMask = nullptr; mitk::ModelFactoryBase::Pointer factory = mitk::LinearModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::GenericParamModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::T2DecayModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); this->m_IsNotABinaryImagePredicate = mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true))), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); this->m_IsBinaryImagePredicate = mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); } void GenericDataFittingView::OnJobFinished() { this->m_Controls.infoBox->append(QString("Fitting finished")); this->m_FittingInProgress = false; this->UpdateGUIControls(); }; void GenericDataFittingView::OnJobError(QString err) { MITK_ERROR << err.toStdString().c_str(); m_Controls.infoBox->append(QString("") + err + QString("")); }; void GenericDataFittingView::OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType results, const ParameterFitBackgroundJob* pJob) { //Store the resulting parameter fit image via convenience helper function in data storage //(handles the correct generation of the nodes and their properties) mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), results, pJob->GetParentNode()); }; void GenericDataFittingView::OnJobProgress(double progress) { QString report = QString("Progress. ") + QString::number(progress); this->m_Controls.infoBox->append(report); }; void GenericDataFittingView::OnJobStatusChanged(QString info) { this->m_Controls.infoBox->append(info); } void GenericDataFittingView::InitModelComboBox() const { this->m_Controls.comboModel->clear(); this->m_Controls.comboModel->addItem(tr("No model selected")); for (ModelFactoryStackType::const_iterator pos = m_FactoryStack.begin(); pos != m_FactoryStack.end(); ++pos) { this->m_Controls.comboModel->addItem(QString::fromStdString((*pos)->GetClassID())); } this->m_Controls.comboModel->setCurrentIndex(0); }; mitk::ModelFitFunctorBase::Pointer GenericDataFittingView::CreateDefaultFitFunctor( const mitk::ModelParameterizerBase* parameterizer) const { mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor = mitk::LevenbergMarquardtModelFitFunctor::New(); mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::Pointer chi2 = mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("Chi^2", chi2); if (m_Controls.checkBox_Constraints->isChecked()) { fitFunctor->SetConstraintChecker(m_modelConstraints); } mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel(); ::itk::LevenbergMarquardtOptimizer::ScalesType scales; scales.SetSize(refModel->GetNumberOfParameters()); scales.Fill(1.0); fitFunctor->SetScales(scales); fitFunctor->SetDebugParameterMaps(m_Controls.checkDebug->isChecked()); return fitFunctor.GetPointer(); } 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 dcf1116823..89d950ed79 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,920 +1,920 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include #include // mitk #include // Qt #include #include #include #include #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 "mitkTimeGridHelper.h" #include "mitkModelFitResultRelationRule.h" #include "mitkModelFitPlotDataHelper.h" #include "ModelFitInspectorView.h" -const std::string ModelFitInspectorView::VIEW_ID = "org.mitk.gui.gt.fit.inspector"; +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(); 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("No input data is selected")); m_Controls.inputNodeSelector->SetPopUpTitel(QString("Choose 3D+t input data that should be viewed!")); m_Controls.inputNodeSelector->SetSelectionIsOptional(false); m_Controls.inputNodeSelector->SetSelectOnlyVisibleNodes(true); 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()); 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 avaible in plugin selection. Failed fit UID:" << selectedFitUD; } }; m_Controls.cmbFit->setCurrentIndex(cmbIndex); return cmbIndex; } void ModelFitInspectorView::OnInputChanged(const QList& 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() { mitk::Point3D currentSelectedPosition = GetRenderWindowPart()->GetSelectedPosition(nullptr); unsigned int currentSelectedTimestep = m_renderWindowPart->GetTimeNavigationController()->GetTime()-> GetPos(); 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(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() { 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); } else { m_PlotCurves.currentPositionPlots = mitk::PlotDataCurveCollection::New(); } changed = true; } auto bookmarks = m_PositionBookmarks.Lock(); if (bookmarks.IsNotNull()) { 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++) { auto collection = RefreshPlotDataCurveCollection(iter.Value(), input, m_currentFit, timeGrid, m_currentModelParameterizer); m_PlotCurves.positionalPlots.emplace(iter.Index(), std::make_pair(iter.Value(), collection)); } 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->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 { m_Controls.groupSettings->setVisible(true); 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); } QTableWidgetItem* newItem = new QTableWidgetItem(QString::fromStdString(pos->first)); m_Controls.tableInputData->setItem(rowIndex, 0, newItem); newItem = new QTableWidgetItem(); newItem->setBackgroundColor(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(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(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(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()) { 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(node->GetData()); if (pointSet.IsNull()) { pointSet = mitk::PointSet::New(); node->SetData(pointSet); } m_PositionBookmarks = pointSet; } } diff --git a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/documentation/UserManual/Manual.dox b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/documentation/UserManual/Manual.dox index 3a2b25ac16..426c619338 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/documentation/UserManual/Manual.dox +++ b/Plugins/org.mitk.gui.qt.igt.app.hummelprotocolmeasurements/documentation/UserManual/Manual.dox @@ -1,18 +1,18 @@ /** -\page org_mitk_gui_qt_igttrackingsemiautomaticmeasurement IGT Tracking Semi Automatic Measurement +\page org_mitk_views_igttrackingsemiautomaticmeasurement The IGT Tracking Semi Automatic Measurement \imageMacro{icon.png,"Icon of IGT Tracking Semi Automatic Measurement",2.00} \tableofcontents \section Overview Dieses PlugIn dient zur semiautomatischen Aufzeichnung von Messreihen mit Trackingsystemen. Entsprechend konfiguriert ist es auch für das Hummel-Protokoll einsetzbar. Zentrale Komponente des PlugIns ist eine IGT Pipeline zur Messdatenaufzeichnung, wie in Abbildung 1 dargestellt. \imageMacro{pipeline.png,"Icon of IGT Tracking Semi Automatic Measurement",10.00} Ein Screenshot der Benutzeroberfläche des PlugIns ist in Abbildung 2 zu sehen. Das Initialisieren und Starten des Trackingsystems erfolgt dabei im nicht dargestellten Tab "Tracking Initialization", der im Wesentlichen aus dem TrackingDeviceConfigurationWidget besteht. Zur Durchführung der Messungen unterstützt das PlugIn das Laden einer Liste mit Dateinamen für die Messungen, wie im oberen Teil des Screenshots zu sehen. Diese Liste wird abgearbeitet, wobei mit dem Button "Start Next Measurement" jeweils die nächste Messung gestartet wird. Die während der Messung aufgezeichneten Daten werden in eine Datei das Ausgabeverzeichnis geschrieben. Dabei entspricht der Dateiname dem aktuellen Namen aus der Liste. Die Anzahl der aufzuzeichnenden Messwerte pro Messung kann in den Einstellungen angegeben werden. Gab es bei einer Messung einen Fehler kann die Messung durch Auswahl des entsprechenden Buttons auch wiederholt werden. \imageMacro{screenshot.png,"Icon of IGT Tracking Semi Automatic Measurement",10.00} Das PlugIn unterstützt außerdem die Ansteuerung eines zweiten Trackingsystems. Dieses System soll einen am Phantom angebrachtes Tool (Reference Sensor) tracken und so sicherstellen, dass sich das Phantom während der Messung nicht bewegt. Wurde eine Bewegung des Phantoms festgestellt wird im unteren Teil des PlugIns "NOT OK" angezeigt und die Messung muss ggf. wiederholt werden. */ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_multilabelsegmentation.dox b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentation.dox similarity index 95% rename from Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_multilabelsegmentation.dox rename to Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentation.dox index 6b11a85216..ed9a27078c 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_multilabelsegmentation.dox +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentation.dox @@ -1,101 +1,107 @@ /** \page org_mitk_views_multilabelsegmentation The Multilabel Segmentation \imageMacro{"multilabelsegmentation.svg", "Icon of the MultiLabelSegmentation Plugin", 12} The difference between a binary and a multi-label segmentation is that a multi-label segmentation can not only contain more than one label but also more than one layer. This allows you to create different labels for different regions of interest encapsulated in one single image. The difference between labels and layers is that labels on one layer cannot overlap but labels on different layers can. +
+The Multilabel Segmentation plugin consists of two views which can be used for: +
    +
  • manual and (semi-)automatic segmentation of anatomical and pathological structures in medical images via the Multilabel Segmentation View +
  • multilabel segmentation postprocessing via \subpage org_mitk_views_multilabelsegmentationutilities +
Hereinafter, a description is given on how multi-label segmentations can be created and modified. Please refer to \ref org_mitk_views_segmentation for a description of the general segmentation tools. \tableofcontents \section org_mitk_views_multilabelsegmentationUserManualCreateOpenSaveImportAdd Data Selection In the 'MultiLabel Segmentation' view you can first select an image. Thereafter, you can either create a new segmentation session by pressing the symbol right to the selection widget or select an existing one. \imageMacro{"org_mitk_views_multilabelsegmentationIMGtoolbar.png", "Data selection",12} \section org_mitk_views_multilabelsegmentationUserManualLayerTable Segmentation Layers Once you started a segmentation session you can add or delete different layers. You can use the layers independently and switch layers by the left and right arrows. \imageMacro{"org_mitk_views_multilabelsegmentationIMGlayerManager.png", "Layer selection",12} \subsection org_mitk_views_multilabelsegmentationUserManualLayerCreation Creating a New Layer A layer is a set of labels that occupy a non-overlapping anatomical space. The best way to describe them is by a real use case. Imagine you are working on a radiotherapy planning application. In the first layer of your segmentation session, you would like to trace the contours of the liver and neighboring organs. You can accommodate all these segmentations in separate labels because they all occupy different anatomical regions and do not overlap. Now say you would like to segment the arteries and veins inside the liver. If you don't trace them in a different layer, you will overwrite the previous ones. You may also need a third layer for segmenting the different irrigation territories in the liver and a fourth layer to contain the lesion you would like to treat. \section org_mitk_views_multilabelsegmentationUserManualLabelTable Segmentation Labels For each layer, you can add one or more labels. Pressing the double arrow on the right, all created labels are shown in the 'Lable Table'. The following label properties are available:
  • Name:
  • the name of the label. Can be a predefined one or any other.
  • Locked:
  • whether the label is locked or editable. A locked label cannot be overwritten by another.
  • Color:
  • the color of the label.
  • Visible:
  • whether the label is currently visible or hidden.
\imageMacro{"org_mitk_views_multilabelsegmentationIMGlabeltable.png", "The 'Label Table' shows all labels in the current segmentation session", 12} \subsection org_mitk_views_multilabelsegmentationUserManualLabelCreation Creating a New Label Click the 'New Label' button to add a new label. A dialog will show up to enter the name and color. Preset organ names and corresponding colors are offered while you type in, but you can set any name. The new name - if not known - will be automatically remembered and made available the next time you create a new label. In the current implementation of the plugin, the maximum number of labels is restricted to 255. If you need more, you will have to create a new segmentation session. \subsection org_mitk_views_multilabelsegmentationUserManualLabelSearch Searching a Label It may happen that many labels (e.g. > 200) are present in a segmentation session and therefore manual searching can be time-consuming. The 'Label Search' edit box allows for quickly finding the label you want. Just start writing its name and you will get assistance for completing its name. If the label you were searching is found, press 'enter' and it will become the active one. \subsection org_mitk_views_multilabelsegmentationUserManualLabelEditing Label Editing Here the actual segmentation takes place. First of all, you have to select the active label by clicking on the corresponding row in the 'Label Table'. Note that only one label can be active at the time. Then you can select segmentation tool in the toolbox. \imageMacro{"org_mitk_views_multilabelsegmentationIMGSegmentationToolbox.png", "Segmentation toolbox", 12} \note Not all segmentation tools can be found here. This is because some of the semi-automatic tools can not be applied in case of multiple labels. \subsection org_mitk_views_multilabelsegmentationUserManualOperationsOnLabels Operations on Labels Depending on your selection in the 'Label Table', several actions are offered: \subsubsection org_mitk_views_multilabelsegmentationUserManualOperationsOnSingleSelection Single Label Selection If you right click on any label in the table, a pop-up menu offers the following actions to be performed on the selected label:
  • Rename...
  • : change the name and/or color of the selected label.
  • Remove...
  • : delete the selected label.
  • Erase...
  • : only clear the contents of the selected label.
  • Merge...
  • : merge two labels by selecting a second label.
  • Random color
  • : assign a random color to the label.
  • View only
  • : make all labels except the current selected label invisible.
  • View/Hide all
  • : make all labels visible / invisible
  • Lock/Unlock all
  • : lock or unlock all labels.
  • Create surface
  • : generate a surface out of the selected label.
  • Create mask
  • : generate a mask out of the selected label. A mask is a binary image with "1" inside and "0" outside.
  • Create cropped mask
  • : generate a binary mask out of the selected label. Crop changes the extent of the resulting image to the extent of the label.
\imageMacro{"org_mitk_views_multilabelsegmentationIMGLabelTableSingleSelectionContextMenu.png", "Context menu for single label selection", 12} \subsubsection org_mitk_views_multilabelsegmentationUserManualOperationsOnMultipleSelection Multiple Label Selection You can select more than one label by shift-click on other labels. If more than one label is selected, different options will appear in the menu: \imageMacro{"org_mitk_views_multilabelsegmentationIMGLabelTableMultipleSelectionContextMenu.png", "Context menu for multiple label selection", 12}
  • Merge selection on current label
  • : transfer the contents of the selected labels in the 'Label Table' into the current one.
  • Remove selected labels
  • : delete the selected labels.
  • Erase selected labels
  • : only clear the contents of the selected labels.
*/ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities.dox b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities.dox new file mode 100644 index 0000000000..86c557c429 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities.dox @@ -0,0 +1,82 @@ +/** +\page org_mitk_views_multilabelsegmentationutilities The Multilabel Segmentation Utilities + +\imageMacro{multilabelsegmentation_utilities.svg,"Icon of the Multilabel Segmentation Utilities Plugin",5.00} + +\imageMacro{QmitkMultiLabelSegmentationUtilities_Overview.png, "The Multilabel Segmentation Utilities View",16.00} + +\tableofcontents + +\section org_mitk_views_multilabelSegmentationUtilitiesOverview Overview + +The Multilabel Segmentation Utilities Plugin allows to postprocess existing segmentations. Currently five different operations exist: + +
    +
  • \ref org_mitk_views_multilabelSegmentationUtilitiesBooleanOperations +
  • \ref org_mitk_views_multilabelSegmentationUtilitiesMorphologicalOperations +
  • \ref org_mitk_views_multilabelSegmentationUtilitiesSurfaceToImage +
  • \ref org_mitk_views_multilabelSegmentationUtilitiesImageMasking +
  • \ref org_mitk_views_multilabelSegmentationUtilitiesConvertToMultiLabel +
+ +\section org_mitk_views_multilabelSegmentationUtilitiesDataSelection Data Selection + +All postprocessing operations provide one or more selection widgets, which allow to select the data for the operation. + +\section org_mitk_views_multilabelSegmentationUtilitiesBooleanOperations Boolean operations + +Boolean operations allows to perform the following fundamental operations on two segmentations: + +
    +
  • Difference: Subtracts the second segmentation from the first segmentation. +
  • Intersection: Extracts the overlapping areas of the two selected segmentations. +
  • Union: Combines the two existing segmentations. +
+ +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{QmitkMultiLabelSegmentationUtilities_BooleanOperations.png,"Boolean operations",6.00} + +\section org_mitk_views_multilabelSegmentationUtilitiesMorphologicalOperations Morphological operations + +Morphological operations are applied to a single segmentation image. Based on a given structuring element the underlying segmentation will be modfied. +The plugin provides a ball and a cross as structuring elements. The follow operations are available: + +
    +
  • Dilation: Each labeled pixel within the segmentation will be dilated based on the selected structuring element. +
  • Erosion: Each labeled pixel within the segmentation will be eroded based on the selected structuring element. +
  • Closing: An erosion followed by a dilation, used for filling small holes. +
  • Opening: A dilation followed by an erosion, used for smoothing edges or eliminating small objects. +
  • Fill Holes: Fills bigger holes within a segmentation. +
+ +\imageMacro{QmitkMultiLabelSegmentationUtilities_MorphologicalOperations.png,"Morphological operations",6.00} + +\section org_mitk_views_multilabelSegmentationUtilitiesSurfaceToImage Surface to image + +Surface to image allows to create a segmentation out of a given surface. +The operation requires a surface and a reference image. +The created segmentation image will have the same geometrical properties like the reference image (dimension, size and Geometry3D). +Depending on the selected mode ("make output binary" or "overwrite background") +the result will be a simple image or a binary image. + +\imageMacro{QmitkMultiLabelSegmentationUtilities_SurfaceToImage.png,"Surface to image",6.00} + +\section org_mitk_views_multilabelSegmentationUtilitiesImageMasking Image masking + +Image masking allows to mask an image with either an existing segmentation or a surface. +The operation requires an image and a binary image mask or a surface. +The result will be an image containing only the pixels that are covered by the respective mask. +Depending on the selected mode ("make output binary" or "overwrite foreground" / "overwrite background") +the result will be a simple image or a binary image. + +\imageMacro{QmitkMultiLabelSegmentationUtilities_ImageMasking.png,"Image masking",6.00} + +\section org_mitk_views_multilabelSegmentationUtilitiesConvertToMultiLabel Convert to Multilabel + +Convert to multilabel allows to interpret the pixel values of a given image as label IDs and convert the image content into the respective Multilabel image. +The new segmentation will contain one layer with as many labels as the original image contains different pixel values. + +\imageMacro{QmitkMultiLabelSegmentationUtilities_ConvertToMultilabel.png,"Convert to Multilabel",6.00} +**/ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_BooleanOperations.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_BooleanOperations.png new file mode 100644 index 0000000000..d6ab9a24d2 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_BooleanOperations.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_ConvertToMultilabel.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_ConvertToMultilabel.png new file mode 100644 index 0000000000..cb7ab21d58 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_ConvertToMultilabel.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_ImageMasking.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_ImageMasking.png new file mode 100644 index 0000000000..dc948cc0ab Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_ImageMasking.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_MorphologicalOperations.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_MorphologicalOperations.png new file mode 100644 index 0000000000..d11d3cf94c Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_MorphologicalOperations.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_Overview.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_Overview.png new file mode 100644 index 0000000000..0a1a1a3990 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_Overview.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_SurfaceToImage.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_SurfaceToImage.png new file mode 100644 index 0000000000..0c06b7191d Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities_SurfaceToImage.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/plugin.xml b/Plugins/org.mitk.gui.qt.multilabelsegmentation/plugin.xml index e0a466e636..7f3d3a7c9a 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/plugin.xml +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/plugin.xml @@ -1,39 +1,39 @@ - - + + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.cpp index eba3f97670..37facd0558 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.cpp +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.cpp @@ -1,83 +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 -#include "QFileDialog" -#include "QMessageBox" - -#include "tinyxml.h" - -#include "mitkLabelSetImage.h" -#include "mitkLabelSetIOHelper.h" #include "QmitkCreateMultiLabelPresetAction.h" -QmitkCreateMultiLabelPresetAction::QmitkCreateMultiLabelPresetAction() -{ -} +#include +#include -QmitkCreateMultiLabelPresetAction::~QmitkCreateMultiLabelPresetAction() -{ -} +#include +#include -void QmitkCreateMultiLabelPresetAction::Run( const QList &selectedNodes ) +void QmitkCreateMultiLabelPresetAction::Run(const QList &selectedNodes) { - foreach ( mitk::DataNode::Pointer referenceNode, selectedNodes ) + for (auto node : selectedNodes) { - if (referenceNode.IsNotNull()) + if (node.IsNull()) + continue; + + mitk::LabelSetImage::Pointer image = dynamic_cast(node->GetData()); + + if (image.IsNull()) + continue; + + const auto filename = QFileDialog::getSaveFileName(nullptr, QStringLiteral("Save LabelSet Preset"), + QString(), QStringLiteral("LabelSet Preset (*.lsetp)")).toStdString(); + + if (filename.empty()) + continue; + + if(!mitk::LabelSetIOHelper::SaveLabelSetImagePreset(filename, image)) { - mitk::LabelSetImage::Pointer referenceImage = dynamic_cast( referenceNode->GetData() ); - assert(referenceImage); - - if(referenceImage->GetNumberOfLabels() <= 1) - { - QMessageBox::information(nullptr, "Create LabelSetImage Preset", "Could not create a LabelSetImage preset.\nNo Labels defined!\n");\ - return; - } - - std::string sName = referenceNode->GetName(); - QString qName; - qName.sprintf("%s.lsetp",sName.c_str()); - QString filename = QFileDialog::getSaveFileName( nullptr,"save file dialog",QString(),"LabelSet Preset(*.lsetp)"); - if ( filename.isEmpty() ) - return; - - std::string fileName = filename.toStdString(); - bool wasSaved = mitk::LabelSetIOHelper::SaveLabelSetImagePreset(fileName,referenceImage); - - if(!wasSaved) - { - QMessageBox::information(nullptr, "Create LabelSetImage Preset", "Could not save a LabelSetImage preset as Xml.\n");\ - return; - } + QMessageBox::critical(nullptr, QStringLiteral("Save LabelSetImage Preset"), + QString("Could not save \"%1\" as preset.").arg(QString::fromStdString(node->GetName()))); + + continue; } } } -void QmitkCreateMultiLabelPresetAction::SetDataStorage(mitk::DataStorage* dataStorage) +void QmitkCreateMultiLabelPresetAction::SetDataStorage(mitk::DataStorage*) { - m_DataStorage = dataStorage; } -void QmitkCreateMultiLabelPresetAction::SetFunctionality(berry::QtViewPart* /*functionality*/) +void QmitkCreateMultiLabelPresetAction::SetFunctionality(berry::QtViewPart*) { - //not needed } void QmitkCreateMultiLabelPresetAction::SetSmoothed(bool) { - //not needed } void QmitkCreateMultiLabelPresetAction::SetDecimated(bool) { - //not needed } diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.h index 3e0b4873ae..2406a22bd8 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.h +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.h @@ -1,46 +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 QMITK_QmitkCreateMultiLabelPresetAction_H -#define QMITK_QmitkCreateMultiLabelPresetAction_H -#include "mitkIContextMenuAction.h" +#ifndef QmitkCreateMultiLabelPresetAction_h +#define QmitkCreateMultiLabelPresetAction_h -#include "org_mitk_gui_qt_multilabelsegmentation_Export.h" +#include -#include "vector" -#include "mitkDataNode.h" - -class MITK_QT_SEGMENTATION QmitkCreateMultiLabelPresetAction : public QObject, public mitk::IContextMenuAction +class QmitkCreateMultiLabelPresetAction : public QObject, public mitk::IContextMenuAction { Q_OBJECT Q_INTERFACES(mitk::IContextMenuAction) public: - - QmitkCreateMultiLabelPresetAction(); - ~QmitkCreateMultiLabelPresetAction() override; - - //interface methods - void Run( const QList& selectedNodes ) override; - void SetDataStorage(mitk::DataStorage* dataStorage) override; - void SetFunctionality(berry::QtViewPart* functionality) override; - void SetSmoothed(bool smoothed) override; - void SetDecimated(bool decimated) override; - -private: - - typedef QList NodeList; - - mitk::DataStorage::Pointer m_DataStorage; + QmitkCreateMultiLabelPresetAction() = default; + ~QmitkCreateMultiLabelPresetAction() override = default; + + void Run(const QList& selectedNodes) override; + void SetDataStorage(mitk::DataStorage*) override; + void SetFunctionality(berry::QtViewPart*) override; + void SetSmoothed(bool) override; + void SetDecimated(bool) override; }; -#endif // QMITK_CreateMultiLabelSegmentation_H +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp index 3785b892c0..b5ab7769e2 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp @@ -1,72 +1,56 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#include "QmitkLoadMultiLabelPresetAction.h" -#include "mitkLabelSetImage.h" -#include "mitkLabelSetIOHelper.h" -#include "mitkRenderingManager.h" +#include "QmitkLoadMultiLabelPresetAction.h" -#include "QFileDialog" -#include "QInputDialog" -#include "QMessageBox" +#include +#include -#include "tinyxml.h" +#include -QmitkLoadMultiLabelPresetAction::QmitkLoadMultiLabelPresetAction() +void QmitkLoadMultiLabelPresetAction::Run(const QList &selectedNodes) { -} + const auto filename = QFileDialog::getOpenFileName(nullptr, QStringLiteral("Load LabelSet Preset"), + QString(), QStringLiteral("LabelSet Preset (*.lsetp)")).toStdString(); -QmitkLoadMultiLabelPresetAction::~QmitkLoadMultiLabelPresetAction() -{ -} + if (filename.empty()) + return; -void QmitkLoadMultiLabelPresetAction::Run( const QList &selectedNodes ) -{ - foreach ( mitk::DataNode::Pointer referenceNode, selectedNodes ) + for (auto node : selectedNodes) { + if (node.IsNull()) + continue; - if (referenceNode.IsNull()) return; - - mitk::LabelSetImage::Pointer referenceImage = dynamic_cast( referenceNode->GetData() ); - assert(referenceImage); + mitk::LabelSetImage::Pointer image = dynamic_cast(node->GetData()); - std::string sName = referenceNode->GetName(); - QString qName; - qName.sprintf("%s.lsetp",sName.c_str()); - QString filename = QFileDialog::getOpenFileName(nullptr,"Load file",QString(),"LabelSet Preset(*.lsetp)"); - if ( filename.isEmpty() ) - return; + if (image.IsNull()) + continue; - std::string fileName = filename.toStdString(); - mitk::LabelSetIOHelper::LoadLabelSetImagePreset(fileName, referenceImage); + mitk::LabelSetIOHelper::LoadLabelSetImagePreset(filename, image); } } -void QmitkLoadMultiLabelPresetAction::SetDataStorage(mitk::DataStorage* dataStorage) +void QmitkLoadMultiLabelPresetAction::SetDataStorage(mitk::DataStorage*) { - m_DataStorage = dataStorage; } -void QmitkLoadMultiLabelPresetAction::SetFunctionality(berry::QtViewPart* /*functionality*/) +void QmitkLoadMultiLabelPresetAction::SetFunctionality(berry::QtViewPart*) { - //not needed } void QmitkLoadMultiLabelPresetAction::SetSmoothed(bool) { - //not needed } void QmitkLoadMultiLabelPresetAction::SetDecimated(bool) { - //not needed } diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.h index d6c98a59a7..ed46186cae 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.h +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.h @@ -1,46 +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 QMITK_QmitkLoadMultiLabelPresetAction_H -#define QMITK_QmitkLoadMultiLabelPresetAction_H -#include "mitkIContextMenuAction.h" +#ifndef QmitkLoadMultiLabelPresetAction_h +#define QmitkLoadMultiLabelPresetAction_h -#include "org_mitk_gui_qt_multilabelsegmentation_Export.h" +#include -#include "vector" -#include "mitkDataNode.h" - -class MITK_QT_SEGMENTATION QmitkLoadMultiLabelPresetAction : public QObject, public mitk::IContextMenuAction +class QmitkLoadMultiLabelPresetAction : public QObject, public mitk::IContextMenuAction { Q_OBJECT Q_INTERFACES(mitk::IContextMenuAction) public: - - QmitkLoadMultiLabelPresetAction(); - ~QmitkLoadMultiLabelPresetAction() override; - - //interface methods - void Run( const QList& selectedNodes ) override; - void SetDataStorage(mitk::DataStorage* dataStorage) override; - void SetFunctionality(berry::QtViewPart* functionality) override; - void SetSmoothed(bool smoothed) override; - void SetDecimated(bool decimated) override; - -private: - - typedef QList NodeList; - - mitk::DataStorage::Pointer m_DataStorage; + QmitkLoadMultiLabelPresetAction() = default; + ~QmitkLoadMultiLabelPresetAction() override = default; + + void Run(const QList& selectedNodes) override; + void SetDataStorage(mitk::DataStorage*) override; + void SetFunctionality(berry::QtViewPart*) override; + void SetSmoothed(bool) override; + void SetDecimated(bool) override; }; -#endif // QMITK_CreateMultiLabelSegmentation_H +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui index eaeef0c8a9..d055c5a2d9 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui @@ -1,760 +1,806 @@ QmitkMultiLabelSegmentationControls 0 0 459 844 0 0 0 0 MS Shell Dlg 2 8 50 false false false false QmitkSegmentation 0 0 Data Selection Patient Image - - - - 0 - 40 - - - + + + + 0 + 40 + + + Create a new segmentation session ... :/multilabelsegmentation/NewSegmentationSession_48x48.png:/multilabelsegmentation/NewSegmentationSession_48x48.png 28 28 N true Segmentation - - - - 0 - 40 - - - + + + + 0 + 40 + + + Layers true false Add a layer to the current segmentation session ... :/Qmitk/AddLayer_48x48.png:/Qmitk/AddLayer_48x48.png 28 28 true Delete the active layer ... :/Qmitk/DeleteLayer_48x48.png:/Qmitk/DeleteLayer_48x48.png 28 28 true Qt::Horizontal 0 20 Change to the previous available layer ... :/Qmitk/PreviousLayer_48x48.png:/Qmitk/PreviousLayer_48x48.png 28 28 true Change to the next available layer ... :/Qmitk/NextLayer_48x48.png:/Qmitk/NextLayer_48x48.png 28 28 true 0 0 50 30 40 30 12 Switch to a layer 0 Labels true Add a new label to the current segmentation session ... :/multilabelsegmentation/NewLabel_48x48.png:/multilabelsegmentation/NewLabel_48x48.png 28 28 N true Lock/Unlock exterior ... :/Qmitk/UnlockExterior_48x48.png :/Qmitk/LockExterior_48x48.png:/Qmitk/UnlockExterior_48x48.png 28 28 true true + + + + Save LabelSet Preset + + + ... + + + + :/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg:/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg + + + + 28 + 28 + + + + true + + + + + + + Load LabelSet Preset + + + ... + + + + :/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg:/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg + + + + 28 + 28 + + + + true + + + Qt::Horizontal 0 20 0 34 Show a table with all labels in the current segmentation session >> 28 28 true false Qt::NoArrow 0 0 0 20 0 0 QTabWidget::tab-bar { alignment: middle; } 0 true false 2D Tools 0 0 50 false 0 0 50 false Qt::Vertical 20 40 3D Tools 0 0 50 false 0 0 50 false Qt::Vertical 20 40 0 0 Interpolation 2 QLayout::SetMinimumSize 2 2 2 2 0 0 Disabled 2D Interpolation 3D Interpolation 0 0 1 0 0 QLayout::SetMinimumSize 0 0 0 0 0 0 0 0 50 false 0 0 QLayout::SetMinimumSize 0 0 0 0 0 0 0 50 50 false 0 0 Qt::Vertical 20 40 m_LabelSetWidget groupBox_DataSelection m_tw2DTools m_gbInterpolation groupBox_Layer groupBox_Labels - - QmitkSingleNodeSelectionWidget - QWidget -
QmitkSingleNodeSelectionWidget.h
- 1 -
- + + QmitkSingleNodeSelectionWidget + QWidget +
QmitkSingleNodeSelectionWidget.h
+ 1 +
+ QmitkToolSelectionBox QWidget
QmitkToolSelectionBox.h
QmitkToolGUIArea QWidget
QmitkToolGUIArea.h
QmitkLabelSetWidget QWidget
Qmitk/QmitkLabelSetWidget.h
1
QmitkSliceBasedInterpolatorWidget QWidget
QmitkSliceBasedInterpolatorWidget.h
1
QmitkSurfaceBasedInterpolatorWidget QWidget
QmitkSurfaceBasedInterpolatorWidget.h
1
QmitkToolGUIArea.h QmitkToolSelectionBox.h
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp index 24ab1cb031..b1d6ae2ba9 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp @@ -1,1062 +1,1104 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkMultiLabelSegmentationView.h" // blueberry #include #include // mitk #include "mitkApplicationCursor.h" #include "mitkLabelSetImage.h" #include "mitkStatusBar.h" #include "mitkToolManagerProvider.h" #include "mitkInteractionEventObserver.h" #include "mitkPlanePositionManager.h" #include "mitkPluginActivator.h" #include "mitkSegTool2D.h" #include "mitkImageTimeSelector.h" #include "mitkNodePredicateSubGeometry.h" // Qmitk +#include #include "QmitkNewSegmentationDialog.h" #include "QmitkRenderWindow.h" #include "QmitkSegmentationOrganNamesHandling.cpp" +#include "QmitkCreateMultiLabelPresetAction.h" +#include "QmitkLoadMultiLabelPresetAction.h" // us #include #include #include #include #include // Qt #include #include #include #include #include #include "tinyxml.h" #include #include const std::string QmitkMultiLabelSegmentationView::VIEW_ID = "org.mitk.views.multilabelsegmentation"; QmitkMultiLabelSegmentationView::QmitkMultiLabelSegmentationView() : m_Parent(nullptr), m_IRenderWindowPart(nullptr), m_ToolManager(nullptr), m_ReferenceNode(nullptr), m_WorkingNode(nullptr), m_AutoSelectionEnabled(false), m_MouseCursorSet(false) { m_SegmentationPredicate = mitk::NodePredicateAnd::New(); m_SegmentationPredicate->AddPredicate(mitk::TNodePredicateDataType::New()); m_SegmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isMask = mitk::NodePredicateAnd::New(isBinary, isImage); mitk::NodePredicateDataType::Pointer isDwi = mitk::NodePredicateDataType::New("DiffusionImage"); mitk::NodePredicateDataType::Pointer isDti = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isOdf = mitk::NodePredicateDataType::New("OdfImage"); auto isSegment = mitk::NodePredicateDataType::New("Segment"); mitk::NodePredicateOr::Pointer validImages = mitk::NodePredicateOr::New(); validImages->AddPredicate(mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isSegment))); validImages->AddPredicate(isDwi); validImages->AddPredicate(isDti); validImages->AddPredicate(isOdf); m_ReferencePredicate = mitk::NodePredicateAnd::New(); m_ReferencePredicate->AddPredicate(validImages); m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(m_SegmentationPredicate)); m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(isMask)); m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); } QmitkMultiLabelSegmentationView::~QmitkMultiLabelSegmentationView() { // Loose LabelSetConnections OnLooseLabelSetConnection(); } void QmitkMultiLabelSegmentationView::CreateQtPartControl(QWidget *parent) { // setup the basic GUI of this view m_Parent = parent; m_Controls.setupUi(parent); + m_Controls.m_tbSavePreset->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg"))); + m_Controls.m_tbLoadPreset->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg"))); + // *------------------------ // * Shortcuts // *------------------------ QShortcut* visibilityShortcut = new QShortcut(QKeySequence("CTRL+H"), parent); connect(visibilityShortcut, &QShortcut::activated, this, &QmitkMultiLabelSegmentationView::OnVisibilityShortcutActivated); QShortcut* labelToggleShortcut = new QShortcut(QKeySequence("CTRL+L"), parent); connect(labelToggleShortcut, &QShortcut::activated, this, &QmitkMultiLabelSegmentationView::OnLabelToggleShortcutActivated); // *------------------------ // * DATA SELECTION WIDGETS // *------------------------ m_Controls.m_ReferenceNodeSelector->SetNodePredicate(m_ReferencePredicate); m_Controls.m_ReferenceNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.m_ReferenceNodeSelector->SetInvalidInfo("Select an image"); m_Controls.m_ReferenceNodeSelector->SetPopUpTitel("Select an image"); m_Controls.m_ReferenceNodeSelector->SetPopUpHint("Select an image that should be used to define the geometry and bounds of the segmentation."); m_Controls.m_WorkingNodeSelector->SetNodePredicate(m_SegmentationPredicate); m_Controls.m_WorkingNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.m_WorkingNodeSelector->SetInvalidInfo("Select a segmentation"); m_Controls.m_WorkingNodeSelector->SetPopUpTitel("Select a segmentation"); m_Controls.m_WorkingNodeSelector->SetPopUpHint("Select a segmentation that should be modified. Only segmentation with the same geometry and within the bounds of the reference image are selected."); connect(m_Controls.m_ReferenceNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this,&QmitkMultiLabelSegmentationView::OnReferenceSelectionChanged); connect(m_Controls.m_WorkingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this,&QmitkMultiLabelSegmentationView::OnSegmentationSelectionChanged); // *------------------------ // * ToolManager // *------------------------ m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(mitk::ToolManagerProvider::MULTILABEL_SEGMENTATION); m_ToolManager->SetDataStorage(*(this->GetDataStorage())); m_ToolManager->InitializeTools(); m_Controls.m_ManualToolSelectionBox2D->SetToolManager(*m_ToolManager); m_Controls.m_ManualToolSelectionBox3D->SetToolManager(*m_ToolManager); // *------------------------ // * LabelSetWidget // *------------------------ m_Controls.m_LabelSetWidget->SetDataStorage(this->GetDataStorage()); m_Controls.m_LabelSetWidget->SetOrganColors(mitk::OrganNamesHandling::GetDefaultOrganColorString()); m_Controls.m_LabelSetWidget->hide(); // *------------------------ // * Interpolation // *------------------------ m_Controls.m_SurfaceBasedInterpolatorWidget->SetDataStorage(*(this->GetDataStorage())); m_Controls.m_SliceBasedInterpolatorWidget->SetDataStorage(*(this->GetDataStorage())); connect(m_Controls.m_cbInterpolation, SIGNAL(activated(int)), this, SLOT(OnInterpolationSelectionChanged(int))); m_Controls.m_cbInterpolation->setCurrentIndex(0); m_Controls.m_swInterpolation->hide(); m_Controls.m_gbInterpolation->hide(); // See T27436 QString segTools2D = tr("Add Subtract Fill Erase Paint Wipe 'Region Growing' FastMarching2D Correction 'Live Wire'"); QString segTools3D = tr("Threshold 'Two Thresholds' 'Auto Threshold' 'Multiple Otsu'"); std::regex extSegTool2DRegEx("SegTool2D$"); std::regex extSegTool3DRegEx("SegTool3D$"); auto tools = m_ToolManager->GetTools(); for (const auto &tool : tools) { if (std::regex_search(tool->GetNameOfClass(), extSegTool2DRegEx)) { segTools2D.append(QString(" '%1'").arg(tool->GetName())); } else if (std::regex_search(tool->GetNameOfClass(), extSegTool3DRegEx)) { segTools3D.append(QString(" '%1'").arg(tool->GetName())); } } // *------------------------ // * ToolSelection 2D // *------------------------ m_Controls.m_ManualToolSelectionBox2D->SetGenerateAccelerators(true); m_Controls.m_ManualToolSelectionBox2D->SetToolGUIArea(m_Controls.m_ManualToolGUIContainer2D); m_Controls.m_ManualToolSelectionBox2D->SetDisplayedToolGroups(segTools2D.toStdString()); // todo: "Correction // 'Live Wire'" m_Controls.m_ManualToolSelectionBox2D->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); connect(m_Controls.m_ManualToolSelectionBox2D, SIGNAL(ToolSelected(int)), this, SLOT(OnManualTool2DSelected(int))); // *------------------------ // * ToolSelection 3D // *------------------------ m_Controls.m_ManualToolSelectionBox3D->SetGenerateAccelerators(true); m_Controls.m_ManualToolSelectionBox3D->SetToolGUIArea(m_Controls.m_ManualToolGUIContainer3D); m_Controls.m_ManualToolSelectionBox3D->SetDisplayedToolGroups(segTools3D.toStdString()); // todo add : FastMarching3D RegionGrowing Watershed m_Controls.m_ManualToolSelectionBox3D->SetLayoutColumns(2); m_Controls.m_ManualToolSelectionBox3D->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); // *------------------------* // * Connect PushButtons (pb) // *------------------------* connect(m_Controls.m_pbNewLabel, SIGNAL(clicked()), this, SLOT(OnNewLabel())); + connect(m_Controls.m_tbSavePreset, SIGNAL(clicked()), this, SLOT(OnSavePreset())); + connect(m_Controls.m_tbLoadPreset, SIGNAL(clicked()), this, SLOT(OnLoadPreset())); connect(m_Controls.m_pbNewSegmentationSession, SIGNAL(clicked()), this, SLOT(OnNewSegmentationSession())); connect(m_Controls.m_pbShowLabelTable, SIGNAL(toggled(bool)), this, SLOT(OnShowLabelTable(bool))); // *------------------------* // * Connect LabelSetWidget // *------------------------* connect(m_Controls.m_LabelSetWidget, SIGNAL(goToLabel(const mitk::Point3D &)), this, SLOT(OnGoToLabel(const mitk::Point3D &))); connect(m_Controls.m_LabelSetWidget, SIGNAL(resetView()), this, SLOT(OnResetView())); // *------------------------* // * DATA SLECTION WIDGET // *------------------------* m_IRenderWindowPart = this->GetRenderWindowPart(); if (m_IRenderWindowPart) { QList controllers; controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()); controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()); controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()); m_Controls.m_SliceBasedInterpolatorWidget->SetSliceNavigationControllers(controllers); } // this->InitializeListeners(); connect(m_Controls.m_btAddLayer, SIGNAL(clicked()), this, SLOT(OnAddLayer())); connect(m_Controls.m_btDeleteLayer, SIGNAL(clicked()), this, SLOT(OnDeleteLayer())); connect(m_Controls.m_btPreviousLayer, SIGNAL(clicked()), this, SLOT(OnPreviousLayer())); connect(m_Controls.m_btNextLayer, SIGNAL(clicked()), this, SLOT(OnNextLayer())); connect(m_Controls.m_btLockExterior, SIGNAL(toggled(bool)), this, SLOT(OnLockExteriorToggled(bool))); connect(m_Controls.m_cbActiveLayer, SIGNAL(currentIndexChanged(int)), this, SLOT(OnChangeLayer(int))); m_Controls.m_btAddLayer->setEnabled(false); m_Controls.m_btDeleteLayer->setEnabled(false); m_Controls.m_btNextLayer->setEnabled(false); m_Controls.m_btPreviousLayer->setEnabled(false); m_Controls.m_cbActiveLayer->setEnabled(false); m_Controls.m_pbNewLabel->setEnabled(false); m_Controls.m_btLockExterior->setEnabled(false); + m_Controls.m_tbSavePreset->setEnabled(false); + m_Controls.m_tbLoadPreset->setEnabled(false); m_Controls.m_pbShowLabelTable->setEnabled(false); // Make sure the GUI notices if appropriate data is already present on creation m_Controls.m_ReferenceNodeSelector->SetAutoSelectNewNodes(true); m_Controls.m_WorkingNodeSelector->SetAutoSelectNewNodes(true); } void QmitkMultiLabelSegmentationView::Activated() { m_ToolManager->SetReferenceData(m_Controls.m_ReferenceNodeSelector->GetSelectedNode()); m_ToolManager->SetWorkingData(m_Controls.m_WorkingNodeSelector->GetSelectedNode()); } void QmitkMultiLabelSegmentationView::Deactivated() { // Not yet implemented } void QmitkMultiLabelSegmentationView::Visible() { // Not yet implemented } void QmitkMultiLabelSegmentationView::Hidden() { // Not yet implemented } int QmitkMultiLabelSegmentationView::GetSizeFlags(bool width) { if (!width) { return berry::Constants::MIN | berry::Constants::MAX | berry::Constants::FILL; } else { return 0; } } int QmitkMultiLabelSegmentationView::ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult) { if (width == false) { return 100; } else { return preferredResult; } } /************************************************************************/ /* protected slots */ /************************************************************************/ void QmitkMultiLabelSegmentationView::OnVisibilityShortcutActivated() { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); bool isVisible = false; workingNode->GetBoolProperty("visible", isVisible); workingNode->SetVisibility(!isVisible); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkMultiLabelSegmentationView::OnLabelToggleShortcutActivated() { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); WaitCursorOn(); workingImage->GetActiveLabelSet()->SetNextActiveLabel(); workingImage->Modified(); WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkMultiLabelSegmentationView::OnManualTool2DSelected(int id) { this->ResetMouseCursor(); mitk::StatusBar::GetInstance()->DisplayText(""); if (id >= 0) { std::string text = "Active Tool: \""; text += m_ToolManager->GetToolById(id)->GetName(); text += "\""; mitk::StatusBar::GetInstance()->DisplayText(text.c_str()); us::ModuleResource resource = m_ToolManager->GetToolById(id)->GetCursorIconResource(); this->SetMouseCursor(resource, 0, 0); } } void QmitkMultiLabelSegmentationView::OnNewLabel() { m_ToolManager->ActivateTool(-1); if (m_ReferenceNode.IsNull()) { QMessageBox::information( m_Parent, "New Segmentation Session", "Please load and select a patient image before starting some action."); return; } if (nullptr == m_ReferenceNode->GetData()) { QMessageBox::information( m_Parent, "New Segmentation Session", "Please load and select a patient image before starting some action."); return; } mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if (!workingNode) { QMessageBox::information( m_Parent, "New Segmentation Session", "Please load and select a patient image before starting some action."); return; } mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); if (!workingImage) { QMessageBox::information( m_Parent, "New Segmentation Session", "Please load and select a patient image before starting some action."); return; } QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog(m_Parent); dialog->SetSuggestionList(mitk::OrganNamesHandling::GetDefaultOrganColorString()); dialog->setWindowTitle("New Label"); int dialogReturnValue = dialog->exec(); if (dialogReturnValue == QDialog::Rejected) { return; } QString segName = dialog->GetSegmentationName(); if (segName.isEmpty()) { segName = "Unnamed"; } workingImage->GetActiveLabelSet()->AddLabel(segName.toStdString(), dialog->GetColor()); UpdateControls(); m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); this->ReinitializeViews(); } +void QmitkMultiLabelSegmentationView::OnSavePreset() +{ + QmitkAbstractNodeSelectionWidget::NodeList nodes; + nodes.append(m_WorkingNode); + + QmitkCreateMultiLabelPresetAction action; + action.Run(nodes); +} + +void QmitkMultiLabelSegmentationView::OnLoadPreset() +{ + QmitkAbstractNodeSelectionWidget::NodeList nodes; + nodes.append(m_WorkingNode); + + QmitkLoadMultiLabelPresetAction action; + action.Run(nodes); +} + void QmitkMultiLabelSegmentationView::OnShowLabelTable(bool value) { if (value) m_Controls.m_LabelSetWidget->show(); else m_Controls.m_LabelSetWidget->hide(); } void QmitkMultiLabelSegmentationView::OnNewSegmentationSession() { mitk::DataNode *referenceNode = m_Controls.m_ReferenceNodeSelector->GetSelectedNode(); if (!referenceNode) { QMessageBox::information( m_Parent, "New Segmentation Session", "Please load and select a patient image before starting some action."); return; } m_ToolManager->ActivateTool(-1); mitk::Image::ConstPointer referenceImage = dynamic_cast(referenceNode->GetData()); assert(referenceImage); const auto currentTimePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); unsigned int imageTimeStep = 0; if (referenceImage->GetTimeGeometry()->IsValidTimePoint(currentTimePoint)) { imageTimeStep = referenceImage->GetTimeGeometry()->TimePointToTimeStep(currentTimePoint); } auto segTemplateImage = referenceImage; if (referenceImage->GetDimension() > 3) { auto result = QMessageBox::question(m_Parent, tr("Generate a static mask?"), tr("The selected image has multiple time steps. You can either generate a simple/static masks resembling the geometry of the first timestep of the image. Or you can generate a dynamic mask that equals the selected image in geometry and number of timesteps; thus a dynamic mask can change over time (e.g. according to the image)."), tr("Yes, generate a static mask"), tr("No, generate a dynamic mask"), QString(), 0, 0); if (result == 0) { auto selector = mitk::ImageTimeSelector::New(); selector->SetInput(referenceImage); selector->SetTimeNr(0); selector->Update(); const auto refTimeGeometry = referenceImage->GetTimeGeometry(); auto newTimeGeometry = mitk::ProportionalTimeGeometry::New(); newTimeGeometry->SetFirstTimePoint(refTimeGeometry->GetMinimumTimePoint()); newTimeGeometry->SetStepDuration(refTimeGeometry->GetMaximumTimePoint() - refTimeGeometry->GetMinimumTimePoint()); mitk::Image::Pointer newImage = selector->GetOutput(); newTimeGeometry->SetTimeStepGeometry(referenceImage->GetGeometry(imageTimeStep), 0); newImage->SetTimeGeometry(newTimeGeometry); segTemplateImage = newImage; } } QString newName = QString::fromStdString(referenceNode->GetName()); newName.append("-labels"); bool ok = false; newName = QInputDialog::getText(m_Parent, "New Segmentation Session", "New name:", QLineEdit::Normal, newName, &ok); if (!ok) { return; } this->WaitCursorOn(); mitk::LabelSetImage::Pointer workingImage = mitk::LabelSetImage::New(); try { workingImage->Initialize(segTemplateImage); } catch (mitk::Exception& e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(m_Parent, "New Segmentation Session", "Could not create a new segmentation session.\n"); return; } this->WaitCursorOff(); mitk::DataNode::Pointer workingNode = mitk::DataNode::New(); workingNode->SetData(workingImage); workingNode->SetName(newName.toStdString()); workingImage->GetExteriorLabel()->SetProperty("name.parent", mitk::StringProperty::New(referenceNode->GetName().c_str())); workingImage->GetExteriorLabel()->SetProperty("name.image", mitk::StringProperty::New(newName.toStdString().c_str())); if (!GetDataStorage()->Exists(workingNode)) { GetDataStorage()->Add(workingNode, referenceNode); } m_Controls.m_WorkingNodeSelector->SetCurrentSelectedNode(workingNode); OnNewLabel(); } void QmitkMultiLabelSegmentationView::OnGoToLabel(const mitk::Point3D& pos) { if (m_IRenderWindowPart) m_IRenderWindowPart->SetSelectedPosition(pos); } void QmitkMultiLabelSegmentationView::OnResetView() { if (m_IRenderWindowPart) m_IRenderWindowPart->ForceImmediateUpdate(); } void QmitkMultiLabelSegmentationView::OnAddLayer() { m_ToolManager->ActivateTool(-1); mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); QString question = "Do you really want to add a layer to the current segmentation session?"; QMessageBox::StandardButton answerButton = QMessageBox::question( m_Controls.m_LabelSetWidget, "Add layer", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton != QMessageBox::Yes) return; try { WaitCursorOn(); workingImage->AddLayer(); WaitCursorOff(); } catch ( mitk::Exception& e ) { WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information( m_Controls.m_LabelSetWidget, "Add Layer", "Could not add a new layer. See error log for details.\n"); return; } OnNewLabel(); } void QmitkMultiLabelSegmentationView::OnDeleteLayer() { m_ToolManager->ActivateTool(-1); mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); if (workingImage->GetNumberOfLayers() < 2) return; QString question = "Do you really want to delete the current layer?"; QMessageBox::StandardButton answerButton = QMessageBox::question( m_Controls.m_LabelSetWidget, "Delete layer", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton != QMessageBox::Yes) { return; } try { this->WaitCursorOn(); workingImage->RemoveLayer(); this->WaitCursorOff(); } catch (mitk::Exception& e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(m_Controls.m_LabelSetWidget, "Delete Layer", "Could not delete the currently active layer. See error log for details.\n"); return; } UpdateControls(); m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); } void QmitkMultiLabelSegmentationView::OnPreviousLayer() { m_ToolManager->ActivateTool(-1); mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage *workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); OnChangeLayer(workingImage->GetActiveLayer() - 1); } void QmitkMultiLabelSegmentationView::OnNextLayer() { m_ToolManager->ActivateTool(-1); mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage *workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); OnChangeLayer(workingImage->GetActiveLayer() + 1); } void QmitkMultiLabelSegmentationView::OnChangeLayer(int layer) { m_ToolManager->ActivateTool(-1); mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage *workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); this->WaitCursorOn(); workingImage->SetActiveLayer(layer); this->WaitCursorOff(); UpdateControls(); m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); } void QmitkMultiLabelSegmentationView::OnDeactivateActiveTool() { m_ToolManager->ActivateTool(-1); } void QmitkMultiLabelSegmentationView::OnLockExteriorToggled(bool checked) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); workingImage->GetLabel(0)->SetLocked(checked); } void QmitkMultiLabelSegmentationView::OnReferenceSelectionChanged(QList /*nodes*/) { m_ToolManager->ActivateTool(-1); auto refNode = m_Controls.m_ReferenceNodeSelector->GetSelectedNode(); m_ReferenceNode = refNode; m_ToolManager->SetReferenceData(m_ReferenceNode); if (m_ReferenceNode.IsNotNull()) { auto segPredicate = mitk::NodePredicateAnd::New(m_SegmentationPredicate.GetPointer(), mitk::NodePredicateSubGeometry::New(refNode->GetData()->GetGeometry())); m_Controls.m_WorkingNodeSelector->SetNodePredicate(segPredicate); if (m_AutoSelectionEnabled) { // hide all image nodes to later show only the automatically selected ones mitk::DataStorage::SetOfObjects::ConstPointer patientNodes = GetDataStorage()->GetSubset(m_ReferencePredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = patientNodes->begin(); iter != patientNodes->end(); ++iter) { (*iter)->SetVisibility(false); } } m_ReferenceNode->SetVisibility(true); } UpdateControls(); } void QmitkMultiLabelSegmentationView::OnSegmentationSelectionChanged(QList /*nodes*/) { m_ToolManager->ActivateTool(-1); if (m_WorkingNode.IsNotNull()) OnLooseLabelSetConnection(); m_WorkingNode = m_Controls.m_WorkingNodeSelector->GetSelectedNode(); m_ToolManager->SetWorkingData(m_WorkingNode); if (m_WorkingNode.IsNotNull()) { OnEstablishLabelSetConnection(); if (m_AutoSelectionEnabled) { // hide all segmentation nodes to later show only the automatically selected ones mitk::DataStorage::SetOfObjects::ConstPointer segmentationNodes = GetDataStorage()->GetSubset(m_SegmentationPredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = segmentationNodes->begin(); iter != segmentationNodes->end(); ++iter) { (*iter)->SetVisibility(false); } } m_WorkingNode->SetVisibility(true); } UpdateControls(); if (m_WorkingNode.IsNotNull()) { m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); this->ReinitializeViews(); } } void QmitkMultiLabelSegmentationView::OnInterpolationSelectionChanged(int index) { if (index == 1) { m_Controls.m_SurfaceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false);//OnToggleWidgetActivation(false); m_Controls.m_swInterpolation->setCurrentIndex(0); m_Controls.m_swInterpolation->show(); } else if (index == 2) { m_Controls.m_SliceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false); m_Controls.m_swInterpolation->setCurrentIndex(1); m_Controls.m_swInterpolation->show(); } else { m_Controls.m_SurfaceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false); m_Controls.m_SliceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false); m_Controls.m_swInterpolation->setCurrentIndex(2); m_Controls.m_swInterpolation->hide(); } } /************************************************************************/ /* protected */ /************************************************************************/ void QmitkMultiLabelSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences* prefs) { if (m_Parent && m_WorkingNode.IsNotNull()) { m_AutoSelectionEnabled = prefs->GetBool("auto selection", false); mitk::BoolProperty::Pointer drawOutline = mitk::BoolProperty::New(prefs->GetBool("draw outline", true)); mitk::BoolProperty::Pointer volumeRendering = mitk::BoolProperty::New(prefs->GetBool("volume rendering", false)); mitk::LabelSetImage* labelSetImage; mitk::DataNode* segmentation; // iterate all segmentations (binary (single label) and LabelSetImages) mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateOr::Pointer allSegmentationsPredicate = mitk::NodePredicateOr::New(isBinaryPredicate, m_SegmentationPredicate); mitk::DataStorage::SetOfObjects::ConstPointer allSegmentations = GetDataStorage()->GetSubset(allSegmentationsPredicate); for (mitk::DataStorage::SetOfObjects::const_iterator it = allSegmentations->begin(); it != allSegmentations->end(); ++it) { segmentation = *it; labelSetImage = dynamic_cast(segmentation->GetData()); if (nullptr != labelSetImage) { // segmentation node is a multi label segmentation segmentation->SetProperty("labelset.contour.active", drawOutline); //segmentation->SetProperty("opacity", mitk::FloatProperty::New(drawOutline->GetValue() ? 1.0f : 0.3f)); segmentation->SetProperty("volumerendering", volumeRendering); // force render window update to show outline segmentation->GetData()->Modified(); } else if (nullptr != segmentation->GetData()) { // node is actually a 'single label' segmentation, // but its outline property can be set in the 'multi label' segmentation preference page as well bool isBinary = false; segmentation->GetBoolProperty("binary", isBinary); if (isBinary) { segmentation->SetProperty("outline binary", drawOutline); segmentation->SetProperty("outline width", mitk::FloatProperty::New(2.0)); //segmentation->SetProperty("opacity", mitk::FloatProperty::New(drawOutline->GetValue() ? 1.0f : 0.3f)); segmentation->SetProperty("volumerendering", volumeRendering); // force render window update to show outline segmentation->GetData()->Modified(); } } else { // "interpolation feedback" data nodes have binary flag but don't have a data set. So skip them for now. MITK_INFO << "DataNode " << segmentation->GetName() << " doesn't contain a base data."; } } } } void QmitkMultiLabelSegmentationView::NodeRemoved(const mitk::DataNode *node) { bool isHelperObject(false); node->GetBoolProperty("helper object", isHelperObject); if (isHelperObject) { return; } if (m_ReferenceNode.IsNotNull() && dynamic_cast(node->GetData())) { // 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 *service = context->getService(ppmRef); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1; service->RemovePlanePosition(id); this->GetDataStorage()->Remove(it->Value()); } context->ungetService(ppmRef); service = nullptr; } } void QmitkMultiLabelSegmentationView::OnEstablishLabelSetConnection() { if (m_WorkingNode.IsNull()) { return; } mitk::LabelSetImage *workingImage = dynamic_cast(m_WorkingNode->GetData()); assert(workingImage); workingImage->GetActiveLabelSet()->AddLabelEvent += mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->RemoveLabelEvent += mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->ModifyLabelEvent += mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent += mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->ActiveLabelEvent += mitk::MessageDelegate1(m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::SelectLabelByPixelValue); - workingImage->BeforeChangeLayerEvent += mitk::MessageDelegate( - this, &QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection); + + // Removed in T27851 to have a chance to react to AfterChangeLayerEvent. Did it brake something? + // workingImage->BeforeChangeLayerEvent += mitk::MessageDelegate( + // this, &QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection); + + workingImage->AfterChangeLayerEvent += mitk::MessageDelegate( + this, &QmitkMultiLabelSegmentationView::UpdateControls); } void QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection() { if (m_WorkingNode.IsNull()) return; auto* workingImage = dynamic_cast(m_WorkingNode->GetData()); if (nullptr == workingImage) return; // data (type) was changed in-place, e.g. LabelSetImage -> Image // Reset LabelSetWidget Events workingImage->GetActiveLabelSet()->AddLabelEvent -= mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->ModifyLabelEvent -= mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent -= mitk::MessageDelegate( m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->ActiveLabelEvent -= mitk::MessageDelegate1(m_Controls.m_LabelSetWidget, &QmitkLabelSetWidget::SelectLabelByPixelValue); - workingImage->BeforeChangeLayerEvent -= mitk::MessageDelegate( - this, &QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection); + + // Removed in T27851 to have a chance to react to AfterChangeLayerEvent. Did it brake something? + // workingImage->BeforeChangeLayerEvent -= mitk::MessageDelegate( + // this, &QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection); + + workingImage->AfterChangeLayerEvent -= mitk::MessageDelegate( + this, &QmitkMultiLabelSegmentationView::UpdateControls); } void QmitkMultiLabelSegmentationView::SetFocus() { } void QmitkMultiLabelSegmentationView::UpdateControls() { mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); bool hasReferenceNode = referenceNode != nullptr; mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); bool hasValidWorkingNode = workingNode != nullptr; m_Controls.m_pbNewLabel->setEnabled(false); m_Controls.m_gbInterpolation->setEnabled(false); m_Controls.m_SliceBasedInterpolatorWidget->setEnabled(false); m_Controls.m_SurfaceBasedInterpolatorWidget->setEnabled(false); m_Controls.m_LabelSetWidget->setEnabled(false); m_Controls.m_btAddLayer->setEnabled(false); m_Controls.m_btDeleteLayer->setEnabled(false); m_Controls.m_cbActiveLayer->setEnabled(false); m_Controls.m_btPreviousLayer->setEnabled(false); m_Controls.m_btNextLayer->setEnabled(false); m_Controls.m_btLockExterior->setChecked(false); m_Controls.m_btLockExterior->setEnabled(false); + m_Controls.m_tbSavePreset->setEnabled(false); + m_Controls.m_tbLoadPreset->setEnabled(false); m_Controls.m_pbShowLabelTable->setChecked(false); m_Controls.m_pbShowLabelTable->setEnabled(false); m_Controls.m_ManualToolSelectionBox3D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); m_Controls.m_ManualToolSelectionBox2D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); if (hasValidWorkingNode) { // TODO adapt tool manager so that this check is done there, e.g. convenience function mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); hasValidWorkingNode = workingImage != nullptr; if (hasValidWorkingNode) { m_Controls.m_pbNewLabel->setEnabled(true); m_Controls.m_btLockExterior->setEnabled(true); + m_Controls.m_tbSavePreset->setEnabled(true); + m_Controls.m_tbLoadPreset->setEnabled(true); m_Controls.m_pbShowLabelTable->setEnabled(true); m_Controls.m_gbInterpolation->setEnabled(true); m_Controls.m_SliceBasedInterpolatorWidget->setEnabled(true); m_Controls.m_SurfaceBasedInterpolatorWidget->setEnabled(true); m_Controls.m_LabelSetWidget->setEnabled(true); m_Controls.m_btAddLayer->setEnabled(true); int activeLayer = workingImage->GetActiveLayer(); int numberOfLayers = workingImage->GetNumberOfLayers(); m_Controls.m_cbActiveLayer->blockSignals(true); m_Controls.m_cbActiveLayer->clear(); for (unsigned int lidx = 0; lidx < workingImage->GetNumberOfLayers(); ++lidx) { m_Controls.m_cbActiveLayer->addItem(QString::number(lidx)); } m_Controls.m_cbActiveLayer->setCurrentIndex(activeLayer); m_Controls.m_cbActiveLayer->blockSignals(false); m_Controls.m_cbActiveLayer->setEnabled(numberOfLayers > 1); m_Controls.m_btDeleteLayer->setEnabled(numberOfLayers > 1); m_Controls.m_btPreviousLayer->setEnabled(activeLayer > 0); m_Controls.m_btNextLayer->setEnabled(activeLayer != numberOfLayers - 1); m_Controls.m_btLockExterior->setChecked(workingImage->GetLabel(0, activeLayer)->GetLocked()); m_Controls.m_pbShowLabelTable->setChecked(workingImage->GetNumberOfLabels() > 1 /*1st is exterior*/); //MLI TODO //m_Controls.m_ManualToolSelectionBox2D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithWorkingDataVisible); } } if (hasValidWorkingNode && hasReferenceNode) { int layer = -1; referenceNode->GetIntProperty("layer", layer); workingNode->SetIntProperty("layer", layer + 1); } this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_ALL); } void QmitkMultiLabelSegmentationView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_IRenderWindowPart != renderWindowPart) { m_IRenderWindowPart = renderWindowPart; m_Parent->setEnabled(true); QList controllers; controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()); controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()); controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()); m_Controls.m_SliceBasedInterpolatorWidget->SetSliceNavigationControllers(controllers); } } void QmitkMultiLabelSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_ToolManager->ActivateTool(-1); m_IRenderWindowPart = nullptr; m_Parent->setEnabled(false); } void QmitkMultiLabelSegmentationView::ResetMouseCursor() { if (m_MouseCursorSet) { mitk::ApplicationCursor::GetInstance()->PopCursor(); m_MouseCursorSet = false; } } void QmitkMultiLabelSegmentationView::SetMouseCursor(const us::ModuleResource resource, int hotspotX, int hotspotY) { // Remove previously set mouse cursor if (m_MouseCursorSet) this->ResetMouseCursor(); if (resource) { us::ModuleResourceStream cursor(resource, std::ios::binary); mitk::ApplicationCursor::GetInstance()->PushCursor(cursor, hotspotX, hotspotY); m_MouseCursorSet = true; } } void QmitkMultiLabelSegmentationView::InitializeListeners() { if (m_Interactor.IsNull()) { us::Module* module = us::GetModuleContext()->GetModule(); std::vector resources = module->FindResources("/", "*", true); for (std::vector::iterator iter = resources.begin(); iter != resources.end(); ++iter) { MITK_INFO << iter->GetResourcePath(); } m_Interactor = mitk::SegmentationInteractor::New(); if (!m_Interactor->LoadStateMachine("SegmentationInteraction.xml", module)) { MITK_WARN << "Error loading state machine"; } if (!m_Interactor->SetEventConfig("ConfigSegmentation.xml", module)) { MITK_WARN << "Error loading state machine configuration"; } // Register as listener via micro services us::ServiceProperties props; props["name"] = std::string("SegmentationInteraction"); m_ServiceRegistration = us::GetModuleContext()->RegisterService(m_Interactor.GetPointer(), props); } } void QmitkMultiLabelSegmentationView::ReinitializeViews() const { if (m_ReferenceNode.IsNotNull() && nullptr != m_ReferenceNode->GetData()) { const auto currentTimePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); unsigned int imageTimeStep = 0; if (m_ReferenceNode->GetData()->GetTimeGeometry()->IsValidTimePoint(currentTimePoint)) { imageTimeStep = m_ReferenceNode->GetData()->GetTimeGeometry()->TimePointToTimeStep(currentTimePoint); } mitk::RenderingManager::GetInstance()->InitializeViews(m_ReferenceNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); } } diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.h index 9d6bc16a0f..c57e243de0 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.h +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.h @@ -1,171 +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. ============================================================================*/ #ifndef QmitkMultiLabelSegmentationView_h #define QmitkMultiLabelSegmentationView_h #include #include "mitkSegmentationInteractor.h" #include #include "ui_QmitkMultiLabelSegmentationControls.h" // berry #include class QmitkRenderWindow; /** * \ingroup ToolManagerEtAl * \ingroup org_mitk_gui_qt_multilabelsegmentation_internal */ class QmitkMultiLabelSegmentationView : public QmitkAbstractView, public mitk::ILifecycleAwarePart { Q_OBJECT public: static const std::string VIEW_ID; QmitkMultiLabelSegmentationView(); ~QmitkMultiLabelSegmentationView() override; typedef std::map NodeTagMapType; // GUI setup void CreateQtPartControl(QWidget *parent) override; // ILifecycleAwarePart interface public: void Activated() override; void Deactivated() override; void Visible() override; void Hidden() override; virtual int GetSizeFlags(bool width); virtual int ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult); protected slots: // reaction to the shortcut for toggling the visibility of the working node void OnVisibilityShortcutActivated(); // reaction to the shortcut for iterating over all labels void OnLabelToggleShortcutActivated(); // reaction to the selection of any 2D segmentation tool void OnManualTool2DSelected(int id); // reaction to button "New Label" void OnNewLabel(); + // reaction to button "Save Preset" + void OnSavePreset(); + + // reaction to button "Load Preset" + void OnLoadPreset(); + // reaction to button "Show Label Table" void OnShowLabelTable(bool value); // reaction to button "New Segmentation Session" void OnNewSegmentationSession(); // reaction to signal "goToLabel" from labelset widget void OnGoToLabel(const mitk::Point3D &pos); void OnResetView(); // reaction to the button "Add Layer" void OnAddLayer(); // reaction to the button "Delete Layer" void OnDeleteLayer(); // reaction to the button "Previous Layer" void OnPreviousLayer(); // reaction to the button "Next Layer" void OnNextLayer(); // reaction to the combobox change "Change Layer" void OnChangeLayer(int); // reaction to the button "Deactive Active Tool" void OnDeactivateActiveTool(); // reaction to the button "Lock exterior" void OnLockExteriorToggled(bool); // reaction to the selection of a new patient (reference) image in the DataStorage combobox void OnReferenceSelectionChanged(QList nodes); // reaction to the selection of a new Segmentation (working) image in the DataStorage combobox void OnSegmentationSelectionChanged(QList nodes); // reaction to ... void OnInterpolationSelectionChanged(int); protected: // reimplemented from QmitkAbstractView void OnPreferencesChanged(const berry::IBerryPreferences* prefs) override; // reimplemented from QmitkAbstractView void NodeRemoved(const mitk::DataNode* node) override; void OnEstablishLabelSetConnection(); void OnLooseLabelSetConnection(); void SetFocus() override; void UpdateControls(); void RenderWindowPartActivated(mitk::IRenderWindowPart *renderWindowPart); void RenderWindowPartDeactivated(mitk::IRenderWindowPart *renderWindowPart); void ResetMouseCursor(); void SetMouseCursor(const us::ModuleResource, int hotspotX, int hotspotY); void InitializeListeners(); void ReinitializeViews() const; /// \brief the Qt parent of our GUI (NOT of this object) QWidget *m_Parent; /// \brief Qt GUI file Ui::QmitkMultiLabelSegmentationControls m_Controls; mitk::IRenderWindowPart *m_IRenderWindowPart; mitk::ToolManager *m_ToolManager; mitk::DataNode::Pointer m_ReferenceNode; mitk::DataNode::Pointer m_WorkingNode; mitk::NodePredicateAnd::Pointer m_ReferencePredicate; mitk::NodePredicateAnd::Pointer m_SegmentationPredicate; bool m_AutoSelectionEnabled; bool m_MouseCursorSet; mitk::SegmentationInteractor::Pointer m_Interactor; /** * Reference to the service registration of the observer, * it is needed to unregister the observer on unload. */ us::ServiceRegistration m_ServiceRegistration; }; #endif // QmitkMultiLabelSegmentationView_h diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui index bbd900d8b9..48d2334831 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui @@ -1,161 +1,169 @@ QmitkBooleanOperationsWidgetControls 0 0 - 251 - 300 + 210 + 91 - + + + + 0 + 0 + + + false 0 0 Difference :/MultiLabelSegmentationUtilities/BooleanOperations/BooleanDifference_48x48.png:/MultiLabelSegmentationUtilities/BooleanOperations/BooleanDifference_48x48.png 24 24 false false Qt::ToolButtonTextUnderIcon Qt::NoArrow false 0 0 Intersection :/MultiLabelSegmentationUtilities/BooleanOperations/BooleanIntersection_48x48.png:/MultiLabelSegmentationUtilities/BooleanOperations/BooleanIntersection_48x48.png 24 24 false false Qt::ToolButtonTextUnderIcon Qt::NoArrow false 0 0 Union :/MultiLabelSegmentationUtilities/BooleanOperations/BooleanUnion_48x48.png:/MultiLabelSegmentationUtilities/BooleanOperations/BooleanUnion_48x48.png 24 24 false false Qt::ToolButtonTextUnderIcon Qt::NoArrow Qt::Vertical 20 - 273 + 40 QmitkDataSelectionWidget QWidget
internal/Common/QmitkDataSelectionWidget.h
+ 1
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidgetControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidgetControls.ui index 4fce705afb..acb4531419 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidgetControls.ui @@ -1,51 +1,58 @@ QmitkConvertToMlWidgetControls 0 0 - 270 - 300 + 98 + 62 - + + + + 0 + 0 + + + Convert Qt::Vertical 20 - 273 + 40 QmitkDataSelectionWidget QWidget
internal/Common/QmitkDataSelectionWidget.h
1
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui index f2efb8aaf6..1f2ad71121 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui @@ -1,277 +1,274 @@ QmitkImageMaskingWidgetControls 0 0 - 197 - 420 + 179 + 296 - + 2 4 - 1 + 0 Masks - + 3 4 - - - 0 - 10 - + + + 0 + 0 + Masking Mode - + Image Masking true Surface Masking Mask make output binary overwrite foreground overwrite background - + Background Value: Qt::Horizontal 40 20 50 0 50 16777215 0.0 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + Foreground Value: Qt::Horizontal 40 20 50 0 50 16777215 1.0 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 - 8 + 40 Labels - - - 3 - + 4 0 0 0 50 Qt::Vertical 20 - 113 + 40 QmitkDataSelectionWidget QWidget
internal/Common/QmitkDataSelectionWidget.h
QmitkLabelSetWidget QWidget
Qmitk/QmitkLabelSetWidget.h
1
QmitkMaskStampWidget QWidget
QmitkMaskStampWidget.h
1
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui index 7f33ec3a47..976fffbdc3 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui @@ -1,429 +1,427 @@ QmitkMorphologicalOperationsWidgetControls 0 0 - 236 - 507 + 155 + 414 - - - 0 - 0 - - - - Form - - + 0 0 0 Masks 3 4 - + + + + 0 + 0 + + + - + Structuring Element - + Ball true Cross - + Radius 1 20 1 Qt::Horizontal 1 20 false 0 0 Dilatation :/SegmentationUtilities/MorphologicalOperations/Dilate_48x48.png:/SegmentationUtilities/MorphologicalOperations/Dilate_48x48.png 32 32 Qt::ToolButtonTextUnderIcon false 0 0 Globally fills holes in segmentation (radius not required) Fill Holes :/SegmentationUtilities/MorphologicalOperations/FillHoles_48x48.png:/SegmentationUtilities/MorphologicalOperations/FillHoles_48x48.png 32 32 Qt::ToolButtonTextUnderIcon false 0 0 Erosion :/SegmentationUtilities/MorphologicalOperations/Erode_48x48.png:/SegmentationUtilities/MorphologicalOperations/Erode_48x48.png 32 32 Qt::ToolButtonTextUnderIcon false 0 0 Closing :/SegmentationUtilities/MorphologicalOperations/Closing_48x48.png:/SegmentationUtilities/MorphologicalOperations/Closing_48x48.png 32 32 Qt::ToolButtonTextUnderIcon false 0 0 Opening :/SegmentationUtilities/MorphologicalOperations/Opening_48x48.png:/SegmentationUtilities/MorphologicalOperations/Opening_48x48.png 32 32 Qt::ToolButtonTextUnderIcon - + Qt::Vertical 20 40 Labels - + 3 4 0 0 0 50 0 0 15 0 50 false 0 0 0 15 50 false - + Qt::Vertical 20 40 QmitkDataSelectionWidget QWidget
internal/Common/QmitkDataSelectionWidget.h
1
QmitkToolSelectionBox QWidget
QmitkToolSelectionBox.h
QmitkToolGUIArea QWidget
QmitkToolGUIArea.h
QmitkLabelSetWidget QWidget
Qmitk/QmitkLabelSetWidget.h
1
sliderMorphFactor valueChanged(int) spinBoxMorphFactor setValue(int) 240 27 766 36 spinBoxMorphFactor valueChanged(int) sliderMorphFactor setValue(int) 784 38 657 38
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui index 6548e7060e..511fcc381f 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui @@ -1,245 +1,252 @@ QmitkSurfaceToImageWidgetControls 0 0 - 198 - 482 + 179 + 183 - + 3 4 0 Masks - + 3 4 - + + + + 0 + 0 + + + make output binary overwrite background - + Background Value: Qt::Horizontal 40 20 50 0 50 16777215 0.0 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + Foreground Value: Qt::Horizontal 40 20 50 0 50 16777215 1.0 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Convert - + Qt::Vertical 20 - 308 + 40 Labels - + 2 2 0 0 0 50 0 20 - + Qt::Vertical 20 - 202 + 40 QmitkDataSelectionWidget QWidget
internal/Common/QmitkDataSelectionWidget.h
1
QmitkLabelSetWidget QWidget
Qmitk/QmitkLabelSetWidget.h
1
QmitkSurfaceStampWidget QWidget
QmitkSurfaceStampWidget.h
1
diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterView.cpp b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterView.cpp index a17296e42a..1ec7c9c470 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterView.cpp +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterView.cpp @@ -1,397 +1,551 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include "mitkWorkbenchUtil.h" #include "ConcentrationCurveConverterView.h" #include "mitkConcentrationCurveGenerator.h" #include "mitkNodePredicateDataType.h" #include "mitkConvertToConcentrationTurboFlashFunctor.h" #include "mitkConvertToConcentrationAbsoluteFunctor.h" #include "mitkConvertToConcentrationRelativeFunctor.h" #include "itkBinaryFunctorImageFilter.h" - - - +#include "boost/math/constants/constants.hpp" + +#include +#include +#include +#include +#include +#include +#include "mitkNodePredicateFunction.h" +#include + +#include // Includes for image casting between ITK and MITK #include "mitkImageTimeSelector.h" #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include #include -const std::string ConcentrationCurveConverterView::VIEW_ID = "org.mitk.ConcentrationCurveConverterView"; +const std::string ConcentrationCurveConverterView::VIEW_ID = "org.mitk.views.pharmacokinetics.concentration.mri"; void ConcentrationCurveConverterView::SetFocus() { m_Controls.btnConvertToConcentration->setFocus(); } void ConcentrationCurveConverterView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnConvertToConcentration->setEnabled(false); connect(m_Controls.btnConvertToConcentration, SIGNAL(clicked()), this, SLOT(OnConvertToConcentrationButtonClicked())); + m_Controls.timeSeriesNodeSelector->SetNodePredicate(this->m_isValidTimeSeriesImagePredicate); + m_Controls.timeSeriesNodeSelector->SetDataStorage(this->GetDataStorage()); + m_Controls.timeSeriesNodeSelector->SetSelectionIsOptional(false); + m_Controls.timeSeriesNodeSelector->SetInvalidInfo("Please select time series."); + + m_Controls.image3DNodeSelector->SetNodePredicate(this->m_isValidPDWImagePredicate); + m_Controls.image3DNodeSelector->SetDataStorage(this->GetDataStorage()); + m_Controls.image3DNodeSelector->SetSelectionIsOptional(false); + m_Controls.image3DNodeSelector->SetInvalidInfo("Please select 3D image."); + + m_Controls.baselineImageNodeSelector->SetNodePredicate(this->m_isValidPDWImagePredicate); + m_Controls.baselineImageNodeSelector->SetDataStorage(this->GetDataStorage()); + m_Controls.baselineImageNodeSelector->SetSelectionIsOptional(false); + m_Controls.baselineImageNodeSelector->SetInvalidInfo("Please select baseline image."); + + m_Controls.t2TimeSeriesNodeSelector->SetNodePredicate(this->m_isValidTimeSeriesImagePredicate); + m_Controls.t2TimeSeriesNodeSelector->SetDataStorage(this->GetDataStorage()); + m_Controls.t2TimeSeriesNodeSelector->SetSelectionIsOptional(false); + m_Controls.t2TimeSeriesNodeSelector->SetInvalidInfo("Please select time series."); + + m_Controls.PDWImageNodeSelector->SetNodePredicate(m_isValidPDWImagePredicate); + m_Controls.PDWImageNodeSelector->SetDataStorage(this->GetDataStorage()); + m_Controls.PDWImageNodeSelector->SetInvalidInfo("Please select PDW Image."); + m_Controls.PDWImageNodeSelector->setEnabled(false); + m_Controls.groupBox_T1->hide(); m_Controls.groupBox_T2->hide(); m_Controls.groupBox3D->hide(); m_Controls.groupBox4D->hide(); m_Controls.groupBoxTurboFlash->hide(); - m_Controls.factorSpinBox->setEnabled(false); - m_Controls.groupBox_ConcentrationParameters->hide(); + m_Controls.groupConcentration->hide(); connect(m_Controls.radioButton_T1, SIGNAL(toggled(bool)),this, SLOT(OnSettingChanged())); connect(m_Controls.radioButton_T2, SIGNAL(toggled(bool)),this, SLOT(OnSettingChanged())); connect(m_Controls.radioButton3D, SIGNAL(toggled(bool)),this, SLOT(OnSettingChanged())); connect(m_Controls.radioButton4D, SIGNAL(toggled(bool)),this, SLOT(OnSettingChanged())); - connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), m_Controls.groupBoxTurboFlash, - SLOT(setVisible(bool))); - connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), this, - SLOT(OnSettingChanged())); - connect(m_Controls.relaxationTime, SIGNAL(valueChanged(double)), this, - SLOT(OnSettingChanged())); - connect(m_Controls.recoveryTime, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); + //Concentration + m_Controls.groupConcentration->hide(); + m_Controls.groupBoxEnhancement->hide(); + m_Controls.groupBoxTurboFlash->hide(); + m_Controls.groupBox_T1MapviaVFA->hide(); + + m_Controls.spinBox_baselineStartTimeStep->setValue(0); + m_Controls.spinBox_baselineEndTimeStep->setValue(0); + + m_Controls.spinBox_baselineEndTimeStep->setMinimum(0); + m_Controls.spinBox_baselineStartTimeStep->setMinimum(0); + + m_Controls.spinBox_baselineStartTimeStepT2->setValue(0); + m_Controls.spinBox_baselineEndTimeStepT2->setValue(0); + + m_Controls.spinBox_baselineEndTimeStepT2->setMinimum(0); + m_Controls.spinBox_baselineStartTimeStepT2->setMinimum(0); + + connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), m_Controls.groupBoxTurboFlash, SLOT(setVisible(bool))); + connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), this, SLOT(OnSettingChanged())); + connect(m_Controls.relaxationtime, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); + connect(m_Controls.recoverytime, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); connect(m_Controls.relaxivity, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); - connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), this, - SLOT(OnSettingChanged())); - connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), this, - SLOT(OnSettingChanged())); - connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), m_Controls.factorSpinBox, - SLOT(setEnabled(bool))); - connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), - m_Controls.factorSpinBox, - SLOT(setEnabled(bool))); - connect(m_Controls.factorSpinBox, SIGNAL(valueChanged(double)), this, - SLOT(OnSettingChanged())); + connect(m_Controls.timeSeriesNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ConcentrationCurveConverterView::OnNodeSelectionChanged); + connect(m_Controls.image3DNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ConcentrationCurveConverterView::OnNodeSelectionChanged); + connect(m_Controls.baselineImageNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ConcentrationCurveConverterView::OnNodeSelectionChanged); + connect(m_Controls.t2TimeSeriesNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ConcentrationCurveConverterView::OnNodeSelectionChanged); + connect(m_Controls.PDWImageNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ConcentrationCurveConverterView::OnSettingChanged); + + connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), this, SLOT(OnSettingChanged())); + connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), this, SLOT(OnSettingChanged())); + connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), m_Controls.groupBoxEnhancement, SLOT(setVisible(bool))); + connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), m_Controls.groupBoxEnhancement, SLOT(setVisible(bool))); + + connect(m_Controls.factorSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); + connect(m_Controls.spinBox_baselineStartTimeStep, SIGNAL(valueChanged(int)), this, SLOT(OnSettingChanged())); + connect(m_Controls.spinBox_baselineEndTimeStep, SIGNAL(valueChanged(int)), this, SLOT(OnSettingChanged())); + connect(m_Controls.spinBox_baselineStartTimeStepT2, SIGNAL(valueChanged(int)), this, SLOT(OnSettingChanged())); + connect(m_Controls.spinBox_baselineEndTimeStepT2, SIGNAL(valueChanged(int)), this, SLOT(OnSettingChanged())); + + connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), m_Controls.groupBox_T1MapviaVFA, SLOT(setVisible(bool))); + connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), this, SLOT(OnSettingChanged())); + connect(m_Controls.FlipangleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); + connect(m_Controls.RelaxivitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); + connect(m_Controls.TRSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); + + connect(m_Controls.T2EchoTimeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); + connect(m_Controls.T2FactorSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSettingChanged())); + + connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), m_Controls.PDWImageNodeSelector, SLOT(setEnabled(bool))); + } void ConcentrationCurveConverterView::OnSettingChanged() { - bool ok = true; + bool ok = false; m_Controls.groupBox_T1->setVisible(m_Controls.radioButton_T1->isChecked()); m_Controls.groupBox_T2->setVisible(m_Controls.radioButton_T2->isChecked()); if(m_Controls.radioButton_T1->isChecked()) { - m_Controls.groupBox_ConcentrationParameters->setVisible(true); m_Controls.groupBox3D->setVisible(m_Controls.radioButton3D->isChecked()); m_Controls.groupBox4D->setVisible(m_Controls.radioButton4D->isChecked()); if(m_Controls.radioButton4D->isChecked()) { + m_Controls.groupConcentration->setVisible(true); ok = m_selectedImage.IsNotNull() && CheckSettings(); } else if(m_Controls.radioButton3D->isChecked()) { + m_Controls.groupConcentration->setVisible(true); ok = m_selectedImage.IsNotNull() && m_selectedBaselineImage.IsNotNull() && CheckSettings(); } } else if (m_Controls.radioButton_T2->isChecked()) { - m_Controls.groupBox_ConcentrationParameters->setVisible(false); - + m_Controls.groupConcentration->setVisible(false); ok = m_selectedImage.IsNotNull() && CheckSettings(); - } m_Controls.btnConvertToConcentration->setEnabled(ok); + } + + bool ConcentrationCurveConverterView::CheckSettings() const { bool ok = true; - if(m_Controls.radioButton_T1->isChecked()) + if (m_Controls.radioButton_T1->isChecked()) { - if (this->m_Controls.radioButtonTurboFlash->isChecked()) - { - ok = ok && (m_Controls.recoveryTime->value() > 0); - ok = ok && (m_Controls.relaxationTime->value() > 0); - ok = ok && (m_Controls.relaxivity->value() > 0); - - } - else if (this->m_Controls.radioButton_absoluteEnhancement->isChecked() - || this->m_Controls.radioButton_relativeEnchancement->isChecked()) - { - ok = ok && (m_Controls.factorSpinBox->value() > 0); - } - else - { - ok = false; - } + if (this->m_Controls.radioButtonTurboFlash->isChecked()) + { + ok = ok && (m_Controls.recoverytime->value() > 0); + ok = ok && (m_Controls.relaxationtime->value() > 0); + ok = ok && (m_Controls.relaxivity->value() > 0); + ok = ok && (m_Controls.AifRecoverytime->value() > 0); + ok = ok && CheckBaselineSelectionSettings(); + } + else if (this->m_Controls.radioButton_absoluteEnhancement->isChecked() + || this->m_Controls.radioButton_relativeEnchancement->isChecked()) + { + ok = ok && (m_Controls.factorSpinBox->value() > 0); + ok = ok && CheckBaselineSelectionSettings(); + } + else if (this->m_Controls.radioButtonUsingT1viaVFA->isChecked()) + { + ok = ok && (m_Controls.FlipangleSpinBox->value() > 0); + ok = ok && (m_Controls.TRSpinBox->value() > 0); + ok = ok && (m_Controls.RelaxivitySpinBox->value() > 0); + ok = ok && (m_Controls.PDWImageNodeSelector->GetSelectedNode().IsNotNull()); + ok = ok && CheckBaselineSelectionSettings(); + } + else + { + ok = false; + } } else if (this->m_Controls.radioButton_T2->isChecked()) { - ok = ok && m_Controls.T2EchoTimeSpinBox->value() > 0; - ok = ok && m_Controls.T2FactorSpinBox->value() > 0; + ok = ok && m_Controls.T2EchoTimeSpinBox->value() > 0; + ok = ok && m_Controls.T2FactorSpinBox->value() > 0; + ok = ok && CheckBaselineSelectionSettings(); } else { - ok=false; + ok = false; } - - return ok; } +bool ConcentrationCurveConverterView::CheckBaselineSelectionSettings() const +{ + if (this->m_Controls.radioButton_T1->isChecked()) + { + return m_Controls.spinBox_baselineStartTimeStep->value() <= m_Controls.spinBox_baselineEndTimeStep->value(); + } + else if (this->m_Controls.radioButton_T2->isChecked()) + { + return m_Controls.spinBox_baselineStartTimeStepT2->value() <= m_Controls.spinBox_baselineEndTimeStepT2->value(); + } + else + { + return 0; + } +} + void ConcentrationCurveConverterView::OnConvertToConcentrationButtonClicked() { mitk::Image::Pointer concentrationImage; mitk::DataNode::Pointer concentrationNode; if(m_Controls.radioButton_T1->isChecked()) { if(m_Controls.radioButton4D->isChecked()) { concentrationImage = this->Convert4DConcentrationImage(this->m_selectedImage); } else if(m_Controls.radioButton3D->isChecked()) { concentrationImage = Convert3DConcentrationImage(this->m_selectedImage, this->m_selectedBaselineImage); } } else if(m_Controls.radioButton_T2->isChecked()) { concentrationImage = this->ConvertT2ConcentrationImgage(this->m_selectedImage); } std::string nameOfResultImage = m_selectedNode->GetName(); nameOfResultImage.append("_Concentration"); concentrationNode = AddConcentrationImage(concentrationImage,nameOfResultImage); } mitk::Image::Pointer ConcentrationCurveConverterView::Convert3DConcentrationImage(mitk::Image::Pointer inputImage,mitk::Image::Pointer baselineImage) { typedef itk::Image InputImageType; InputImageType::Pointer itkInputImage = InputImageType::New(); InputImageType::Pointer itkBaselineImage = InputImageType::New(); mitk::CastToItkImage(inputImage, itkInputImage ); mitk::CastToItkImage(baselineImage, itkBaselineImage ); mitk::Image::Pointer outputImage; if(this->m_Controls.radioButtonTurboFlash->isChecked()) { typedef mitk::ConvertToConcentrationTurboFlashFunctor ConversionFunctorTurboFlashType; typedef itk::BinaryFunctorImageFilter FilterTurboFlashType; ConversionFunctorTurboFlashType ConversionTurboFlashFunctor; - ConversionTurboFlashFunctor.initialize(m_Controls.relaxationTime->value(), m_Controls.relaxivity->value(), m_Controls.recoveryTime->value()); + ConversionTurboFlashFunctor.initialize(m_Controls.relaxationtime->value(), m_Controls.relaxivity->value(), m_Controls.recoverytime->value()); FilterTurboFlashType::Pointer ConversionTurboFlashFilter = FilterTurboFlashType::New(); ConversionTurboFlashFilter->SetFunctor(ConversionTurboFlashFunctor); ConversionTurboFlashFilter->SetInput1(itkInputImage); ConversionTurboFlashFilter->SetInput2(itkBaselineImage); ConversionTurboFlashFilter->Update(); outputImage = mitk::ImportItkImage(ConversionTurboFlashFilter->GetOutput())->Clone(); } else if(this->m_Controls.radioButton_absoluteEnhancement->isChecked()) { typedef mitk::ConvertToConcentrationAbsoluteFunctor ConversionFunctorAbsoluteType; typedef itk::BinaryFunctorImageFilter FilterAbsoluteType; ConversionFunctorAbsoluteType ConversionAbsoluteFunctor; ConversionAbsoluteFunctor.initialize(m_Controls.factorSpinBox->value()); FilterAbsoluteType::Pointer ConversionAbsoluteFilter = FilterAbsoluteType::New(); ConversionAbsoluteFilter->SetFunctor(ConversionAbsoluteFunctor); ConversionAbsoluteFilter->SetInput1(itkInputImage); ConversionAbsoluteFilter->SetInput2(itkBaselineImage); ConversionAbsoluteFilter->Update(); outputImage = mitk::ImportItkImage(ConversionAbsoluteFilter->GetOutput())->Clone(); } else if(m_Controls.radioButton_relativeEnchancement->isChecked()) { typedef mitk::ConvertToConcentrationRelativeFunctor ConversionFunctorRelativeType; typedef itk::BinaryFunctorImageFilter FilterRelativeType; ConversionFunctorRelativeType ConversionRelativeFunctor; ConversionRelativeFunctor.initialize(m_Controls.factorSpinBox->value()); FilterRelativeType::Pointer ConversionRelativeFilter = FilterRelativeType::New(); ConversionRelativeFilter->SetFunctor(ConversionRelativeFunctor); ConversionRelativeFilter->SetInput1(itkInputImage); ConversionRelativeFilter->SetInput2(itkBaselineImage); ConversionRelativeFilter->Update(); outputImage = mitk::ImportItkImage(ConversionRelativeFilter->GetOutput())->Clone(); } return outputImage; } mitk::DataNode::Pointer ConcentrationCurveConverterView::AddConcentrationImage(mitk::Image* image, std::string nodeName) const { if (!image) { mitkThrow() << "Cannot generate concentration node. Passed image is null. parameter name: "; } mitk::DataNode::Pointer result = mitk::DataNode::New(); result->SetData(image); result->SetName(nodeName); result->SetVisibility(true); this->GetDataStorage()->Add(result, m_selectedNode); return result; }; mitk::Image::Pointer ConcentrationCurveConverterView::Convert4DConcentrationImage(mitk::Image::Pointer inputImage) { //Compute Concentration image mitk::ConcentrationCurveGenerator::Pointer concentrationGen = mitk::ConcentrationCurveGenerator::New(); concentrationGen->SetDynamicImage(inputImage); concentrationGen->SetisTurboFlashSequence(m_Controls.radioButtonTurboFlash->isChecked()); concentrationGen->SetAbsoluteSignalEnhancement(m_Controls.radioButton_absoluteEnhancement->isChecked()); concentrationGen->SetRelativeSignalEnhancement(m_Controls.radioButton_relativeEnchancement->isChecked()); + concentrationGen->SetUsingT1Map(m_Controls.radioButtonUsingT1viaVFA->isChecked()); + concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStepT2->value()); + concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStepT2->value()); concentrationGen->SetisT2weightedImage(false); if (m_Controls.radioButtonTurboFlash->isChecked()) { - concentrationGen->SetRecoveryTime(m_Controls.recoveryTime->value()); - concentrationGen->SetRelaxationTime(m_Controls.relaxationTime->value()); + concentrationGen->SetRecoveryTime(m_Controls.recoverytime->value()); + concentrationGen->SetRelaxationTime(m_Controls.relaxationtime->value()); concentrationGen->SetRelaxivity(m_Controls.relaxivity->value()); + concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); + concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); + } + else if (this->m_Controls.radioButtonUsingT1viaVFA->isChecked()) + { + concentrationGen->SetRecoveryTime(m_Controls.TRSpinBox->value()); + concentrationGen->SetRelaxivity(m_Controls.RelaxivitySpinBox->value()); + concentrationGen->SetT10Image(dynamic_cast(m_Controls.PDWImageNodeSelector->GetSelectedNode()->GetData())); + concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); + concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); + //Convert Flipangle from degree to radiant + double alpha = m_Controls.FlipangleSpinBox->value()/360*2* boost::math::constants::pi(); + concentrationGen->SetFlipAngle(alpha); } else { concentrationGen->SetFactor(m_Controls.factorSpinBox->value()); + concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); + concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); } mitk::Image::Pointer concentrationImage = concentrationGen->GetConvertedImage(); return concentrationImage; } mitk::Image::Pointer ConcentrationCurveConverterView::ConvertT2ConcentrationImgage(mitk::Image::Pointer inputImage) { //Compute Concentration image mitk::ConcentrationCurveGenerator::Pointer concentrationGen = mitk::ConcentrationCurveGenerator::New(); concentrationGen->SetDynamicImage(inputImage); concentrationGen->SetisTurboFlashSequence(false); concentrationGen->SetAbsoluteSignalEnhancement(false); concentrationGen->SetRelativeSignalEnhancement(false); concentrationGen->SetisT2weightedImage(true); concentrationGen->SetT2Factor(m_Controls.T2FactorSpinBox->value()); concentrationGen->SetT2EchoTime(m_Controls.T2EchoTimeSpinBox->value()); + concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); + concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); mitk::Image::Pointer concentrationImage = concentrationGen->GetConvertedImage(); return concentrationImage; } -void ConcentrationCurveConverterView::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*source*/,const QList& selectedNodes ){ - +void ConcentrationCurveConverterView::OnNodeSelectionChanged(QList/*nodes*/) +{ m_selectedNode = nullptr; m_selectedImage = nullptr; m_selectedBaselineNode = nullptr; m_selectedBaselineImage = nullptr; - m_Controls.timeserieslabel->setText("No (valid) series selected."); - m_Controls.BaselineImageLabel->setText("No (valid) baseline image selected."); - m_Controls.ImageLabel->setText("No (valid) image selected."); - m_Controls.T2_SeriesLabel->setText("No (valid) series selected."); - - m_Controls.btnConvertToConcentration->setEnabled(false); - - QList nodes = selectedNodes; - mitk::NodePredicateDataType::Pointer imagePredicate = mitk::NodePredicateDataType::New("Image"); - - if(m_Controls.radioButton_T1->isChecked()) + if (m_Controls.radioButton_T1->isChecked()) { - if (nodes.size() > 0 && imagePredicate->CheckNode(nodes.front())) + if (m_Controls.radioButton4D->isChecked()) + { + if (m_Controls.timeSeriesNodeSelector->GetSelectedNode().IsNotNull()) { - this->m_selectedNode = nodes.front(); - this->m_selectedImage = dynamic_cast(this->m_selectedNode->GetData()); - m_Controls.timeserieslabel->setText((this->m_selectedNode->GetName()).c_str()); - nodes.pop_front(); - } + this->m_selectedNode = m_Controls.timeSeriesNodeSelector->GetSelectedNode(); + m_selectedImage = dynamic_cast(m_selectedNode->GetData()); - if (nodes.size() > 0 && imagePredicate->CheckNode(nodes.front())) + } + else { - - this->m_selectedBaselineNode = nodes.front(); - this->m_selectedBaselineImage = dynamic_cast(this->m_selectedBaselineNode->GetData()); - this->m_Controls.BaselineImageLabel->setText((this->m_selectedBaselineNode->GetName()).c_str()); + this->m_selectedNode = nullptr; + this->m_selectedImage = nullptr; } - } - else if(m_Controls.radioButton_T2->isChecked()) - { - if (nodes.size() > 0 && imagePredicate->CheckNode(nodes.front())) + } + else if (m_Controls.radioButton3D->isChecked()) + { + if (m_Controls.image3DNodeSelector->GetSelectedNode().IsNotNull() && m_Controls.baselineImageNodeSelector->GetSelectedNode().IsNotNull()) { - this->m_selectedNode = nodes.front(); - this->m_selectedImage = dynamic_cast(this->m_selectedNode->GetData()); - m_Controls.T2_SeriesLabel->setText((this->m_selectedNode->GetName()).c_str()); - nodes.pop_front(); + this->m_selectedNode = m_Controls.image3DNodeSelector->GetSelectedNode(); + m_selectedImage = dynamic_cast(m_selectedNode->GetData()); + this->m_selectedBaselineNode = m_Controls.baselineImageNodeSelector->GetSelectedNode(); + m_selectedBaselineImage = dynamic_cast(m_selectedBaselineNode->GetData()); } - } + else + { + this->m_selectedNode = nullptr; + this->m_selectedImage = nullptr; + m_selectedBaselineNode = nullptr; + m_selectedBaselineImage = nullptr; + } + } + if (this->m_selectedImage.IsNotNull()) + { + m_Controls.spinBox_baselineStartTimeStep->setMaximum((this->m_selectedImage->GetDimension(3)) - 1); + m_Controls.spinBox_baselineEndTimeStep->setMaximum((this->m_selectedImage->GetDimension(3)) - 1); + } + } + if (m_Controls.radioButton_T2->isChecked()) + { + if (m_Controls.t2TimeSeriesNodeSelector->GetSelectedNode().IsNotNull()) + { + this->m_selectedNode = m_Controls.t2TimeSeriesNodeSelector->GetSelectedNode(); + m_selectedImage = dynamic_cast(m_selectedNode->GetData()); + } + else + { + this->m_selectedNode = nullptr; + this->m_selectedImage = nullptr; + } + if (this->m_selectedImage.IsNotNull()) + { + m_Controls.spinBox_baselineStartTimeStepT2->setMaximum((this->m_selectedImage->GetDimension(3)) - 1); + m_Controls.spinBox_baselineEndTimeStepT2->setMaximum((this->m_selectedImage->GetDimension(3)) - 1); + } + } m_Controls.btnConvertToConcentration->setEnabled(m_selectedImage.IsNotNull() && CheckSettings()); } ConcentrationCurveConverterView::ConcentrationCurveConverterView() { + mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); + mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); + mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); + mitk::NodePredicateAnd::Pointer isLegacyMask = mitk::NodePredicateAnd::New(isImage, isBinary); + mitk::NodePredicateDimension::Pointer is3D = mitk::NodePredicateDimension::New(3); + mitk::NodePredicateOr::Pointer isMask = mitk::NodePredicateOr::New(isLegacyMask, isLabelSet); + mitk::NodePredicateAnd::Pointer isNoMask = mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isMask)); + mitk::NodePredicateAnd::Pointer is3DImage = mitk::NodePredicateAnd::New(isImage, is3D, isNoMask); + + this->m_IsMaskPredicate = mitk::NodePredicateAnd::New(isMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); + + this->m_IsNoMaskImagePredicate = mitk::NodePredicateAnd::New(isNoMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); + + auto isDynamicData = mitk::NodePredicateFunction::New([](const mitk::DataNode* node) + { + return (node && node->GetData() && node->GetData()->GetTimeSteps() > 1); + }); + + auto modelFitResultRelationRule = mitk::ModelFitResultRelationRule::New(); + auto isNoModelFitNodePredicate = mitk::NodePredicateNot::New(modelFitResultRelationRule->GetConnectedSourcesDetector()); + + this->m_isValidPDWImagePredicate = mitk::NodePredicateAnd::New(is3DImage, isNoModelFitNodePredicate); + this->m_isValidTimeSeriesImagePredicate = mitk::NodePredicateAnd::New(isDynamicData, isImage, isNoMask); } diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterView.h b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterView.h index fe42e3ca54..24bc782d46 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterView.h +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterView.h @@ -1,99 +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 ConcentrationCurveConverterView_h #define ConcentrationCurveConverterView_h #include #include #include "ui_ConcentrationCurveConverterViewControls.h" #include /*! * @brief Test Plugin for SUV calculations of PET images */ class ConcentrationCurveConverterView : public QmitkAbstractView { Q_OBJECT public: // typedef itk::Image ImageType; typedef itk::Image ConvertedImageType; /*! @brief The view's unique ID - required by MITK */ static const std::string VIEW_ID; ConcentrationCurveConverterView(); protected slots: /*! * @brief Is triggered of the update button is clicked and the selected node should get the (new) iso level set. */ void OnConvertToConcentrationButtonClicked(); void OnSettingChanged(); bool CheckSettings() const; protected: // Overridden base class functions /*! * @brief Sets up the UI controls and connects the slots and signals. Gets * called by the framework to create the GUI at the right time. * @param[in,out] parent The parent QWidget, as this class itself is not a QWidget * subclass. */ void CreateQtPartControl(QWidget* parent) override; /*! * @brief Sets the focus to the plot curve button. Gets called by the framework to set the * focus on the right widget. */ void SetFocus() override; + bool CheckBaselineSelectionSettings() const; + /*! Helper method that adds an concentration image as child node to the current m_selectedNode and returns this new child node.*/ mitk::DataNode::Pointer AddConcentrationImage(mitk::Image* image, std::string nodeName) const; /*! \brief called by QmitkFunctionality when DataManager's selection has changed */ - void OnSelectionChanged( berry::IWorkbenchPart::Pointer source, - const QList& nodes) override; + void OnNodeSelectionChanged(QList/*nodes*/); // Variables /*! @brief The view's UI controls */ Ui::ConcentrationCurveConverterViewControls m_Controls; mitk::DataNode::Pointer m_selectedNode; mitk::Image::Pointer m_selectedImage; mitk::DataNode::Pointer m_selectedBaselineNode; mitk::Image::Pointer m_selectedBaselineImage; private: /**Converts the selected image to a concentration image based on the given gui settings.*/ mitk::Image::Pointer Convert4DConcentrationImage(mitk::Image::Pointer inputImage); mitk::Image::Pointer Convert3DConcentrationImage(mitk::Image::Pointer inputImage, mitk::Image::Pointer baselineImage); mitk::Image::Pointer ConvertT2ConcentrationImgage(mitk::Image::Pointer inputImage); + mitk::NodePredicateBase::Pointer m_IsNoMaskImagePredicate; + mitk::NodePredicateBase::Pointer m_IsMaskPredicate; + mitk::NodePredicateBase::Pointer m_isValidPDWImagePredicate; + mitk::NodePredicateBase::Pointer m_isValidTimeSeriesImagePredicate; }; #endif diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterViewControls.ui b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterViewControls.ui index 15dec472cf..ef67520b67 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterViewControls.ui +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.concentration.mri/src/internal/ConcentrationCurveConverterViewControls.ui @@ -1,354 +1,469 @@ ConcentrationCurveConverterViewControls 0 0 475 - 671 + 825 0 0 QmitkTemplate 10 T1 weighted MRI T2 weighted MRI T1 weighted images 10 10 10 3D Image 4D Image 4D Image - + Selected Time Series: - - - No series selected - - + 3D Image - - - - Selected 3D Image - - + + - - - - No Image selected - - + + - + - Baseline Image (without CA): + Selected 3D Image: - + QFrame::NoFrame QFrame::Plain - No Baseline Image selected + Selected Baseline Image: - - - Configuration - - + + - + 10 - - 10 - - - + + - Relative Signal Enhancement + Absolute Signal Enhancement - - + + - Absolute Signal Enhancement + Relative Signal Enhancement - - + + - Conversion Faktor k: + Using T1 Map via Variable Flip Angle - - - - - - - - - 10 - - - 10 - - Turbo FLASH Sequence + TurboFLASH Sequence - - - 20 + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Enhancement Parameters: - - - - Turbo FLASH Parameters: - + + + + + Conversion Factor k: + + + + + + + + + + Baseline Range Selection: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + - - + + - - - - Relaxivity - - - - - - - - - - - - - Recovery Time - - - - - - - Relaxation Time - - - - - - - true - - - - 0 - 0 - - + + - [s] + Start Time Frame - - + + - [] + End Time Frame - - - - [s] - - + + - - - + + + + + + + + T1 Map via Variable Flip Angle Parameters: + + + + + + Flip Angle [ ° ] : + + + + + + + Repetition Time TR [ms] : + + + + + + + Relaxivity [mMâ»Â¹ sâ»Â¹] : + + + + + + + + + + + + + Proton Density Weighted Image : + + + + + + + 10000.000000000000000 + + + + + + + + + + + + + true + + + Turbo FLASH Parameters: + + + + + + Recovery Time [s]: + + + + + + + + + + AIF Recovery Time [s]: + + + + + + + + + + + + + Relaxation Time [s]: + + + + + + + + + + Relaxivity [ ]: + + + + + + + + + Convert To Concentration + + + + + + 0 + 2 + + + + + 0 + 0 + + T2 weighted images - + + + + - - + + + + + + + + + + + + Start Time Frame + + + + + + + End Time Frame + + + + + + + Baseline Range Selection: + + + + - - - - Conversion Factor k - - + + - + - Echo Time TE + Echo Time TE [ms] - - + + Selected Time Series: - - + + - No Series Selected + Conversion Factor k - - - - Convert To Concentration - - - Qt::Vertical 20 40 + + + QmitkSingleNodeSelectionWidget + QWidget +
QmitkSingleNodeSelectionWidget.h
+ 1 +
+
diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.curvedescriptor/src/internal/PerfusionCurveDescriptionParameterView.cpp b/Plugins/org.mitk.gui.qt.pharmacokinetics.curvedescriptor/src/internal/PerfusionCurveDescriptionParameterView.cpp index 58a2b09b74..8a3f465aff 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.curvedescriptor/src/internal/PerfusionCurveDescriptionParameterView.cpp +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.curvedescriptor/src/internal/PerfusionCurveDescriptionParameterView.cpp @@ -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. ============================================================================*/ #include #include #include "mitkWorkbenchUtil.h" #include "PerfusionCurveDescriptionParameterView.h" #include "QmitkDescriptionParameterBackgroundJob.h" #include "mitkAreaUnderTheCurveDescriptionParameter.h" #include "mitkAreaUnderFirstMomentDescriptionParameter.h" #include "mitkMeanResidenceTimeDescriptionParameter.h" #include "mitkTimeToPeakCurveDescriptionParameter.h" #include "mitkMaximumCurveDescriptionParameter.h" #include "mitkPixelBasedDescriptionParameterImageGenerator.h" #include "mitkCurveParameterFunctor.h" #include "mitkExtractTimeGrid.h" #include const std::string PerfusionCurveDescriptionParameterView::VIEW_ID = - "org.mitk.gui.qt.pharmacokinetics.curvedescriptor"; + "org.mitk.views.pharmacokinetics.curvedescriptor"; void PerfusionCurveDescriptionParameterView::SetFocus() { m_Controls.btnCalculateParameters->setFocus(); } void PerfusionCurveDescriptionParameterView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnCalculateParameters->setEnabled(false); connect(m_Controls.btnCalculateParameters, SIGNAL(clicked()), this, SLOT(OnCalculateParametersButtonClicked())); InitParameterList(); } void PerfusionCurveDescriptionParameterView::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*source*/, const QList& /*nodes*/) { m_Controls.btnCalculateParameters->setEnabled(false); QList dataNodes = this->GetDataManagerSelection(); if (dataNodes.empty()) { m_selectedNode = nullptr; m_selectedImage = nullptr; } else { m_selectedNode = dataNodes[0]; m_selectedImage = dynamic_cast(m_selectedNode->GetData()); } m_Controls.lableSelectedImage->setText("No series selected."); if (m_selectedImage.IsNotNull()) { if (m_selectedImage->GetTimeGeometry()->CountTimeSteps() > 1) { m_Controls.btnCalculateParameters->setEnabled(true); m_Controls.lableSelectedImage->setText((this->m_selectedNode->GetName()).c_str()); } else { this->OnJobStatusChanged("Cannot compute parameters. Selected image must have multiple time steps."); } } else if (m_selectedNode.IsNotNull()) { this->OnJobStatusChanged("Cannot compute parameters. Selected node is not an image."); } else { this->OnJobStatusChanged("Cannot compute parameters. No node selected."); } } PerfusionCurveDescriptionParameterView::PerfusionCurveDescriptionParameterView() { m_selectedNode = nullptr; m_selectedImage = nullptr; m_selectedMask = nullptr; } void PerfusionCurveDescriptionParameterView::InitParameterList() { m_ParameterMap.clear(); mitk::CurveDescriptionParameterBase::Pointer parameterFunction = mitk::AreaUnderTheCurveDescriptionParameter::New().GetPointer(); m_ParameterMap.insert(std::make_pair(ParameterNameType("AUC"), parameterFunction)); parameterFunction = mitk::AreaUnderFirstMomentDescriptionParameter::New().GetPointer(); m_ParameterMap.insert(std::make_pair(ParameterNameType("AUMC"), parameterFunction)); parameterFunction = mitk::MeanResidenceTimeDescriptionParameter::New().GetPointer(); m_ParameterMap.insert(std::make_pair(ParameterNameType("MRT"), parameterFunction)); parameterFunction = mitk::MaximumCurveDescriptionParameter::New().GetPointer(); m_ParameterMap.insert(std::make_pair(ParameterNameType("Maximum"), parameterFunction)); parameterFunction = mitk::TimeToPeakCurveDescriptionParameter::New().GetPointer(); m_ParameterMap.insert(std::make_pair(ParameterNameType("TimeToPeak"), parameterFunction)); for (ParameterMapType::const_iterator pos = m_ParameterMap.begin(); pos != m_ParameterMap.end(); ++pos) { QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(pos->first), this->m_Controls.parameterlist); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setCheckState(Qt::Unchecked); } }; void PerfusionCurveDescriptionParameterView::ConfigureFunctor(mitk::CurveParameterFunctor* functor) const { functor->SetGrid(mitk::ExtractTimeGrid(m_selectedImage)); for (int pos = 0; pos < this->m_Controls.parameterlist->count(); ++pos) { QListWidgetItem* item = this->m_Controls.parameterlist->item(pos); mitk::CurveDescriptionParameterBase::Pointer parameterFunction = m_ParameterMap.at( item->text().toStdString()); if (item->checkState() == Qt::Checked) { functor->RegisterDescriptionParameter(item->text().toStdString(), parameterFunction); } } } void PerfusionCurveDescriptionParameterView::OnCalculateParametersButtonClicked() { mitk::PixelBasedDescriptionParameterImageGenerator::Pointer generator = mitk::PixelBasedDescriptionParameterImageGenerator::New(); mitk::CurveParameterFunctor::Pointer functor = mitk::CurveParameterFunctor::New(); this->ConfigureFunctor(functor); generator->SetFunctor(functor); generator->SetDynamicImage(m_selectedImage); generator->SetMask(m_selectedMask); ///////////////////////// //create job and put it into the thread pool DescriptionParameterBackgroundJob* pJob = new DescriptionParameterBackgroundJob(generator, this->m_selectedNode); pJob->setAutoDelete(true); connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnJobFinished())); connect(pJob, SIGNAL(ResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const DescriptionParameterBackgroundJob*)), this, SLOT(OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const DescriptionParameterBackgroundJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(JobProgress(double)), this, SLOT(OnJobProgress(double))); connect(pJob, SIGNAL(JobStatusChanged(QString)), this, SLOT(OnJobStatusChanged(QString))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } void PerfusionCurveDescriptionParameterView::OnJobFinished() { this->m_Controls.infoBox->append(QString("Fitting finished")); }; void PerfusionCurveDescriptionParameterView::OnJobError(QString err) { MITK_ERROR << err.toStdString().c_str(); m_Controls.infoBox->append(QString("") + err + QString("")); }; void PerfusionCurveDescriptionParameterView::OnJobResultsAreAvailable( mitk::modelFit::ModelFitResultNodeVectorType results, const DescriptionParameterBackgroundJob* pJob) { for (auto image : results) { this->GetDataStorage()->Add(image, pJob->GetParentNode()); } }; void PerfusionCurveDescriptionParameterView::OnJobProgress(double progress) { this->m_Controls.progressBar->setValue(100 * progress); QString report = QString("Progress. ") + QString::number(progress); this->m_Controls.infoBox->append(report); }; void PerfusionCurveDescriptionParameterView::OnJobStatusChanged(QString info) { this->m_Controls.infoBox->append(info); MITK_INFO << info.toStdString().c_str(); } diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/documentation/UserManual/Manual.dox b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/documentation/UserManual/Manual.dox index 46579d046d..126c9e07f2 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/documentation/UserManual/Manual.dox +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/documentation/UserManual/Manual.dox @@ -1,104 +1,104 @@ /** -\page org_mitk_views_pharmacokinetics_mri DCE MR Perfusion Datafit View +\page org_mitk_views_pharmacokinetics_mri The DCE MR Perfusion DataFit View \imageMacro{pharmacokinetics_mri_doc.svg,"Icon of the DCE MR Perfusion View",3.0} \tableofcontents \section FIT_DCE_Introduction Introduction In dynamic contrast-enhanced (DCE) MRI, pharmacokinetic (PK) modeling can be used to quantify tissue physiology. Parameters describing the tissue microvasculature can be derived by fitting a pharmacokinetic model, e.g. a compartment model, to the dynamic data. This view offers a comprehensive set of tools to perform pharmacokinetic analysis. \section FIT_DCE_Contact Contact information If you have any questions, need support, find a bug or have a feature request, feel free to contact us at www.mitk.org. \subsection FIT_DCE_Cite Citation information If you use the view for your research please cite our work as reference:\n\n Debus C and Floca R, Ingrisch M, Kompan I, Maier-Hein K, Abdollahi A, Nolden M, MITK-ModelFit: generic open-source framework for model fits and their exploration in medical imaging – design, implementation and application on the example of DCE-MRI. https://doi.org/10.1186/s12859-018-2588-1 (BMC Bioinformatics 2019 20:31) \section FIT_DCE_Data_and_ROI_Selection Time series and mask selection \imageMacro{dce_mri_maskAndFittingStrategy.png, "Time series and mask selection.", 10} In principle, every model can be fitted on the entire image. However, for model configuration reasons (e.g. AIF required) and computational time cost, this is often not advisable. Therefore, apart from the image to be fitted (Selected Time Series), a ROI segmentation can be defined (Selected Mask), within which model fitting is performed. The view currently offers Pixel based and/or ROI based averaged fits of time-varying curves. The ROI based fitting option becomes enabled, if a mask is selected. \section FIT_DCE_General_models Supported models Currently the following pharmacokinetic models for gadolinium-based contrast agent are available: - The Descriptive Brix model \ref FIT_DCE_lit_ref1 "[1]" - A semi-quantitative two/three segment linear model (2SL/3SL) - The standard tofts model \ref FIT_DCE_lit_ref2 "[2]" - The extended Tofts model \ref FIT_DCE_lit_ref3 "[3]" - The two compartment exchange model (2CXM) \ref FIT_DCE_lit_ref4 "[4, 5]" \section FIT_DCE_Settings Model settings \imageMacro{dce_mri_modelSettings.png, "Model settings of the view for the standard Tofts model.", 10} \subsection FIT_DCE_Settings_model Model specific settings Selecting one of the \ref FIT_DCE_General_models "supported models" will open below tabs for further configuration of the model. - The descriptive Brix model requires only definition of the duration of the bolus, i.e. the overall time of the injection (Injection Time [min]). - The 3SL is a semi-quantitative descriptive model that distinguishes three different segments of the signal: A constant baseline, the initial fast rise (wash-in) and the final slow rise / signal decrease (washout). Each of these segments is approximated by a linear curve, with change points in-between. It requires no further configuration. - The standard Tofts model, the extended Tofts model and the 2CXM are compartment models that require the input of the concentration time curve in the tissue feeding artery, the arterial input function (AIF). In the DCE MR Perfusion Datafit View, the arterial input function can be defined in several ways. For patient individual image derived AIFs, select the radio button Select AIF from Image. In that case, a segmentation ROI for the artery has to be selected. This can be done by clicking on the AIF Mask selection widget and selecting a suitable AIF segmentation from the data loaded in the Data Manager. In cases where the respective artery does not lie in the same image as the investigated tissue (e.g. in animal experiments, where a slice through the heart is used for AIF extraction), a dedicated AIF image can be selected using the corresponding Dedicated AIF image selection widget. An alternative option is to define the AIF via an external file by selecting Select AIF from File (e.g. for population derived AIFs or AIFs from blood sampling). By clicking the Browse button, one can select a csv file that holds the AIF values and corresponding timepoints (in tuple format (Time, Value)). Caution: the file must not contain a header line, but the first line must start with Time and Intensity values. Furthermore, the Hematocrit Level has to be set (from 0 to 1) for conversion from whole blood to plasma concentration. It is set as default to the literature value of 0.45. \subsection FIT_DCE_Settings_start Start parameter \imageMacro{dce_mri_start.png, "Example screenshot for start parameter settings.", 10} In cases of noisy data it can be useful to define the initial starting values of the parameter estimates, at which optimization starts, in order to prevent optimization results in local optima. Each model has default scalar values (applied to every voxel) for initial values of each parameter, however these can be adjusted. Moreover, initial values can also be defined locally for each individual voxel via starting value images. To load a starting value image, change the Type from scalar to image. This can be done by double-clicking on the type cell. In the Value column, selection of a starting value image will be available. \subsection FIT_DCE_Settings_constraint Constraints settings \imageMacro{dce_mri_constraints.png, "Example screenshot for constraints settings.", 10} To limit the fitting search space and to exclude unphysical/illogical results for model parameter estimates, constraints to individual parameters as well as combinations can be imposed. Each model has default constraints, however, new ones can be defined or removed by the + and – buttons in the table. The first column specifies the parameter(s) involved in the constraint (if multiple parameters are selected, their sum will be used) by selection in the drop down menu. The second column Type defines whether the constraint defines an upper or lower boundary. Value defines the actual constraint value, that should not be crossed, and Width allows for a certain tolerance width. \subsection FIT_DCE_Settings_concentration Signal to concentration conversion settings \imageMacro{dce_mri_concentration.png, "Example screenshot for concentration conversion settings.", 10} Most models require contrast agent concentration values as input rather than raw signal intensities (i.e. all compartment models). The DCE MR Perfusion DataFit View offers a variety of tools for the conversion from signal to concentration: by means of relative and absolute signal enhancement, via a T1-map calculated by the variable flip angle method, as well as a special conversion for turbo flash sequences. For the conversion methods, a baseline image prior to contrast agent arrival is required. In many data sets, multiple baseline images are available. The Baseline Range Selection allows for selection of a range of time frames, from which the average image (along the time dimension) is calculated and set as baseline input image. Remark: The number of the first time frame is 0. \section FIT_DCE_Fitting Executing a fit In order to distinguish results from different model fits to the data, a Fitting name can be defined. As default, the name of the model and the fitting strategy (pixel/ROI) are given. This name will then be appended by the respective parameter name.\n\n For development purposes and evaluation of the fits, the option Generate debug parameter images is available. Enabling this option will result in additional parameter maps displaying the status of the optimizer at fit termination. In the following definitions, an evaluation describes the process of cost function calculation and evaluation by the optimizer for a given parameter set. - Stop condition: Reasons for the fit termination, i.e. criterion reached, maximum number of iterations,... - Optimization time: The overall time from fitting start to termination. - Number of iterations: The number of iterations from fitting start to termination. - Constraint penalty ratio: Ratio between evaluations that were penalized and all evaluations. 0.0 means no evaluation was penalized; 1.0 all evaluations were. Evaluations that hit the failure threshold count as penalized, too. - Constraint last failed parameter: Ratio between evaluations that were beyond the failure threshold. 0.0 means no evaluation was a failure (but some may be penalized). - Constraint failure ratio: Index of the first (in terms of index position) parameter, which failed the constraints in the last evaluation. After all necessary configurations are set, the button Start Modelling is enabled, which starts the fitting routine. Progress can be seen in the message box on the bottom. Resulting parameter maps will afterwards be added to the Data Manager as sub-nodes of the analyzed 4D image. \section FIT_DCE_lit References/Literature - \anchor FIT_DCE_lit_ref1 [1] Brix G, Semmler W, Port R, Schad LR, Layer G, Lorenz WJ. Pharmacokinetic parameters in CNS Gd-DTPA enhanced MR imaging. J Comput Assist Tomogr. 1991;15:621–8. - \anchor FIT_DCE_lit_ref2 [2] Tofts PS, Kermode AG. Measurement of the blood-brain barrier permeability and leakage space using dynamic MR imaging. 1. Fundamental concepts. Magn Reson Med. 1991;17:357–67. - \anchor FIT_DCE_lit_ref3 [3] Sourbron SP, Buckley DL. On the scope and interpretation of the Tofts models for DCE-MRI. Magn Reson Med. 2011;66:735–45. - \anchor FIT_DCE_lit_ref4 [4] Brix G, Kiessling F, Lucht R, Darai S, Wasser K, Delorme S, et al. Microcirculation and microvasculature in breast tumors: Pharmacokinetic analysis of dynamic MR image series. Magn Reson Med. 2004;52:420–9. - \anchor FIT_DCE_lit_ref5 [5] Sourbron, Buckley. Tracer kinetic modelling in MRI: estimating perfusion and capillary permeability - pdf. Phys Med Biol. 2012. http://iopscience.iop.org/article/10.1088/0031-9155/57/2/R1/pdf. Accessed 1 May 2016. */ diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/plugin.xml b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/plugin.xml index bfb82f6dd2..84bf024784 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/plugin.xml +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/plugin.xml @@ -1,12 +1,12 @@ - diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp index 9ee2b5e3f5..5158170817 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp @@ -1,1485 +1,1485 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "MRPerfusionView.h" #include "boost/tokenizer.hpp" #include "boost/math/constants/constants.hpp" #include #include "mitkWorkbenchUtil.h" #include "mitkAterialInputFunctionGenerator.h" #include "mitkConcentrationCurveGenerator.h" #include #include #include #include "mitkThreeStepLinearModelFactory.h" #include "mitkThreeStepLinearModelParameterizer.h" #include "mitkTwoStepLinearModelFactory.h" #include "mitkTwoStepLinearModelParameterizer.h" #include #include #include #include #include "mitkTwoCompartmentExchangeModelFactory.h" #include "mitkTwoCompartmentExchangeModelParameterizer.h" #include "mitkNumericTwoCompartmentExchangeModelFactory.h" #include "mitkNumericTwoCompartmentExchangeModelParameterizer.h" #include #include #include #include #include #include #include #include "mitkNodePredicateFunction.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Includes for image casting between ITK and MITK #include #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include #include -const std::string MRPerfusionView::VIEW_ID = "org.mitk.gui.qt.pharmacokinetics.mri"; +const std::string MRPerfusionView::VIEW_ID = "org.mitk.views.pharmacokinetics.mri"; inline double convertToDouble(const std::string& data) { std::istringstream stepStream(data); stepStream.imbue(std::locale("C")); double value = 0.0; if (!(stepStream >> value) || !(stepStream.eof())) { mitkThrow() << "Cannot convert string to double. String: " << data; } return value; } void MRPerfusionView::SetFocus() { m_Controls.btnModelling->setFocus(); } void MRPerfusionView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnModelling->setEnabled(false); this->InitModelComboBox(); m_Controls.timeSeriesNodeSelector->SetNodePredicate(this->m_isValidTimeSeriesImagePredicate); m_Controls.timeSeriesNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.timeSeriesNodeSelector->SetSelectionIsOptional(false); m_Controls.timeSeriesNodeSelector->SetInvalidInfo("Please select time series."); m_Controls.timeSeriesNodeSelector->SetAutoSelectNewNodes(true); m_Controls.maskNodeSelector->SetNodePredicate(this->m_IsMaskPredicate); m_Controls.maskNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.maskNodeSelector->SetSelectionIsOptional(true); m_Controls.maskNodeSelector->SetEmptyInfo("Please select (optional) mask."); connect(m_Controls.btnModelling, SIGNAL(clicked()), this, SLOT(OnModellingButtonClicked())); connect(m_Controls.comboModel, SIGNAL(currentIndexChanged(int)), this, SLOT(OnModellSet(int))); connect(m_Controls.radioPixelBased, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.timeSeriesNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &MRPerfusionView::OnNodeSelectionChanged); connect(m_Controls.maskNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &MRPerfusionView::OnNodeSelectionChanged); connect(m_Controls.AIFMaskNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &MRPerfusionView::UpdateGUIControls); connect(m_Controls.AIFImageNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &MRPerfusionView::UpdateGUIControls); //AIF setting m_Controls.groupAIF->hide(); m_Controls.btnAIFFile->setEnabled(false); m_Controls.btnAIFFile->setEnabled(false); m_Controls.radioAIFImage->setChecked(true); m_Controls.AIFMaskNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.AIFMaskNodeSelector->SetNodePredicate(m_IsMaskPredicate); m_Controls.AIFMaskNodeSelector->setVisible(true); m_Controls.AIFMaskNodeSelector->setEnabled(true); m_Controls.AIFMaskNodeSelector->SetAutoSelectNewNodes(true); m_Controls.AIFImageNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.AIFImageNodeSelector->SetNodePredicate(this->m_isValidTimeSeriesImagePredicate); m_Controls.AIFImageNodeSelector->setEnabled(false); m_Controls.checkDedicatedAIFImage->setEnabled(true); m_Controls.HCLSpinBox->setValue(mitk::AterialInputFunctionGenerator::DEFAULT_HEMATOCRIT_LEVEL); m_Controls.spinBox_baselineEndTimeStep->setMinimum(0); m_Controls.spinBox_baselineStartTimeStep->setMinimum(0); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.AIFMaskNodeSelector, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.labelAIFMask, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.AIFMaskNodeSelector, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.AIFImageNodeSelector, SLOT(setVisible(bool))); connect(m_Controls.checkDedicatedAIFImage, SIGNAL(toggled(bool)), m_Controls.AIFImageNodeSelector, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.btnAIFFile, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.aifFilePath, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.btnAIFFile, SIGNAL(clicked()), this, SLOT(LoadAIFfromFile())); //Brix setting m_Controls.groupDescBrix->hide(); connect(m_Controls.injectiontime, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); //Num2CX setting m_Controls.groupNum2CXM->hide(); connect(m_Controls.odeStepSize, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); //Model fit configuration m_Controls.groupBox_FitConfiguration->hide(); m_Controls.checkBox_Constraints->setEnabled(false); m_Controls.constraintManager->setEnabled(false); m_Controls.initialValuesManager->setEnabled(false); m_Controls.initialValuesManager->setDataStorage(this->GetDataStorage()); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.initialValuesManager, SIGNAL(initialValuesChanged(void)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), m_Controls.initialValuesManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setVisible(bool))); //Concentration m_Controls.groupConcentration->hide(); m_Controls.groupBoxEnhancement->hide(); m_Controls.groupBoxTurboFlash->hide(); m_Controls.radioButtonNoConversion->setChecked(true); m_Controls.groupBox_T1MapviaVFA->hide(); m_Controls.spinBox_baselineStartTimeStep->setValue(0); m_Controls.spinBox_baselineEndTimeStep->setValue(0); connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), m_Controls.groupBoxTurboFlash, SLOT(setVisible(bool))); connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.relaxationtime, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.recoverytime, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.relaxivity, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), m_Controls.groupBoxEnhancement, SLOT(setVisible(bool))); connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), m_Controls.groupBoxEnhancement, SLOT(setVisible(bool))); connect(m_Controls.factorSpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.spinBox_baselineStartTimeStep, SIGNAL(valueChanged(int)), this, SLOT(UpdateGUIControls())); connect(m_Controls.spinBox_baselineEndTimeStep, SIGNAL(valueChanged(int)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), m_Controls.groupBox_T1MapviaVFA, SLOT(setVisible(bool))); connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.FlipangleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.RelaxivitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.TRSpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); m_Controls.PDWImageNodeSelector->SetNodePredicate(m_isValidPDWImagePredicate); m_Controls.PDWImageNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.PDWImageNodeSelector->SetInvalidInfo("Please select PDW Image."); m_Controls.PDWImageNodeSelector->setEnabled(false); connect(m_Controls.radioButtonUsingT1viaVFA, SIGNAL(toggled(bool)), m_Controls.PDWImageNodeSelector, SLOT(setEnabled(bool))); UpdateGUIControls(); } bool MRPerfusionView::IsTurboFlashSequenceFlag() const { return this->m_Controls.radioButtonTurboFlash->isChecked(); }; void MRPerfusionView::UpdateGUIControls() { m_Controls.lineFitName->setPlaceholderText(QString::fromStdString(this->GetDefaultFitName())); m_Controls.lineFitName->setEnabled(!m_FittingInProgress); m_Controls.checkBox_Constraints->setEnabled(m_modelConstraints.IsNotNull()); bool isDescBrixFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr || dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr || dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isNum2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isSLFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr || dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; m_Controls.groupAIF->setVisible(isToftsFactory || is2CXMFactory); m_Controls.groupDescBrix->setVisible(isDescBrixFactory); m_Controls.groupNum2CXM->setVisible(isNum2CXMFactory); m_Controls.groupConcentration->setVisible(isToftsFactory || is2CXMFactory || isSLFactory); m_Controls.groupBox_FitConfiguration->setVisible(m_selectedModelFactory); m_Controls.groupBox->setEnabled(!m_FittingInProgress); m_Controls.comboModel->setEnabled(!m_FittingInProgress); m_Controls.groupAIF->setEnabled(!m_FittingInProgress); m_Controls.groupDescBrix->setEnabled(!m_FittingInProgress); m_Controls.groupNum2CXM->setEnabled(!m_FittingInProgress); m_Controls.groupConcentration->setEnabled(!m_FittingInProgress); m_Controls.groupBox_FitConfiguration->setEnabled(!m_FittingInProgress); m_Controls.radioROIbased->setEnabled(m_selectedMask.IsNotNull()); m_Controls.btnModelling->setEnabled(m_selectedImage.IsNotNull() && m_selectedModelFactory.IsNotNull() && !m_FittingInProgress && CheckModelSettings()); m_Controls.spinBox_baselineStartTimeStep->setEnabled(m_Controls.radioButtonTurboFlash->isChecked() || m_Controls.radioButton_absoluteEnhancement->isChecked() || m_Controls.radioButton_relativeEnchancement->isChecked() || m_Controls.radioButtonUsingT1viaVFA->isChecked()); m_Controls.spinBox_baselineEndTimeStep->setEnabled(m_Controls.radioButton_absoluteEnhancement->isChecked() || m_Controls.radioButton_relativeEnchancement->isChecked() || m_Controls.radioButtonUsingT1viaVFA->isChecked() || m_Controls.radioButtonTurboFlash->isChecked()); } void MRPerfusionView::OnModellSet(int index) { m_selectedModelFactory = nullptr; if (index > 0) { if (static_cast(index) <= m_FactoryStack.size() ) { m_selectedModelFactory = m_FactoryStack[index - 1]; } else { MITK_WARN << "Invalid model index. Index outside of the factory stack. Factory stack size: "<< m_FactoryStack.size() << "; invalid index: "<< index; } } if (m_selectedModelFactory) { this->m_modelConstraints = dynamic_cast (m_selectedModelFactory->CreateDefaultConstraints().GetPointer()); m_Controls.initialValuesManager->setInitialValues(m_selectedModelFactory->GetParameterNames(), m_selectedModelFactory->GetDefaultInitialParameterization()); if (this->m_modelConstraints.IsNull()) { this->m_modelConstraints = mitk::SimpleBarrierConstraintChecker::New(); } m_Controls.constraintManager->setChecker(this->m_modelConstraints, this->m_selectedModelFactory->GetParameterNames()); } UpdateGUIControls(); } std::string MRPerfusionView::GetFitName() const { std::string fitName = m_Controls.lineFitName->text().toStdString(); if (fitName.empty()) { fitName = m_Controls.lineFitName->placeholderText().toStdString(); } return fitName; } std::string MRPerfusionView::GetDefaultFitName() const { std::string defaultName = "undefined model"; if (this->m_selectedModelFactory.IsNotNull()) { defaultName = this->m_selectedModelFactory->GetClassID(); } if (this->m_Controls.radioPixelBased->isChecked()) { defaultName += "_pixel"; } else { defaultName += "_roi"; } return defaultName; } void MRPerfusionView::OnModellingButtonClicked() { //check if all static parameters set if (m_selectedModelFactory.IsNotNull() && CheckModelSettings()) { m_HasGeneratedNewInput = false; m_HasGeneratedNewInputAIF = false; mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr; mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr; bool isDescBrixFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is3LinearFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2LinearFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isExtToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isStanToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isNum2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; if (isDescBrixFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateDescriptiveBrixModel_PixelBased(fitSession, generator); } else { GenerateDescriptiveBrixModel_ROIBased(fitSession, generator); } } else if (is2LinearFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateLinearModelFit_PixelBased(fitSession, generator); } else { GenerateLinearModelFit_ROIBased(fitSession, generator); } } else if (is3LinearFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateLinearModelFit_PixelBased(fitSession, generator); } else { GenerateLinearModelFit_ROIBased(fitSession, generator); } } else if (isStanToftsFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } else if (isExtToftsFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } else if (is2CXMFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } else if (isNum2CXMFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } //add other models with else if if (generator.IsNotNull() && fitSession.IsNotNull()) { m_FittingInProgress = true; UpdateGUIControls(); DoFit(fitSession, generator); } else { QMessageBox box; box.setText("Fitting error!"); box.setInformativeText("Could not establish fitting job. Error when setting ab generator, model parameterizer or session info."); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } else { QMessageBox box; box.setText("Static parameters for model are not set!"); box.setInformativeText("Some static parameters, that are needed for calculation are not set and equal to zero. Modeling not possible"); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } void MRPerfusionView::OnNodeSelectionChanged(QList/*nodes*/) { m_selectedMaskNode = nullptr; m_selectedMask = nullptr; if (m_Controls.timeSeriesNodeSelector->GetSelectedNode().IsNotNull()) { this->m_selectedNode = m_Controls.timeSeriesNodeSelector->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); if (m_selectedImage) { this->m_Controls.initialValuesManager->setReferenceImageGeometry(m_selectedImage->GetGeometry()); } else { this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr); } } else { this->m_selectedNode = nullptr; this->m_selectedImage = nullptr; this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr); } if (m_Controls.maskNodeSelector->GetSelectedNode().IsNotNull()) { this->m_selectedMaskNode = m_Controls.maskNodeSelector->GetSelectedNode(); this->m_selectedMask = dynamic_cast(m_selectedMaskNode->GetData()); if (this->m_selectedMask.IsNotNull() && this->m_selectedMask->GetTimeSteps() > 1) { MITK_INFO << "Selected mask has multiple timesteps. Only use first timestep to mask model fit. Mask name: " << m_Controls.maskNodeSelector->GetSelectedNode()->GetName(); mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedMask = maskedImageTimeSelector->GetOutput(); } } if (m_selectedMask.IsNull()) { this->m_Controls.radioPixelBased->setChecked(true); } if (this->m_selectedImage.IsNotNull()) { m_Controls.spinBox_baselineStartTimeStep->setMaximum((this->m_selectedImage->GetDimension(3))-1); m_Controls.spinBox_baselineEndTimeStep->setMaximum((this->m_selectedImage->GetDimension(3)) - 1); } UpdateGUIControls(); } bool MRPerfusionView::CheckModelSettings() const { bool ok = true; //check wether any model is set at all. Otherwise exit with false if (m_selectedModelFactory.IsNotNull()) { bool isDescBrixFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is3LinearFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2LinearFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr|| dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isNum2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; if (isDescBrixFactory) { //if all static parameters for this model are set, exit with true, Otherwise exit with false ok = m_Controls.injectiontime->value() > 0; } else if (is3LinearFactory || is2LinearFactory) { if (this->m_Controls.radioButtonTurboFlash->isChecked() ) { ok = ok && (m_Controls.recoverytime->value() > 0); ok = ok && (m_Controls.relaxationtime->value() > 0); ok = ok && (m_Controls.relaxivity->value() > 0); ok = ok && (m_Controls.AifRecoverytime->value() > 0); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButton_absoluteEnhancement->isChecked() || this->m_Controls.radioButton_relativeEnchancement->isChecked() ) { ok = ok && (m_Controls.factorSpinBox->value() > 0); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButtonUsingT1viaVFA->isChecked() ) { ok = ok && (m_Controls.FlipangleSpinBox->value() > 0); ok = ok && (m_Controls.TRSpinBox->value() > 0); ok = ok && (m_Controls.RelaxivitySpinBox->value() > 0); ok = ok && (m_Controls.PDWImageNodeSelector->GetSelectedNode().IsNotNull()); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButtonNoConversion->isChecked()) { ok = true; } else { ok = false; } } else if (isToftsFactory || is2CXMFactory || isNum2CXMFactory) { if (this->m_Controls.radioAIFImage->isChecked()) { ok = ok && m_Controls.AIFMaskNodeSelector->GetSelectedNode().IsNotNull(); if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { ok = ok && m_Controls.AIFImageNodeSelector->GetSelectedNode().IsNotNull(); } } else if (this->m_Controls.radioAIFFile->isChecked()) { ok = ok && (this->AIFinputGrid.size() != 0) && (this->AIFinputFunction.size() != 0); } else { ok = false; } if (this->m_Controls.radioButtonTurboFlash->isChecked() ) { ok = ok && (m_Controls.recoverytime->value() > 0); ok = ok && (m_Controls.relaxationtime->value() > 0); ok = ok && (m_Controls.relaxivity->value() > 0); ok = ok && (m_Controls.AifRecoverytime->value() > 0); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButton_absoluteEnhancement->isChecked() || this->m_Controls.radioButton_relativeEnchancement->isChecked() ) { ok = ok && (m_Controls.factorSpinBox->value() > 0); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButtonUsingT1viaVFA->isChecked() ) { ok = ok && (m_Controls.FlipangleSpinBox->value() > 0); ok = ok && (m_Controls.TRSpinBox->value() > 0); ok = ok && (m_Controls.RelaxivitySpinBox->value() > 0); ok = ok && (m_Controls.PDWImageNodeSelector->GetSelectedNode().IsNotNull()); ok = ok && CheckBaselineSelectionSettings(); } else if (this->m_Controls.radioButtonNoConversion->isChecked()) { ok = ok && true; } else { ok = false; } if (isNum2CXMFactory) { ok = ok && (this->m_Controls.odeStepSize->value() > 0); } } //add other models as else if and check wether all needed static parameters are set else { ok = false; } if (this->m_Controls.radioButton_StartParameters->isChecked() && !this->m_Controls.initialValuesManager->hasValidInitialValues()) { std::string warning = "Warning. Invalid start parameters. At least one parameter as an invalid image setting as source."; MITK_ERROR << warning; m_Controls.infoBox->append(QString("") + QString::fromStdString(warning) + QString("")); ok = false; }; } else { ok = false; } return ok; } bool MRPerfusionView::CheckBaselineSelectionSettings() const { return m_Controls.spinBox_baselineStartTimeStep->value() <= m_Controls.spinBox_baselineEndTimeStep->value(); } void MRPerfusionView::ConfigureInitialParametersOfParameterizer(mitk::ModelParameterizerBase* parameterizer) const { if (m_Controls.radioButton_StartParameters->isChecked()) { //use user defined initial parameters mitk::InitialParameterizationDelegateBase::Pointer paramDelegate = m_Controls.initialValuesManager->getInitialParametrizationDelegate(); parameterizer->SetInitialParameterizationDelegate(paramDelegate); } } void MRPerfusionView::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(); //Model configuration (static parameters) can be done now modelParameterizer->SetTau(m_Controls.injectiontime->value()); mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(this->m_selectedImage); imageTimeSelector->SetTimeNr(0); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::DescriptivePharmacokineticBrixModelParameterizer::BaseImageType::Pointer baseImage; mitk::CastToItkImage(imageTimeSelector->GetOutput(), baseImage); modelParameterizer->SetBaseImage(baseImage); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); roiUID = m_selectedMask->GetUID(); } fitGenerator->SetDynamicImage(m_selectedImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); } void MRPerfusionView::GenerateDescriptiveBrixModel_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { if (m_selectedMask.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(m_selectedMask); signalGenerator->SetDynamicImage(m_selectedImage); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Model configuration (static parameters) can be done now modelParameterizer->SetTau(m_Controls.injectiontime->value()); modelParameterizer->SetBaseValue(roiSignal[0]); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(m_selectedImage)); generator = fitGenerator.GetPointer(); std::string roiUID = this->m_selectedMask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), 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 void MRPerfusionView::GenerateLinearModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); roiUID = this->m_selectedMask->GetUID(); } fitGenerator->SetDynamicImage(m_selectedImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); } template void MRPerfusionView::GenerateLinearModelFit_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { if (m_selectedMask.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(m_selectedMask); signalGenerator->SetDynamicImage(m_selectedImage); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Model configuration (static parameters) can be done now this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(m_selectedImage)); generator = fitGenerator.GetPointer(); std::string roiUID = this->m_selectedMask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), 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 void MRPerfusionView::GenerateAIFbasedModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); PrepareConcentrationImage(); mitk::AIFBasedModelBase::AterialInputFunctionType aif; mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid; GetAIF(aif, aifTimeGrid); modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); mitk::NumericTwoCompartmentExchangeModelParameterizer* numTCXParametrizer = dynamic_cast (modelParameterizer.GetPointer()); if (numTCXParametrizer) { numTCXParametrizer->SetODEINTStepSize(this->m_Controls.odeStepSize->value()); } //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); roiUID = this->m_selectedMask->GetUID(); } fitGenerator->SetDynamicImage(this->m_inputImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, this->m_inputImage, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), 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 void MRPerfusionView::GenerateAIFbasedModelFit_ROIBased( mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { if (m_selectedMask.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); PrepareConcentrationImage(); mitk::AIFBasedModelBase::AterialInputFunctionType aif; mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid; GetAIF(aif, aifTimeGrid); modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); mitk::NumericTwoCompartmentExchangeModelParameterizer* numTCXParametrizer = dynamic_cast (modelParameterizer.GetPointer()); if (numTCXParametrizer) { numTCXParametrizer->SetODEINTStepSize(this->m_Controls.odeStepSize->value()); } //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); signalGenerator->SetDynamicImage(this->m_inputImage); 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(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(this->m_inputImage)); generator = fitGenerator.GetPointer(); std::string roiUID = this->m_selectedMask->GetUID(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, this->m_inputImage, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), 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 MRPerfusionView::DoFit(const mitk::modelFit::ModelFitInfo* fitSession, mitk::ParameterFitImageGeneratorBase* generator) { this->m_Controls.infoBox->append(QString("" + QString("Fitting Data Set . . .") + QString (""))); ///////////////////////// //create job and put it into the thread pool mitk::modelFit::ModelFitResultNodeVectorType additionalNodes; if (m_HasGeneratedNewInput) { additionalNodes.push_back(m_inputNode); } if (m_HasGeneratedNewInputAIF) { additionalNodes.push_back(m_inputAIFNode); } ParameterFitBackgroundJob* pJob = new ParameterFitBackgroundJob(generator, fitSession, this->m_selectedNode, additionalNodes); pJob->setAutoDelete(true); connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnJobFinished())); connect(pJob, SIGNAL(ResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), this, SLOT(OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(JobProgress(double)), this, SLOT(OnJobProgress(double))); connect(pJob, SIGNAL(JobStatusChanged(QString)), this, SLOT(OnJobStatusChanged(QString))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } MRPerfusionView::MRPerfusionView() : m_FittingInProgress(false), m_HasGeneratedNewInput(false), m_HasGeneratedNewInputAIF(false) { m_selectedImage = nullptr; m_selectedMask = nullptr; mitk::ModelFactoryBase::Pointer factory = mitk::DescriptivePharmacokineticBrixModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::TwoStepLinearModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::ThreeStepLinearModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::StandardToftsModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::ExtendedToftsModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::TwoCompartmentExchangeModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::NumericTwoCompartmentExchangeModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isLegacyMask = mitk::NodePredicateAnd::New(isImage, isBinary); mitk::NodePredicateDimension::Pointer is3D = mitk::NodePredicateDimension::New(3); mitk::NodePredicateOr::Pointer isMask = mitk::NodePredicateOr::New(isLegacyMask, isLabelSet); mitk::NodePredicateAnd::Pointer isNoMask = mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isMask)); mitk::NodePredicateAnd::Pointer is3DImage = mitk::NodePredicateAnd::New(isImage, is3D, isNoMask); this->m_IsMaskPredicate = mitk::NodePredicateAnd::New(isMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); this->m_IsNoMaskImagePredicate = mitk::NodePredicateAnd::New(isNoMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); auto isDynamicData = mitk::NodePredicateFunction::New([](const mitk::DataNode* node) { return (node && node->GetData() && node->GetData()->GetTimeSteps() > 1); }); auto modelFitResultRelationRule = mitk::ModelFitResultRelationRule::New(); auto isNoModelFitNodePredicate = mitk::NodePredicateNot::New(modelFitResultRelationRule->GetConnectedSourcesDetector()); this->m_isValidPDWImagePredicate = mitk::NodePredicateAnd::New(is3DImage, isNoModelFitNodePredicate); this->m_isValidTimeSeriesImagePredicate = mitk::NodePredicateAnd::New(isDynamicData, isImage, isNoMask); } void MRPerfusionView::OnJobFinished() { this->m_Controls.infoBox->append(QString("Fitting finished.")); this->m_FittingInProgress = false; this->UpdateGUIControls(); }; void MRPerfusionView::OnJobError(QString err) { MITK_ERROR << err.toStdString().c_str(); m_Controls.infoBox->append(QString("") + err + QString("")); }; void MRPerfusionView::OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType results, const ParameterFitBackgroundJob* pJob) { //Store the resulting parameter fit image via convenience helper function in data storage //(handles the correct generation of the nodes and their properties) mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), results, pJob->GetParentNode()); //this stores the concentration image and AIF concentration image, if generated for this fit in the storage. //if not generated for this fit, relevant nodes are empty. mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), pJob->GetAdditionalRelevantNodes(), pJob->GetParentNode()); }; void MRPerfusionView::OnJobProgress(double progress) { QString report = QString("Progress. ") + QString::number(progress); this->m_Controls.infoBox->append(report); }; void MRPerfusionView::OnJobStatusChanged(QString info) { this->m_Controls.infoBox->append(info); } void MRPerfusionView::InitModelComboBox() const { this->m_Controls.comboModel->clear(); this->m_Controls.comboModel->addItem(tr("No model selected")); for (ModelFactoryStackType::const_iterator pos = m_FactoryStack.begin(); pos != m_FactoryStack.end(); ++pos) { this->m_Controls.comboModel->addItem(QString::fromStdString((*pos)->GetClassID())); } this->m_Controls.comboModel->setCurrentIndex(0); }; mitk::DataNode::Pointer MRPerfusionView::GenerateConcentrationNode(mitk::Image* image, const std::string& nodeName) const { if (!image) { mitkThrow() << "Cannot generate concentration node. Passed image is null. parameter name: "; } mitk::DataNode::Pointer result = mitk::DataNode::New(); result->SetData(image); result->SetName(nodeName); result->SetVisibility(true); return result; }; mitk::Image::Pointer MRPerfusionView::ConvertConcentrationImage(bool AIFMode) { //Compute Concentration image mitk::ConcentrationCurveGenerator::Pointer concentrationGen = mitk::ConcentrationCurveGenerator::New(); if (m_Controls.checkDedicatedAIFImage->isChecked() && AIFMode) { concentrationGen->SetDynamicImage(this->m_selectedAIFImage); } else { concentrationGen->SetDynamicImage(this->m_selectedImage); } concentrationGen->SetisTurboFlashSequence(IsTurboFlashSequenceFlag()); concentrationGen->SetAbsoluteSignalEnhancement(m_Controls.radioButton_absoluteEnhancement->isChecked()); concentrationGen->SetRelativeSignalEnhancement(m_Controls.radioButton_relativeEnchancement->isChecked()); concentrationGen->SetUsingT1Map(m_Controls.radioButtonUsingT1viaVFA->isChecked()); if (IsTurboFlashSequenceFlag()) { if (AIFMode) { concentrationGen->SetRecoveryTime(m_Controls.AifRecoverytime->value()); } else { concentrationGen->SetRecoveryTime(m_Controls.recoverytime->value()); } concentrationGen->SetRelaxationTime(m_Controls.relaxationtime->value()); concentrationGen->SetRelaxivity(m_Controls.relaxivity->value()); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); } else if (this->m_Controls.radioButtonUsingT1viaVFA->isChecked()) { concentrationGen->SetRecoveryTime(m_Controls.TRSpinBox->value()); concentrationGen->SetRelaxivity(m_Controls.RelaxivitySpinBox->value()); concentrationGen->SetT10Image(dynamic_cast(m_Controls.PDWImageNodeSelector->GetSelectedNode()->GetData())); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); //Convert Flipangle from degree to radiant double alpha = m_Controls.FlipangleSpinBox->value()/360*2* boost::math::constants::pi(); concentrationGen->SetFlipAngle(alpha); } else { concentrationGen->SetFactor(m_Controls.factorSpinBox->value()); concentrationGen->SetBaselineStartTimeStep(m_Controls.spinBox_baselineStartTimeStep->value()); concentrationGen->SetBaselineEndTimeStep(m_Controls.spinBox_baselineEndTimeStep->value()); } mitk::Image::Pointer concentrationImage = concentrationGen->GetConvertedImage(); return concentrationImage; } void MRPerfusionView::GetAIF(mitk::AIFBasedModelBase::AterialInputFunctionType& aif, mitk::AIFBasedModelBase::AterialInputFunctionType& aifTimeGrid) { if (this->m_Controls.radioAIFFile->isChecked()) { aif.clear(); aifTimeGrid.clear(); aif.SetSize(AIFinputFunction.size()); aifTimeGrid.SetSize(AIFinputGrid.size()); aif.fill(0.0); aifTimeGrid.fill(0.0); itk::Array::iterator aifPos = aif.begin(); for (std::vector::const_iterator pos = AIFinputFunction.begin(); pos != AIFinputFunction.end(); ++pos, ++aifPos) { *aifPos = *pos; } itk::Array::iterator gridPos = aifTimeGrid.begin(); for (std::vector::const_iterator pos = AIFinputGrid.begin(); pos != AIFinputGrid.end(); ++pos, ++gridPos) { *gridPos = *pos; } } else if (this->m_Controls.radioAIFImage->isChecked()) { aif.clear(); aifTimeGrid.clear(); mitk::AterialInputFunctionGenerator::Pointer aifGenerator = mitk::AterialInputFunctionGenerator::New(); //Hematocrit level aifGenerator->SetHCL(this->m_Controls.HCLSpinBox->value()); //mask settings this->m_selectedAIFMaskNode = m_Controls.AIFMaskNodeSelector->GetSelectedNode(); this->m_selectedAIFMask = dynamic_cast(this->m_selectedAIFMaskNode->GetData()); if (this->m_selectedAIFMask->GetTimeSteps() > 1) { MITK_INFO << "Selected AIF mask has multiple timesteps. Only use first timestep to mask model fit. AIF Mask name: " << m_selectedAIFMaskNode->GetName() ; mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedAIFMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedAIFMask = maskedImageTimeSelector->GetOutput(); } if (this->m_selectedAIFMask.IsNotNull()) { aifGenerator->SetMask(this->m_selectedAIFMask); } //image settings if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { this->m_selectedAIFImageNode = m_Controls.AIFImageNodeSelector->GetSelectedNode(); this->m_selectedAIFImage = dynamic_cast(this->m_selectedAIFImageNode->GetData()); } else { this->m_selectedAIFImageNode = m_selectedNode; this->m_selectedAIFImage = m_selectedImage; } this->PrepareAIFConcentrationImage(); aifGenerator->SetDynamicImage(this->m_inputAIFImage); aif = aifGenerator->GetAterialInputFunction(); aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid(); } else { mitkThrow() << "Cannot generate AIF. View is in a invalide state. No AIF mode selected."; } } void MRPerfusionView::LoadAIFfromFile() { QFileDialog dialog; dialog.setNameFilter(tr("Images (*.csv")); QString fileName = dialog.getOpenFileName(); m_Controls.aifFilePath->setText(fileName); std::string m_aifFilePath = fileName.toStdString(); //Read Input typedef boost::tokenizer< boost::escaped_list_separator > Tokenizer; ///////////////////////////////////////////////////////////////////////////////////////////////// //AIF Data std::ifstream in1(m_aifFilePath.c_str()); if (!in1.is_open()) { this->m_Controls.infoBox->append(QString("Could not open AIF File!")); } std::vector< std::string > vec1; std::string line1; while (getline(in1, line1)) { Tokenizer tok(line1); vec1.assign(tok.begin(), tok.end()); this->AIFinputGrid.push_back(convertToDouble(vec1[0])); this->AIFinputFunction.push_back(convertToDouble(vec1[1])); } } void MRPerfusionView::PrepareConcentrationImage() { mitk::Image::Pointer concentrationImage = this->m_selectedImage; mitk::DataNode::Pointer concentrationNode = this->m_selectedNode; m_HasGeneratedNewInput = false; if (!this->m_Controls.radioButtonNoConversion->isChecked()) { concentrationImage = this->ConvertConcentrationImage(false); concentrationNode = GenerateConcentrationNode(concentrationImage, "Concentration"); m_HasGeneratedNewInput = true; } m_inputImage = concentrationImage; m_inputNode = concentrationNode; } void MRPerfusionView::PrepareAIFConcentrationImage() { mitk::Image::Pointer concentrationImage = this->m_selectedImage; mitk::DataNode::Pointer concentrationNode = this->m_selectedNode; m_HasGeneratedNewInputAIF = false; if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { concentrationImage = this->m_selectedAIFImage; concentrationNode = this->m_selectedAIFImageNode; } if (!this->m_Controls.radioButtonNoConversion->isChecked()) { if (!IsTurboFlashSequenceFlag() && !this->m_Controls.checkDedicatedAIFImage->isChecked()) { if (m_inputImage.IsNull()) { mitkThrow() << "Cannot get AIF concentration image. Invalid view state. Input image is not defined yet, but should be."; } //we can directly use the concentration input image/node (generated by GetConcentrationImage) also for the AIF concentrationImage = this->m_inputImage; concentrationNode = this->m_inputNode; } else { concentrationImage = this->ConvertConcentrationImage(true); concentrationNode = GenerateConcentrationNode(concentrationImage, "AIF Concentration"); m_HasGeneratedNewInputAIF = true; } } m_inputAIFImage = concentrationImage; m_inputAIFNode = concentrationNode; } mitk::ModelFitFunctorBase::Pointer MRPerfusionView::CreateDefaultFitFunctor( const mitk::ModelParameterizerBase* parameterizer) const { mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor = mitk::LevenbergMarquardtModelFitFunctor::New(); mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::Pointer chi2 = mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("Chi^2", chi2); if (m_Controls.checkBox_Constraints->isChecked()) { fitFunctor->SetConstraintChecker(m_modelConstraints); } mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel(); ::itk::LevenbergMarquardtOptimizer::ScalesType scales; scales.SetSize(refModel->GetNumberOfParameters()); scales.Fill(1.0); fitFunctor->SetScales(scales); fitFunctor->SetDebugParameterMaps(m_Controls.checkDebug->isChecked()); return fitFunctor.GetPointer(); } diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/documentation/UserManual/Manual.dox b/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/documentation/UserManual/Manual.dox index 57414c01dd..feb1aa49d5 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/documentation/UserManual/Manual.dox +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/documentation/UserManual/Manual.dox @@ -1,46 +1,46 @@ /** -\page org_mitk_gui_qt_pharmacokinetics_pet Dynamic PET DataFit View +\page org_mitk_views_pharmacokinetics_pet The Dynamic PET DataFit View \imageMacro{pharmacokinetics_pet_doc.svg,"Icon of the DCE MR Perfusion View",3.0} \tableofcontents \section FIT_PET_Overview Overview Pharmacokinetic analysis of concentration time curves is also of interest in the context of dynamic PET acquisition over the accumulation of a radioactive tracer in tissue. \section FIT_PET_Contact Contact information This plug-in is being developed by Charlotte Debus and the SIDT group (Software development for Integrated Diagnostics and Therapy) at the German Cancer Research Center (DKFZ). If you have any questions, need support, find a bug or have a feature request, feel free to contact us at www.mitk.org. \subsection FIT_PET_Cite Citation information If you use the view for your research please cite our work as reference:\n\n Debus C and Floca R, Ingrisch M, Kompan I, Maier-Hein K, Abdollahi A, Nolden M, MITK-ModelFit: generic open-source framework for model fits and their exploration in medical imaging – design, implementation and application on the example of DCE-MRI (arXiv:1807.07353) \section FIT_PET_General General information All models require definition of the arterial tracer concentration, i.e. the AIF. For AIF definition see section 3. Instead of the hematocrit level, the whole blood to plasma correction value needs to be specified. The literature value commonly used is 0.1 Since PET images are already in concentration units of activity per volume ([Bq/ml], translates to number of nuclei per volume), no conversion of signal intensities to concentration is offered in the plugin. If, however, conversion of the 4D images to standard uptake values (SUV) is desired, this can be performed with the separate PET SUV calculation plugin. Start parameters and parameter constraints can be defined in the same manner as for the DCE tool. \subsection FIT_PET_General_models Supported models The PET dynamic plugin works in analogy to the DCE MRI perfusion plugin. It currently supports the following compartmental models: - One tissue compartment model (without blood volume VB) - Extended one tissue compartment model (with blood volume VB) - Two tissue compartment model (with blood volume) - Two tissue compartment model for FDG (without back exchange k4) \section FIT_PET_Settings Model Settings \subsection FIT_PET_Settings_start Start parameter In cases of noisy data it can be useful to define the initial starting values of the parameter estimates, at which optimization starts, in order to prevent optimization results in local optima. Each model has default scalar values (applied to every voxel) for initial values of each parameter, however these can be adjusted. Moreover, initial values can also be defined locally for each individual voxel via starting value images. \subsection FIT_PET_Settings_constraint Constraint settings To limit the fitting search space and to exclude unphysical/illogical results for model parameter estimates, constraints to individual parameters as well as combinations can be imposed. Each model has default constraints, however, new ones can be defined or removed by the + and – buttons in the table. The first column specifies the parameter(s) involved in the constraint (if multiple selected, their sum will be used) by selection in the drop down menu. The second column defines whether the constraints defines an upper or lower boundary. Value and Width define the actual constraint value, that should not be crossed, and a certain tolerance width. */ diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/plugin.xml b/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/plugin.xml index aaf2b2ee75..8ac3ef9396 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/plugin.xml +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/plugin.xml @@ -1,12 +1,12 @@ - diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/src/internal/PETDynamicView.cpp b/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/src/internal/PETDynamicView.cpp index dc4a82d910..9e4a74c8c1 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/src/internal/PETDynamicView.cpp +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.pet/src/internal/PETDynamicView.cpp @@ -1,946 +1,946 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "PETDynamicView.h" #include "mitkWorkbenchUtil.h" #include "mitkAterialInputFunctionGenerator.h" #include "mitkOneTissueCompartmentModelFactory.h" #include "mitkOneTissueCompartmentModelParameterizer.h" #include "mitkExtendedOneTissueCompartmentModelFactory.h" #include "mitkExtendedOneTissueCompartmentModelParameterizer.h" #include "mitkTwoTissueCompartmentFDGModelFactory.h" #include "mitkTwoTissueCompartmentFDGModelParameterizer.h" #include "mitkTwoTissueCompartmentModelFactory.h" #include "mitkTwoTissueCompartmentModelParameterizer.h" #include "mitkNumericTwoTissueCompartmentModelFactory.h" #include "mitkNumericTwoTissueCompartmentModelParameterizer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Includes for image casting between ITK and MITK #include #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include #include #include -const std::string PETDynamicView::VIEW_ID = "org.mitk.gui.qt.pharmacokinetics.pet"; +const std::string PETDynamicView::VIEW_ID = "org.mitk.views.pharmacokinetics.pet"; inline double convertToDouble(const std::string& data) { std::istringstream stepStream(data); double value = 0.0; stepStream >> value; return value; } void PETDynamicView::SetFocus() { m_Controls.btnModelling->setFocus(); } void PETDynamicView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnModelling->setEnabled(false); m_Controls.errorMessageLabel->hide(); this->InitModelComboBox(); connect(m_Controls.btnModelling, SIGNAL(clicked()), this, SLOT(OnModellingButtonClicked())); connect(m_Controls.comboModel, SIGNAL(currentIndexChanged(int)), this, SLOT(OnModellSet(int))); connect(m_Controls.radioPixelBased, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); //AIF setting m_Controls.groupAIF->hide(); m_Controls.btnAIFFile->setEnabled(false); m_Controls.btnAIFFile->setEnabled(false); m_Controls.radioAIFImage->setChecked(true); m_Controls.comboAIFMask->SetDataStorage(this->GetDataStorage()); m_Controls.comboAIFMask->SetPredicate(m_IsMaskPredicate); m_Controls.comboAIFMask->setVisible(true); m_Controls.comboAIFMask->setEnabled(true); m_Controls.comboAIFImage->SetDataStorage(this->GetDataStorage()); m_Controls.comboAIFImage->SetPredicate(m_IsNoMaskImagePredicate); m_Controls.comboAIFImage->setEnabled(false); m_Controls.checkDedicatedAIFImage->setEnabled(true); m_Controls.HCLSpinBox->setValue(0.0); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFMask, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.labelAIFMask, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFMask, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFImage, SLOT(setVisible(bool))); connect(m_Controls.checkDedicatedAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFImage, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.btnAIFFile, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.aifFilePath, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.btnAIFFile, SIGNAL(clicked()), this, SLOT(LoadAIFfromFile())); //Model fit configuration m_Controls.groupBox_FitConfiguration->hide(); m_Controls.checkBox_Constraints->setEnabled(false); m_Controls.constraintManager->setEnabled(false); m_Controls.initialValuesManager->setEnabled(false); m_Controls.initialValuesManager->setDataStorage(this->GetDataStorage()); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.initialValuesManager, SIGNAL(initialValuesChanged(void)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), m_Controls.initialValuesManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setVisible(bool))); UpdateGUIControls(); } void PETDynamicView::UpdateGUIControls() { m_Controls.lineFitName->setPlaceholderText(QString::fromStdString(this->GetDefaultFitName())); m_Controls.lineFitName->setEnabled(!m_FittingInProgress); m_Controls.checkBox_Constraints->setEnabled(m_modelConstraints.IsNotNull()); bool is1TCMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isExt1TCMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isFDGCMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool is2TCMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr || dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; m_Controls.groupAIF->setVisible(is1TCMFactory || isExt1TCMFactory || isFDGCMFactory || is2TCMFactory); m_Controls.groupBox_FitConfiguration->setVisible(m_selectedModelFactory); m_Controls.groupBox->setEnabled(!m_FittingInProgress); m_Controls.comboModel->setEnabled(!m_FittingInProgress); m_Controls.groupAIF->setEnabled(!m_FittingInProgress); m_Controls.groupBox_FitConfiguration->setEnabled(!m_FittingInProgress); m_Controls.radioROIbased->setEnabled(m_selectedMask.IsNotNull()); m_Controls.btnModelling->setEnabled(m_selectedImage.IsNotNull() && m_selectedModelFactory.IsNotNull() && !m_FittingInProgress && CheckModelSettings()); } //void PETDynamicView::OnModelSettingChanged() //{ // bool ok = m_selectedImage.IsNotNull() && m_selectedModelFactory.IsNotNull() && !m_FittingInProgress && CheckModelSettings(); // m_Controls.btnModelling->setEnabled(ok); //} void PETDynamicView::OnModellSet(int index) { m_selectedModelFactory = nullptr; if (index > 0) { if (static_cast(index) <= m_FactoryStack.size() ) { m_selectedModelFactory = m_FactoryStack[index - 1]; } else { MITK_WARN << "Invalid model index. Index outside of the factory stack. Factory stack size: "<< m_FactoryStack.size() << "; invalid index: "<< index; } } if (m_selectedModelFactory) { this->m_modelConstraints = dynamic_cast (m_selectedModelFactory->CreateDefaultConstraints().GetPointer()); m_Controls.initialValuesManager->setInitialValues(m_selectedModelFactory->GetParameterNames(), m_selectedModelFactory->GetDefaultInitialParameterization()); if (this->m_modelConstraints.IsNull()) { this->m_modelConstraints = mitk::SimpleBarrierConstraintChecker::New(); } m_Controls.constraintManager->setChecker(this->m_modelConstraints, this->m_selectedModelFactory->GetParameterNames()); } m_Controls.checkBox_Constraints->setEnabled(m_modelConstraints.IsNotNull()); UpdateGUIControls(); } std::string PETDynamicView::GetFitName() const { std::string fitName = m_Controls.lineFitName->text().toStdString(); if (fitName.empty()) { fitName = m_Controls.lineFitName->placeholderText().toStdString(); } return fitName; } std::string PETDynamicView::GetDefaultFitName() const { std::string defaultName = "undefined model"; if (this->m_selectedModelFactory.IsNotNull()) { defaultName = this->m_selectedModelFactory->GetClassID(); } if (this->m_Controls.radioPixelBased->isChecked()) { defaultName += "_pixel"; } else { defaultName += "_roi"; } return defaultName; } void PETDynamicView::OnModellingButtonClicked() { //check if all static parameters set if (m_selectedModelFactory.IsNotNull() && CheckModelSettings()) { mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr; mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr; bool isOTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isextOTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isFDGFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isTTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isNumTTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; if (isOTCFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } else if (isextOTCFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } else if (isFDGFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } else if (isTTCFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } else if (isNumTTCFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateModelFit_PixelBased(fitSession, generator); } else { GenerateModelFit_ROIBased(fitSession, generator); } } //add other models with else if if (generator.IsNotNull() && fitSession.IsNotNull()) { m_FittingInProgress = true; DoFit(fitSession, generator); } else { QMessageBox box; box.setText("Fitting error!"); box.setInformativeText("Could not establish fitting job. Error when setting ab generator, model parameterizer or session info."); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } else { QMessageBox box; box.setText("Static parameters for model are not set!"); box.setInformativeText("Some static parameters, that are needed for calculation are not set and equal to zero. Modeling not possible"); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } void PETDynamicView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList& selectedNodes) { m_selectedMaskNode = nullptr; m_selectedMask = nullptr; m_Controls.errorMessageLabel->setText(""); m_Controls.masklabel->setText("No (valid) mask selected."); m_Controls.timeserieslabel->setText("No (valid) series selected."); QList nodes = selectedNodes; if (nodes.size() > 0 && this->m_IsNoMaskImagePredicate->CheckNode(nodes.front())) { this->m_selectedNode = nodes.front(); auto selectedImage = dynamic_cast(this->m_selectedNode->GetData()); m_Controls.timeserieslabel->setText((this->m_selectedNode->GetName()).c_str()); if (selectedImage != this->m_selectedImage) { if (selectedImage) { this->m_Controls.initialValuesManager->setReferenceImageGeometry(selectedImage->GetGeometry()); } else { this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr); } } this->m_selectedImage = selectedImage; nodes.pop_front(); } else { this->m_selectedNode = nullptr; this->m_selectedImage = nullptr; this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr); } if (nodes.size() > 0 && this->m_IsMaskPredicate->CheckNode(nodes.front())) { this->m_selectedMaskNode = nodes.front(); this->m_selectedMask = dynamic_cast(this->m_selectedMaskNode->GetData()); if (this->m_selectedMask->GetTimeSteps() > 1) { MITK_INFO << "Selected mask has multiple timesteps. Only use first timestep to mask model fit. Mask name: " << m_selectedMaskNode->GetName(); mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedMask = maskedImageTimeSelector->GetOutput(); } m_Controls.masklabel->setText((this->m_selectedMaskNode->GetName()).c_str()); } if (m_selectedMask.IsNull()) { this->m_Controls.radioPixelBased->setChecked(true); } m_Controls.errorMessageLabel->show(); UpdateGUIControls(); } bool PETDynamicView::CheckModelSettings() const { bool ok = true; //check wether any model is set at all. Otherwise exit with false if (m_selectedModelFactory.IsNotNull()) { bool isOTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isextOTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isFDGFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isTTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; bool isNumTTCFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != nullptr; if (isOTCFactory || isextOTCFactory || isFDGFactory || isTTCFactory || isNumTTCFactory) { if (this->m_Controls.radioAIFImage->isChecked()) { ok = ok && m_Controls.comboAIFMask->GetSelectedNode().IsNotNull(); if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { ok = ok && m_Controls.comboAIFImage->GetSelectedNode().IsNotNull(); } } else if (this->m_Controls.radioAIFFile->isChecked()) { ok = ok && (this->AIFinputGrid.size() != 0) && (this->AIFinputFunction.size() != 0); } else { ok = false; } } //add other models as else if and check wether all needed static parameters are set else { ok = false; } if (this->m_Controls.radioButton_StartParameters->isChecked() && !this->m_Controls.initialValuesManager->hasValidInitialValues()) { std::string warning = "Warning. Invalid start parameters. At least one parameter as an invalid image setting as source."; MITK_ERROR << warning; m_Controls.infoBox->append(QString("") + QString::fromStdString(warning) + QString("")); ok = false; }; } else { ok = false; } return ok; } void PETDynamicView::ConfigureInitialParametersOfParameterizer(mitk::ModelParameterizerBase* parameterizer) const { if (m_Controls.radioButton_StartParameters->isChecked()) { //use user defined initial parameters mitk::InitialParameterizationDelegateBase::Pointer paramDelegate = m_Controls.initialValuesManager->getInitialParametrizationDelegate(); parameterizer->SetInitialParameterizationDelegate(paramDelegate); } } template void PETDynamicView::GenerateModelFit_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::TimeGridType aifTimeGrid; GetAIF(aif, aifTimeGrid); //Model configuration (static parameters) can be done now modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); roiUID = m_selectedMask->GetUID(); } fitGenerator->SetDynamicImage(this->m_selectedImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), 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 void PETDynamicView::GenerateModelFit_ROIBased( mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); //Compute AIF mitk::AterialInputFunctionGenerator::Pointer aifGenerator = mitk::AterialInputFunctionGenerator::New(); aifGenerator->SetDynamicImage(this->m_selectedImage); aifGenerator->SetMask(this->m_selectedAIFMask); mitk::AIFBasedModelBase::AterialInputFunctionType aif = aifGenerator->GetAterialInputFunction(); mitk::AIFBasedModelBase::TimeGridType aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid(); //Model configuration (static parameters) can be done now modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); signalGenerator->SetDynamicImage(m_selectedImage); 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(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(m_selectedImage)); generator = fitGenerator.GetPointer(); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { roiUID = m_selectedMask->GetUID(); } //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), 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 PETDynamicView::DoFit(const mitk::modelFit::ModelFitInfo* fitSession, mitk::ParameterFitImageGeneratorBase* generator) { std::stringstream message; message << "" << "Fitting Data Set . . ." << ""; m_Controls.errorMessageLabel->setText(message.str().c_str()); m_Controls.errorMessageLabel->show(); ///////////////////////// //create job and put it into the thread pool ParameterFitBackgroundJob* pJob = new ParameterFitBackgroundJob(generator, fitSession, this->m_selectedNode); pJob->setAutoDelete(true); connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnJobFinished())); connect(pJob, SIGNAL(ResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), this, SLOT(OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(JobProgress(double)), this, SLOT(OnJobProgress(double))); connect(pJob, SIGNAL(JobStatusChanged(QString)), this, SLOT(OnJobStatusChanged(QString))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } PETDynamicView::PETDynamicView() : m_FittingInProgress(false) { m_selectedImage = nullptr; m_selectedMask = nullptr; mitk::ModelFactoryBase::Pointer factory = mitk::OneTissueCompartmentModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::ExtendedOneTissueCompartmentModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::TwoTissueCompartmentModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::TwoTissueCompartmentFDGModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::NumericTwoTissueCompartmentModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isLegacyMask = mitk::NodePredicateAnd::New(isImage, isBinary); mitk::NodePredicateOr::Pointer isMask = mitk::NodePredicateOr::New(isLegacyMask, isLabelSet); mitk::NodePredicateAnd::Pointer isNoMask = mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isMask)); this->m_IsMaskPredicate = mitk::NodePredicateAnd::New(isMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); this->m_IsNoMaskImagePredicate = mitk::NodePredicateAnd::New(isNoMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); } void PETDynamicView::OnJobFinished() { this->m_Controls.infoBox->append(QString("Fitting finished")); this->m_FittingInProgress = false; }; void PETDynamicView::OnJobError(QString err) { MITK_ERROR << err.toStdString().c_str(); m_Controls.infoBox->append(QString("") + err + QString("")); }; void PETDynamicView::OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType results, const ParameterFitBackgroundJob* pJob) { //Store the resulting parameter fit image via convenience helper function in data storage //(handles the correct generation of the nodes and their properties) mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), results, pJob->GetParentNode()); m_Controls.errorMessageLabel->setText(""); m_Controls.errorMessageLabel->hide(); }; void PETDynamicView::OnJobProgress(double progress) { QString report = QString("Progress. ") + QString::number(progress); this->m_Controls.infoBox->append(report); }; void PETDynamicView::OnJobStatusChanged(QString info) { this->m_Controls.infoBox->append(info); } void PETDynamicView::InitModelComboBox() const { this->m_Controls.comboModel->clear(); this->m_Controls.comboModel->addItem(tr("No model selected")); for (ModelFactoryStackType::const_iterator pos = m_FactoryStack.begin(); pos != m_FactoryStack.end(); ++pos) { this->m_Controls.comboModel->addItem(QString::fromStdString((*pos)->GetClassID())); } this->m_Controls.comboModel->setCurrentIndex(0); }; mitk::ModelFitFunctorBase::Pointer PETDynamicView::CreateDefaultFitFunctor( const mitk::ModelParameterizerBase* parameterizer) const { mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor = mitk::LevenbergMarquardtModelFitFunctor::New(); mitk::SumOfSquaredDifferencesFitCostFunction::Pointer evaluation = mitk::SumOfSquaredDifferencesFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("sum_diff^2", evaluation); mitk::ChiSquareFitCostFunction::Pointer chi2 = mitk::ChiSquareFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("Chi^2", chi2); mitk::ReducedChiSquareFitCostFunction::Pointer redchi2 = mitk::ReducedChiSquareFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("redChi^2", redchi2); if (m_Controls.checkBox_Constraints->isChecked()) { fitFunctor->SetConstraintChecker(m_modelConstraints); } mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel(); ::itk::LevenbergMarquardtOptimizer::ScalesType scales; scales.SetSize(refModel->GetNumberOfParameters()); scales.Fill(1.0); fitFunctor->SetScales(scales); return fitFunctor.GetPointer(); } void PETDynamicView::GetAIF(mitk::AIFBasedModelBase::AterialInputFunctionType& aif, mitk::AIFBasedModelBase::AterialInputFunctionType& aifTimeGrid) { if (this->m_Controls.radioAIFFile->isChecked()) { aif.clear(); aifTimeGrid.clear(); aif.SetSize(AIFinputFunction.size()); aifTimeGrid.SetSize(AIFinputGrid.size()); aif.fill(0.0); aifTimeGrid.fill(0.0); itk::Array::iterator aifPos = aif.begin(); for (std::vector::const_iterator pos = AIFinputFunction.begin(); pos != AIFinputFunction.end(); ++pos, ++aifPos) { *aifPos = *pos; } itk::Array::iterator gridPos = aifTimeGrid.begin(); for (std::vector::const_iterator pos = AIFinputGrid.begin(); pos != AIFinputGrid.end(); ++pos, ++gridPos) { *gridPos = *pos; } } else if (this->m_Controls.radioAIFImage->isChecked()) { aif.clear(); aifTimeGrid.clear(); mitk::AterialInputFunctionGenerator::Pointer aifGenerator = mitk::AterialInputFunctionGenerator::New(); //Hematocrit level aifGenerator->SetHCL(this->m_Controls.HCLSpinBox->value()); //mask settings this->m_selectedAIFMaskNode = m_Controls.comboAIFMask->GetSelectedNode(); this->m_selectedAIFMask = dynamic_cast(this->m_selectedAIFMaskNode->GetData()); if (this->m_selectedAIFMask->GetTimeSteps() > 1) { MITK_INFO << "Selected AIF mask has multiple timesteps. Only use first timestep to mask model fit. AIF Mask name: " << m_selectedAIFMaskNode->GetName() ; mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedAIFMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedAIFMask = maskedImageTimeSelector->GetOutput(); } if (this->m_selectedAIFMask.IsNotNull()) { aifGenerator->SetMask(this->m_selectedAIFMask); } //image settings if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { this->m_selectedAIFImageNode = m_Controls.comboAIFImage->GetSelectedNode(); this->m_selectedAIFImage = dynamic_cast(this->m_selectedAIFImageNode->GetData()); } else { this->m_selectedAIFImageNode = m_selectedNode; this->m_selectedAIFImage = m_selectedImage; } aifGenerator->SetDynamicImage(this->m_selectedAIFImage); aif = aifGenerator->GetAterialInputFunction(); aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid(); } else { mitkThrow() << "Cannot generate AIF. View is in a invalide state. No AIF mode selected."; } } void PETDynamicView::LoadAIFfromFile() { QFileDialog dialog; dialog.setNameFilter(tr("Images (*.csv")); QString fileName = dialog.getOpenFileName(); m_Controls.aifFilePath->setText(fileName); std::string m_aifFilePath = fileName.toStdString(); //Read Input typedef boost::tokenizer< boost::escaped_list_separator > Tokenizer; ///////////////////////////////////////////////////////////////////////////////////////////////// //AIF Data std::ifstream in1(m_aifFilePath.c_str()); if (!in1.is_open()) { m_Controls.errorMessageLabel->setText("Could not open AIF File!"); } std::vector< std::string > vec1; std::string line1; while (getline(in1, line1)) { Tokenizer tok(line1); vec1.assign(tok.begin(), tok.end()); // if (vec1.size() < 3) continue; this->AIFinputGrid.push_back(convertToDouble(vec1[0])); this->AIFinputFunction.push_back(convertToDouble(vec1[1])); } } diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/documentation/UserManual/Manual.dox b/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/documentation/UserManual/Manual.dox index 667143d2b5..90b183b1e3 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/documentation/UserManual/Manual.dox +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/documentation/UserManual/Manual.dox @@ -1,5 +1,5 @@ /** -\page org_mitk_gui_qt_pharmacokinetics_simulation The Perfusion Data Simulation View +\page org_mitk_views_pharmacokinetics_simulation The Perfusion Data Simulation View */ diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/plugin.xml b/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/plugin.xml index 8528041735..6a016c3091 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/plugin.xml +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/plugin.xml @@ -1,12 +1,12 @@ - diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/src/internal/PerfusionDataSimulationView.cpp b/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/src/internal/PerfusionDataSimulationView.cpp index b886c853a0..9a250cb94a 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/src/internal/PerfusionDataSimulationView.cpp +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.simulation/src/internal/PerfusionDataSimulationView.cpp @@ -1,1000 +1,1000 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include "mitkWorkbenchUtil.h" #include "PerfusionDataSimulationView.h" #include "itkUnaryFunctorImageFilter.h" #include #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "mitkITKImageImport.h" #include "mitkGaussianNoiseFunctor.h" #include "mitkTwoCompartmentExchangeModel.h" #include "mitkTwoCompartmentExchangeModelParameterizer.h" #include "mitkTwoCompartmentExchangeModelFactory.h" #include "mitkNumericTwoCompartmentExchangeModel.h" #include "mitkNumericTwoCompartmentExchangeModelParameterizer.h" #include "mitkNumericTwoCompartmentExchangeModelFactory.h" #include "mitkExtendedToftsModel.h" #include "mitkExtendedToftsModelFactory.h" #include "mitkExtendedToftsModelParameterizer.h" #include "mitkTwoTissueCompartmentModel.h" #include "mitkTwoTissueCompartmentModelParameterizer.h" #include "mitkTwoTissueCompartmentModelFactory.h" #include "mitkOneTissueCompartmentModel.h" #include "mitkOneTissueCompartmentModelParameterizer.h" #include "mitkOneTissueCompartmentModelFactory.h" #include "mitkModelSignalImageGenerator.h" #include #include #include #include #include "mitkSimpleFunctorBase.h" #include "mitkArbitraryTimeGeometry.h" #include #include -const std::string PerfusionDataSimulationView::VIEW_ID = "org.mitk.gui.qt.pharmacokinetics.simulation"; +const std::string PerfusionDataSimulationView::VIEW_ID = "org.mitk.views.pharmacokinetics.simulation"; inline double convertToDouble(const std::string& data) { std::istringstream stepStream(data); stepStream.imbue(std::locale("C")); double value = 0.0; if (!(stepStream >> value) || !(stepStream.eof())) { mitkThrow() << "Cannot convert string to double. String: " << data; } return value; } inline double FindeMaxConcentration(std::vector Concentration) { double maximum = Concentration[0]; for (std::vector::size_type i =0; isetFocus(); } void PerfusionDataSimulationView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.GenerateBtn->setEnabled(false); m_Controls.groupBox_TM->hide(); m_Controls.groupBox_2CXM->hide(); m_Controls.groupBox_2TCM->hide(); m_Controls.groupBox_1TCM->hide(); m_Controls.groupBox_CNR->hide(); this->InitModelComboBox(); /** #2 @todo Reduce code ? */ m_Controls.comboBox_F->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_F->SetPredicate(m_IsNotABinaryImagePredicate); m_Controls.comboBox_PS->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_PS->SetPredicate(m_IsNotABinaryImagePredicate); m_Controls.comboBox_fp->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_fp->SetPredicate(m_IsNotABinaryImagePredicate); m_Controls.comboBox_fi->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_fi->SetPredicate(m_IsNotABinaryImagePredicate); m_Controls.comboBox_Ktrans->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_Ktrans->SetPredicate(m_IsNotABinaryImagePredicate); m_Controls.comboBox_vp->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_vp->SetPredicate(m_IsNotABinaryImagePredicate); m_Controls.comboBox_ve->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_ve->SetPredicate(m_IsNotABinaryImagePredicate); m_Controls.comboBox_K1->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_K1->SetPredicate(m_IsNotABinaryImagePredicate); m_Controls.comboBox_K2->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_K2->SetPredicate(m_IsNotABinaryImagePredicate); m_Controls.comboBox_K3->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_K3->SetPredicate(m_IsNotABinaryImagePredicate); m_Controls.comboBox_K4->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_K4->SetPredicate(m_IsNotABinaryImagePredicate); m_Controls.comboBox_VB->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_VB->SetPredicate(m_IsNotABinaryImagePredicate); m_Controls.comboBox_k1->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_k1->SetPredicate(m_IsNotABinaryImagePredicate); m_Controls.comboBox_k2->SetDataStorage(this->GetDataStorage()); m_Controls.comboBox_k2->SetPredicate(m_IsNotABinaryImagePredicate); connect(m_Controls.AifFileBtn, SIGNAL(clicked()), this, SLOT(LoadAIFFile())); connect(m_Controls.ModelSelection, SIGNAL(currentIndexChanged(int)), this, SLOT(OnModellSet(int))); connect(m_Controls.GenerateBtn, SIGNAL(clicked()), this, SLOT(OnGenerateDataButtonClicked())); connect(m_Controls.comboBox_F, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.comboBox_PS, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.comboBox_fp, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.comboBox_fi, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.comboBox_VB, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.comboBox_K1, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.comboBox_K2, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.comboBox_K3, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.comboBox_K4, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.comboBox_k1, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.comboBox_k2, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.comboBox_Ktrans, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.comboBox_vp, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.comboBox_ve, SIGNAL(currentIndexChanged(int)), this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.CNRSpinBox, SIGNAL(valueChanged(double)),this, SLOT(OnSimulationConfigurationChanged())); connect(m_Controls.NoiseCheckBox, SIGNAL(stateChanged(int)),this, SLOT(OnSimulationConfigurationChanged())); // UpdateDataSelection(); } void PerfusionDataSimulationView::UpdateDataSelection() { } //void PerfusionDataSimulationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, // const QList& selectedNodes) //{ // UpdateDataSelection(); // m_Controls.GenerateBtn->setEnabled(CheckModelSettings()); //} void PerfusionDataSimulationView::OnSimulationConfigurationChanged() { // UpdateDataSelection(); m_Controls.groupBox_CNR->setVisible(m_Controls.NoiseCheckBox->isChecked()); this->m_CNR = m_Controls.CNRSpinBox->value(); m_Controls.GenerateBtn->setEnabled( CheckModelSettings() ); } void PerfusionDataSimulationView::OnModellSet(int index) { m_selectedModelFactory = nullptr; if (index > 0) { if (static_cast(index) <= m_FactoryStack.size() ) { m_selectedModelFactory = m_FactoryStack[index - 1]; } else { MITK_WARN << "Invalid model index. Index outside of the factory stack. Factory stack size: "<< m_FactoryStack.size() << "; invalid index: "<< index; } } bool isToftsFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; bool is2CXMFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr || dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; bool is2TCMFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; bool is1TCMFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; m_Controls.groupBox_TM->setVisible(isToftsFactory); m_Controls.groupBox_2CXM->setVisible(is2CXMFactory ); m_Controls.groupBox_2TCM->setVisible(is2TCMFactory ); m_Controls.groupBox_1TCM->setVisible(is1TCMFactory ); m_Controls.GenerateBtn->setEnabled( CheckModelSettings() ); // UpdateDataSelection(); } bool PerfusionDataSimulationView::CheckModelSettings() { bool ok = true; if(m_selectedModelFactory.IsNull()) { return false; } if(this->m_AterialInputFunction.GetSize() == 0 || this->m_TimeGrid.GetSize() == 0) { return false; } bool isToftsFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; bool is2CXMFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr || dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; bool is2TCMFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; bool is1TCMFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; if(isToftsFactory) { ok = ok && m_Controls.comboBox_Ktrans->GetSelectedNode().IsNotNull(); ok = ok && m_Controls.comboBox_vp->GetSelectedNode().IsNotNull(); ok = ok && m_Controls.comboBox_ve->GetSelectedNode().IsNotNull(); } else if(is2CXMFactory) { ok = ok && m_Controls.comboBox_F->GetSelectedNode().IsNotNull(); ok = ok && m_Controls.comboBox_PS->GetSelectedNode().IsNotNull(); ok = ok && m_Controls.comboBox_fp->GetSelectedNode().IsNotNull(); ok = ok && m_Controls.comboBox_fi->GetSelectedNode().IsNotNull(); } else if(is2TCMFactory) { ok = ok && m_Controls.comboBox_K1->GetSelectedNode().IsNotNull(); ok = ok && m_Controls.comboBox_K2->GetSelectedNode().IsNotNull(); ok = ok && m_Controls.comboBox_K3->GetSelectedNode().IsNotNull(); ok = ok && m_Controls.comboBox_K4->GetSelectedNode().IsNotNull(); ok = ok && m_Controls.comboBox_VB->GetSelectedNode().IsNotNull(); } else if(is1TCMFactory) { ok = ok && m_Controls.comboBox_k1->GetSelectedNode().IsNotNull(); ok = ok && m_Controls.comboBox_k2->GetSelectedNode().IsNotNull(); } else { return false; } if(m_Controls.NoiseCheckBox->isChecked()) { if(m_CNR !=0 && m_MaxConcentration !=0) { this->m_Sigma = m_MaxConcentration/m_CNR; } if(m_Sigma==0) { return false; } } return ok; } void PerfusionDataSimulationView::InitModelComboBox() const { this->m_Controls.ModelSelection->clear(); this->m_Controls.ModelSelection->addItem(tr("No model selected")); for (ModelFactoryStackType::const_iterator pos = m_FactoryStack.begin(); pos != m_FactoryStack.end(); ++pos) { this->m_Controls.ModelSelection->addItem(QString::fromStdString((*pos)->GetClassID())); } this->m_Controls.ModelSelection->setCurrentIndex(0); }; void PerfusionDataSimulationView::LoadAIFFile() { QFileDialog dialog; dialog.setNameFilter(tr("Images (*.csv")); QString fileName = dialog.getOpenFileName(); m_Controls.AifFilePath->setText( fileName ); std::string aifFilePath = fileName.toStdString(); //Read Input typedef boost::tokenizer< boost::escaped_list_separator > Tokenizer; ///////////////////////////////////////////////////////////////////////////////////////////////// //AIF Data std::vector inputFunction; std::vector inputGrid; std::ifstream in1(aifFilePath.c_str()); if(!in1.is_open()) { m_Controls.errorMessageLabel->setText("Could not open AIF File!"); } std::vector< std::string > vec1; std::string line1; while (getline(in1,line1)) { Tokenizer tok(line1); vec1.assign(tok.begin(),tok.end()); // if (vec1.size() < 3) continue; inputGrid.push_back(convertToDouble(vec1[0])); inputFunction.push_back(convertToDouble(vec1[1])); } this->m_MaxConcentration = FindeMaxConcentration(inputFunction); itk::Array aif; itk::Array grid; aif.SetSize(inputFunction.size()); grid.SetSize(inputGrid.size()); aif.fill(0.0); grid.fill(0.0); itk::Array::iterator aifPos = aif.begin(); for(std::vector::const_iterator pos = inputFunction.begin(); pos != inputFunction.end(); ++pos, ++aifPos) { *aifPos = *pos; } itk::Array::iterator gridPos = grid.begin(); for(std::vector::const_iterator pos = inputGrid.begin(); pos != inputGrid.end(); ++pos, ++gridPos) { *gridPos = *pos; } this->m_AterialInputFunction = aif; this->m_TimeGrid = grid; m_Controls.GenerateBtn->setEnabled( CheckModelSettings() ); // UpdateDataSelection(); } /** @todo #2 Same function for Numeric and analytic version of FillParameterMap2CXM */ void PerfusionDataSimulationView::FillParameterMap2CXM() { ParameterMapType stack; mitk::DataNode::Pointer m_selectedNode = m_Controls.comboBox_F->GetSelectedNode(); mitk::Image::Pointer m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::TwoCompartmentExchangeModel::NAME_PARAMETER_F,m_selectedImage)); m_selectedNode = m_Controls.comboBox_PS->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::TwoCompartmentExchangeModel::NAME_PARAMETER_PS,m_selectedImage)); m_selectedNode = m_Controls.comboBox_fp->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::TwoCompartmentExchangeModel::NAME_PARAMETER_vp,m_selectedImage)); m_selectedNode = m_Controls.comboBox_fi->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::TwoCompartmentExchangeModel::NAME_PARAMETER_ve,m_selectedImage)); this->m_ParameterImageMap = stack; } void PerfusionDataSimulationView::FillParameterMapNumeric2CXM() { ParameterMapType stack; mitk::DataNode::Pointer m_selectedNode = m_Controls.comboBox_F->GetSelectedNode(); mitk::Image::Pointer m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::NumericTwoCompartmentExchangeModel::NAME_PARAMETER_F,m_selectedImage)); m_selectedNode = m_Controls.comboBox_PS->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::NumericTwoCompartmentExchangeModel::NAME_PARAMETER_PS,m_selectedImage)); m_selectedNode = m_Controls.comboBox_fp->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::NumericTwoCompartmentExchangeModel::NAME_PARAMETER_vp,m_selectedImage)); m_selectedNode = m_Controls.comboBox_fi->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::NumericTwoCompartmentExchangeModel::NAME_PARAMETER_ve,m_selectedImage)); this->m_ParameterImageMap = stack; } void PerfusionDataSimulationView::FillParameterMapETM() { ParameterMapType stack; mitk::DataNode::Pointer m_selectedNode = m_Controls.comboBox_Ktrans->GetSelectedNode(); mitk::Image::Pointer m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::ExtendedToftsModel::NAME_PARAMETER_Ktrans,m_selectedImage)); m_selectedNode = m_Controls.comboBox_vp->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::ExtendedToftsModel::NAME_PARAMETER_vp,m_selectedImage)); m_selectedNode = m_Controls.comboBox_ve->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::ExtendedToftsModel::NAME_PARAMETER_ve,m_selectedImage)); this->m_ParameterImageMap = stack; } void PerfusionDataSimulationView::FillParameterMap2TCM() { ParameterMapType stack; mitk::DataNode::Pointer m_selectedNode = m_Controls.comboBox_K1->GetSelectedNode(); mitk::Image::Pointer m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::TwoTissueCompartmentModel::NAME_PARAMETER_K1,m_selectedImage)); m_selectedNode = m_Controls.comboBox_K2->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::TwoTissueCompartmentModel::NAME_PARAMETER_k2,m_selectedImage)); m_selectedNode = m_Controls.comboBox_K3->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::TwoTissueCompartmentModel::NAME_PARAMETER_k3,m_selectedImage)); m_selectedNode = m_Controls.comboBox_K4->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::TwoTissueCompartmentModel::NAME_PARAMETER_k4,m_selectedImage)); m_selectedNode = m_Controls.comboBox_VB->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::TwoTissueCompartmentModel::NAME_PARAMETER_VB,m_selectedImage)); this->m_ParameterImageMap = stack; } void PerfusionDataSimulationView::FillParameterMap1TCM() { ParameterMapType stack; mitk::DataNode::Pointer m_selectedNode = m_Controls.comboBox_k1->GetSelectedNode(); mitk::Image::Pointer m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::OneTissueCompartmentModel::NAME_PARAMETER_k1,m_selectedImage)); m_selectedNode = m_Controls.comboBox_k2->GetSelectedNode(); m_selectedImage = dynamic_cast(m_selectedNode->GetData()); stack.insert(std::make_pair(mitk::OneTissueCompartmentModel::NAME_PARAMETER_k2,m_selectedImage)); this->m_ParameterImageMap = stack; } void PerfusionDataSimulationView::OnGenerateDataButtonClicked() { mitk::Image::Pointer m_DynamicImage = mitk::Image::New(); bool isToftsFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; bool isPhysBrixFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; bool isNumPhysBrixFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; bool is2TCMFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; bool is1TCMFactory = dynamic_cast(m_selectedModelFactory.GetPointer()) != nullptr; if(isToftsFactory) { this->FillParameterMapETM(); m_DynamicImage = this->GenerateETModelData(); } if (isPhysBrixFactory) { this->FillParameterMap2CXM(); m_DynamicImage = this->Generate2CXModelData(); } if (isNumPhysBrixFactory) { this->FillParameterMapNumeric2CXM(); m_DynamicImage = this->GenerateNumeric2CXModelData(); } if (is2TCMFactory) { this->FillParameterMap2TCM(); m_DynamicImage = this->Generate2TCModelData(); } if (is1TCMFactory) { this->FillParameterMap1TCM(); m_DynamicImage = this->Generate1TCModelData(); } mitk::DataNode::Pointer resultNode = mitk::DataNode::New(); std::string nameOfResultImage = "SimulationData_"; nameOfResultImage.append(m_selectedModelFactory->GetModelDisplayName()); resultNode->SetProperty("name", mitk::StringProperty::New(nameOfResultImage) ); resultNode->SetData(m_DynamicImage); // set data of new node this->GetDataStorage()->Add(resultNode); } mitk::Image::Pointer PerfusionDataSimulationView::Generate2CXModelData() { mitk::ModelSignalImageGenerator::Pointer generator = mitk::ModelSignalImageGenerator::New(); mitk::TwoCompartmentExchangeModelParameterizer::Pointer modelParameterizer = mitk::TwoCompartmentExchangeModelParameterizer::New(); /** @todo #2 necessary? Generator need to have a map with Parameters in order of Model in order to pass vector parameters correctly to Model. * I wanted to make it independend from the order the images are passed on */ for(ParameterMapType::const_iterator pos = this->m_ParameterImageMap.begin(); pos != this->m_ParameterImageMap.end(); ++pos) { if(pos->first == mitk::TwoCompartmentExchangeModel::NAME_PARAMETER_F) { generator->SetParameterInputImage(mitk::TwoCompartmentExchangeModel::POSITION_PARAMETER_F, pos->second); } if(pos->first == mitk::TwoCompartmentExchangeModel::NAME_PARAMETER_PS) { generator->SetParameterInputImage(mitk::TwoCompartmentExchangeModel::POSITION_PARAMETER_PS, pos->second); } if(pos->first == mitk::TwoCompartmentExchangeModel::NAME_PARAMETER_ve) { generator->SetParameterInputImage(mitk::TwoCompartmentExchangeModel::POSITION_PARAMETER_ve, pos->second); } if(pos->first == mitk::TwoCompartmentExchangeModel::NAME_PARAMETER_vp) { generator->SetParameterInputImage(mitk::TwoCompartmentExchangeModel::POSITION_PARAMETER_vp, pos->second); } } modelParameterizer->SetAIF(this->m_AterialInputFunction); modelParameterizer->SetAIFTimeGrid(this->m_TimeGrid); modelParameterizer->SetDefaultTimeGrid(this->m_TimeGrid); generator->SetParameterizer(modelParameterizer); mitk::Image::Pointer generatedImage = generator->GetGeneratedImage(); mitk::Image::Pointer resultImage = mitk::Image::New(); if(m_Controls.NoiseCheckBox->isChecked()) { typedef itk::Image ImageType; mitk::Image::Pointer tempImage = mitk::Image::New(); tempImage->Initialize(generatedImage); mitk::ArbitraryTimeGeometry* timeGeometry = dynamic_cast (generatedImage->GetTimeGeometry()); tempImage->SetTimeGeometry(timeGeometry); ImageType::Pointer itkImage = ImageType::New(); mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(generatedImage); for(unsigned int i = 0; i< generatedImage->GetTimeSteps(); ++i) { imageTimeSelector->SetTimeNr(i); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkInputImage = imageTimeSelector->GetOutput(); mitk::CastToItkImage(mitkInputImage, itkImage ); typedef mitk::GaussianNoiseFunctor NoiseFunctorType; typedef itk::UnaryFunctorImageFilter NoiseFilterType; NoiseFilterType::Pointer noiseFilter = NoiseFilterType::New(); NoiseFunctorType noiseFunctor; noiseFunctor.SetMean(0.0); noiseFunctor.SetSigma(this->m_Sigma); noiseFilter->SetFunctor(noiseFunctor); noiseFilter->SetInput(itkImage); mitk::Image::Pointer outputImage = mitk::ImportItkImage(noiseFilter->GetOutput())->Clone(); mitk::ImageReadAccessor accessor(outputImage); tempImage->SetVolume(accessor.GetData(), i); } resultImage = tempImage->Clone(); } else { resultImage = generatedImage; } return resultImage; } /** @todo #2 Synergie? Function implementation for every Model to complicated? */ mitk::Image::Pointer PerfusionDataSimulationView::GenerateNumeric2CXModelData() { mitk::ModelSignalImageGenerator::Pointer generator = mitk::ModelSignalImageGenerator::New(); mitk::NumericTwoCompartmentExchangeModelParameterizer::Pointer modelParameterizer = mitk::NumericTwoCompartmentExchangeModelParameterizer::New(); for(ParameterMapType::const_iterator pos = this->m_ParameterImageMap.begin(); pos != this->m_ParameterImageMap.end(); ++pos) { if(pos->first == mitk::NumericTwoCompartmentExchangeModel::NAME_PARAMETER_F) { generator->SetParameterInputImage(mitk::NumericTwoCompartmentExchangeModel::POSITION_PARAMETER_F, pos->second); } if(pos->first == mitk::NumericTwoCompartmentExchangeModel::NAME_PARAMETER_PS) { generator->SetParameterInputImage(mitk::NumericTwoCompartmentExchangeModel::POSITION_PARAMETER_PS, pos->second); } if(pos->first == mitk::NumericTwoCompartmentExchangeModel::NAME_PARAMETER_ve) { generator->SetParameterInputImage(mitk::NumericTwoCompartmentExchangeModel::POSITION_PARAMETER_ve, pos->second); } if(pos->first == mitk::NumericTwoCompartmentExchangeModel::NAME_PARAMETER_vp) { generator->SetParameterInputImage(mitk::NumericTwoCompartmentExchangeModel::POSITION_PARAMETER_vp, pos->second); } } modelParameterizer->SetAIF(this->m_AterialInputFunction); modelParameterizer->SetAIFTimeGrid(this->m_TimeGrid); modelParameterizer->SetDefaultTimeGrid(this->m_TimeGrid); modelParameterizer->SetODEINTStepSize(0.05); generator->SetParameterizer(modelParameterizer); mitk::Image::Pointer generatedImage = generator->GetGeneratedImage(); mitk::Image::Pointer resultImage = mitk::Image::New(); if(m_Controls.NoiseCheckBox->isChecked()) { typedef itk::Image ImageType; mitk::Image::Pointer tempImage = mitk::Image::New(); tempImage->Initialize(generatedImage); mitk::ArbitraryTimeGeometry* timeGeometry = dynamic_cast (generatedImage->GetTimeGeometry()); tempImage->SetTimeGeometry(timeGeometry); ImageType::Pointer itkImage = ImageType::New(); mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(generatedImage); for(unsigned int i = 0; i< generatedImage->GetTimeSteps(); ++i) { imageTimeSelector->SetTimeNr(i); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkInputImage = imageTimeSelector->GetOutput(); mitk::CastToItkImage(mitkInputImage, itkImage ); typedef mitk::GaussianNoiseFunctor NoiseFunctorType; typedef itk::UnaryFunctorImageFilter NoiseFilterType; NoiseFilterType::Pointer noiseFilter = NoiseFilterType::New(); NoiseFunctorType noiseFunctor; noiseFunctor.SetMean(0.0); noiseFunctor.SetSigma(this->m_Sigma); noiseFilter->SetFunctor(noiseFunctor); noiseFilter->SetInput(itkImage); mitk::Image::Pointer outputImage = mitk::ImportItkImage(noiseFilter->GetOutput())->Clone(); mitk::ImageReadAccessor accessor(outputImage); tempImage->SetVolume(accessor.GetData(), i); } resultImage = tempImage->Clone(); } else { resultImage = generatedImage; } return resultImage; } mitk::Image::Pointer PerfusionDataSimulationView::GenerateETModelData() { mitk::ModelSignalImageGenerator::Pointer generator = mitk::ModelSignalImageGenerator::New(); mitk::ExtendedToftsModelParameterizer::Pointer modelParameterizer = mitk::ExtendedToftsModelParameterizer::New(); for(ParameterMapType::const_iterator pos = this->m_ParameterImageMap.begin(); pos != this->m_ParameterImageMap.end(); ++pos) { if(pos->first == mitk::ExtendedToftsModel::NAME_PARAMETER_Ktrans) { generator->SetParameterInputImage(mitk::ExtendedToftsModel::POSITION_PARAMETER_Ktrans, pos->second); } if(pos->first == mitk::ExtendedToftsModel::NAME_PARAMETER_vp) { generator->SetParameterInputImage(mitk::ExtendedToftsModel::POSITION_PARAMETER_vp, pos->second); } if(pos->first == mitk::ExtendedToftsModel::NAME_PARAMETER_ve) { generator->SetParameterInputImage(mitk::ExtendedToftsModel::POSITION_PARAMETER_ve, pos->second); } } modelParameterizer->SetAIF(this->m_AterialInputFunction); modelParameterizer->SetAIFTimeGrid(this->m_TimeGrid); modelParameterizer->SetDefaultTimeGrid(this->m_TimeGrid); generator->SetParameterizer(modelParameterizer); mitk::Image::Pointer generatedImage = generator->GetGeneratedImage(); mitk::Image::Pointer resultImage = mitk::Image::New(); if(m_Controls.NoiseCheckBox->isChecked()) { typedef itk::Image ImageType; mitk::Image::Pointer tempImage = mitk::Image::New(); tempImage->Initialize(generatedImage); mitk::ArbitraryTimeGeometry* timeGeometry = dynamic_cast (generatedImage->GetTimeGeometry()); tempImage->SetTimeGeometry(timeGeometry); ImageType::Pointer itkImage = ImageType::New(); mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(generatedImage); for(unsigned int i = 0; i< generatedImage->GetTimeSteps(); ++i) { imageTimeSelector->SetTimeNr(i); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkInputImage = imageTimeSelector->GetOutput(); mitk::CastToItkImage(mitkInputImage, itkImage ); typedef mitk::GaussianNoiseFunctor NoiseFunctorType; typedef itk::UnaryFunctorImageFilter NoiseFilterType; NoiseFilterType::Pointer noiseFilter = NoiseFilterType::New(); NoiseFunctorType noiseFunctor; noiseFunctor.SetMean(0.0); noiseFunctor.SetSigma(this->m_Sigma); noiseFilter->SetFunctor(noiseFunctor); noiseFilter->SetInput(itkImage); mitk::Image::Pointer outputImage = mitk::ImportItkImage(noiseFilter->GetOutput())->Clone(); mitk::ImageReadAccessor accessor(outputImage); tempImage->SetVolume(accessor.GetData(), i); } resultImage = tempImage->Clone(); } else { resultImage = generatedImage; } return resultImage; } mitk::Image::Pointer PerfusionDataSimulationView::Generate2TCModelData() { mitk::ModelSignalImageGenerator::Pointer generator = mitk::ModelSignalImageGenerator::New(); mitk::TwoTissueCompartmentModelParameterizer::Pointer modelParameterizer = mitk::TwoTissueCompartmentModelParameterizer::New(); for(ParameterMapType::const_iterator pos = this->m_ParameterImageMap.begin(); pos != this->m_ParameterImageMap.end(); ++pos) { if(pos->first == mitk::TwoTissueCompartmentModel::NAME_PARAMETER_K1) { generator->SetParameterInputImage(mitk::TwoTissueCompartmentModel::POSITION_PARAMETER_K1, pos->second); } if(pos->first == mitk::TwoTissueCompartmentModel::NAME_PARAMETER_k2) { generator->SetParameterInputImage(mitk::TwoTissueCompartmentModel::POSITION_PARAMETER_k2, pos->second); } if(pos->first == mitk::TwoTissueCompartmentModel::NAME_PARAMETER_k3) { generator->SetParameterInputImage(mitk::TwoTissueCompartmentModel::POSITION_PARAMETER_k3, pos->second); } if(pos->first == mitk::TwoTissueCompartmentModel::NAME_PARAMETER_k4) { generator->SetParameterInputImage(mitk::TwoTissueCompartmentModel::POSITION_PARAMETER_k4, pos->second); } if(pos->first == mitk::TwoTissueCompartmentModel::NAME_PARAMETER_VB) { generator->SetParameterInputImage(mitk::TwoTissueCompartmentModel::POSITION_PARAMETER_VB, pos->second); } } modelParameterizer->SetAIF(this->m_AterialInputFunction); modelParameterizer->SetAIFTimeGrid(this->m_TimeGrid); modelParameterizer->SetDefaultTimeGrid(this->m_TimeGrid); generator->SetParameterizer(modelParameterizer); mitk::Image::Pointer generatedImage = generator->GetGeneratedImage(); mitk::Image::Pointer resultImage = mitk::Image::New(); if(m_Controls.NoiseCheckBox->isChecked()) { typedef itk::Image ImageType; mitk::Image::Pointer tempImage = mitk::Image::New(); tempImage->Initialize(generatedImage); mitk::ArbitraryTimeGeometry* timeGeometry = dynamic_cast (generatedImage->GetTimeGeometry()); tempImage->SetTimeGeometry(timeGeometry); ImageType::Pointer itkImage = ImageType::New(); mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(generatedImage); for(unsigned int i = 0; i< generatedImage->GetTimeSteps(); ++i) { imageTimeSelector->SetTimeNr(i); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkInputImage = imageTimeSelector->GetOutput(); mitk::CastToItkImage(mitkInputImage, itkImage ); typedef mitk::GaussianNoiseFunctor NoiseFunctorType; typedef itk::UnaryFunctorImageFilter NoiseFilterType; NoiseFilterType::Pointer noiseFilter = NoiseFilterType::New(); NoiseFunctorType noiseFunctor; noiseFunctor.SetMean(0.0); noiseFunctor.SetSigma(this->m_Sigma); noiseFilter->SetFunctor(noiseFunctor); noiseFilter->SetInput(itkImage); mitk::Image::Pointer outputImage = mitk::ImportItkImage(noiseFilter->GetOutput())->Clone(); mitk::ImageReadAccessor accessor(outputImage); tempImage->SetVolume(accessor.GetData(), i); } resultImage = tempImage->Clone(); } else { resultImage = generatedImage; } return resultImage; } mitk::Image::Pointer PerfusionDataSimulationView::Generate1TCModelData() { mitk::ModelSignalImageGenerator::Pointer generator = mitk::ModelSignalImageGenerator::New(); mitk::OneTissueCompartmentModelParameterizer::Pointer modelParameterizer = mitk::OneTissueCompartmentModelParameterizer::New(); for(ParameterMapType::const_iterator pos = this->m_ParameterImageMap.begin(); pos != this->m_ParameterImageMap.end(); ++pos) { if(pos->first == mitk::OneTissueCompartmentModel::NAME_PARAMETER_k1) { generator->SetParameterInputImage(mitk::OneTissueCompartmentModel::POSITION_PARAMETER_k1, pos->second); } if(pos->first == mitk::OneTissueCompartmentModel::NAME_PARAMETER_k2) { generator->SetParameterInputImage(mitk::OneTissueCompartmentModel::POSITION_PARAMETER_k2, pos->second); } } modelParameterizer->SetAIF(this->m_AterialInputFunction); modelParameterizer->SetAIFTimeGrid(this->m_TimeGrid); modelParameterizer->SetDefaultTimeGrid(this->m_TimeGrid); generator->SetParameterizer(modelParameterizer); mitk::Image::Pointer generatedImage = generator->GetGeneratedImage(); mitk::Image::Pointer resultImage = mitk::Image::New(); if(m_Controls.NoiseCheckBox->isChecked()) { typedef itk::Image ImageType; mitk::Image::Pointer tempImage = mitk::Image::New(); tempImage->Initialize(generatedImage); mitk::ArbitraryTimeGeometry* timeGeometry = dynamic_cast (generatedImage->GetTimeGeometry()); tempImage->SetTimeGeometry(timeGeometry); ImageType::Pointer itkImage = ImageType::New(); mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(generatedImage); for(unsigned int i = 0; i< generatedImage->GetTimeSteps(); ++i) { imageTimeSelector->SetTimeNr(i); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkInputImage = imageTimeSelector->GetOutput(); mitk::CastToItkImage(mitkInputImage, itkImage ); typedef mitk::GaussianNoiseFunctor NoiseFunctorType; typedef itk::UnaryFunctorImageFilter NoiseFilterType; NoiseFilterType::Pointer noiseFilter = NoiseFilterType::New(); NoiseFunctorType noiseFunctor; noiseFunctor.SetMean(0.0); noiseFunctor.SetSigma(this->m_Sigma); noiseFilter->SetFunctor(noiseFunctor); noiseFilter->SetInput(itkImage); mitk::Image::Pointer outputImage = mitk::ImportItkImage(noiseFilter->GetOutput())->Clone(); mitk::ImageReadAccessor accessor(outputImage); tempImage->SetVolume(accessor.GetData(), i); } resultImage = tempImage->Clone(); } else { resultImage = generatedImage; } return resultImage; } PerfusionDataSimulationView::PerfusionDataSimulationView() { m_Sigma = 0; m_CNR=0; m_MaxConcentration=0; m_selectedModelFactory = nullptr; mitk::ModelFactoryBase::Pointer factory = mitk::ExtendedToftsModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::TwoCompartmentExchangeModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::NumericTwoCompartmentExchangeModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::TwoTissueCompartmentModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::OneTissueCompartmentModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); m_IsNotABinaryImagePredicate = mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true))), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); } diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/documentation/UserManual/Manual.dox b/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/documentation/UserManual/Manual.dox index 2967567cfe..ee77922748 100644 --- a/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/documentation/UserManual/Manual.dox +++ b/Plugins/org.mitk.gui.qt.photoacoustics.imageprocessing/documentation/UserManual/Manual.dox @@ -1,66 +1,66 @@ /** -\page org_mitk_gui_qt_photoacoustics_imageprocessing Photoacoustics Imageprocessing Plugin +\page org_mitk_views_photoacoustics_imageprocessing The Photoacoustics Imageprocessing Plugin \imageMacro{pai.png,"Icon of Imageprocessing",2.00} \tableofcontents \section org_mitk_gui_qt_photoacoustics_imageprocessingOverview Overview This plugin offers an interface to perform image processing on photoacoustic, as well as ultrasound images, i.e. to use beamforming and post-processing filters. For convenience, image processing can be done automatically for a whole batch of files containing PA or US data. \section org_mitk_gui_qt_photoacoustics_imageprocessingPrerequisites Prerequisites To use the much more performant openCL filters which run on the graphics card, MITK has to be able to use openCL, for which it is necessary to install the openCL implementation provided by your graphics card vendor. \section org_mitk_gui_qt_photoacoustics_imageprocessingFiltering Using the filters To perform image processing, simply load an image into MITK and select it in the Data manager. Only the selected image will be processed by the filters. \imageMacro{QmikPhotoacousticsImageProcessing_DataManager.png,"Select the image to be processed",7.62} Before performing reconstruction or using other filters those can be configured using the plugin's settings panel. \imageMacro{QmikPhotoacousticsImageProcessing_Settings.png,"The plugin's GUI",7.62} \subsection org_mitk_gui_qt_photoacoustics_imageprocessingImageDetails Image Details To create the .nrrd images necessary for the plugin from raw data, one can use e.g. pynrrd, a python package for very straightforward creation of .nrrd images. The Beamforming Filter is also able to read certain paramters, as the scan depth and the transducer pitch from the selected image. To this end, the image must have a time-axis spacing in µs and a horizontal spacing in mm. \subsection org_mitk_gui_qt_photoacoustics_imageprocessingBeamforming The Beamforming Settings For beamforming, three beamforming algorithms are available:
  • DAS (Delay And Sum)
  • DMAS (Delay Multiply And Sum)
  • sDMAS (signed Delay Multiply And Sum)
Each of those can be coupled with either spherical delay calculation or a quadratic approximation for the delays. To supress noise, one of the following apodizations can be chosen to be used when beamforming:
  • Box (No apodization)
  • Hamming
  • Von Hann
Other Standard beamforming parameters are available, which have to be chosen depending on the source image to attain a correctly reconstructed image. As mentioned above, Plugin is able to calculate the used scan depth as well as the transducer pitch from the selected image if the time-axis spacing is in microseconds, and the horizontal spacing in mm. If such a spacing is given, check the box "Auto Get Depth" to make the plugin read those values by itself. If the US source or the laser used for imaging is not located at the top of the image, an option is given to cut off pixels at the top of the image until the source. This value should be calibrated by the user to match the used hardware. If one wishes to beamform only certain slices of a given image, those can be selected by checking "select slices" and setting the "min" and "max" values accordingly, which are to be understood as closed interval boundaries. \subsection org_mitk_gui_qt_photoacoustics_imageprocessingBandpass The Bandpass Settings The bandpass uses an itk implementation of an 1D Fast Fourier Transform (FFT) to transform the image vertically, then filters the image using a Tukey window in the frequency domain and performs an inverse 1D FFT to get the filtered image. The "smoothness" of the tukey window can be chosen by using the "Tukey window alpha" parameter. The Tukey window interpolates between a Box window (alpha = 0) and a Von Hann window (alpha = 1). The filtered frequencies can be set by defining the High and Low pass frequencies. \subsection org_mitk_gui_qt_photoacoustics_imageprocessingCrop The Crop Filter Settings The crop filter cuts off parts of the image at the top and the bottom. The amount of pixels cut off can be configured using the "Cut Top" and "Cut Bottom" parameters. \subsection org_mitk_gui_qt_photoacoustics_imageprocessingBMode The BMode Filter Settings The B-mode filters available are:
  • An absolute filter
  • An envelope detection filter
If desired, the filter can also resample the image to a given spacing; to do this, check the "Do Resampling" box and set the desired spacing in mm. Afterwards a logarithmic filter can be applied, if "Add Logfilter" is checked. \subsection org_mitk_gui_qt_photoacoustics_imageprocessingBatch Batch Processing When processing large amounts of data, an option is available to automatically process multiple images by applying all filters in order to those images and saving the resulting images. In the first row of the Batch Processing Panel one can select which filters should be applied to the image; in the second row one can select whether the resulting image from the filter should be saved. After pressing the "Start Batch Processing" button, one can choose first the images to be processed, and then the folder where they will be saved. */ diff --git a/Plugins/org.mitk.gui.qt.python/documentation/UserManual/QmitkPython.dox b/Plugins/org.mitk.gui.qt.python/documentation/UserManual/QmitkPython.dox index 114be980c4..73d1ecb6af 100644 --- a/Plugins/org.mitk.gui.qt.python/documentation/UserManual/QmitkPython.dox +++ b/Plugins/org.mitk.gui.qt.python/documentation/UserManual/QmitkPython.dox @@ -1,33 +1,33 @@ /** -\page org_mitk_gui_qt_python The Python Plugin +\page org_mitk_views_python The Python Plugin Available sections: - - \ref org_mitk_gui_qt_pythonOverview - - \ref org_mitk_gui_qt_pythonUsage - - \ref org_mitk_gui_qt_PythonConsole - - \ref org_mitk_gui_qt_PythonSnippets + - \ref org_mitk_views_pythonOverview + - \ref org_mitk_views_pythonUsage + - \ref org_mitk_views_pythonConsole + - \ref org_mitk_views_pythonSnippets -\section org_mitk_gui_qt_pythonOverview Overview +\section org_mitk_views_pythonOverview Overview The Python view provides the graphical front end to run Python code through the mitkPython module. Furthermore the SimpleITK/VTK/OpenCV Python wrapping can be used. Images and surfaces in the DataManager can be transferred via a drag & drop mechanism into the MITK Python Console. -\section org_mitk_gui_qt_pythonUsage Transfer data +\section org_mitk_views_pythonUsage Transfer data Images and surfaces can be tranferred from the data manger into the python console. To transfer an image or surface simply drag it from the data manager into the Variable Stack view, as shown in Figure. A new entry will appear in the Variable Stack, as soon as the data is transferred. As soon as the -entry is available the object can be accessed and modified in the python console. Three dimensional +entry is available the object can be accessed and modified in the python console. Three dimensional images will be copied in-memory to python via numpy and a SimpleITK image object is created with the same properties. When a two dimensional image is transferred the user can choose to transfer it as an OpenCV image object. Surfaces are fully memory mapped as a vtkPolyData object. To transfer an image or surface from the python runtime to the data manager just double click on the corresponding entry in the Variable Stack View. \imageMacro{MitkPythonPluginView.png,"Screenshot of the MITK Python Plugin",6} -\section org_mitk_gui_qt_PythonConsole Console +\section org_mitk_views_pythonConsole Console The Python console can be used for interactive programming. All items in the data storage can be accessed in the python console. The console can also be used to load python scripts and run them. -\section org_mitk_gui_qt_PythonSnippets Snippets +\section org_mitk_views_pythonSnippets Snippets The python plugin contains some code snippets of SimpleITK/VTK/OpenCV that can be run in the python console. Snippets can be modified and saved by the user. */ diff --git a/Plugins/org.mitk.gui.qt.radiomics/documentation/UserManual/QmitkPhenotypingPortalPage.dox b/Plugins/org.mitk.gui.qt.radiomics/documentation/UserManual/QmitkPhenotypingPortalPage.dox index aebb075d1b..550c6d9daf 100644 --- a/Plugins/org.mitk.gui.qt.radiomics/documentation/UserManual/QmitkPhenotypingPortalPage.dox +++ b/Plugins/org.mitk.gui.qt.radiomics/documentation/UserManual/QmitkPhenotypingPortalPage.dox @@ -1,41 +1,40 @@ /** \page org_mitk_gui_qt_mitkphenotyping The Phenotyping \tableofcontents -MITK Phenotyping is a selection of algorithms that can be used to extract image-based phenotypes, for example using a radiomics approach. The software is part of the research of the Division of Medical Image Computing of the German Cancer Research Center (DKFZ). MITK Phenotyping is not intended to be a single application, it is rather a collection of the necessary plugins within the offical MITK releases. +MITK Phenotyping is a selection of algorithms that can be used to extract image-based phenotypes, for example using a radiomics approach. The software is part of the research of the Division of Medical Image Computing of the German Cancer Research Center (DKFZ). MITK Phenotyping is not intended to be a single application, it is rather a collection of the necessary plugins within the offical MITK releases. -The functionality of MITK Phenotyping can be accessed in different ways: Using the graphical interface using the Plugins listed below, using command line applications, or using one of the programming interfaces. +The functionality of MITK Phenotyping can be accessed in different ways: Using the graphical interface using the Plugins listed below, using command line applications, or using one of the programming interfaces. -\section org_mitk_gui_qt_mitkphenotyping_Tutorials Tutorials -\li \subpage org_mitk_views_radiomicstutorial_gui_portal A tutorial on how to use the grapical interface of MITK Phenotying +\section org_mitk_gui_qt_mitkphenotyping_Tutorials Tutorials +\li \subpage org_mitk_views_radiomicstutorial_gui_portal A tutorial on how to use the grapical interface of MITK Phenotying \section org_mitk_gui_qt_mitkphenotyping_Views Views \subsection sub2 Specific Views: -Views that were developed with the main focus on Radiomics. They still might be used in other use-cases as well: +Views that were developed with the main focus on Radiomics. They still might be used in other use-cases as well: \li \subpage org_mitk_views_radiomicstransformationview : Image transformations like Resampling, Laplacian of Gaussian, and Wavelet Transformations \li \subpage org_mitk_views_radiomicsmaskprocessingview : Processing and Cleaning of Masks \li \subpage org_mitk_views_radiomicsarithmetricview : Processing images using mathematical operations \li \subpage org_mitk_views_radiomicsstatisticview : Calculate Radiomics Features \subsection sub1 Non-Specific Views: -This section contains views that are included within MITK Phenotyping, but were developed with a broader application in mind. +This section contains views that are included within MITK Phenotyping, but were developed with a broader application in mind. \li \ref org_mitk_views_basicimageprocessing : Deprecated plugin for performing different image-related tasks like subtraction, mutliplaction, filtering etc. \li \ref org_mitk_views_matchpoint_algorithm_browser : Selection of MatchPoint (Registration) Algorithm \li \ref org_mitk_views_matchpoint_algorithm_control : Configuring and Controlling MatchPoint (Registration) Algorithm \li \ref org_mitk_views_matchpoint_evaluator : Evaluate the Registration performance using MatchPoint \li \ref org_mitk_views_matchpoint_manipulator : Adapt a registration calculated using MatchPoint \li \ref org_mitk_views_matchpoint_mapper : Apply a MatchPoint Registration to a specific image \li \ref org_mitk_views_matchpoint_visualizer : Visualize a Registration obtained with MatchPoint -\li \ref org_mitk_views_matchpoint_algorithm_batch : Running MatchPoint over multiple images (BatchMode) \li \ref org_mitk_views_multilabelsegmentation : Create and editing of Multilabel-Segmentations. \li \ref org_mitk_views_segmentation : Create simple segmentations -\li \ref org_mitk_views_segmentationutilities : Utilities for the processing of simple segmentations. +\li \ref org_mitk_views_segmentationutilities : Utilities for the processing of simple segmentations. \section radiomics_miniapps MiniApps (Command line Tools) \li \subpage MiniAppExplainPage Explanation of the Command Line App concept in MITK \li \subpage mitkBasicImageProcessingMiniAppsPortalPage : List of common preprocessing MiniApps \li \subpage mitkClassificationMiniAppsPortalPage : (Incomplete) list of MITK Classification MiniApps */ diff --git a/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/QmitkRemeshing.dox b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/QmitkRemeshing.dox index 4f609da6ec..884f46b296 100644 --- a/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/QmitkRemeshing.dox +++ b/Plugins/org.mitk.gui.qt.remeshing/documentation/UserManual/QmitkRemeshing.dox @@ -1,19 +1,19 @@ /** -\page org_mitk_gui_qt_remeshing The Remeshing +\page org_mitk_views_remeshing The Remeshing \imageMacro{RemeshingIcon.png,"Icon of the Remeshing Plugin.",2.00} \tableofcontents -\section org_mitk_gui_qt_remeshingOverview Overview +\section org_mitk_views_remeshingOverview Overview The Remeshing View allows you to remesh surfaces. If done right, remeshing can dramatically increase the quality of your surface mesh. However, you might lose precision if you reduce the vertex density of your surface mesh too strong. Even when you preserve the detail of your mesh, there might be a tiny distance between your original surface and the remeshed surface. Hence, be careful when using remeshed surfaces for evaluation purposes and always keep the original versions. -\section org_mitk_gui_qt_remeshingUsage Usage +\section org_mitk_views_remeshingUsage Usage The Remeshing View provides details on its parameters in its user interface. When in doubt, start with the default parameters and iterate to your desired result by reducing the density parameter. */ diff --git a/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.cpp b/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.cpp index 8a1d0b8716..9e230b5dfc 100644 --- a/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.cpp +++ b/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.cpp @@ -1,161 +1,163 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkRemeshingView.h" #include #include #include #include #include #include #include #include #include #include #include +const std::string QmitkRemeshingView::VIEW_ID = "org.mitk.views.remeshing"; + QmitkRemeshingView::QmitkRemeshingView() { } QmitkRemeshingView::~QmitkRemeshingView() { } void QmitkRemeshingView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.remeshPushButton->setIcon(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/Remeshing/RemeshingIcon.svg"))); m_Controls.selectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls.selectionWidget->SetSelectionIsOptional(true); m_Controls.selectionWidget->SetEmptyInfo(QStringLiteral("Select a surface")); m_Controls.selectionWidget->SetAutoSelectNewNodes(true); m_Controls.selectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), mitk::NodePredicateNot::New(mitk::NodePredicateOr::New( mitk::NodePredicateProperty::New("helper object"), mitk::NodePredicateProperty::New("hidden object"))))); connect(m_Controls.selectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkRemeshingView::OnSelectedSurfaceChanged); connect(m_Controls.densitySlider, SIGNAL(valueChanged(int)), this, SLOT(OnDensityChanged(int))); connect(m_Controls.densitySpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnDensityChanged(int))); connect(m_Controls.remeshPushButton, SIGNAL(clicked()), this, SLOT(OnRemeshButtonClicked())); this->OnSelectedSurfaceChanged(m_Controls.selectionWidget->GetSelectedNodes()); } void QmitkRemeshingView::OnSelectedSurfaceChanged(const QmitkSingleNodeSelectionWidget::NodeList& nodes) { if (!nodes.empty() && nodes.front().IsNotNull()) { m_MaxNumberOfVertices = static_cast(static_cast(nodes.front()->GetData())->GetVtkPolyData()->GetNumberOfPoints()); this->EnableWidgets(true); } else { m_MaxNumberOfVertices = 0; this->EnableWidgets(false); } } void QmitkRemeshingView::OnDensityChanged(int density) { if (density != m_Controls.densitySlider->value()) m_Controls.densitySlider->setValue(density); if (density != m_Controls.densitySpinBox->value()) m_Controls.densitySpinBox->setValue(density); } void QmitkRemeshingView::OnRemeshButtonClicked() { mitk::DataNode::Pointer selectedNode = m_Controls.selectionWidget->GetSelectedNode(); mitk::Surface::ConstPointer surface = static_cast(selectedNode->GetData()); int density = m_Controls.densitySpinBox->value(); int numVertices = std::max(100, static_cast(m_MaxNumberOfVertices * (density * 0.01))); double gradation = m_Controls.remeshingComboBox->currentText() == QStringLiteral("Adaptive") ? 1.0 : 0.0; const QString quality = m_Controls.qualityComboBox->currentText(); int subsampling; if (QStringLiteral("High (slow)") == quality) { subsampling = 50; } else if (QStringLiteral("Maximum (very slow)") == quality) { subsampling = 500; } else // The default is "Medium (fast)". { subsampling = 10; } bool boundaryFixing = m_Controls.preserveEdgesCheckBox->isChecked(); mitk::ACVD::RemeshFilter::Pointer remesher = mitk::ACVD::RemeshFilter::New(); remesher->SetInput(surface); remesher->SetTimeStep(0); remesher->SetNumVertices(numVertices); remesher->SetGradation(gradation); remesher->SetSubsampling(subsampling); remesher->SetEdgeSplitting(0.0); remesher->SetOptimizationLevel(1.0); remesher->SetForceManifold(false); remesher->SetBoundaryFixing(boundaryFixing); try { remesher->Update(); } catch(const mitk::Exception& exception) { MITK_ERROR << exception.GetDescription(); return; } mitk::Surface::Pointer remeshedSurface = remesher->GetOutput(); mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetName(QString("%1 (%2%)").arg(selectedNode->GetName().c_str()).arg(density).toStdString()); newNode->SetProperty("material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME)); newNode->SetData(remeshedSurface); this->GetDataStorage()->Add(newNode, selectedNode); } void QmitkRemeshingView::EnableWidgets(bool enable) { m_Controls.densitySlider->setEnabled(enable); m_Controls.densitySpinBox->setEnabled(enable); m_Controls.remeshingComboBox->setEnabled(enable); m_Controls.qualityComboBox->setEnabled(enable); m_Controls.preserveEdgesCheckBox->setEnabled(enable); m_Controls.remeshPushButton->setEnabled(enable); m_Controls.explanationLabel->setVisible(enable); } void QmitkRemeshingView::SetFocus() { m_Controls.selectionWidget->setFocus(); } diff --git a/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.h b/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.h index 66d3728c8c..33b384f4d6 100644 --- a/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.h +++ b/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.h @@ -1,43 +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 QmitkRemeshingView_h #define QmitkRemeshingView_h #include #include #include class QmitkRemeshingView : public QmitkAbstractView { Q_OBJECT public: + static const std::string VIEW_ID; + QmitkRemeshingView(); ~QmitkRemeshingView() override; void CreateQtPartControl(QWidget* parent) override; void SetFocus() override; private slots: void OnSelectedSurfaceChanged(const QmitkSingleNodeSelectionWidget::NodeList& nodes); void OnDensityChanged(int numVertices); void OnRemeshButtonClicked(); private: void EnableWidgets(bool enable); Ui::QmitkRemeshingViewControls m_Controls; int m_MaxNumberOfVertices; }; #endif 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 835a75a74b..771ab561da 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox +++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox @@ -1,300 +1,299 @@ /** \page org_mitk_views_segmentation The Segmentation View \imageMacro{segmentation.svg,"Icon of the segmentation view",2.00} Some of the features described below are closed source additions to the open source toolkit MITK and are not available in every application. \tableofcontents \section org_mitk_views_segmentationUserManualOverview Overview -Segmentation is the act of partitioning an image into subsets by either manual or automated delineation to create i.e. a distinction between foreground and background. +Segmentation is the act of partitioning an image into subsets by either manual or automated delineation to create i.e. a distinction between foreground and background. The MITK segmentation plugin allows you to create segmentations of anatomical and pathological structures in medical images. The plugin consists of a number of views:
  • Segmentation View: Manual and (semi-)automatic segmentation
  • \subpage org_mitk_views_segmentationutilities : Segmentation post-processing -
  • \subpage org_mitk_views_deformableclippingplane : Clipping of existing segmentations using a resection plane
In this documentation, the features and usage of the segmentation view will be described. For an introduction to the segmentation utilities and clipping plane views, please be referred to the respective documentation pages. \imageMacro{QmitkSegmentationPlugin_Overview.png,"Segmentation plugin overview", 16.00} \section org_mitk_views_segmentationPreferences Preferences The segmentation plugin offers a number of preferences which can be set via the MITK Workbench application preferences: \imageMacro{QmitkSegmentationPreferences.png,"Segmentation preferences", 10.00}
  • Slim view: Allows you to show or hide the tool button description of the segmentation view
  • 2D display: Specify whether the segmentation is drawn as outline or as a transparent overlay
  • 3D display: Activate 3D volume rendering for your segmentation
  • Data node selection mode: If activated the segmentation image is automatically chosen from the data manager selection.
  • Smoothed surface creation: Set certain smoothing parameters for surface creation
\section org_mitk_views_segmentationUserManualTechnical Technical Issues The segmentation plugin makes a number of assumptions:
  • Images must be 2D, 3D, or 3D+t.
  • Images must be single-values, i.e. CT, MRI or "normal" ultrasound. Images from color doppler or photographic (RGB) images are only partially supported (please be aware that some tools might not be compatible with this image type).
  • Segmentations are handled as binary images of the same extent as the original image.
\section org_mitk_views_segmentationUserManualImageSelection Data Selection & Creating New Segmentations -To select a reference image for the segmentation, click on the Patient Image selection widget and choose a suitable image from the selection available in the data manager. +To select a reference image for the segmentation, click on the Patient Image selection widget and choose a suitable image from the selection available in the data manager. By default the auto selection mode is enabled (see \ref org_mitk_views_segmentationPreferences).\n Once a patient image is selected, a new segmentation can be created on this reference image by clicking the New... button to the right of the Segmentation selection widget. An input field will appear which allows you to set the name and display color of the segmentation. Notice that the input field suggests names once you start typing and that it also suggests colors for known organ names. Once generated the segmentation will be added with "binary mask" icon to the data manager as sub-node of the reference image. This item is automatically selected for you, allowing you to start editing the new segmentation right away. \subsection org_mitk_views_segmentationUserManualManualKringeling2 Selecting Segmentations for Editing Alternatively to creating a new segmentation, an existing one can be edited as well. As you might have segmented multiple structures within a single image, the application needs to know which of them to use for editing. -For that you click the segmentation selection widget and a selection field will open, containing all suitable segmentations for the parent dataset available in the data manager. +For that you click the segmentation selection widget and a selection field will open, containing all suitable segmentations for the parent dataset available in the data manager. \section org_mitk_views_segmentationUserManualToolOverview Segmentation Tool Overview MITK offers a comprehensive set of slice-based 2D and (semi-)automated 3D segmentation tools. The manual 2D tools require some user interaction and can only be applied to a single image slice whereas the 3D tools operate on the whole image. The 3D tools usually only require a small amount of user interaction, i.e. placing seed points or setting/adjusting parameters. You can switch between the different toolsets by selecting the 2D or 3D tab in the segmentation view. \imageMacro{QmitkSegmentation_ToolOverview.png,"An overview of the existing 2D and 3D tools in MITK.",5.50} \section org_mitk_views_segmentationUserManualManualKringeling 2D Segmentation Tools With 2D manual contouring you define which voxels are part of the segmentation and which ones are not. This allows you to create segmentations of any structures of interest in an image. You can also use manual contouring to correct segmentations that result from sub-optimal automatic methods. The drawback of manual contouring is that you might need to define contours on many 2D slices. However, this is mitigated by the interpolation feature, which will make suggestions for a segmentation. \subsection org_mitk_views_segmentationUserManualManualKringeling3 Selecting Editing Tools To start using one of the editing tools, click its button from the displayed toolbox. The selected editing tool will be active and its corresponding button will stay pressed until you click the button again. Selecting a different tool also deactivates the previous one.\n If you have to delineate a lot of images, shortcuts to switch between tools becomes convenient. For that, just hit the first letter of each tool to activate it (A for Add, S for Subtract, etc.). \subsection org_mitk_views_segmentationUserManualManualKringeling4 Using Editing Tools All of the editing tools work by the same principle: you use the mouse (left button) to click anywhere in a 2D window (any of the orientations axial, sagittal, or coronal), move the mouse while holding the mouse button and release to finish the editing action. Multi-step undo and redo is fully supported by all editing tools. Use the application-wide undo button in the toolbar to revert erroneous %actions. Remark: If you are familiar with the MITK Workbench, you know that clicking and moving the mouse in any of the 2D render windows will move around the crosshair that defines what part of the image is displayed. This behavior is disabled whilst any of the manual segmentation tools are active- otherwise you might have a hard time concentrating on the contour you are drawing. \subsection org_mitk_views_segmentationUserManualAddSubtractTools Add and Subtract Tools \imageMacro{QmitkSegmentation_IMGIconAddSubtract.png,"Add and Subtract Tools",7.70} -Use the left mouse button to draw a closed contour. When releasing the mouse button, the contour will be added (Add tool) to or removed (Subtract tool) from the current segmentation. +Use the left mouse button to draw a closed contour. When releasing the mouse button, the contour will be added (Add tool) to or removed (Subtract tool) from the current segmentation. Adding and subtracting voxels can be iteratively repeated for the same segmentation. Holding CTRL / CMD while drawing will invert the current tool's behavior (i.e. instead of adding voxels, they will be subtracted). \subsection org_mitk_views_segmentationUserManualPaintWipeTools Paint and Wipe Tools \imageMacro{QmitkSegmentation_IMGIconPaintWipe.png,"Paint and Wipe Tools",7.68} Use the Size slider to change the radius of the round paintbrush tool. Move the mouse in any 2D window and press the left button to draw or erase pixels. Holding CTRL / CMD while drawing will invert the current tool's behavior (i.e. instead of painting voxels, they will be wiped). \subsection org_mitk_views_segmentationUserManualRegionGrowingTool Region Growing Tool \imageMacro{QmitkSegmentation_IMGIconRegionGrowing.png,"Region Growing Tool",3.81} -Click at one point in a 2D slice widget to add an image region to the segmentation with the region growing tool. +Click at one point in a 2D slice widget to add an image region to the segmentation with the region growing tool. Region Growing selects all pixels around the mouse cursor that have a similar gray value as the pixel below the mouse cursor. This enables you to quickly create segmentations of structures that have a good contrast to surrounding tissue. The tool operates based on the current level window, so changing the level window to optimize the contrast for the ROI is encouraged. Moving the mouse up/down is different from left/right: Moving up the cursor while holding the left mouse button widens the range for the included grey values; moving it down narrows it. Moving the mouse left and right will shift the range. The tool will select more or less pixels, corresponding to the changing gray value range. \if THISISNOTIMPLEMENTEDATTHEMOMENT A common issue with region growing is the so called "leakage" which happens when the structure of interest is connected to other pixels, of similar gray values, through a narrow "bridge" at the border of the structure. The Region Growing tool comes with a "leakage detection/removal" feature. If leakage happens, you can left-click into the leakage region and the tool will try to automatically remove this region (see illustration below). \imageMacro{QmitkSegmentation_IMGLeakage.png,"Leakage correction feature of the Region Growing tool",11.28} \endif \subsection org_mitk_views_segmentationUserManualCorrectionTool Correction Tool
\imageMacro{QmitkSegmentation_IMGIconCorrection.png,"Correction Tool",3.77} You do not have to draw a closed contour to use the Correction tool and do not need to switch between the Add and Substract tool to perform small corrective changes. The following figure shows the usage of this tool:
  • a) If the user draws a line which %starts and ends outside the segmenation a part of it is cut off.
  • b) If the line is drawn fully inside the segmentation the marked region is added to the segmentation.
\imageMacro{QmitkSegmentation_IMGCorrectionActions.png,"Usage of the Correction tool.",13.50}
\subsection org_mitk_views_segmentationUserManualFillTool Fill Tool \imageMacro{QmitkSegmentation_IMGIconFill.png,"Fill Tool",3.81} Left-click inside a segmentation with holes to completely fill all holes. Left-click inside a hole to fill only this specific hole. \subsection org_mitk_views_segmentationUserManualEraseTool Erase Tool \imageMacro{QmitkSegmentation_IMGIconErase.png,"Erase Tool",3.79} This tool removes a connected part of pixels that form a segmentation. You may use it to remove single segmentations (left-click on specific segmentation) or to clear a whole slice at once (left-click outside a segmentation). \subsection org_mitk_views_segmentationUserManualLiveWireTool Live Wire Tool \imageMacro{QmitkSegmentation_IMGIconLiveWire.png,"Live Wire Tool",3.01} The Live Wire Tool acts as a magnetic lasso with a contour snapping to edges of objects. \imageMacro{QmitkSegmentation_IMGLiveWireUsage.PNG,"Steps for using the Live Wire Tool",16.00}
  • (1) To start the tool you have to double-click near the edge of the object you want to segment. The initial anchor point will snap to the edge within a 3x3 region.
  • (2) Move the mouse. You don't have trace the edge of the object. The contour will automatically snap to it.
  • (3) To fix a segment you can set anchor points by single left mouse button click.
  • (4) Go on with moving the mouse and setting anchor points.
  • (5) To close the contour double-click on the initial anchor point.
  • (6) After closing, the contour can be edited by moving, inserting and deleting anchor points.
The contour will be transferred to its binary image representation by deactivating the tool. \subsection org_mitk_views_segmentationUserManual2DFastMarchingTool 2D Fast Marching Tool \imageMacro{QmitkSegmentation_IMG2DFastMarchingUsage.png,"2D Fast Marching Tool",3.01} Provides a fast marching based 2D interaction segmentation tool. You start with setting seed points in an image slice. Via several sliders you can adapt parameters and see the fast marching result instantly. \subsection org_mitk_views_segmentationUserManualManualKringeling5 2D and 3D Interpolation Creating segmentations using 2D manual contouring for large image volumes may be very time-consuming, because structures of interest may cover a large range of slices. The segmentation view offers two helpful features to mitigate this drawback:
  • 2D Interpolation
  • 3D Interpolation
The 2D Interpolation creates suggestions for a segmentation whenever you have a slice that
  • has got neighboring slices with segmentations (these do not need to be direct neighbors but could also be a couple of slices away) AND
  • 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.
\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 Confirm for single slice button below the toolbox. You may also review the interpolations visually and then accept all of them at once by selecting Confirm for all slices. The 3D interpolation 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. +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 Confirm-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 position nodes in the data manager which are stored as sub-nodes of the selected segmentation. +You can navigate between the drawn contours by clicking on the corresponding position 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 Show Position Nodes checkbox and these nodes will be hidden. If you want to delete a drawn contour we recommend to use the Erase-Tool since Redo/Undo is not yet working for 3D interpolation. -The current state of the 3D interpolation can be saved across application restart. For that, just click on save project during the interpolation is active. +The current state of the 3D interpolation can be saved across application restart. For that, just click on save project during the interpolation is active. After restarting the application and load your project you can click on "Reinit Interpolation" within the 3D interpolation GUI area. \section org_mitk_views_segmentationUserManual3DSegmentationTools 3D Segmentation Tools The 3D tools operate on the whole image and require usually a small amount of interaction like placing seed-points or specifying certain parameters. All 3D tools provide an immediate segmentation feedback, which is displayed as a transparent green overlay. For accepting a preview you have to press the Confirm button of the selected tool. The following 3D tools are available: \subsection org_mitk_views_segmentationUserManual3DThresholdTool 3D Threshold Tool The thresholding tool simply applies a 3D threshold to the patient image. All pixels with values equal or above the selected threshold are labeled as part of the segmentation. You can change the threshold by either moving the slider of setting a certain value in the spinbox. \imageMacro{QmitkSegmentation_3DThresholdTool.png,"3D Threshold tool",10.00} \subsection org_mitk_views_segmentationUserManual3DULTool 3D Upper/Lower Threshold Tool The Upper/Lower Thresholding tool works similar to the simple 3D threshold tool but allows you to define an upper and lower threshold. All pixels with values within this threshold interval will be labeled as part of the segmentation. \imageMacro{QmitkSegmentation_3DULThresholdTool.png,"3D Upper/Lower Threshold tool",10.00} \subsection org_mitk_views_segmentationUserManual3DOtsuTool 3D Otsu Tool The 3D Otsu tool provides a more sophisticated thresholding algorithm. It allows you to define a number of regions. Based on the image histogram the pixels will then be divided into different regions. The more regions you define the longer the calculation will take. You can select afterwards which of these regions you want to confirm as segmentation. \imageMacro{QmitkSegmentation_3DOtsuTool.png,"3D Otsu tool",10.00} \subsection org_mitk_views_segmentationUserManual3DFMTool 3D Fast Marching Tool The 3D Fast Marching tool works similar to the 2D pendant but on the whole image. Depending on your image size the calculation might take some time. You can interactively set the parameters of the algorithm via the GUI. The resulting segmentation will be automatically updated. \imageMacro{QmitkSegmentation_3DFMTool.png,"3D Fast Marching tool",10.00} \subsection org_mitk_views_segmentationUserManual3DRGTool 3D Region Growing Tool The 3D Region Growing tool works similar to the 2D pendant. At the beginning you have to place a seedpoint and define a threshold interval. If you press Run Segmentation a preview is calculated. By moving the Adapt region growing slider you can interactively adapt the segmentation result. \imageMacro{QmitkSegmentation_3DRGTool.png,"3D Region Growing tool",10.00} \subsection org_mitk_views_segmentationUserManual3DWatershedTool 3D Watershed Tool This tool provides a watershed based segmentation algorithm. For a detailed explanation of the parameters level and threshold, please be referred to https://itk.org/Doxygen/html/classitk_1_1WatershedImageFilter.html . Remark: The tool is (due to its implementation) faster if you use lower levels. So in doubt you should start with high levels (supposed to undersegment) and then lower the levels until you are happy. \imageMacro{QmitkSegmentation_3DWatershedTool.png,"3D Watershed tool",10.00} \subsection org_mitk_views_segmentationUserManualPickingTool Picking Tool The Picking tool allows you to select islands within your segmentation. This is especially useful if e.g. a thresholding provided you with several areas within your image but you are just interested in one special region. \imageMacro{QmitkSegmentation_PickingTool.png,"Picking tool",10.00} \section org_mitk_views_segmentationUserManualPostprocessing Additional things you can do with segmentations Segmentations are never an end in themselves. Consequently, the segmentation view adds a couple of "post-processing" actions, accessible through the context-menu of the data manager. \imageMacro{QmitkSegmentation_IMGDataManagerContextMenu.png,"Context menu items for segmentations.",10.58}
  • Create polygon %model 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).
  • Create smoothed polygon %model 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.
  • Autocrop 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.
\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.
  • Dynamic masks can be created on each time frame individually.
  • Static masks will be defined on one time frame and will be the same for all other time frames.
In general, segmentation is applied on the time frame that is selected when execution is performed. If you alter the time frame, the segmentation preview is adapted. \section org_mitk_views_segmentationUserManualTechnicalDetail Technical Information for Developers For technical specifications see \subpage QmitkSegmentationTechnicalPage and for information on the extensions of the tools system \subpage toolextensions . */ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox index 65cf6d1b77..2355581d0b 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox +++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox @@ -1,62 +1,80 @@ /** \page org_mitk_views_segmentationutilities The Segmentation Utilities View \imageMacro{segmentation_utilities.svg,"Icon of the Segmentation Utilities View",5.00} -\imageMacro{QmitkSegmentationUtilities_Overview.png,"The Segmentation Utilities view",16.00} +\imageMacro{QmitkSegmentationUtilities_Overview.png,"The Segmentation Utilities View",16.00} \tableofcontents \section org_mitk_views_segmentationUtilitiesManualOverview Overview -The Segmentation Utilities View allows you to postprocess existing segmentations +The Segmentation Utilities View allows to postprocess existing segmentations. Currently five different operations exist: +
    +
  • \ref org_mitk_views_segmentationUtilitiesBooleanOperations +
  • \ref org_mitk_views_segmentationUtilitiesContourToImage +
  • \ref org_mitk_views_segmentationUtilitiesImageMasking +
  • \ref org_mitk_views_segmentationUtilitiesMorphologicalOperations +
  • \ref org_mitk_views_segmentationUtilitiesSurfaceToImage +
-\section org_mitk_views_segmentationUtilitiesImageSelection Image Selection +\section org_mitk_views_segmentationUtilitiesDataSelection Data Selection -Usually the data selection in the Segmentation Utilities View is done via drop down box which let you just select the appropriate data. +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 +\section org_mitk_views_segmentationUtilitiesBooleanOperations Boolean operations -Boolean operations allow you to create the +Boolean operations allows to perform the following fundamental operations on two segmentations:
    -
  • Union: Combines two existing segmentations -
  • Intersection: Keeps just the overlapping areas of two existing segmentations -
  • Difference: Subtracts one segmentation from the other +
  • Difference: Subtracts the second segmentation from the first segmentation. +
  • Intersection: Extracts the overlapping areas of the two selected segmentations. +
  • Union: Combines the two existing segmentations.
-of two segmentations. The selected segmentations must have the same geometry (size, spacing, ...) +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_segmentationUtilitiesContourToImage Contour to image + +Contour to image allows to create a segmentation out of a given contour-model. +The operation requires a contour model set and a reference image. +The created segmentation image will have the same geometrical properties like the reference image (dimension, size and Geometry3D). -\imageMacro{QmitkSegmentationUtilities_IMGBooleanOperations.png,"Boolean operations of the SegmentationUtlitiesView",6.00} +\imageMacro{QmitkSegmentationUtilities_ContourToImage.png,"Contour to image",6.00} \section org_mitk_views_segmentationUtilitiesImageMasking Image masking -You can mask your grey value image with either an existing segmentation or a surface. The result will be an image containing only -the pixels that are cover by the respective mask. +Image masking allows to mask an image with either an existing segmentation or a surface. +The operation requires an image and a binary image mask or a surface. +The result will be an image containing only the pixels that are covered by the respective mask. -\imageMacro{QmitkSegmentationUtilities_IMGImageMasking.png,"Image masking widget of the Segmentation Utilities View",6.00} +\imageMacro{QmitkSegmentationUtilities_ImageMasking.png,"Image masking",6.00} -\section org_mitk_views_segmentationUtilitiesMorphologicalOperators Morphological Operators +\section org_mitk_views_segmentationUtilitiesMorphologicalOperations Morphological operations -The morphological operators are applied to a single segmentation image. Based on a given structuring element the underlying segmentation will be modfied. -MITK provides a ball and a cross as structuring elements. The follow operators are at your disposal: +Morphological operations are applied to a single segmentation image. Based on a given structuring element the underlying segmentation will be modfied. +The plugin provides a ball and a cross as structuring elements. The follow operations are available:
    -
  • Dilation: Each labeled pixel within the segmentation will be dilated based on the selected structuring element -
  • Erosion: Each labeled pixel within the segmentation will be eroded based on the selected structuring element -
  • Opening: A dilation followed by an erosion, used for smoothing edges or eliminating small objects -
  • Closing An erosion followed by an dilation, used for filling small holes -
  • Fill Holes Fills bigger holes within a segmentation +
  • Dilation: Each labeled pixel within the segmentation will be dilated based on the selected structuring element. +
  • Erosion: Each labeled pixel within the segmentation will be eroded based on the selected structuring element. +
  • Opening: A dilation followed by an erosion, used for smoothing edges or eliminating small objects. +
  • Closing: An erosion followed by an dilation, used for filling small holes. +
  • Fill Holes: Fills bigger holes within a segmentation.
-\imageMacro{QmitkSegmentationUtilities_IMGMorphologicalOperators.png,"Morphological operators widget of the Segmentation Utilities View",6.00} +\imageMacro{QmitkSegmentationUtilities_MorphologicalOperations.png,"Morphological operations",6.00} -\section org_mitk_views_segmentationUtilitiesSurfaceToImage Surface to binary image +\section org_mitk_views_segmentationUtilitiesSurfaceToImage Surface to image -This widget lets you fill you meshes into an empty binary image. It is required that a reference grey value image is present. -The created binary image will have the same geometrical properties like the reference image +Surface to image allows to create a segmentation out of a given surface. +The operation requires a surface and a reference image. +The created segmentation image will have the same geometrical properties like the reference image (dimension, size and Geometry3D). -\imageMacro{QmitkSegmentationUtilities_IMGSurfaceToImage.png,"Surface to image widget of the Segmentation Utilities View",6.00} +\imageMacro{QmitkSegmentationUtilities_SurfaceToImage.png,"Surface to image",6.00} **/ 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 new file mode 100644 index 0000000000..279d519f73 Binary files /dev/null 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_ContourToImage.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_ContourToImage.png new file mode 100644 index 0000000000..14c4c541e2 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_ContourToImage.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_IMGBooleanOperations.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_IMGBooleanOperations.png deleted file mode 100644 index e3e5692d0c..0000000000 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_IMGBooleanOperations.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_IMGImageMasking.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_IMGImageMasking.png deleted file mode 100644 index 0f5ed11b37..0000000000 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_IMGImageMasking.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_IMGMorphologicalOperators.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_IMGMorphologicalOperators.png deleted file mode 100644 index 87aae2af13..0000000000 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_IMGMorphologicalOperators.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_IMGSurfaceToImage.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_IMGSurfaceToImage.png deleted file mode 100644 index 0a6719b7d1..0000000000 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_IMGSurfaceToImage.png and /dev/null 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 new file mode 100644 index 0000000000..ae33391505 Binary files /dev/null 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 new file mode 100644 index 0000000000..21791ca18e Binary files /dev/null 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 f7137585b7..418deb4520 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/QmitkSegmentationUtilities_SurfaceToImage.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_SurfaceToImage.png new file mode 100644 index 0000000000..72d48655fb Binary files /dev/null and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_SurfaceToImage.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/SegmentationUtilities.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/SegmentationUtilities.png deleted file mode 100644 index afd45175ff..0000000000 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/SegmentationUtilities.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/files.cmake b/Plugins/org.mitk.gui.qt.segmentation/files.cmake index ae0e49a418..dd452ef63f 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/files.cmake +++ b/Plugins/org.mitk.gui.qt.segmentation/files.cmake @@ -1,81 +1,74 @@ set(SRC_CPP_FILES QmitkSegmentationPreferencePage.cpp ) set(INTERNAL_CPP_FILES mitkPluginActivator.cpp QmitkSegmentationView.cpp QmitkThresholdAction.cpp QmitkCreatePolygonModelAction.cpp #QmitkStatisticsAction.cpp QmitkAutocropAction.cpp QmitkAutocropLabelSetImageAction.cpp - QmitkDeformableClippingPlaneView.cpp Common/QmitkDataSelectionWidget.cpp SegmentationUtilities/QmitkSegmentationUtilitiesView.cpp SegmentationUtilities/QmitkSegmentationUtilityWidget.cpp SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.cpp SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidget.cpp SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.cpp SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp ) set(UI_FILES src/internal/QmitkSegmentationControls.ui - src/internal/QmitkDeformableClippingPlaneViewControls.ui src/internal/Common/QmitkDataSelectionWidgetControls.ui src/internal/SegmentationUtilities/QmitkSegmentationUtilitiesViewControls.ui src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidgetControls.ui src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui ) set(MOC_H_FILES src/QmitkSegmentationPreferencePage.h src/internal/mitkPluginActivator.h src/internal/QmitkSegmentationView.h src/internal/QmitkThresholdAction.h src/internal/QmitkCreatePolygonModelAction.h #src/internal/QmitkStatisticsAction.h src/internal/QmitkAutocropAction.h src/internal/QmitkAutocropLabelSetImageAction.h - src/internal/QmitkDeformableClippingPlaneView.h src/internal/Common/QmitkDataSelectionWidget.h src/internal/SegmentationUtilities/QmitkSegmentationUtilitiesView.h src/internal/SegmentationUtilities/QmitkSegmentationUtilityWidget.h src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.h src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidget.h src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.h src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h ) set(CACHED_RESOURCE_FILES resources/segmentation.svg - resources/deformablePlane.png - resources/clipping_plane_translate_48x48.png - resources/clipping_plane_rotate48x48.png - resources/clipping_plane_deform48x48.png resources/segmentation_utilities.svg plugin.xml ) set(QRC_FILES resources/segmentation.qrc resources/SegmentationUtilities.qrc resources/BooleanOperationsWidget.qrc resources/MorphologicalOperationsWidget.qrc ) set(CPP_FILES) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.segmentation/plugin.xml b/Plugins/org.mitk.gui.qt.segmentation/plugin.xml index 24f382cdeb..a72db4b004 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/plugin.xml +++ b/Plugins/org.mitk.gui.qt.segmentation/plugin.xml @@ -1,94 +1,80 @@ Allows the segmentation of images using different tools. - - - Allow the clipping of a volume using a deformable plane. - Edit segmentations using standard operations. - - - - - - diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui index 81d8c22089..67c4a615ba 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui @@ -1,170 +1,178 @@ QmitkBooleanOperationsWidgetControls 0 0 - 251 - 300 + 210 + 91 - + + + + 0 + 0 + + + false 0 0 Subtracts first segmentation from the second one Difference :/SegmentationUtilities/BooleanOperations/BooleanDifference_48x48.png:/SegmentationUtilities/BooleanOperations/BooleanDifference_48x48.png 24 24 false false Qt::ToolButtonTextUnderIcon Qt::NoArrow false 0 0 Keeps just the overlapping areas of two the segmentations Intersection :/SegmentationUtilities/BooleanOperations/BooleanIntersection_48x48.png:/SegmentationUtilities/BooleanOperations/BooleanIntersection_48x48.png 24 24 false false Qt::ToolButtonTextUnderIcon Qt::NoArrow false 0 0 Combines the two segmentations Union :/SegmentationUtilities/BooleanOperations/BooleanUnion_48x48.png:/SegmentationUtilities/BooleanOperations/BooleanUnion_48x48.png 24 24 false false Qt::ToolButtonTextUnderIcon Qt::NoArrow Qt::Vertical 20 - 273 + 40 QmitkDataSelectionWidget QWidget
internal/Common/QmitkDataSelectionWidget.h
+ 1
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidgetControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidgetControls.ui index d69bca34b7..4f9be6b5bf 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidgetControls.ui @@ -1,48 +1,56 @@ QmitkContourModelToImageWidgetControls 0 0 - 180 - 154 + 98 + 62 - + + + + 0 + 0 + + + Process Qt::Vertical 20 - 273 + 40 QmitkDataSelectionWidget QWidget
internal/Common/QmitkDataSelectionWidget.h
+ 1
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui index 785181e13c..1882ccdfbf 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui @@ -1,74 +1,82 @@ QmitkImageMaskingWidgetControls 0 0 - 139 - 143 + 147 + 155 - + + + + 0 + 0 + + + Masking Mode Image Masking true Surface Masking Mask Qt::Vertical 20 - 273 + 40 QmitkDataSelectionWidget QWidget
internal/Common/QmitkDataSelectionWidget.h
+ 1
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui index 1b91504084..0d1c003d6c 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui @@ -1,319 +1,323 @@ QmitkMorphologicalOperationsWidgetControls 0 0 - 263 - 469 + 184 + 377 - - Form - - + - + + + + 0 + 0 + + + Structuring Element - + Ball true Cross 3D Operation 2D Operation - Axial 2D Operation - Sagital 2D Operation - Coronal - + Radius 1 20 1 Qt::Horizontal 1 20 false 0 0 Dilation :/SegmentationUtilities/MorphologicalOperations/Dilate_48x48.png:/SegmentationUtilities/MorphologicalOperations/Dilate_48x48.png 32 32 Qt::ToolButtonTextUnderIcon false 0 0 Erosion :/SegmentationUtilities/MorphologicalOperations/Erode_48x48.png:/SegmentationUtilities/MorphologicalOperations/Erode_48x48.png 32 32 Qt::ToolButtonTextUnderIcon false 0 0 Closing :/SegmentationUtilities/MorphologicalOperations/Closing_48x48.png:/SegmentationUtilities/MorphologicalOperations/Closing_48x48.png 32 32 Qt::ToolButtonTextUnderIcon false 0 0 Opening :/SegmentationUtilities/MorphologicalOperations/Opening_48x48.png:/SegmentationUtilities/MorphologicalOperations/Opening_48x48.png 32 32 Qt::ToolButtonTextUnderIcon false 0 0 Globally fills holes in segmentation (structuring element and radius not required) Fill Holes :/SegmentationUtilities/MorphologicalOperations/FillHoles_48x48.png:/SegmentationUtilities/MorphologicalOperations/FillHoles_48x48.png 32 32 Qt::ToolButtonTextUnderIcon Qt::Vertical 20 - 518 + 40 QmitkDataSelectionWidget QWidget
internal/Common/QmitkDataSelectionWidget.h
1
sliderMorphFactor valueChanged(int) spinBoxMorphFactor setValue(int) 240 27 766 36 spinBoxMorphFactor valueChanged(int) sliderMorphFactor setValue(int) 784 38 657 38
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui index e9f38c05c7..a9ae0103d8 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui @@ -1,48 +1,56 @@ QmitkSurfaceToImageWidgetControls 0 0 - 94 - 78 + 98 + 62 - + + + + 0 + 0 + + + Convert Qt::Vertical 20 - 273 + 40 QmitkDataSelectionWidget QWidget
internal/Common/QmitkDataSelectionWidget.h
+ 1
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/mitkPluginActivator.cpp index 173eff2449..75912cd1a5 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/mitkPluginActivator.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/mitkPluginActivator.cpp @@ -1,65 +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 "mitkPluginActivator.h" #include "QmitkSegmentationView.h" #include "QmitkThresholdAction.h" #include "QmitkCreatePolygonModelAction.h" #include "QmitkAutocropAction.h" #include "QmitkAutocropLabelSetImageAction.h" #include "QmitkSegmentationPreferencePage.h" -#include "QmitkDeformableClippingPlaneView.h" #include "SegmentationUtilities/QmitkSegmentationUtilitiesView.h" using namespace mitk; ctkPluginContext* PluginActivator::m_context = nullptr; PluginActivator* PluginActivator::m_Instance = nullptr; PluginActivator::PluginActivator() { m_Instance = this; } PluginActivator::~PluginActivator() { m_Instance = nullptr; } void PluginActivator::start(ctkPluginContext *context) { BERRY_REGISTER_EXTENSION_CLASS(QmitkSegmentationView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkThresholdAction, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkCreatePolygonModelAction, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkAutocropAction, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkAutocropLabelSetImageAction, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkSegmentationPreferencePage, context) - BERRY_REGISTER_EXTENSION_CLASS(QmitkDeformableClippingPlaneView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkSegmentationUtilitiesView, context) this->m_context = context; } void PluginActivator::stop(ctkPluginContext *) { this->m_context = nullptr; } PluginActivator* PluginActivator::getDefault() { return m_Instance; } ctkPluginContext*PluginActivator::getContext() { return m_context; } diff --git a/Plugins/org.mitk.gui.qt.viewnavigator/documentation/UserManual/QmitkViewNavigator.dox b/Plugins/org.mitk.gui.qt.viewnavigator/documentation/UserManual/QmitkViewNavigator.dox index e44be368d5..e3fc8577b8 100644 --- a/Plugins/org.mitk.gui.qt.viewnavigator/documentation/UserManual/QmitkViewNavigator.dox +++ b/Plugins/org.mitk.gui.qt.viewnavigator/documentation/UserManual/QmitkViewNavigator.dox @@ -1,43 +1,43 @@ /** -\page org_mitk_gui_qt_viewnavigator The View Navigator +\page org_mitk_views_viewnavigatorview The View Navigator \imageMacro{view-manager.png,"Icon of the view navigator",2.00} \tableofcontents -\section org_mitk_gui_qt_viewnavigatorOverview Overview +\section org_mitk_views_viewnavigatorviewOverview Overview This view allows for the easy navigation of the available views. You can select which view to open by double-clicking the name of the view in the view navigator. It provides a keyworded, grouped and searchable list of views. -\section org_mitk_gui_qt_viewnavigatorUsage Usage +\section org_mitk_views_viewnavigatorviewUsage Usage You can toggle the View Navigator on and off by clicking on its icon in the menu bar. Alternatively it is also available via the Window->Show Views dialog. \imageMacro{QmitkViewNavigatorGUI.png,"The View Navigator GUI",6.00} Once the View Navigator has been opened you will see a list divided in workflows/perspectives and views. Via this list you can access any view or perspective provided by the application. They are further organized based on their associated category. An entry on the list is opened by a double left-click. -\subsection org_mitk_gui_qt_viewnavigatorSearch Search +\subsection org_mitk_views_viewnavigatorviewSearch Search You can search the lists for view/workflow names, keywords or categories. \imageMacro{QmitkViewNavigatorSearch.png,"Search the View Navigator",4.00} -\subsection org_mitk_gui_qt_viewnavigatorCustomWorkflows Custom Workflows +\subsection org_mitk_views_viewnavigatorviewCustomWorkflows Custom Workflows \imageMacro{QmitkViewNavigatorContextMenuWorkflows.png,"The workflow context menu",4.00} A right click on a workflow opens a context menu that allows you to copy that workflow to create your own custom one. \note The duplicated workflow will look like the basic state of the original workflow. Any changes to the original workflow will not be copied. \imageMacro{QmitkViewNavigatorNewWorkflow.png,"Custom workflow creation dialog",4.00} Your new workflow will appear in the list and you can modify it independently of the original workflow. Any changes will be stored for future sessions unless the workflow is deleted. \note Resetting a custom perspective will return it to the original state after duplicating. \imageMacro{QmitkViewNavigatorContextMenuCustomWorkflow.png,"Custom workflow context menu",4.00} */ diff --git a/Plugins/org.mitk.matchpoint.core.helper/src/internal/MatchPointBrowserConstants.cpp b/Plugins/org.mitk.matchpoint.core.helper/src/internal/MatchPointBrowserConstants.cpp index 4341cdfe47..6acedb7c5e 100644 --- a/Plugins/org.mitk.matchpoint.core.helper/src/internal/MatchPointBrowserConstants.cpp +++ b/Plugins/org.mitk.matchpoint.core.helper/src/internal/MatchPointBrowserConstants.cpp @@ -1,22 +1,22 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "MatchPointBrowserConstants.h" -const std::string MatchPointBrowserConstants::VIEW_ID = "org.mitk.gui.qt.algorithm.browser"; +const std::string MatchPointBrowserConstants::VIEW_ID = "org.mitk.views.matchpoint.algorithm.browser"; const std::string MatchPointBrowserConstants::MDAR_DIRECTORIES_NODE_NAME = "Deployed algorithm directories"; const std::string MatchPointBrowserConstants::MDAR_FILES_NODE_NAME = "Deployed algorithms"; const std::string MatchPointBrowserConstants::DEBUG_OUTPUT_NODE_NAME = "debug output"; const std::string MatchPointBrowserConstants::LOAD_FROM_APPLICATION_DIR = "load from application dir"; const std::string MatchPointBrowserConstants::LOAD_FROM_HOME_DIR = "load from home dir"; const std::string MatchPointBrowserConstants::LOAD_FROM_CURRENT_DIR = "load from current dir"; const std::string MatchPointBrowserConstants::LOAD_FROM_AUTO_LOAD_DIR = "load from auto-load dir";