diff --git a/Applications/PluginGenerator/ProjectTemplate/CMakeExternals/MITK.cmake b/Applications/PluginGenerator/ProjectTemplate/CMakeExternals/MITK.cmake index 43155e16cd..60335dbd92 100644 --- a/Applications/PluginGenerator/ProjectTemplate/CMakeExternals/MITK.cmake +++ b/Applications/PluginGenerator/ProjectTemplate/CMakeExternals/MITK.cmake @@ -1,211 +1,210 @@ #----------------------------------------------------------------------------- # MITK #----------------------------------------------------------------------------- set(MITK_DEPENDS) set(proj_DEPENDENCIES) set(proj MITK) if(NOT MITK_DIR) #----------------------------------------------------------------------------- # Create CMake options to customize the MITK build #----------------------------------------------------------------------------- option(MITK_USE_SUPERBUILD "Use superbuild for MITK" ON) option(MITK_USE_BLUEBERRY "Build the BlueBerry platform in MITK" ON) option(MITK_BUILD_EXAMPLES "Build the MITK examples" OFF) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) option(MITK_BUILD_TESTING "Build the MITK unit tests" OFF) option(MITK_USE_ACVD "Use Approximated Centroidal Voronoi Diagrams" OFF) option(MITK_USE_CTK "Use CTK in MITK" ${MITK_USE_BLUEBERRY}) option(MITK_USE_DCMTK "Use DCMTK in MITK" ON) option(MITK_USE_Qt5 "Use Qt 5 library in MITK" ON) option(MITK_USE_DCMQI "Use dcmqi in MITK" OFF) option(MITK_USE_OpenCV "Use Intel's OpenCV library" OFF) option(MITK_USE_Python3 "Enable Python wrapping in MITK" OFF) if(MITK_USE_BLUEBERRY AND 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() if(MITK_USE_CTK AND NOT MITK_USE_Qt5) message("Forcing MITK_USE_Qt5 to ON because of MITK_USE_CTK") set(MITK_USE_Qt5 ON CACHE BOOL "Use Qt 5 library in MITK" FORCE) endif() set(MITK_USE_CableSwig ${MITK_USE_Python3}) - set(MITK_USE_GDCM 1) set(MITK_USE_ITK 1) set(MITK_USE_VTK 1) mark_as_advanced(MITK_USE_SUPERBUILD MITK_BUILD_ALL_PLUGINS MITK_BUILD_TESTING ) set(mitk_cmake_boolean_args MITK_USE_SUPERBUILD MITK_USE_BLUEBERRY MITK_BUILD_EXAMPLES MITK_BUILD_ALL_PLUGINS MITK_USE_ACVD MITK_USE_CTK MITK_USE_DCMTK MITK_USE_Qt5 MITK_USE_DCMQI MITK_USE_OpenCV MITK_USE_Python3 ) if(MITK_USE_Qt5) # Look for Qt at the superbuild level, to catch missing Qt libs early find_package(Qt5Widgets REQUIRED) endif() set(additional_mitk_cmakevars ) # Configure the set of default 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") 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 "" 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") foreach(_arg MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES MITK_ACCESSBYITK_DIMENSIONS) mark_as_advanced(${_arg}) list(APPEND additional_mitk_cmakevars "-D${_arg}:STRING=${${_arg}}") endforeach() #----------------------------------------------------------------------------- # Create options to inject pre-build dependencies #----------------------------------------------------------------------------- - foreach(proj CTK DCMTK DCMQI GDCM VTK ACVD ITK OpenCV CableSwig) + foreach(proj CTK DCMTK DCMQI VTK ACVD ITK OpenCV CableSwig) if(MITK_USE_${proj}) set(MITK_${proj}_DIR "${${proj}_DIR}" CACHE PATH "Path to ${proj} build directory") mark_as_advanced(MITK_${proj}_DIR) if(MITK_${proj}_DIR) list(APPEND additional_mitk_cmakevars "-D${proj}_DIR:PATH=${MITK_${proj}_DIR}") endif() endif() endforeach() set(MITK_BOOST_ROOT "${BOOST_ROOT}" CACHE PATH "Path to Boost directory") mark_as_advanced(MITK_BOOST_ROOT) if(MITK_BOOST_ROOT) list(APPEND additional_mitk_cmakevars "-DBOOST_ROOT:PATH=${MITK_BOOST_ROOT}") endif() set(MITK_SOURCE_DIR "" CACHE PATH "MITK source code location. If empty, MITK will be cloned from MITK_GIT_REPOSITORY") set(MITK_GIT_REPOSITORY "https://phabricator.mitk.org/source/mitk.git" CACHE STRING "The git repository for cloning MITK") set(MITK_GIT_TAG "origin/master" CACHE STRING "The git tag/hash to be used when cloning from MITK_GIT_REPOSITORY") mark_as_advanced(MITK_SOURCE_DIR MITK_GIT_REPOSITORY MITK_GIT_TAG) #----------------------------------------------------------------------------- # Create the final variable containing superbuild boolean args #----------------------------------------------------------------------------- set(mitk_boolean_args) foreach(mitk_cmake_arg ${mitk_cmake_boolean_args}) list(APPEND mitk_boolean_args -D${mitk_cmake_arg}:BOOL=${${mitk_cmake_arg}}) endforeach() #----------------------------------------------------------------------------- # Additional MITK CMake variables #----------------------------------------------------------------------------- if(MITK_USE_Qt5) list(APPEND additional_mitk_cmakevars "-DCMAKE_PREFIX_PATH:PATH=${CMAKE_PREFIX_PATH}") endif() if(MITK_USE_CTK) list(APPEND additional_mitk_cmakevars "-DGIT_EXECUTABLE:FILEPATH=${GIT_EXECUTABLE}") endif() if(MITK_INITIAL_CACHE_FILE) list(APPEND additional_mitk_cmakevars "-DMITK_INITIAL_CACHE_FILE:INTERNAL=${MITK_INITIAL_CACHE_FILE}") endif() if(MITK_USE_SUPERBUILD) set(MITK_BINARY_DIR ${proj}-superbuild) else() set(MITK_BINARY_DIR ${proj}-build) endif() set(proj_DEPENDENCIES) set(MITK_DEPENDS ${proj}) # Configure the MITK souce code location if(NOT MITK_SOURCE_DIR) set(mitk_source_location SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj} GIT_REPOSITORY ${MITK_GIT_REPOSITORY} GIT_TAG ${MITK_GIT_TAG} ) else() set(mitk_source_location SOURCE_DIR ${MITK_SOURCE_DIR} ) endif() ExternalProject_Add(${proj} ${mitk_source_location} BINARY_DIR ${MITK_BINARY_DIR} PREFIX ${proj}${ep_suffix} INSTALL_COMMAND "" CMAKE_GENERATOR ${gen} CMAKE_ARGS ${ep_common_args} ${mitk_boolean_args} ${additional_mitk_cmakevars} -DBUILD_SHARED_LIBS:BOOL=ON -DBUILD_TESTING:BOOL=${MITK_BUILD_TESTING} CMAKE_CACHE_ARGS ${ep_common_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) if(MITK_USE_SUPERBUILD) set(MITK_DIR "${CMAKE_CURRENT_BINARY_DIR}/${MITK_BINARY_DIR}/MITK-build") else() set(MITK_DIR "${CMAKE_CURRENT_BINARY_DIR}/${MITK_BINARY_DIR}") endif() else() # The project is provided using MITK_DIR, nevertheless since other # projects may depend on MITK, let's add an 'empty' one MacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") # Further, do some sanity checks in the case of a pre-built MITK set(my_itk_dir ${ITK_DIR}) set(my_vtk_dir ${VTK_DIR}) find_package(MITK REQUIRED) if(my_itk_dir AND NOT my_itk_dir STREQUAL ${ITK_DIR}) message(FATAL_ERROR "ITK packages do not match:\n ${MY_PROJECT_NAME}: ${my_itk_dir}\n MITK: ${ITK_DIR}") endif() if(my_vtk_dir AND NOT my_vtk_dir STREQUAL ${VTK_DIR}) message(FATAL_ERROR "VTK packages do not match:\n ${MY_PROJECT_NAME}: ${my_vtk_dir}\n MITK: ${VTK_DIR}") endif() endif() diff --git a/CMake/PackageDepends/MITK_Boost_Config.cmake b/CMake/PackageDepends/MITK_Boost_Config.cmake index 04243ce630..7275754c8a 100644 --- a/CMake/PackageDepends/MITK_Boost_Config.cmake +++ b/CMake/PackageDepends/MITK_Boost_Config.cmake @@ -1,15 +1,15 @@ -set(Boost_ADDITIONAL_VERSIONS "1.78.0" "1.78") +set(Boost_ADDITIONAL_VERSIONS "1.80.0" "1.80") find_package(Boost REQUIRED COMPONENTS ${Boost_REQUIRED_COMPONENTS_BY_MODULE}) if(Boost_REQUIRED_COMPONENTS_BY_MODULE) foreach(boost_component ${Boost_REQUIRED_COMPONENTS_BY_MODULE}) list(APPEND ALL_LIBRARIES "Boost::${boost_component}") endforeach() endif() list(APPEND ALL_LIBRARIES "Boost::boost") if(MSVC) list(APPEND ALL_LIBRARIES "Boost::dynamic_linking" "bcrypt") endif() diff --git a/CMake/PackageDepends/MITK_DCMTK_Config.cmake b/CMake/PackageDepends/MITK_DCMTK_Config.cmake index c331c55e9a..dc4d5610b7 100644 --- a/CMake/PackageDepends/MITK_DCMTK_Config.cmake +++ b/CMake/PackageDepends/MITK_DCMTK_Config.cmake @@ -1,10 +1,10 @@ -if(NOT WIN32 AND NOT APPLE) - set(MISSING_LIBS_REQUIRED_BY_DCMTK tiff z) -endif() +foreach(dcmtk_component ${DCMTK_REQUIRED_COMPONENTS_BY_MODULE}) + set(dcmtk_component "DCMTK::${dcmtk_component}") + list(APPEND _dcmtk_required_components_by_module ${dcmtk_component}) +endforeach() -if(NOT DCMTK_FOUND) - find_package(DCMTK REQUIRED) -endif() +find_package(DCMTK COMPONENTS ${_dcmtk_required_components_by_module} REQUIRED) -list(APPEND ALL_INCLUDE_DIRECTORIES ${DCMTK_INCLUDE_DIRS}) -list(APPEND ALL_LIBRARIES ${DCMTK_LIBRARIES} ${MISSING_LIBS_REQUIRED_BY_DCMTK}) +foreach(dcmtk_component ${_dcmtk_required_components_by_module}) + list(APPEND ALL_LIBRARIES ${dcmtk_component}) +endforeach() diff --git a/CMake/PackageDepends/MITK_GDCM_Config.cmake b/CMake/PackageDepends/MITK_GDCM_Config.cmake deleted file mode 100644 index 3a85869601..0000000000 --- a/CMake/PackageDepends/MITK_GDCM_Config.cmake +++ /dev/null @@ -1,10 +0,0 @@ -foreach(gdcm_component ${GDCM_REQUIRED_COMPONENTS_BY_MODULE}) - if(NOT gdcm_component MATCHES "^gdcm") - set(gdcm_component "gdcm${gdcm_component}") - endif() - list(APPEND _gdcm_required_components_by_module ${gdcm_component}) -endforeach() - -find_package(GDCM COMPONENTS ${_gdcm_required_components_by_module} REQUIRED) - -list(APPEND ALL_LIBRARIES ${_gdcm_required_components_by_module}) diff --git a/CMake/PackageDepends/MITK_OpenCV_Config.cmake b/CMake/PackageDepends/MITK_OpenCV_Config.cmake index e8e59a903c..32b3c5888a 100644 --- a/CMake/PackageDepends/MITK_OpenCV_Config.cmake +++ b/CMake/PackageDepends/MITK_OpenCV_Config.cmake @@ -1,7 +1,12 @@ -list(APPEND ALL_LIBRARIES ${OpenCV_LIBS}) -list(APPEND ALL_INCLUDE_DIRECTORIES ${OpenCV_INCLUDE_DIRS}) +foreach(opencv_module ${OpenCV_REQUIRED_COMPONENTS_BY_MODULE}) + if(NOT opencv_module MATCHES "^opencv_") + set(opencv_module "opencv_${opencv_module}") + endif() + list(APPEND _opencv_required_components_by_module ${opencv_module}) +endforeach() -# adding option for videoinput library on windows (for directshow based frame grabbing) -if(WIN32) - option(MITK_USE_videoInput "Use videoInput (DirectShow wrapper) library" OFF) -endif(WIN32) +find_package(OpenCV COMPONENTS ${_opencv_required_components_by_module} REQUIRED) + +foreach(opencv_module ${_opencv_required_components_by_module}) + list(APPEND ALL_LIBRARIES ${opencv_module}) +endforeach() diff --git a/CMake/mitkFunctionCreateBlueBerryApplication.cmake b/CMake/mitkFunctionCreateBlueBerryApplication.cmake index 7c1b5a730e..ebbcaaade2 100644 --- a/CMake/mitkFunctionCreateBlueBerryApplication.cmake +++ b/CMake/mitkFunctionCreateBlueBerryApplication.cmake @@ -1,235 +1,235 @@ #! #! Create a BlueBerry application. #! #! \brief This function will create a BlueBerry application together with all #! necessary provisioning and configuration data and install support. #! #! \param NAME (required) The name of the executable. #! \param DESCRIPTION (optional) A human-readable description of your application. #! The usage depends on the CPack generator (on Windows, this is a descriptive #! text for the created shortcuts). #! \param SOURCES (optional) A list of source files to compile into your executable. Defaults #! to .cpp. #! \param PLUGINS (optional) A list of required plug-ins. Defaults to all known plug-ins. #! \param EXCLUDE_PLUGINS (optional) A list of plug-ins which should not be used. Mainly #! useful if PLUGINS was not used. #! \param LINK_LIBRARIES A list of libraries to be linked with the executable. #! \param LIBRARY_DIRS A list of directories to pass through to MITK_INSTALL_TARGETS #! \param NO_PROVISIONING (option) Do not create provisioning files. #! \param NO_INSTALL (option) Do not install this executable #! #! Assuming that there exists a file called MyApp.cpp, an example call looks like: #! \code #! mitkFunctionCreateBlueBerryApplication( #! NAME MyApp #! DESCRIPTION "MyApp - New ways to explore medical data" #! EXCLUDE_PLUGINS org.mitk.gui.qt.extapplication #! ) #! \endcode #! function(mitkFunctionCreateBlueBerryApplication) cmake_parse_arguments(_APP "NO_PROVISIONING;NO_INSTALL" "NAME;DESCRIPTION" "SOURCES;PLUGINS;EXCLUDE_PLUGINS;LINK_LIBRARIES;LIBRARY_DIRS" ${ARGN}) if(NOT _APP_NAME) message(FATAL_ERROR "NAME argument cannot be empty.") endif() if(NOT _APP_SOURCES) set(_APP_SOURCES ${_APP_NAME}.cpp) endif() if(NOT _APP_PLUGINS) ctkFunctionGetAllPluginTargets(_APP_PLUGINS) else() set(_plugins ${_APP_PLUGINS}) set(_APP_PLUGINS) foreach(_plugin ${_plugins}) string(REPLACE "." "_" _plugin_target ${_plugin}) list(APPEND _APP_PLUGINS ${_plugin_target}) endforeach() # get all plug-in dependencies ctkFunctionGetPluginDependencies(_plugin_deps PLUGINS ${_APP_PLUGINS} ALL) # add the dependencies to the list of application plug-ins list(APPEND _APP_PLUGINS ${_plugin_deps}) endif() # ----------------------------------------------------------------------- # Set up include and link dirs for the executable # ----------------------------------------------------------------------- include_directories( ${org_blueberry_core_runtime_INCLUDE_DIRS} ) # ----------------------------------------------------------------------- # Add executable icon (Windows) # ----------------------------------------------------------------------- set(WINDOWS_ICON_RESOURCE_FILE "") if(WIN32) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/icons/${_APP_NAME}.rc") set(WINDOWS_ICON_RESOURCE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/icons/${_APP_NAME}.rc") endif() endif() # ----------------------------------------------------------------------- # Create the executable and link libraries # ----------------------------------------------------------------------- set(_app_compile_flags ) if(WIN32) - set(_app_compile_flags "${_app_compile_flags} -DPOCO_NO_UNWINDOWS -DWIN32_LEAN_AND_MEAN") + set(_app_compile_flags "${_app_compile_flags} -DWIN32_LEAN_AND_MEAN") endif() if(MITK_SHOW_CONSOLE_WINDOW) add_executable(${_APP_NAME} MACOSX_BUNDLE ${_APP_SOURCES} ${WINDOWS_ICON_RESOURCE_FILE}) else() add_executable(${_APP_NAME} MACOSX_BUNDLE WIN32 ${_APP_SOURCES} ${WINDOWS_ICON_RESOURCE_FILE}) endif() if(NOT CMAKE_CURRENT_SOURCE_DIR MATCHES "^${CMAKE_SOURCE_DIR}/.*") foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) if("${CMAKE_CURRENT_SOURCE_DIR}/" MATCHES "^${MITK_EXTENSION_DIR}/.*") get_filename_component(MITK_EXTENSION_ROOT_FOLDER "${MITK_EXTENSION_DIR}" NAME) set_property(TARGET ${_APP_NAME} PROPERTY FOLDER "${MITK_EXTENSION_ROOT_FOLDER}/Applications") break() endif() endforeach() else() set_property(TARGET ${_APP_NAME} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Applications") endif() mitk_use_modules(TARGET ${_APP_NAME} MODULES MitkAppUtil) set_target_properties(${_APP_NAME} PROPERTIES COMPILE_FLAGS "${_app_compile_flags}") target_link_libraries(${_APP_NAME} PRIVATE org_blueberry_core_runtime ${_APP_LINK_LIBRARIES}) if(WIN32) target_link_libraries(${_APP_NAME} PRIVATE ${QT_QTMAIN_LIBRARY}) endif() if(WIN32 AND MITK_UTF8) mitk_add_manifest(${_APP_NAME}) endif() # ----------------------------------------------------------------------- # Add executable icon and bundle name (Mac) # ----------------------------------------------------------------------- if(APPLE) if( _APP_DESCRIPTION) set_target_properties(${_APP_NAME} PROPERTIES MACOSX_BUNDLE_NAME "${_APP_DESCRIPTION}") else() set_target_properties(${_APP_NAME} PROPERTIES MACOSX_BUNDLE_NAME "${_APP_NAME}") endif() set(icon_name "icon.icns") set(icon_full_path "${CMAKE_CURRENT_SOURCE_DIR}/icons/${icon_name}") if(EXISTS "${icon_full_path}") set_target_properties(${_APP_NAME} PROPERTIES MACOSX_BUNDLE_ICON_FILE "${icon_name}") file(COPY ${icon_full_path} DESTINATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${_APP_NAME}.app/Contents/Resources/") INSTALL (FILES ${icon_full_path} DESTINATION "${_APP_NAME}.app/Contents/Resources/") endif() endif() # ----------------------------------------------------------------------- # Set build time dependencies # ----------------------------------------------------------------------- # This ensures that all enabled plug-ins are up-to-date when the # executable is build. if(_APP_PLUGINS) ctkMacroGetAllProjectTargetLibraries("${_APP_PLUGINS}" _project_plugins) if(_APP_EXCLUDE_PLUGINS AND _project_plugins) set(_exclude_targets) foreach(_exclude_plugin ${_APP_EXCLUDE_PLUGINS}) string(REPLACE "." "_" _exclude_target ${_exclude_plugin}) list(APPEND _exclude_targets ${_exclude_target}) endforeach() list(REMOVE_ITEM _project_plugins ${_exclude_targets}) endif() if(_project_plugins) add_dependencies(${_APP_NAME} ${_project_plugins}) endif() endif() # ----------------------------------------------------------------------- # Additional files needed for the executable # ----------------------------------------------------------------------- if(NOT _APP_NO_PROVISIONING) # Create a provisioning file, listing all plug-ins set(_prov_file "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${_APP_NAME}.provisioning") mitkFunctionCreateProvisioningFile(FILE ${_prov_file} PLUGINS ${_APP_PLUGINS} EXCLUDE_PLUGINS ${_APP_EXCLUDE_PLUGINS} ) endif() # Create a .ini file for initial parameters if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_APP_NAME}.ini") configure_file(${_APP_NAME}.ini ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${_APP_NAME}.ini) endif() # Create batch and VS user files for Windows platforms include(mitkFunctionCreateWindowsBatchScript) if(WIN32) set(template_name "start${_APP_NAME}.bat.in") if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${template_name}") foreach(BUILD_TYPE debug release) mitkFunctionCreateWindowsBatchScript(${template_name} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/start${_APP_NAME}_${BUILD_TYPE}.bat ${BUILD_TYPE}) endforeach() endif() mitkFunctionConfigureVisualStudioUserProjectFile( NAME ${_APP_NAME} ) endif(WIN32) # ----------------------------------------------------------------------- # Install support # ----------------------------------------------------------------------- if(NOT _APP_NO_INSTALL) # This installs all third-party CTK plug-ins mitkFunctionInstallThirdPartyCTKPlugins(${_APP_PLUGINS} EXCLUDE ${_APP_EXCLUDE_PLUGINS}) if(COMMAND BlueBerryApplicationInstallHook) set(_real_app_plugins ${_APP_PLUGINS}) if(_APP_EXCLUDE_PLUGINS) list(REMOVE_ITEM _real_app_plugins ${_APP_EXCLUDE_PLUGINS}) endif() BlueBerryApplicationInstallHook(APP_NAME ${_APP_NAME} PLUGINS ${_real_app_plugins}) endif() # Install the executable MITK_INSTALL_TARGETS(EXECUTABLES ${_APP_NAME} LIBRARY_DIRS ${_APP_LIBRARY_DIRS} GLOB_PLUGINS ) if(NOT _APP_NO_PROVISIONING) # Install the provisioning file mitkFunctionInstallProvisioningFiles(${_prov_file}) endif() # On Linux, create a shell script to start a relocatable application if(UNIX AND NOT APPLE) install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME "${_APP_NAME}.sh") elseif(WIN32) install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledWin32App.bat" DESTINATION "." RENAME "${_APP_NAME}.bat") endif() # Tell cpack the executables that you want in the start menu as links set(MITK_CPACK_PACKAGE_EXECUTABLES ${MITK_CPACK_PACKAGE_EXECUTABLES} "${_APP_NAME};${_APP_DESCRIPTION}" CACHE INTERNAL "Collecting windows shortcuts to executables") endif() endfunction() function(FunctionCreateBlueBerryApplication) message(SEND_ERROR "The function FunctionCreateBlueBerryApplication was renamed to mitkFunctionCreateBlueBerryApplication in MITK 2015.05") endfunction() diff --git a/CMakeExternals/ANN.cmake b/CMakeExternals/ANN.cmake index 25822784e8..b5256cf40c 100644 --- a/CMakeExternals/ANN.cmake +++ b/CMakeExternals/ANN.cmake @@ -1,56 +1,50 @@ #----------------------------------------------------------------------------- # ANN #----------------------------------------------------------------------------- if(MITK_USE_ANN) # Sanity checks if(DEFINED ANN_DIR AND NOT EXISTS ${ANN_DIR}) message(FATAL_ERROR "ANN_DIR variable is defined but corresponds to non-existing directory") endif() set(proj ANN) set(proj_DEPENDENCIES ) set(ANN_DEPENDS ${proj}) if(NOT DEFINED ANN_DIR) set(additional_args ) if(CTEST_USE_LAUNCHERS) list(APPEND additional_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() - set(patch_cmd - ${CMAKE_COMMAND} -Dproj:STRING=${proj} -Dproj_target:STRING=ann -P ${CMAKE_CURRENT_LIST_DIR}/GenerateDefaultCMakeBuildSystem.cmake - COMMAND ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/ANN.patch - ) - ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/ann_1.1.2.tar.gz - URL_MD5 7ffaacc7ea79ca39d4958a6378071365 - PATCH_COMMAND ${patch_cmd} + GIT_REPOSITORY https://github.com/MITK/ANN.git + GIT_TAG v1.1.2-patched CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_args} CMAKE_CACHE_ARGS ${ep_common_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) set(ANN_DIR ${ep_prefix}/lib/cmake/ANN) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/ANN.patch b/CMakeExternals/ANN.patch deleted file mode 100644 index fb191edf81..0000000000 --- a/CMakeExternals/ANN.patch +++ /dev/null @@ -1,148 +0,0 @@ -diff -urNb ann_1.1.2/src/ANN.cpp ANN/src/ANN.cpp ---- ann_1.1.2/src/ANN.cpp 2010-01-28 05:40:01.000000000 +0100 -+++ ANN/src/ANN.cpp 2022-03-17 21:55:57.011720500 +0100 -@@ -48,9 +48,9 @@ - ANNpoint p, - ANNpoint q) - { -- register int d; -- register ANNcoord diff; -- register ANNcoord dist; -+ int d; -+ ANNcoord diff; -+ ANNcoord dist; - - dist = 0; - for (d = 0; d < dim; d++) { -diff -urNb ann_1.1.2/src/kd_fix_rad_search.cpp ANN/src/kd_fix_rad_search.cpp ---- ann_1.1.2/src/kd_fix_rad_search.cpp 2010-01-28 05:40:01.000000000 +0100 -+++ ANN/src/kd_fix_rad_search.cpp 2022-03-17 21:56:09.257624400 +0100 -@@ -147,11 +147,11 @@ - - void ANNkd_leaf::ann_FR_search(ANNdist box_dist) - { -- register ANNdist dist; // distance to data point -- register ANNcoord* pp; // data coordinate pointer -- register ANNcoord* qq; // query coordinate pointer -- register ANNcoord t; -- register int d; -+ ANNdist dist; // distance to data point -+ ANNcoord* pp; // data coordinate pointer -+ ANNcoord* qq; // query coordinate pointer -+ ANNcoord t; -+ int d; - - for (int i = 0; i < n_pts; i++) { // check points in bucket - -diff -urNb ann_1.1.2/src/kd_pr_search.cpp ANN/src/kd_pr_search.cpp ---- ann_1.1.2/src/kd_pr_search.cpp 2010-01-28 05:40:01.000000000 +0100 -+++ ANN/src/kd_pr_search.cpp 2022-03-17 21:56:20.260622500 +0100 -@@ -180,12 +180,12 @@ - - void ANNkd_leaf::ann_pri_search(ANNdist box_dist) - { -- register ANNdist dist; // distance to data point -- register ANNcoord* pp; // data coordinate pointer -- register ANNcoord* qq; // query coordinate pointer -- register ANNdist min_dist; // distance to k-th closest point -- register ANNcoord t; -- register int d; -+ ANNdist dist; // distance to data point -+ ANNcoord* pp; // data coordinate pointer -+ ANNcoord* qq; // query coordinate pointer -+ ANNdist min_dist; // distance to k-th closest point -+ ANNcoord t; -+ int d; - - min_dist = ANNprPointMK->max_key(); // k-th smallest distance so far - -diff -urNb ann_1.1.2/src/kd_search.cpp ANN/src/kd_search.cpp ---- ann_1.1.2/src/kd_search.cpp 2010-01-28 05:40:01.000000000 +0100 -+++ ANN/src/kd_search.cpp 2022-03-17 21:56:28.132124600 +0100 -@@ -171,12 +171,12 @@ - - void ANNkd_leaf::ann_search(ANNdist box_dist) - { -- register ANNdist dist; // distance to data point -- register ANNcoord* pp; // data coordinate pointer -- register ANNcoord* qq; // query coordinate pointer -- register ANNdist min_dist; // distance to k-th closest point -- register ANNcoord t; -- register int d; -+ ANNdist dist; // distance to data point -+ ANNcoord* pp; // data coordinate pointer -+ ANNcoord* qq; // query coordinate pointer -+ ANNdist min_dist; // distance to k-th closest point -+ ANNcoord t; -+ int d; - - min_dist = ANNkdPointMK->max_key(); // k-th smallest distance so far - -diff -urNb ann_1.1.2/src/kd_util.cpp ANN/src/kd_util.cpp ---- ann_1.1.2/src/kd_util.cpp 2010-01-28 05:40:01.000000000 +0100 -+++ ANN/src/kd_util.cpp 2022-03-17 21:56:34.316330300 +0100 -@@ -127,10 +127,10 @@ - const ANNpoint hi, // high point of box - int dim) // dimension of space - { -- register ANNdist dist = 0.0; // sum of squared distances -- register ANNdist t; -+ ANNdist dist = 0.0; // sum of squared distances -+ ANNdist t; - -- for (register int d = 0; d < dim; d++) { -+ for (int d = 0; d < dim; d++) { - if (q[d] < lo[d]) { // q is left of box - t = ANNdist(lo[d]) - ANNdist(q[d]); - dist = ANN_SUM(dist, ANN_POW(t)); -@@ -238,8 +238,8 @@ - int l = 0; // left end of current subarray - int r = n-1; // right end of current subarray - while (l < r) { -- register int i = (r+l)/2; // select middle as pivot -- register int k; -+ int i = (r+l)/2; // select middle as pivot -+ int k; - - if (PA(i,d) > PA(r,d)) // make sure last > pivot - PASWAP(i,r) -diff -urNb ann_1.1.2/src/pr_queue.h ANN/src/pr_queue.h ---- ann_1.1.2/src/pr_queue.h 2010-01-28 05:40:01.000000000 +0100 -+++ ANN/src/pr_queue.h 2022-03-17 21:56:44.266327900 +0100 -@@ -86,9 +86,9 @@ - PQinfo inf) // item info - { - if (++n > max_size) annError("Priority queue overflow.", ANNabort); -- register int r = n; -+ int r = n; - while (r > 1) { // sift up new item -- register int p = r/2; -+ int p = r/2; - ANN_FLOP(1) // increment floating ops - if (pq[p].key <= kv) // in proper order - break; -@@ -105,9 +105,9 @@ - { - kv = pq[1].key; // key of min item - inf = pq[1].info; // information of min item -- register PQkey kn = pq[n--].key;// last item in queue -- register int p = 1; // p points to item out of position -- register int r = p<<1; // left child of p -+ PQkey kn = pq[n--].key;// last item in queue -+ int p = 1; // p points to item out of position -+ int r = p<<1; // left child of p - while (r <= n) { // while r is still within the heap - ANN_FLOP(2) // increment floating ops - // set r to smaller child of p -diff -urNb ann_1.1.2/src/pr_queue_k.h ANN/src/pr_queue_k.h ---- ann_1.1.2/src/pr_queue_k.h 2010-01-28 05:40:01.000000000 +0100 -+++ ANN/src/pr_queue_k.h 2022-03-17 21:56:55.450345900 +0100 -@@ -100,7 +100,7 @@ - PQKkey kv, // key value - PQKinfo inf) // item info - { -- register int i; -+ int i; - // slide larger values up - for (i = n; i > 0; i--) { - if (mk[i-1].key > kv) diff --git a/CMakeExternals/ANNCMakeLists.txt b/CMakeExternals/ANNCMakeLists.txt deleted file mode 100644 index b65ec9384e..0000000000 --- a/CMakeExternals/ANNCMakeLists.txt +++ /dev/null @@ -1,80 +0,0 @@ -cmake_minimum_required(VERSION 3.1) - -project(ANN) - -set(${PROJECT_NAME}_MAJOR_VERSION 1) -set(${PROJECT_NAME}_MINOR_VERSION 1) -set(${PROJECT_NAME}_PATCH_VERSION 2) -set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) - -set(ANN_HEADERS - include/ANN/ANN.h - include/ANN/ANNperf.h - include/ANN/ANNx.h -) - -set(ANN_SOURCES - src/ANN.cpp - src/bd_fix_rad_search.cpp - src/bd_pr_search.cpp - src/bd_search.cpp - src/bd_tree.cpp - src/brute.cpp - src/kd_dump.cpp - src/kd_fix_rad_search.cpp - src/kd_pr_search.cpp - src/kd_search.cpp - src/kd_split.cpp - src/kd_tree.cpp - src/kd_util.cpp - src/perf.cpp -) - -add_library(ann SHARED ${ANN_HEADERS} ${ANN_SOURCES}) -target_include_directories(ann - PUBLIC "$" - "$" -) -target_compile_definitions(ann PRIVATE $<$:DLL_EXPORTS>) -set_target_properties(ann PROPERTIES - SOVERSION ${${PROJECT_NAME}_VERSION}) - -set(${PROJECT_NAME}_LIBRARIES ann) - -# Install support - -install(TARGETS ${${PROJECT_NAME}_LIBRARIES} EXPORT ${PROJECT_NAME}_TARGETS - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib - RUNTIME DESTINATION bin -) -install(FILES ${ANN_HEADERS} - DESTINATION include/${PROJECT_NAME} -) - -# Config files -configure_file( - ${PROJECT_NAME}Config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake - @ONLY -) -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}ConfigVersion.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - @ONLY -) - -export(EXPORT ${PROJECT_NAME}_TARGETS - FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake -) - -set(config_package_location lib/cmake/${PROJECT_NAME}) -install(EXPORT ${PROJECT_NAME}_TARGETS - FILE ${PROJECT_NAME}Targets.cmake - DESTINATION ${config_package_location} -) -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" - DESTINATION ${config_package_location} -) diff --git a/CMakeExternals/Boost.cmake b/CMakeExternals/Boost.cmake index 9c086d7ef2..dcebc7c38b 100644 --- a/CMakeExternals/Boost.cmake +++ b/CMakeExternals/Boost.cmake @@ -1,342 +1,343 @@ #----------------------------------------------------------------------------- # Boost #----------------------------------------------------------------------------- include(mitkFunctionGetMSVCVersion) #[[ Sanity checks ]] if(DEFINED BOOST_ROOT AND NOT EXISTS ${BOOST_ROOT}) message(FATAL_ERROR "BOOST_ROOT variable is defined but corresponds to non-existing directory") endif() string(REPLACE "^^" ";" MITK_USE_Boost_LIBRARIES "${MITK_USE_Boost_LIBRARIES}") set(proj Boost) set(proj_DEPENDENCIES ) set(Boost_DEPENDS ${proj}) if(NOT DEFINED BOOST_ROOT AND NOT MITK_USE_SYSTEM_Boost) #[[ Reset variables. ]] set(patch_cmd "") set(configure_cmd "") set(install_cmd "") set(BOOST_ROOT ${ep_prefix}) + set(Boost_DIR "${BOOST_ROOT}/lib/cmake/Boost-1.80.0") if(WIN32) set(BOOST_LIBRARYDIR "${BOOST_ROOT}/lib") endif() #[[ If you update Boost, make sure that the FindBoost module of the minimum required version of CMake supports the new version of Boost. In case you are using a higher version of CMake, download at least the source code of the minimum required version of CMake to look into the right version of the FindBoost module: /share/cmake-/Modules/FindBoost.cmake Search for a list called _Boost_KNOWN_VERSIONS. If the new version is not included in this list, you have three options: * Update the minimum required version of CMake. This may require adaptions of other parts of our CMake scripts and has the most impact on other MITK developers. Yet this is the safest and cleanest option. * Set Boost_ADDITIONAL_VERSIONS (see the documentation of the FindBoost module). As Boost libraries and dependencies between them are hard-coded in the FindBoost module only for known versions, this may cause trouble for other MITK developers relying on new components of Boost or components with changed dependencies. * Copy a newer version of the FindBoost module into our CMake directory. Our CMake directory has a higher precedence than the default CMake module directory. Doublecheck if the minimum required version of CMake is able to process the newer version of the FindBoost module. Also, DO NOT FORGET to mention this option right above the call of cmake_minimum_required() in the top-level CMakeLists.txt file AND in this file right above the set(url) command below so if we update the minimum required version of CMake or use another option in the future, we do not forget to remove our copy of the FindBoost module again. ]] - set(url "${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/boost_1_78_0_b1.tar.gz") - set(md5 bbaa634603e3789d7dd0c21d0bdf4f09) + set(url "${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/boost_1_80_0.tar.gz") + set(md5 077f074743ea7b0cb49c6ed43953ae95) if(MITK_USE_Boost_LIBRARIES) #[[ Boost has a two-step build process. In the first step, a bootstrap script is called to build b2, an executable that is used to actually build Boost in the second step. The bootstrap script expects a toolset (compiler) argument that is used to build the b2 executable. The scripts and their expected argument format differ between Windows and Unix. ]] if(WIN32) mitkFunctionGetMSVCVersion() if(VISUAL_STUDIO_VERSION_MINOR EQUAL 0) #[[ Use just the major version in the toolset name. ]] set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}) elseif(VISUAL_STUDIO_VERSION_MAJOR EQUAL 14 AND VISUAL_STUDIO_VERSION_MINOR LESS 20) #[[ Assume Visual Studio 2017. ]] set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}1) elseif(VISUAL_STUDIO_VERSION_MAJOR EQUAL 14 AND VISUAL_STUDIO_VERSION_MINOR LESS 30) #[[ Assume Visual Studio 2019. ]] set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}2) elseif(VISUAL_STUDIO_VERSION_MAJOR EQUAL 14 AND VISUAL_STUDIO_VERSION_MINOR LESS 40) #[[ Assume Visual Studio 2022. ]] set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}3) else() #[[ Fallback to the generic case. Be prepared to add another elseif branch above for future versions of Visual Studio. ]] set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}) endif() else() #[[ We support GCC and Clang on Unix. On macOS, the toolset must be set to clang. The actual compiler for all of these toolkits is set further below, after the bootstrap script but before b2. ]] if(CMAKE_CXX_COMPILER_ID STREQUAL GNU) set(toolset gcc) elseif(CMAKE_CXX_COMPILER_ID STREQUAL Clang OR APPLE) set(toolset clang) endif() if(toolset) set(bootstrap_args --with-toolset=${toolset}) endif() #[[ At least give it a shot if the toolset is something else and let the bootstrap script decide on the toolset by not passing any argument. ]] endif() #[[ The call of b2 is more complex. b2 arguments are grouped into options and properties. Options follow the standard format for arguments while properties are plain key-value pairs. ]] set(b2_options --build-dir= --stagedir= --ignore-site-config #[[ Build independent of any site.config file ]] -q #[[ Stop at first error ]] ) if(APPLE AND CMAKE_OSX_SYSROOT) #[[ Specify the macOS platform SDK to be used. ]] list(APPEND b2_options --sysroot=${CMAKE_OSX_SYSROOT}) endif() foreach(lib ${MITK_USE_Boost_LIBRARIES}) list(APPEND b2_options --with-${lib}) endforeach() set(b2_properties threading=multi runtime-link=shared "cxxflags=${MITK_CXX${MITK_CXX_STANDARD}_FLAG} ${CMAKE_CXX_FLAGS}" ) if(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND b2_properties address-model=64) else() list(APPEND b2_properties address-model=32) endif() if(BUILD_SHARED_LIBS) list(APPEND b2_properties link=shared) else() list(APPEND b2_properties link=static) endif() list(APPEND b2_properties "\ $<$:variant=debug>\ $<$:variant=release>\ $<$:variant=release>\ $<$:variant=release>") if(WIN32) set(bootstrap_cmd if not exist b2.exe \( call bootstrap.bat ${bootstrap_args} \)) set(b2_cmd b2 ${b2_options} ${b2_properties} stage) else() set(bootstrap_cmd #[[ test -e ./b2 || ]] ./bootstrap.sh ${bootstrap_args}) set(b2_cmd ./b2 ${b2_options} ${b2_properties} stage) #[[ We already told Boost if we want to use GCC or Clang but so far we were not able to specify the exact same compiler we set in CMake when configuring the MITK superbuild for the first time. For example, this can be different from the system default when multiple versions of the same compiler are installed at the same time. The bootstrap script creates a configuration file for b2 that should be modified if necessary before b2 is called. We look for a line like using gcc ; and replace it with something more specific like using gcc : : /usr/bin/gcc-7.3 ; We use the stream editor sed for the replacement but since macOS is based on BSD Unix, we use the limited but portable BSD syntax instead of the more powerful GNU syntax. We also use | instead of the more commonly used / separator for sed because the replacement contains slashes. 2021/06/15: The custom project-config.jam does not work well with SDK paths on macOS anymore, so we use a custom project-config.jam only on Linux for now. ]] if(toolset AND NOT APPLE) set(configure_cmd sed -i.backup "\ s|\ using[[:space:]][[:space:]]*${toolset}[[:space:]]*$|\ using ${toolset} : : ${CMAKE_CXX_COMPILER} $|\ g" /project-config.jam ) endif() endif() endif() if(WIN32) set(dummy_cmd cd .) else() set(dummy_cmd true) #[[ "cd ." does not work reliably ]] endif() if(NOT patch_cmd) set(patch_cmd ${dummy_cmd}) #[[ Do nothing ]] endif() if(NOT configure_cmd) set(configure_cmd ${dummy_cmd}) #[[ Do nothing ]] endif() if(WIN32) set(install_cmd if not exist $ \( ${CMAKE_COMMAND} -E copy_directory /boost /include/boost \) ) else() set(install_cmd # test -e /include/boost/config.hpp || ${CMAKE_COMMAND} -E copy_directory /boost /include/boost ) endif() ExternalProject_Add(${proj} URL ${url} URL_MD5 ${md5} PATCH_COMMAND ${patch_cmd} CONFIGURE_COMMAND ${configure_cmd} BUILD_COMMAND "" INSTALL_COMMAND ${install_cmd} ) ExternalProject_Add_Step(${proj} bootstrap COMMAND ${bootstrap_cmd} DEPENDEES patch DEPENDERS configure WORKING_DIRECTORY ) ExternalProject_Add_Step(${proj} b2 COMMAND ${b2_cmd} DEPENDEES bootstrap DEPENDERS build WORKING_DIRECTORY ) if(WIN32) #[[ Reuse already extracted files. ]] set(stamp_dir ${ep_prefix}/src/Boost-stamp) configure_file( ${CMAKE_CURRENT_LIST_DIR}/extract-Boost.replacement.cmake ${stamp_dir}/extract-Boost.replacement.cmake COPYONLY) ExternalProject_Add_Step(${proj} pre_download COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-pre_download.cmake DEPENDEES mkdir DEPENDERS download WORKING_DIRECTORY ${stamp_dir} ) endif() set(install_manifest_dependees install) if(MITK_USE_Boost_LIBRARIES) if(WIN32) #[[ Move DLLs from lib to bin directory. ]] ExternalProject_Add_Step(${proj} post_install COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-post_install-WIN32.cmake DEPENDEES install WORKING_DIRECTORY /lib ) set(install_manifest_dependees post_install) elseif(APPLE) #[[ Boost does not follow the common practice of either using rpath or absolute paths for referencing dependencies. We have to use the install_name_tool to fix this. ]] ExternalProject_Add_Step(${proj} post_install COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-post_install-APPLE.cmake DEPENDEES install WORKING_DIRECTORY /lib ) set(install_manifest_dependees post_install) endif() endif() ExternalProject_Add_Step(${proj} install_manifest COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-install_manifest.cmake DEPENDEES ${install_manifest_dependees} WORKING_DIRECTORY ${ep_prefix} ) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/CMakeExternals/CTK.cmake b/CMakeExternals/CTK.cmake index ed64006736..a14e888731 100644 --- a/CMakeExternals/CTK.cmake +++ b/CMakeExternals/CTK.cmake @@ -1,100 +1,102 @@ #----------------------------------------------------------------------------- # 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 "7210c5bc") - 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() + set(pythonqt_location_args + "-DPythonQt_GIT_REPOSITORY:STRING=https://github.com/MITK/PythonQt.git" + -DPythonQt_REVISION_TAG:STRING=patched-10-patched + ) + ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} - GIT_REPOSITORY https://github.com/commontk/CTK - GIT_TAG ${revision_tag} + GIT_REPOSITORY https://github.com/MITK/CTK.git + GIT_TAG ec816cbb-patched 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} -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 + ${pythonqt_location_args} ${${proj}_CUSTOM_CMAKE_ARGS} CMAKE_CACHE_ARGS ${ep_common_cache_args} ${${proj}_CUSTOM_CMAKE_CACHE_ARGS} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} ${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS} DEPENDS ${proj_DEPENDENCIES} ) ExternalProject_Get_Property(${proj} binary_dir) set(CTK_DIR ${binary_dir}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/ChangeHDF5LibsInstallNameForMac.cmake.in b/CMakeExternals/ChangeHDF5LibsInstallNameForMac.cmake.in deleted file mode 100644 index ebe1f89651..0000000000 --- a/CMakeExternals/ChangeHDF5LibsInstallNameForMac.cmake.in +++ /dev/null @@ -1,17 +0,0 @@ -# Get all the shared libraries which are located in the HDF5-install/lib directory -file(GLOB dylibFiles @HDF5_DIR@/lib/*.dylib) - -# For each shared library call the install_name_tool in order to change the install name of the according library -foreach(_dylib ${dylibFiles}) - message("Fixing HDF5 install name for lib: ${_dylib}") - execute_process(COMMAND install_name_tool -id ${_dylib} ${_dylib}) - foreach(_dep_dylib ${dylibFiles}) - get_filename_component(_dep_dylib_name ${_dep_dylib} NAME) - execute_process(COMMAND install_name_tool -change ${_dep_dylib_name} \@loader_path/${_dep_dylib_name} ${_dylib}) - endforeach() -endforeach() - - - - - diff --git a/CMakeExternals/CppUnit.cmake b/CMakeExternals/CppUnit.cmake index 7ebf18c970..6fef5798d4 100644 --- a/CMakeExternals/CppUnit.cmake +++ b/CMakeExternals/CppUnit.cmake @@ -1,49 +1,46 @@ #----------------------------------------------------------------------------- # CppUnit #----------------------------------------------------------------------------- # Sanity checks if(DEFINED CppUnit_DIR AND NOT EXISTS ${CppUnit_DIR}) message(FATAL_ERROR "CppUnit_DIR variable is defined but corresponds to non-existing directory") endif() set(proj CppUnit) set(proj_DEPENDENCIES ) set(${proj}_DEPENDS ${proj}) if(NOT DEFINED CppUnit_DIR) set(additional_args ) if(CTEST_USE_LAUNCHERS) list(APPEND additional_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/cppunit-1.15.1.tar.gz - URL_MD5 9dc669e6145cadd9674873e24943e6dd - PATCH_COMMAND - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/${proj}config.h.cmake /config/config.h.cmake - COMMAND ${CMAKE_COMMAND} -Dproj=${proj} -Dproj_target:STRING=cppunit -P ${CMAKE_CURRENT_LIST_DIR}/GenerateDefaultCMakeBuildSystem.cmake + GIT_REPOSITORY https://github.com/MITK/CppUnit.git + GIT_TAG v1.15.1-patched CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_args} CMAKE_CACHE_ARGS ${ep_common_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) set(${proj}_DIR ${ep_prefix}/lib/cmake/CppUnit) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/CMakeExternals/CppUnitCMakeLists.txt b/CMakeExternals/CppUnitCMakeLists.txt deleted file mode 100644 index 34577fe5f0..0000000000 --- a/CMakeExternals/CppUnitCMakeLists.txt +++ /dev/null @@ -1,223 +0,0 @@ -cmake_minimum_required(VERSION 3.18) - -project(CppUnit) - -set(${PROJECT_NAME}_MAJOR_VERSION 1) -set(${PROJECT_NAME}_MINOR_VERSION 15) -set(${PROJECT_NAME}_PATCH_VERSION 1) -set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) - -# Generates include/cppunit/config-auto.h -# This is originally done by autoconf - -include(CheckIncludeFile) -include(CheckIncludeFileCXX) -include(CheckCXXSourceCompiles) -include(CheckCSourceCompiles) -include(CheckLibraryExists) -include(CheckFunctionExists) - -#Not used == not seen in any *.h *.cpp files -#Not used FUNC_STRING_COMPARE_STRING_FIRST - -check_include_file_cxx(sstream CPPUNIT_HAVE_SSTREAM) -check_include_file_cxx(strstream CPPUNIT_HAVE_STRSTREAM) -set (CMAKE_REQUIRED_DEFINITIONS -DHAVE_STRSTREAM=CPPUNIT_HAVE_STRSTREAM) -check_cxx_source_compiles( -"#ifdef HAVE_STRSTREAM -#include -#else -#include -#endif -int main() { - std::ostrstream message; - message << \"Hello\"; - return 0; -}" CPPUNIT_HAVE_CLASS_STRSTREAM) - -check_include_file_cxx(cmath CPPUNIT_HAVE_CMATH) -#Not used, dld library is obsolete anyway HAVE_DLD -#Not used HAVE_DLERROR -check_include_file(dlfcn.h CPPUNIT_HAVE_DLFCN_H) - -check_c_source_compiles( -"#include -int main() { - return finite(3); -}" CPPUNIT_HAVE_FINITE) - -check_c_source_compiles( -"#include -int main() { - return _finite(3); -}" CPPUNIT_HAVE__FINITE) - -check_include_file_cxx(cxxabi.h CPPUNIT_HAVE_GCC_ABI_DEMANGLE) -#Not used HAVE_INTTYPES_H - -check_c_source_compiles( -"#include -int main() { - return isfinite(3); -}" CPPUNIT_HAVE_ISFINITE) - -check_library_exists(dl dlopen "" CPPUNIT_HAVE_LIBDL) -#Not used HAVE_MEMORY_H - -check_cxx_source_compiles( -"namespace Outer { - namespace Inner { - int i = 0; - } -} -using namespace Outer::Inner; -int main() { - return i; -}" CPPUNIT_HAVE_NAMESPACES) - -check_cxx_source_compiles( -"#include -class Base { -public: - Base() {} - virtual int f() { return 0; } -}; -class Derived : public Base { -public: - Derived() {} - virtual int f() { return 1; } -}; -int main() { - Derived d; - Base * ptr = &d; - return typeid(*ptr) == typeid(Derived); -}" CPPUNIT_HAVE_RTTI) - -check_library_exists(dl shl_load "" CPPUNIT_HAVE_SHL_LOAD) - -#Not used HAVE_STDINT_H -#Not used HAVE_STDLIB_H -#Not used HAVE_STRINGS_H -#Not used HAVE_STRING_H -#Not used HAVE_SYS_STAT_H -#Not used HAVE_SYS_TYPES_H -#Not used HAVE_UNISTD_H -#Not used PACKAGE -#Not used PACKAGE_BUGREPORT -#Not used PACKAGE_NAME -#Not used PACKAGE_STRING -#Not used PACKAGE_TARNAME -#Not used PACKAGE_VERSION -#Not used STDC_HEADERS -check_include_file_cxx(typeinfo CPPUNIT_USE_TYPEINFO_NAME) -#CPPUNIT_VERSION ok - -configure_file(config/config.h.cmake ${CMAKE_CURRENT_SOURCE_DIR}/include/cppunit/config-auto.h) -## - - -set(cppunit_SRCS - src/cppunit/AdditionalMessage.cpp - src/cppunit/Asserter.cpp - src/cppunit/BriefTestProgressListener.cpp - src/cppunit/CompilerOutputter.cpp - src/cppunit/DefaultProtector.h - src/cppunit/DefaultProtector.cpp - src/cppunit/DynamicLibraryManager.cpp - src/cppunit/DynamicLibraryManagerException.cpp - src/cppunit/Exception.cpp - src/cppunit/Message.cpp - src/cppunit/PlugInManager.cpp - src/cppunit/PlugInParameters.cpp - src/cppunit/Protector.cpp - src/cppunit/ProtectorChain.h - src/cppunit/ProtectorContext.h - src/cppunit/ProtectorChain.cpp - src/cppunit/RepeatedTest.cpp - src/cppunit/ShlDynamicLibraryManager.cpp - src/cppunit/SourceLine.cpp - src/cppunit/StringTools.cpp - src/cppunit/SynchronizedObject.cpp - src/cppunit/Test.cpp - src/cppunit/TestAssert.cpp - src/cppunit/TestCase.cpp - src/cppunit/TestCaseDecorator.cpp - src/cppunit/TestComposite.cpp - src/cppunit/TestDecorator.cpp - src/cppunit/TestFactoryRegistry.cpp - src/cppunit/TestFailure.cpp - src/cppunit/TestLeaf.cpp - src/cppunit/TestNamer.cpp - src/cppunit/TestPath.cpp - src/cppunit/TestPlugInDefaultImpl.cpp - src/cppunit/TestResult.cpp - src/cppunit/TestResultCollector.cpp - src/cppunit/TestRunner.cpp - src/cppunit/TestSetUp.cpp - src/cppunit/TestSuccessListener.cpp - src/cppunit/TestSuite.cpp - src/cppunit/TestSuiteBuilderContext.cpp - src/cppunit/TextOutputter.cpp - src/cppunit/TextTestProgressListener.cpp - src/cppunit/TextTestResult.cpp - src/cppunit/TextTestRunner.cpp - src/cppunit/TypeInfoHelper.cpp - src/cppunit/UnixDynamicLibraryManager.cpp - src/cppunit/Win32DynamicLibraryManager.cpp - src/cppunit/XmlDocument.cpp - src/cppunit/XmlElement.cpp - src/cppunit/XmlOutputter.cpp - src/cppunit/XmlOutputterHook.cpp -) - -add_library(cppunit SHARED ${cppunit_SRCS}) -target_include_directories(cppunit - PUBLIC "$" - "$" -) -target_compile_definitions(cppunit PRIVATE CPPUNIT_BUILD_DLL) - -set_target_properties(cppunit PROPERTIES - VERSION ${${PROJECT_NAME}_VERSION} - SOVERSION ${${PROJECT_NAME}_VERSION} -) - -set(${PROJECT_NAME}_LIBRARIES cppunit) - -# Install support - -install(TARGETS ${${PROJECT_NAME}_LIBRARIES} EXPORT ${PROJECT_NAME}_TARGETS - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib - RUNTIME DESTINATION bin -) -install(DIRECTORY include/cppunit - DESTINATION include -) - -# Config files -configure_file( - ${PROJECT_NAME}Config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake - @ONLY -) -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}ConfigVersion.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - @ONLY -) - -export(EXPORT ${PROJECT_NAME}_TARGETS - FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake -) - -set(config_package_location lib/cmake/${PROJECT_NAME}) -install(EXPORT ${PROJECT_NAME}_TARGETS - FILE ${PROJECT_NAME}Targets.cmake - DESTINATION ${config_package_location} -) -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" - DESTINATION ${config_package_location} -) diff --git a/CMakeExternals/CppUnitconfig.h.cmake b/CMakeExternals/CppUnitconfig.h.cmake deleted file mode 100644 index 4d7ba00387..0000000000 --- a/CMakeExternals/CppUnitconfig.h.cmake +++ /dev/null @@ -1,103 +0,0 @@ -/* Inspired by config/config.h.in, config.h.cmake is used by CMake. */ - -/* define if library uses std::string::compare(string,pos,n) */ -//Not used #undef FUNC_STRING_COMPARE_STRING_FIRST - -/* define to 1 if the library defines strstream */ -#cmakedefine01 CPPUNIT_HAVE_CLASS_STRSTREAM - -/* Define to 1 if you have the header file. */ -#cmakedefine01 CPPUNIT_HAVE_CMATH - -/* Define if you have the GNU dld library. */ -//Not used, dld library is obsolete anyway #undef HAVE_DLD - -/* Define to 1 if you have the `dlerror' function. */ -//Not used #undef HAVE_DLERROR - -/* Define to 1 if you have the header file. */ -#cmakedefine01 CPPUNIT_HAVE_DLFCN_H - -/* Define to 1 if you have the `finite' function. */ -#cmakedefine01 CPPUNIT_HAVE_FINITE - -/* Define to 1 if you have the `_finite' function. */ -#cmakedefine01 CPPUNIT_HAVE__FINITE - -/* define to 1 if the compiler supports GCC C ABI name demangling */ -#cmakedefine01 CPPUNIT_HAVE_GCC_ABI_DEMANGLE - -/* Define to 1 if you have the header file. */ -//Not used #undef HAVE_INTTYPES_H - -/* define if compiler has isfinite */ -#cmakedefine CPPUNIT_HAVE_ISFINITE - -/* Define if you have the libdl library or equivalent. */ -#cmakedefine CPPUNIT_HAVE_LIBDL - -/* Define to 1 if you have the header file. */ -//Not used #undef HAVE_MEMORY_H - -/* define to 1 if the compiler implements namespaces */ -#cmakedefine01 CPPUNIT_HAVE_NAMESPACES - -/* define to 1 if the compiler supports Run-Time Type Identification */ -#cmakedefine01 CPPUNIT_HAVE_RTTI - -/* Define if you have the shl_load function. */ -#cmakedefine CPPUNIT_HAVE_SHL_LOAD - -/* define to 1 if the compiler has stringstream */ -#cmakedefine01 CPPUNIT_HAVE_SSTREAM - -/* Define to 1 if you have the header file. */ -//Not used #undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -//Not used #undef HAVE_STDLIB_H - -/* Define to 1 if you have the header file. */ -//Not used #undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -//Not used #undef HAVE_STRING_H - -/* Define to 1 if you have the header file. */ -#cmakedefine01 CPPUNIT_HAVE_STRSTREAM - -/* Define to 1 if you have the header file. */ -//Not used #undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -//Not used #undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -//Not used #undef HAVE_UNISTD_H - -/* Name of package */ -//Not used #undef PACKAGE - -/* Define to the address where bug reports for this package should be sent. */ -//Not used #undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -//Not used #undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -//Not used #undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -//Not used #undef PACKAGE_TARNAME - -/* Define to the version of this package. */ -//Not used #undef PACKAGE_VERSION - -/* Define to 1 if you have the ANSI C header files. */ -//Not used #undef STDC_HEADERS - -/* Define to 1 to use type_info::name() for class names */ -#cmakedefine01 CPPUNIT_USE_TYPEINFO_NAME - -/* Version number of package */ -#define CPPUNIT_VERSION @CppUnit_VERSION@ diff --git a/CMakeExternals/DCMTK.cmake b/CMakeExternals/DCMTK.cmake index 5c2cec181a..8564e8ca01 100644 --- a/CMakeExternals/DCMTK.cmake +++ b/CMakeExternals/DCMTK.cmake @@ -1,71 +1,70 @@ #----------------------------------------------------------------------------- # DCMTK #----------------------------------------------------------------------------- if(MITK_USE_DCMTK) # Sanity checks if(DEFINED DCMTK_DIR AND NOT EXISTS ${DCMTK_DIR}) message(FATAL_ERROR "DCMTK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj DCMTK) set(proj_DEPENDENCIES ) set(DCMTK_DEPENDS ${proj}) if(NOT DEFINED DCMTK_DIR) if(DCMTK_DICOM_ROOT_ID) set(DCMTK_CXX_FLAGS "${DCMTK_CXX_FLAGS} -DSITE_UID_ROOT=\\\"${DCMTK_DICOM_ROOT_ID}\\\"") set(DCMTK_C_FLAGS "${DCMTK_CXX_FLAGS} -DSITE_UID_ROOT=\\\"${DCMTK_DICOM_ROOT_ID}\\\"") endif() set(additional_args ) if(CTEST_USE_LAUNCHERS) list(APPEND additional_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() mitk_query_custom_ep_vars() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/dcmtk-3.6.6.tar.gz - URL_MD5 f815879d315b916366a9da71339c7575 + GIT_REPOSITORY https://github.com/DCMTK/dcmtk.git + GIT_TAG DCMTK-3.6.7 CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_args} "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} ${DCMTK_CXX_FLAGS}" "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} ${DCMTK_C_FLAGS}" - -DDCMTK_ENABLE_BUILTIN_DICTIONARY:BOOL=ON + -DDCMTK_DEFAULT_DICT:STRING=builtin -DDCMTK_ENABLE_CXX11:BOOL=ON - -DDCMTK_CXX11_FLAGS:STRING=-std=c++${MITK_CXX_STANDARD} -DDCMTK_ENABLE_STL:BOOL=ON -DDCMTK_WITH_DOXYGEN:BOOL=OFF -DDCMTK_WITH_ZLIB:BOOL=OFF # see bug #9894 -DDCMTK_WITH_OPENSSL:BOOL=OFF # see bug #9894 -DDCMTK_WITH_PNG:BOOL=OFF # see bug #9894 -DDCMTK_WITH_TIFF:BOOL=OFF # see bug #9894 -DDCMTK_WITH_XML:BOOL=OFF # see bug #9894 -DDCMTK_WITH_ICONV:BOOL=OFF # see bug #9894 -DDCMTK_WITH_ICU:BOOL=OFF # see T26438 ${${proj}_CUSTOM_CMAKE_ARGS} CMAKE_CACHE_ARGS ${ep_common_cache_args} ${${proj}_CUSTOM_CMAKE_CACHE_ARGS} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} ${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS} DEPENDS ${proj_DEPENDENCIES} ) set(DCMTK_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/Eigen.cmake b/CMakeExternals/Eigen.cmake index d54af9fdd2..a39df68097 100644 --- a/CMakeExternals/Eigen.cmake +++ b/CMakeExternals/Eigen.cmake @@ -1,43 +1,43 @@ #----------------------------------------------------------------------------- # Eigen #----------------------------------------------------------------------------- if(MITK_USE_Eigen) # Sanity checks if(DEFINED Eigen_DIR AND NOT EXISTS ${Eigen_DIR}) message(FATAL_ERROR "Eigen_DIR variable is defined but corresponds to non-existing directory") endif() set(proj Eigen) set(proj_DEPENDENCIES ) set(Eigen_DEPENDS ${proj}) if(NOT DEFINED Eigen_DIR) ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/eigen-3.4.0.tar.gz - URL_MD5 4c527a9171d71a72a9d4186e65bea559 + GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git + GIT_TAG 3.4.0 CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} -DBUILD_TESTING:BOOL=ON -DEIGEN_BUILD_PKGCONFIG:BOOL=OFF CMAKE_CACHE_ARGS ${ep_common_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} ) set(Eigen_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/EmptyFileForPatching.dummy b/CMakeExternals/EmptyFileForPatching.dummy deleted file mode 100644 index d20bf55bfb..0000000000 --- a/CMakeExternals/EmptyFileForPatching.dummy +++ /dev/null @@ -1 +0,0 @@ -@CONTENTS@ diff --git a/CMakeExternals/ExternalProjectList.cmake b/CMakeExternals/ExternalProjectList.cmake index f35f0a1975..ec089a198d 100644 --- a/CMakeExternals/ExternalProjectList.cmake +++ b/CMakeExternals/ExternalProjectList.cmake @@ -1,33 +1,32 @@ mitkFunctionAddExternalProject(NAME Poco ON COMPONENTS Foundation Net Util XML Zip) mitkFunctionAddExternalProject(NAME DCMTK ON DOC "EXPERIMENTAL, superbuild only: Use DCMTK in MITK") mitkFunctionAddExternalProject(NAME OpenIGTLink OFF) mitkFunctionAddExternalProject(NAME tinyxml2 ON ADVANCED) -mitkFunctionAddExternalProject(NAME GDCM ON ADVANCED) mitkFunctionAddExternalProject(NAME Eigen ON ADVANCED DOC "Use the Eigen library") mitkFunctionAddExternalProject(NAME ANN ON ADVANCED DOC "Use Approximate Nearest Neighbor Library") mitkFunctionAddExternalProject(NAME CppUnit ON ADVANCED DOC "Use CppUnit for unit tests") mitkFunctionAddExternalProject(NAME HDF5 ON ADVANCED) mitkFunctionAddExternalProject(NAME OpenCV OFF) mitkFunctionAddExternalProject(NAME Vigra OFF DEPENDS HDF5) mitkFunctionAddExternalProject(NAME ITK ON NO_CACHE DEPENDS HDF5) mitkFunctionAddExternalProject(NAME VTK ON NO_CACHE) mitkFunctionAddExternalProject(NAME Boost ON NO_CACHE) mitkFunctionAddExternalProject(NAME ZLIB OFF ADVANCED) mitkFunctionAddExternalProject(NAME lz4 ON ADVANCED) mitkFunctionAddExternalProject(NAME cpprestsdk OFF DEPENDS Boost ZLIB ADVANCED) mitkFunctionAddExternalProject(NAME ACVD OFF DOC "Use Approximated Centroidal Voronoi Diagrams") mitkFunctionAddExternalProject(NAME CTK ON DEPENDS Qt5 DCMTK DOC "Use CTK in MITK") mitkFunctionAddExternalProject(NAME DCMQI ON DEPENDS DCMTK ITK DOC "Use dcmqi in MITK") -mitkFunctionAddExternalProject(NAME MatchPoint OFF ADVANCED DEPENDS ITK DOC "Use the MatchPoint translation image registration library") +mitkFunctionAddExternalProject(NAME MatchPoint OFF ADVANCED DEPENDS Boost ITK DOC "Use the MatchPoint translation image registration library") mitkFunctionAddExternalProject(NAME nlohmann_json ON ADVANCED) if(MITK_USE_Qt5) mitkFunctionAddExternalProject(NAME Qwt ON ADVANCED DEPENDS Qt5) endif() if(UNIX AND NOT APPLE) mitkFunctionAddExternalProject(NAME PCRE OFF ADVANCED NO_PACKAGE) mitkFunctionAddExternalProject(NAME SWIG OFF ADVANCED NO_PACKAGE DEPENDS PCRE) elseif(WIN32) mitkFunctionAddExternalProject(NAME SWIG OFF ADVANCED NO_PACKAGE) endif() diff --git a/CMakeExternals/GDCM.cmake b/CMakeExternals/GDCM.cmake deleted file mode 100644 index 5d09b10c61..0000000000 --- a/CMakeExternals/GDCM.cmake +++ /dev/null @@ -1,71 +0,0 @@ -#----------------------------------------------------------------------------- -# GDCM -#----------------------------------------------------------------------------- - -# Sanity checks -if(DEFINED GDCM_DIR AND NOT EXISTS ${GDCM_DIR}) - message(FATAL_ERROR "GDCM_DIR variable is defined but corresponds to non-existing directory") -endif() - -# Check if an external ITK build tree was specified. -# If yes, use the GDCM from ITK, otherwise ITK will complain -if(ITK_DIR) - find_package(ITK) - if(ITK_GDCM_DIR) - set(GDCM_DIR ${ITK_GDCM_DIR}) - endif() -endif() - - -set(proj GDCM) -set(proj_DEPENDENCIES ) -set(GDCM_DEPENDS ${proj}) - -if(NOT DEFINED GDCM_DIR) - - set(additional_args ) - if(CTEST_USE_LAUNCHERS) - list(APPEND additional_args - "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" - ) - endif() - - # On Mac some assertions fail that prevent reading certain DICOM files. Bug #19995 - if(APPLE) - list(APPEND additional_args - "-DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG} -DNDEBUG" - ) - endif() - - mitk_query_custom_ep_vars() - - ExternalProject_Add(${proj} - LIST_SEPARATOR ${sep} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/gdcm-3.0.10.tar.gz - URL_MD5 28c70d02c2005a8c9d2a5847c8ba3c00 - CMAKE_GENERATOR ${gen} - CMAKE_GENERATOR_PLATFORM ${gen_platform} - CMAKE_ARGS - ${ep_common_args} - ${additional_args} - -DGDCM_BUILD_SHARED_LIBS:BOOL=ON - -DGDCM_BUILD_DOCBOOK_MANPAGES:BOOL=OFF - ${${proj}_CUSTOM_CMAKE_ARGS} - CMAKE_CACHE_ARGS - ${ep_common_cache_args} - ${${proj}_CUSTOM_CMAKE_CACHE_ARGS} - CMAKE_CACHE_DEFAULT_ARGS - ${ep_common_cache_default_args} - ${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS} - DEPENDS ${proj_DEPENDENCIES} - ) - set(GDCM_DIR ${ep_prefix}) - mitkFunctionInstallExternalCMakeProject(${proj}) - -else() - - mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") - - find_package(GDCM) - -endif() diff --git a/CMakeExternals/GenerateDefaultCMakeBuildSystem.cmake b/CMakeExternals/GenerateDefaultCMakeBuildSystem.cmake deleted file mode 100644 index 0aa7d7df91..0000000000 --- a/CMakeExternals/GenerateDefaultCMakeBuildSystem.cmake +++ /dev/null @@ -1,49 +0,0 @@ -set(ProjConfig.cmake.in " -set(${proj}_LIBRARIES @${proj}_LIBRARIES@) - -if(NOT TARGET ${proj_target}) - include(\"\${CMAKE_CURRENT_LIST_DIR}/${proj}Targets.cmake\") -endif() -") - -set(ProjConfigVersion.cmake.in " -# The created file sets PACKAGE_VERSION_EXACT if the current version string and -# the requested version string are exactly the same and it sets -# PACKAGE_VERSION_COMPATIBLE if the current version major number == requested version major number -# and the current version minor number >= requested version minor number - -set(PACKAGE_VERSION_MAJOR @${proj}_MAJOR_VERSION@) -set(PACKAGE_VERSION_MINOR @${proj}_MINOR_VERSION@) -set(PACKAGE_VERSION_PATCH @${proj}_PATCH_VERSION@) -set(PACKAGE_VERSION \"@${proj}_VERSION@\") - -if(PACKAGE_VERSION VERSION_EQUAL PACKAGE_FIND_VERSION) - set(PACKAGE_VERSION_EXACT TRUE) -else() - set(PACKAGE_VERSION_EXACT FALSE) - if(NOT PACKAGE_VERSION_MAJOR EQUAL PACKAGE_FIND_VERSION_MAJOR) - set(PACKAGE_VERSION_COMPATIBLE FALSE) - elseif(PACKAGE_VERSION_MINOR LESS PACKAGE_FIND_VERSION_MINOR) - set(PACKAGE_VERSION_COMPATIBLE FALSE) - else() - set(PACKAGE_VERSION_CcOMPATIBLE TRUE) - endif() -endif() - -# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it: -if(\"\${CMAKE_SIZEOF_VOID_P}\" STREQUAL \"\" OR \"@CMAKE_SIZEOF_VOID_P@\" STREQUAL \"\") - return() -endif() - -# check that the installed version has the same 32/64bit-ness as the one which is currently searching: -if(NOT \"\${CMAKE_SIZEOF_VOID_P}\" STREQUAL \"@CMAKE_SIZEOF_VOID_P@\") - math(EXPR installedBits \"@CMAKE_SIZEOF_VOID_P@ * 8\") - set(PACKAGE_VERSION \"\${PACKAGE_VERSION} (\${installedBits}bit)\") - set(PACKAGE_VERSION_UNSUITABLE TRUE) -endif() -") - -configure_file(${CMAKE_CURRENT_LIST_DIR}/${proj}CMakeLists.txt "${CMAKE_CURRENT_BINARY_DIR}/CMakeLists.txt" COPYONLY) - -file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${proj}Config.cmake.in "${ProjConfig.cmake.in}") -file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${proj}ConfigVersion.cmake.in "${ProjConfigVersion.cmake.in}") diff --git a/CMakeExternals/HDF5.cmake b/CMakeExternals/HDF5.cmake index b336c0cd5b..bc0d68bba7 100644 --- a/CMakeExternals/HDF5.cmake +++ b/CMakeExternals/HDF5.cmake @@ -1,59 +1,59 @@ #----------------------------------------------------------------------------- # HDF5 #----------------------------------------------------------------------------- if(MITK_USE_HDF5) # Sanity checks if(DEFINED HDF5_DIR AND NOT EXISTS ${HDF5_DIR}) message(FATAL_ERROR "HDF5_DIR variable is defined but corresponds to non-existing directory") endif() set(proj HDF5) set(proj_DEPENDENCIES ) set(HDF5_DEPENDS ${proj}) if(NOT DEFINED HDF5_DIR) set(additional_args ) if(CTEST_USE_LAUNCHERS) list(APPEND additional_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() # We might build static libs with -DBUILD_SHARED_LIBS=0 but this conflicts with # the in ITK integrated version! So we need to go the way with dynamic libs. Too # bad :( This would be fixed by using an external HDF-Installation with ITK/VTK ExternalProject_Add(${proj} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/hdf5-1.8.17.tar.gz - URL_MD5 7d572f8f3b798a628b8245af0391a0ca + GIT_REPOSITORY https://github.com/HDFGroup/hdf5.git + GIT_TAG hdf5-1_8_17 CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_args} -DHDF5_BUILD_HL_LIB:BOOL=ON -DHDF5_BUILD_CPP_LIB:BOOL=ON -DCMAKE_INSTALL_PREFIX:PATH= CMAKE_CACHE_ARGS ${ep_common_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) ExternalProject_Get_Property(${proj} install_dir) if(WIN32) set(HDF5_DIR ${install_dir}/cmake/) else() set(HDF5_DIR ${install_dir}/share/cmake) endif() else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif(MITK_USE_HDF5) diff --git a/CMakeExternals/ITK-5.2.1.patch b/CMakeExternals/ITK-5.2.1.patch deleted file mode 100644 index 8ad1cb5184..0000000000 --- a/CMakeExternals/ITK-5.2.1.patch +++ /dev/null @@ -1,35 +0,0 @@ -diff --git a/Modules/Video/BridgeOpenCV/include/itkOpenCVVideoIO.h b/Modules/Video/BridgeOpenCV/include/itkOpenCVVideoIO.h -index 1570a182..96a70f17 100644 ---- a/Modules/Video/BridgeOpenCV/include/itkOpenCVVideoIO.h -+++ b/Modules/Video/BridgeOpenCV/include/itkOpenCVVideoIO.h -@@ -168,7 +168,7 @@ public: - const std::vector & dim, - const char * fourCC, - unsigned int nChannels, -- IOComponentType componentType); -+ IOComponentEnum componentType); - - protected: - OpenCVVideoIO(); -diff --git a/Modules/Video/BridgeOpenCV/src/itkOpenCVVideoIO.cxx b/Modules/Video/BridgeOpenCV/src/itkOpenCVVideoIO.cxx -index 7d7db3b8..8ce71c8f 100644 ---- a/Modules/Video/BridgeOpenCV/src/itkOpenCVVideoIO.cxx -+++ b/Modules/Video/BridgeOpenCV/src/itkOpenCVVideoIO.cxx -@@ -436,7 +436,7 @@ OpenCVVideoIO::SetWriterParameters(TemporalRatioType fps, - const std::vector & dim, - const char * fourCC, - unsigned int nChannels, -- IOComponentType componentType) -+ IOComponentEnum componentType) - { - if (this->m_ReaderOpen || this->m_WriterOpen) - { -@@ -444,7 +444,7 @@ OpenCVVideoIO::SetWriterParameters(TemporalRatioType fps, - } - - // Make sure componentType is acceptable (right now we only support char) -- if (componentType != UCHAR) -+ if (componentType != IOComponentEnum::UCHAR) - { - itkExceptionMacro("OpenCV IO only supports writing video with pixels of UCHAR"); - } diff --git a/CMakeExternals/ITK.cmake b/CMakeExternals/ITK.cmake index b93d27df57..7fee8515d0 100644 --- a/CMakeExternals/ITK.cmake +++ b/CMakeExternals/ITK.cmake @@ -1,89 +1,86 @@ #----------------------------------------------------------------------------- # ITK #----------------------------------------------------------------------------- # Sanity checks if(DEFINED ITK_DIR AND NOT EXISTS ${ITK_DIR}) message(FATAL_ERROR "ITK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj ITK) -set(proj_DEPENDENCIES GDCM) +set(proj_DEPENDENCIES ) if(MITK_USE_OpenCV) list(APPEND proj_DEPENDENCIES OpenCV) endif() if(MITK_USE_HDF5) list(APPEND proj_DEPENDENCIES HDF5) endif() set(ITK_DEPENDS ${proj}) if(NOT DEFINED ITK_DIR) set(additional_cmake_args -DUSE_WRAP_ITK:BOOL=OFF) list(APPEND additional_cmake_args -DITKV4_COMPATIBILITY:BOOL=OFF -DITK_LEGACY_REMOVE:BOOL=ON ) if(MITK_USE_OpenCV) list(APPEND additional_cmake_args -DModule_ITKVideoBridgeOpenCV:BOOL=ON -DOpenCV_DIR:PATH=${OpenCV_DIR} "-DCMAKE_CONFIGURATION_TYPES:STRING=Debug$Release" ) endif() # Keep the behaviour of ITK 4.3 which by default turned on ITK Review # see MITK bug #17338 list(APPEND additional_cmake_args -DModule_ITKReview:BOOL=ON -DModule_ITKOpenJPEG:BOOL=ON # for 4.7, the OpenJPEG is needed by review but the variable must be set -DModule_IsotropicWavelets:BOOL=ON ) if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() mitk_query_custom_ep_vars() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} UPDATE_COMMAND "" - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/InsightToolkit-5.2.1.tar.gz - URL_MD5 48c1fe49f75fdaa91b31bbf9dda01a42 - PATCH_COMMAND ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/ITK-5.2.1.patch + GIT_REPOSITORY https://github.com/MITK/ITK.git + GIT_TAG v5.2.1-patched CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_cmake_args} -DBUILD_EXAMPLES:BOOL=OFF - -DITK_USE_SYSTEM_GDCM:BOOL=ON - -DGDCM_DIR:PATH=${GDCM_DIR} -DITK_USE_SYSTEM_HDF5:BOOL=ON -DHDF5_DIR:PATH=${HDF5_DIR} ${${proj}_CUSTOM_CMAKE_ARGS} CMAKE_CACHE_ARGS ${ep_common_cache_args} ${${proj}_CUSTOM_CMAKE_CACHE_ARGS} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} ${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS} DEPENDS ${proj_DEPENDENCIES} ) set(ITK_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/CMakeExternals/MatchPoint.cmake b/CMakeExternals/MatchPoint.cmake index 85d8a0556f..d7b84cbc9d 100644 --- a/CMakeExternals/MatchPoint.cmake +++ b/CMakeExternals/MatchPoint.cmake @@ -1,75 +1,74 @@ #----------------------------------------------------------------------------- # MatchPoint #----------------------------------------------------------------------------- if(MITK_USE_MatchPoint) set(MatchPoint_SOURCE_DIR "" CACHE PATH "Location of the MatchPoint source directory") mark_as_advanced(MatchPoint_SOURCE_DIR) # Sanity checks if(DEFINED MatchPoint_DIR AND NOT EXISTS ${MatchPoint_DIR}) message(FATAL_ERROR "MatchPoint_DIR variable is defined but corresponds to non-existing directory") endif() if(NOT MatchPoint_DIR AND MatchPoint_SOURCE_DIR AND NOT EXISTS ${MatchPoint_SOURCE_DIR}) message(FATAL_ERROR "MatchPoint_SOURCE_DIR variable is defined but corresponds to non-existing directory") endif() set(proj MatchPoint) - set(proj_DEPENDENCIES ITK) + set(proj_DEPENDENCIES Boost ITK) set(MatchPoint_DEPENDS ${proj}) if(NOT MatchPoint_DIR) set(additional_cmake_args) if(MITK_USE_OpenCV) list(APPEND additional_cmake_args "-DCMAKE_CONFIGURATION_TYPES:STRING=Debug$Release") endif() if(MatchPoint_SOURCE_DIR) set(download_step SOURCE_DIR ${MatchPoint_SOURCE_DIR}) else() set(download_step - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/MatchPoint_rev_f7699d1e.tar.gz - URL_MD5 8a24fbdccd6f18f158f49a0ad4fa3faa + GIT_REPOSITORY https://github.com/MIC-DKFZ/MatchPoint.git + GIT_TAG e63dfdbb ) endif() string(REPLACE "-DBOOST_ALL_DYN_LINK" "" modified_ep_common_args "${ep_common_args}") ExternalProject_Add(${proj} ${download_step} # INSTALL_COMMAND "${CMAKE_COMMAND} -P cmake_install.cmake" CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${modified_ep_common_args} ${additional_cmake_args} -DBUILD_TESTING:BOOL=OFF -DITK_DIR:PATH=${ITK_DIR} #/src/ITK-build - -DMAP_USE_SYSTEM_GDCM:BOOL=ON + "-DBoost_DIR:PATH=${Boost_DIR}" -DMAP_USE_SYSTEM_HDF5:BOOL=ON -DMAP_DISABLE_ITK_IO_FACTORY_AUTO_REGISTER:BOOL=ON -DMAP_WRAP_Plastimatch:BOOL=ON -DMAP_BUILD_Ontology:BOOL=ON -DMAP_BUILD_Ontology_simple:BOOL=ON - -DGDCM_DIR:PATH=${GDCM_DIR} -DHDF5_DIR:PATH=${HDF5_DIR} CMAKE_CACHE_ARGS ${ep_common_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) ExternalProject_Get_Property(${proj} binary_dir) set(${proj}_DIR ${binary_dir}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif(MITK_USE_MatchPoint) diff --git a/CMakeExternals/OpenCV.cmake b/CMakeExternals/OpenCV.cmake index 36aacee775..65d61e2f88 100644 --- a/CMakeExternals/OpenCV.cmake +++ b/CMakeExternals/OpenCV.cmake @@ -1,85 +1,72 @@ #----------------------------------------------------------------------------- # OpenCV #----------------------------------------------------------------------------- if(MITK_USE_OpenCV) # Sanity checks if(DEFINED OpenCV_DIR AND NOT EXISTS ${OpenCV_DIR}) message(FATAL_ERROR "OpenCV_DIR variable is defined but corresponds to non-existing directory") endif() set(proj OpenCV) set(proj_DEPENDENCIES) set(OpenCV_DEPENDS ${proj}) if(NOT DEFINED OpenCV_DIR) - set(additional_cmake_args - -DBUILD_opencv_java:BOOL=OFF - -DBUILD_opencv_ts:BOOL=OFF - -DBUILD_PERF_TESTS:BOOL=OFF - -DBUILD_opencv_python:BOOL=OFF - -DBUILD_opencv_python3:BOOL=OFF - -DBUILD_opencv_python_bindings_generator:BOOL=OFF - #-DBUILD_NEW_PYTHON_SUPPORT:BOOL=OFF - ) - - if(MITK_USE_Qt5) - list(APPEND additional_cmake_args - -DWITH_QT:BOOL=OFF - -DWITH_QT_OPENGL:BOOL=OFF - -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE} - ) - endif() + set(additional_cmake_args) if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" - ) + ) endif() mitk_query_custom_ep_vars() - set(opencv_url ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/opencv-3.4.16.tar.gz) - set(opencv_url_md5 ce69441a75d3358e22fbfb579fab52a9) - ExternalProject_Add(${proj} + GIT_REPOSITORY https://github.com/opencv/opencv.git + GIT_TAG 4.6.0 LIST_SEPARATOR ${sep} - URL ${opencv_url} - URL_MD5 ${opencv_url_md5} CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} + -DBUILD_JAVA:BOOL=OFF + -DBUILD_opencv_ts:BOOL=OFF + -DBUILD_PERF_TESTS:BOOL=OFF + -DBUILD_opencv_python3:BOOL=OFF + -DBUILD_opencv_python_bindings_generator:BOOL=OFF + -DBUILD_opencv_python_tests:BOOL=OFF + -DWITH_QT:BOOL=OFF -DBUILD_TESTS:BOOL=OFF -DBUILD_DOCS:BOOL=OFF -DBUILD_EXAMPLES:BOOL=OFF - -DBUILD_DOXYGEN_DOCS:BOOL=OFF -DWITH_CUDA:BOOL=OFF -DWITH_VTK:BOOL=OFF -DENABLE_CXX11:BOOL=ON -DWITH_IPP:BOOL=OFF -DBUILD_IPP_IW:BOOL=OFF -DENABLE_PRECOMPILED_HEADERS:BOOL=OFF ${additional_cmake_args} ${${proj}_CUSTOM_CMAKE_ARGS} CMAKE_CACHE_ARGS ${ep_common_cache_args} ${${proj}_CUSTOM_CMAKE_CACHE_ARGS} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} ${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS} DEPENDS ${proj_DEPENDENCIES} ) set(OpenCV_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/OpenIGTLink-92bc3d7b.patch b/CMakeExternals/OpenIGTLink-92bc3d7b.patch deleted file mode 100644 index 314b33090e..0000000000 --- a/CMakeExternals/OpenIGTLink-92bc3d7b.patch +++ /dev/null @@ -1,65 +0,0 @@ ---- OpenIGTLink/Source/CMakeLists.txt.original 2017-06-28 12:28:12.000000000 +0200 -+++ OpenIGTLink/Source/CMakeLists.txt 2019-03-31 04:04:46.392945177 +0200 -@@ -71,12 +71,11 @@ - igtlTransformMessage.cxx - ) - --SET(OpenIGTLink_INCLUDE_FILES) --IF( MSVC OR ${CMAKE_GENERATOR} MATCHES "Xcode" ) -- LIST(APPEND OpenIGTLink_INCLUDE_FILES -+SET(OpenIGTLink_INCLUDE_FILES - igtlutil/igtl_header.h - igtlutil/igtl_image.h - igtlutil/igtl_position.h -+ igtlutil/igtl_status.h - igtlutil/igtl_transform.h - igtlutil/igtl_types.h - igtlutil/igtl_util.h -@@ -117,7 +116,6 @@ - igtlWindows.h - igtlCommon.h - ) --ENDIF() - - # Add support for OpenIGTLink version 2 - IF (${OpenIGTLink_PROTOCOL_VERSION} GREATER "1" ) -@@ -150,7 +148,7 @@ - igtlBindMessage.cxx - igtlNDArrayMessage.cxx - ) -- IF( MSVC OR ${CMAKE_GENERATOR} MATCHES "Xcode" ) -+ - LIST(APPEND OpenIGTLink_INCLUDE_FILES - igtlutil/igtl_colortable.h - igtlutil/igtl_imgmeta.h -@@ -180,7 +178,6 @@ - igtlBindMessage.h - igtlNDArrayMessage.h - ) -- ENDIF() - ENDIF() - - # Add support for OpenIGTLink version 3 -@@ -191,14 +188,13 @@ - igtlutil/igtl_command.c - igtlutil/igtl_query.c - ) -- IF( MSVC OR ${CMAKE_GENERATOR} MATCHES "Xcode" ) -+ - LIST(APPEND OpenIGTLink_INCLUDE_FILES - igtlCommandMessage.h - igtlQueryMessage.h - igtlutil/igtl_command.h - igtlutil/igtl_query.h - ) -- ENDIF() - ENDIF() - - ADD_LIBRARY(OpenIGTLink ${OpenIGTLink_SOURCES} ${OpenIGTLink_INCLUDE_FILES}) -@@ -227,4 +223,4 @@ - INSTALL(TARGETS OpenIGTLink EXPORT OpenIGTLink - RUNTIME DESTINATION ${OpenIGTLink_INSTALL_BIN_DIR} COMPONENT RuntimeLibraries - LIBRARY DESTINATION ${OpenIGTLink_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries -- ARCHIVE DESTINATION ${OpenIGTLink_INSTALL_LIB_DIR} COMPONENT Development) -\ No newline at end of file -+ ARCHIVE DESTINATION ${OpenIGTLink_INSTALL_LIB_DIR} COMPONENT Development) diff --git a/CMakeExternals/OpenIGTLink.cmake b/CMakeExternals/OpenIGTLink.cmake index 62611eafa8..086f2ccce7 100644 --- a/CMakeExternals/OpenIGTLink.cmake +++ b/CMakeExternals/OpenIGTLink.cmake @@ -1,58 +1,57 @@ #----------------------------------------------------------------------------- # OpenIGTLink #----------------------------------------------------------------------------- if(MITK_USE_OpenIGTLink) # Sanity checks if(DEFINED OpenIGTLink_DIR AND NOT EXISTS ${OpenIGTLink_DIR}) message(FATAL_ERROR "OpenIGTLink_DIR variable is defined but corresponds to non-existing directory") endif() set(proj OpenIGTLink) set(proj_DEPENDENCIES ) set(${proj}_DEPENDS ${proj}) if(NOT DEFINED OpenIGTLink_DIR) set(additional_cmake_args ) if(CTEST_USE_LAUNCHERS) set(additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() mitk_query_custom_ep_vars() ExternalProject_Add(${proj} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/OpenIGTLink-release-3.0.tar.gz - URL_MD5 0a759655da037f6df2087dd2690d1ae2 - PATCH_COMMAND ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/OpenIGTLink-92bc3d7b.patch + GIT_REPOSITORY https://github.com/openigtlink/OpenIGTLink.git + GIT_TAG d4eaae93 CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_cmake_args} -DBUILD_EXAMPLES:BOOL=OFF -DOpenIGTLink_PROTOCOL_VERSION_2:BOOL=ON -DOpenIGTLink_INSTALL_LIB_DIR:STRING=lib -DOpenIGTLink_INSTALL_PACKAGE_DIR:STRING=lib/cmake/OpenIGTLink -DOpenIGTLink_INSTALL_NO_DOCUMENTATION:BOOL=ON ${${proj}_CUSTOM_CMAKE_ARGS} CMAKE_CACHE_ARGS ${ep_common_cache_args} ${${proj}_CUSTOM_CMAKE_CACHE_ARGS} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} ${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS} DEPENDS ${proj_DEPENDENCIES} ) set(OpenIGTLink_DIR "${ep_prefix}/lib/cmake/OpenIGTLink") else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/Poco.cmake b/CMakeExternals/Poco.cmake index dd5d7bcd72..22b544244f 100644 --- a/CMakeExternals/Poco.cmake +++ b/CMakeExternals/Poco.cmake @@ -1,93 +1,103 @@ #----------------------------------------------------------------------------- # Poco #----------------------------------------------------------------------------- if(MITK_USE_Poco) # Sanity checks if(DEFINED Poco_DIR AND NOT EXISTS ${Poco_DIR}) message(FATAL_ERROR "Poco_DIR variable is defined but corresponds to non-existing directory") endif() set(proj Poco) set(proj_DEPENDENCIES ) set(${proj}_DEPENDS ${proj}) if(NOT DEFINED ${proj}_DIR) set(additional_cmake_args ) if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() mitk_query_custom_ep_vars() set(ssl_args -DENABLE_CRYPTO:BOOL=OFF -DENABLE_NETSSL:BOOL=OFF -DENABLE_NETSSL_WIN:BOOL=OFF ) if(OpenSSL_FOUND) set(ssl_args -DENABLE_CRYPTO:BOOL=ON -DENABLE_NETSSL:BOOL=ON -DFORCE_OPENSSL:BOOL=ON ) if(OPENSSL_ROOT_DIR) list(APPEND ssl_args "-DOPENSSL_ROOT_DIR:PATH=${OPENSSL_ROOT_DIR}" ) endif() endif() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/poco-1.9.0.tar.gz - URL_MD5 1011839033f72de138f0c523c2caa121 + GIT_REPOSITORY https://github.com/pocoproject/poco.git + GIT_TAG poco-1.12.2-release CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_cmake_args} ${ssl_args} - -DENABLE_XML:BOOL=ON - -DENABLE_JSON:BOOL=ON - -DENABLE_MONGODB:BOOL=OFF - -DENABLE_PDF:BOOL=OFF - -DENABLE_UTIL:BOOL=ON - -DENABLE_NET:BOOL=ON + -DENABLE_ACTIVERECORD:BOOL=OFF + -DENABLE_ACTIVERECORD_COMPILER:BOOL=OFF + -DENABLE_APACHECONNECTOR:BOOL=OFF + -DENABLE_CPPPARSER:BOOL=OFF -DENABLE_DATA:BOOL=OFF - -DENABLE_DATA_SQLITE:BOOL=OFF -DENABLE_DATA_MYSQL:BOOL=OFF -DENABLE_DATA_ODBC:BOOL=OFF - -DENABLE_SEVENZIP:BOOL=OFF - -DENABLE_ZIP:BOOL=ON - -DENABLE_APACHECONNECTOR:BOOL=OFF - -DENABLE_CPPPARSER:BOOL=OFF - -DENABLE_POCODOC:BOOL=OFF + -DENABLE_DATA_POSTGRESQL:BOOL=OFF + -DENABLE_DATA_SQLITE:BOOL=OFF + -DENABLE_ENCODINGS:BOOL=OFF + -DENABLE_ENCODINGS_COMPILER:BOOL=OFF + -DENABLE_FOUNDATION:BOOL=ON + -DENABLE_JSON:BOOL=ON + -DENABLE_JWT:BOOL=OFF + -DENABLE_MONGODB:BOOL=OFF + -DENABLE_NET:BOOL=ON -DENABLE_PAGECOMPILER:BOOL=OFF -DENABLE_PAGECOMPILER_FILE2PAGE:BOOL=OFF + -DENABLE_PDF:BOOL=OFF + -DENABLE_POCODOC:BOOL=OFF + -DENABLE_PROMETHEUS:BOOL=OFF + -DENABLE_REDIS:BOOL=OFF + -DENABLE_SEVENZIP:BOOL=OFF + -DENABLE_TESTS:BOOL=OFF + -DENABLE_UTIL:BOOL=ON + -DENABLE_XML:BOOL=ON + -DENABLE_ZIP:BOOL=ON ${${proj}_CUSTOM_CMAKE_ARGS} CMAKE_CACHE_ARGS ${ep_common_cache_args} ${${proj}_CUSTOM_CMAKE_CACHE_ARGS} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} ${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS} DEPENDS ${proj_DEPENDENCIES} ) set(${proj}_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/Qwt.cmake b/CMakeExternals/Qwt.cmake index e14f51089a..319d941763 100644 --- a/CMakeExternals/Qwt.cmake +++ b/CMakeExternals/Qwt.cmake @@ -1,61 +1,57 @@ #----------------------------------------------------------------------------- # Qwt #----------------------------------------------------------------------------- if(MITK_USE_Qwt) # Sanity checks if(DEFINED Qwt_DIR AND NOT EXISTS ${Qwt_DIR}) message(FATAL_ERROR "Qwt_DIR variable is defined but corresponds to non-existing directory") endif() set(proj Qwt) set(proj_DEPENDENCIES ) set(${proj}_DEPENDS ${proj}) if(NOT DEFINED ${proj}_DIR) - set(patch_cmd ${CMAKE_COMMAND} -Dproj:STRING=${proj} -Dproj_target:STRING=qwt -P ${CMAKE_CURRENT_LIST_DIR}/GenerateDefaultCMakeBuildSystem.cmake) - set(qt54patch_cmd ${CMAKE_COMMAND} -DTEMPLATE_FILE:FILEPATH=${MITK_SOURCE_DIR}/CMakeExternals/EmptyFileForPatching.dummy -P ${MITK_SOURCE_DIR}/CMakeExternals/PatchQwt-6.1.0.cmake) - set(additional_cmake_args "-DQt5Svg_DIR:PATH=${Qt5Svg_DIR}" "-DQt5OpenGL_DIR:PATH=${Qt5OpenGL_DIR}" "-DQt5PrintSupport_DIR:PATH=${Qt5PrintSupport_DIR}" "-DQt5Concurrent_DIR:PATH=${Qt5Concurrent_DIR}" "-DQt5Designer_DIR:PATH=${Qt5_DIR}Designer" ) if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/qwt-6.1.5.tar.bz2 - URL_MD5 d65582f99312796ed42c3be3208ed3db - PATCH_COMMAND ${CMAKE_COMMAND} -Dproj=${proj} -Dproj_target:STRING=qwt -P ${CMAKE_CURRENT_LIST_DIR}/GenerateDefaultCMakeBuildSystem.cmake + GIT_REPOSITORY https://github.com/MITK/Qwt.git + GIT_TAG v6.2.0-patched CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_cmake_args} - ${qt_project_args} CMAKE_CACHE_ARGS + -DQWT_BUILD_DESIGNER_PLUGIN:BOOL=OFF ${ep_common_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) set(${proj}_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/QwtCMakeLists.txt b/CMakeExternals/QwtCMakeLists.txt deleted file mode 100644 index 5c91a4acac..0000000000 --- a/CMakeExternals/QwtCMakeLists.txt +++ /dev/null @@ -1,264 +0,0 @@ -cmake_minimum_required(VERSION 3.18) - -project(Qwt) - -set(${PROJECT_NAME}_MAJOR_VERSION 6) -set(${PROJECT_NAME}_MINOR_VERSION 1) -set(${PROJECT_NAME}_PATCH_VERSION 0) -set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_VERSION}) - -set(QWT_MOC_HEADERS - - # General - qwt_dyngrid_layout.h - qwt_magnifier.h - qwt_panner.h - qwt_picker.h - qwt_text_label.h - - # QwtPlot - qwt_abstract_legend.h - qwt_legend.h - qwt_legend_label.h - qwt_plot.h - qwt_plot_renderer.h - qwt_plot_canvas.h - qwt_plot_panner.h - qwt_plot_picker.h - qwt_plot_zoomer.h - qwt_plot_magnifier.h - qwt_sampling_thread.h - qwt_scale_widget.h - - # QwtOpenGL - qwt_plot_glcanvas.h - - # QwtWidgets - qwt_abstract_slider.h - qwt_abstract_scale.h - qwt_analog_clock.h - qwt_compass.h - qwt_counter.h - qwt_dial.h - qwt_knob.h - qwt_slider.h - qwt_thermo.h - qwt_wheel.h -) - -set(QWT_SOURCES - - # General - qwt_abstract_scale_draw.cpp - qwt_clipper.cpp - qwt_color_map.cpp - qwt_column_symbol.cpp - qwt_date.cpp - qwt_date_scale_draw.cpp - qwt_date_scale_engine.cpp - qwt_dyngrid_layout.cpp - qwt_event_pattern.cpp - qwt_graphic.cpp - qwt_interval.cpp - qwt_interval_symbol.cpp - qwt_math.cpp - qwt_magnifier.cpp - qwt_null_paintdevice.cpp - qwt_painter.cpp - qwt_painter_command.cpp - qwt_panner.cpp - qwt_picker.cpp - qwt_picker_machine.cpp - qwt_pixel_matrix.cpp - qwt_point_3d.cpp - qwt_point_polar.cpp - qwt_round_scale_draw.cpp - qwt_scale_div.cpp - qwt_scale_draw.cpp - qwt_scale_map.cpp - qwt_spline.cpp - qwt_scale_engine.cpp - qwt_symbol.cpp - qwt_system_clock.cpp - qwt_text_engine.cpp - qwt_text_label.cpp - qwt_text.cpp - qwt_transform.cpp - qwt_widget_overlay.cpp - - # QwtPlot - qwt_curve_fitter.cpp - qwt_abstract_legend.cpp - qwt_legend.cpp - qwt_legend_data.cpp - qwt_legend_label.cpp - qwt_plot.cpp - qwt_plot_renderer.cpp - qwt_plot_xml.cpp - qwt_plot_axis.cpp - qwt_plot_curve.cpp - qwt_plot_dict.cpp - qwt_plot_directpainter.cpp - qwt_plot_grid.cpp - qwt_plot_histogram.cpp - qwt_plot_item.cpp - qwt_plot_abstract_barchart.cpp - qwt_plot_barchart.cpp - qwt_plot_multi_barchart.cpp - qwt_plot_intervalcurve.cpp - qwt_plot_zoneitem.cpp - qwt_plot_tradingcurve.cpp - qwt_plot_spectrogram.cpp - qwt_plot_spectrocurve.cpp - qwt_plot_scaleitem.cpp - qwt_plot_legenditem.cpp - qwt_plot_seriesitem.cpp - qwt_plot_shapeitem.cpp - qwt_plot_marker.cpp - qwt_plot_textlabel.cpp - qwt_plot_layout.cpp - qwt_plot_canvas.cpp - qwt_plot_panner.cpp - qwt_plot_rasteritem.cpp - qwt_plot_picker.cpp - qwt_plot_zoomer.cpp - qwt_plot_magnifier.cpp - qwt_plot_rescaler.cpp - qwt_point_mapper.cpp - qwt_raster_data.cpp - qwt_matrix_raster_data.cpp - qwt_sampling_thread.cpp - qwt_series_data.cpp - qwt_point_data.cpp - qwt_scale_widget.cpp - - # QwtSvg - qwt_plot_svgitem.cpp - - # QwtOpenGL - qwt_plot_glcanvas.cpp - - # QwtWidgets - qwt_abstract_slider.cpp - qwt_abstract_scale.cpp - qwt_arrow_button.cpp - qwt_analog_clock.cpp - qwt_compass.cpp - qwt_compass_rose.cpp - qwt_counter.cpp - qwt_dial.cpp - qwt_dial_needle.cpp - qwt_knob.cpp - qwt_slider.cpp - qwt_thermo.cpp - qwt_wheel.cpp - -) - -set(_qwt_moc_headers ) -foreach(_header ${QWT_MOC_HEADERS}) - list(APPEND _qwt_moc_headers src/${_header}) -endforeach() - -set(_qwt_sources ) -foreach(_source ${QWT_SOURCES}) - list(APPEND _qwt_sources src/${_source}) -endforeach() - -find_package(Qt5Svg REQUIRED) -find_package(Qt5OpenGL REQUIRED) -find_package(Qt5PrintSupport REQUIRED) -find_package(Qt5Concurrent REQUIRED) - -qt5_wrap_cpp(_qwt_sources ${_qwt_moc_headers}) - -add_library(qwt SHARED ${_qwt_sources}) -target_link_libraries(qwt PUBLIC Qt5::Svg Qt5::OpenGL Qt5::PrintSupport Qt5::Concurrent) - -target_compile_definitions(qwt PUBLIC QWT_DLL PRIVATE QWT_MAKEDLL) -set_target_properties(qwt PROPERTIES - SOVERSION ${${PROJECT_NAME}_VERSION} -) - - -# Build the designer plug-in - -option(QWT_BUILD_DESIGNER_PLUGIN "Build the Qt Designer plugin" ON) - if (QWT_BUILD_DESIGNER_PLUGIN) - - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) - - set(_qwt_designer_sources - designer/qwt_designer_plotdialog.cpp - designer/qwt_designer_plugin.cpp - ) - - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/plugins/designer) - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/plugins/designer) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/plugins/designer) - - find_package(Qt5Designer REQUIRED) - include_directories(${Qt5Designer_INCLUDE_DIRS}) - qt5_wrap_cpp(_qwt_designer_sources - designer/qwt_designer_plugin.h - designer/qwt_designer_plotdialog.h - ) - qt5_add_resources(_qwt_designer_sources designer/qwt_designer_plugin.qrc) - - add_library(qwt_designer_plugin SHARED ${_qwt_designer_sources}) - target_link_libraries(qwt_designer_plugin qwt Qt5::Designer) - - set_target_properties(qwt_designer_plugin PROPERTIES - SOVERSION ${${PROJECT_NAME}_VERSION} - COMPILE_DEFINITIONS QWT_DLL) -endif() - -set(${PROJECT_NAME}_LIBRARIES qwt) - -# Install support - -if (QWT_BUILD_DESIGNER_PLUGIN) - install(TARGETS qwt_designer_plugin - RUNTIME DESTINATION plugins/designer - LIBRARY DESTINATION plugins/designer - ) -endif() - -install(TARGETS ${${PROJECT_NAME}_LIBRARIES} EXPORT ${PROJECT_NAME}_TARGETS - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib - RUNTIME DESTINATION bin - INCLUDES DESTINATION include/${PROJECT_NAME} -) -install(DIRECTORY src/ - DESTINATION include/${PROJECT_NAME} - FILES_MATCHING PATTERN "*.h" -) - -# Config files - -configure_file( - ${PROJECT_NAME}Config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake - @ONLY -) -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}ConfigVersion.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - @ONLY -) - -export(EXPORT ${PROJECT_NAME}_TARGETS - FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake -) - -set(config_package_location lib/cmake/${PROJECT_NAME}) -install(EXPORT ${PROJECT_NAME}_TARGETS - FILE ${PROJECT_NAME}Targets.cmake - DESTINATION ${config_package_location} -) -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" - DESTINATION ${config_package_location} -) diff --git a/CMakeExternals/SWIG.cmake b/CMakeExternals/SWIG.cmake index eccbfd6aae..62a99606fe 100644 --- a/CMakeExternals/SWIG.cmake +++ b/CMakeExternals/SWIG.cmake @@ -1,72 +1,76 @@ #------------------------------------------------------------ # SWIG (Simple Wrapper Interface Generator) #----------------------------------------------------------- if(MITK_USE_SWIG) if(DEFINED SWIG_DIR AND NOT EXISTS ${SWIG_DIR}) message(FATAL_ERROR "SWIG_DIR variable is defined but corresponds to non-existing directory") endif() - set(SWIG_TARGET_VERSION 3.0.2) + set(SWIG_TARGET_VERSION 4.0.2) set(proj SWIG) if(WIN32) set(proj_DEPENDENCIES) else() set(proj_DEPENDENCIES PCRE) endif() set(SWIG_DEPENDS ${proj}) if(NOT SWIG_DIR) # We don't "install" SWIG in the common install prefix, # since it is only used as a tool during the MITK super-build # to generate the Python wrappings for some projects. # binary SWIG for windows if(WIN32) set(swig_source_dir ${CMAKE_CURRENT_BINARY_DIR}/swigwin-${SWIG_TARGET_VERSION}) # swig.exe available as pre-built binary on Windows: ExternalProject_Add(${proj} URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/swigwin-${SWIG_TARGET_VERSION}.zip - URL_MD5 "3f18de4fc09ab9abb0d3be37c11fbc8f" + URL_MD5 "009926b512aee9318546bdd4c7eab6f9" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" ) ExternalProject_Get_Property(${proj} source_dir) set(SWIG_DIR ${source_dir}) set(SWIG_EXECUTABLE ${source_dir}/swig.exe) else() # swig uses bison find it by cmake and pass it down find_package(BISON) set(BISON_FLAGS "" CACHE STRING "Flags used by bison") mark_as_advanced( BISON_FLAGS) ExternalProject_add(${proj} LIST_SEPARATOR ${sep} URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/swig-${SWIG_TARGET_VERSION}.tar.gz - # Custom install dir for SWIG + URL_MD5 7c3e46cb5af2b469722cafa0d91e127b + # Switching to Git would require additional prerequisites: + # - autotools-dev + # - automake + # GIT_REPOSITORY https://github.com/swig/swig.git + # GIT_TAG v${SWIG_TARGET_VERSION} INSTALL_DIR ${ep_prefix}/src/${proj}-install - URL_MD5 "62f9b0d010cef36a13a010dc530d0d41" CONFIGURE_COMMAND /./configure CC=${CMAKE_C_COMPILER}${CMAKE_C_COMPILER_ARG1} LDFLAGS=${CMAKE_LINKER_FLAGS} ${CMAKE_LINKER_FLAGS_RELEASE} CXX=${CMAKE_CXX_COMPILER}${CMAKE_CXX_COMPILER_ARG1} "--prefix=" "--with-pcre-prefix=${PCRE_DIR}" --without-octave "--with-python=${Python3_EXECUTABLE}" DEPENDS ${proj_DEPENDENCIES} ) ExternalProject_Get_Property(${proj} install_dir) set(SWIG_DIR ${install_dir}/share/swig/${SWIG_TARGET_VERSION}) set(SWIG_EXECUTABLE ${install_dir}/bin/swig) endif() else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/VTK.cmake b/CMakeExternals/VTK.cmake index f0c58f66d5..69fa6d4da1 100644 --- a/CMakeExternals/VTK.cmake +++ b/CMakeExternals/VTK.cmake @@ -1,88 +1,85 @@ #----------------------------------------------------------------------------- # VTK #----------------------------------------------------------------------------- # Sanity checks if(DEFINED VTK_DIR AND NOT EXISTS ${VTK_DIR}) message(FATAL_ERROR "VTK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj VTK) set(proj_DEPENDENCIES ) set(VTK_DEPENDS ${proj}) -if(MITK_USE_HDF5) - list(APPEND proj_DEPENDENCIES HDF5) -endif() - if(NOT DEFINED VTK_DIR) set(additional_cmake_args ) if(WIN32) list(APPEND additional_cmake_args -DCMAKE_CXX_MP_FLAG:BOOL=ON ) else() list(APPEND additional_cmake_args -DVTK_MODULE_USE_EXTERNAL_VTK_freetype:BOOL=ON ) endif() # Optionally enable memory leak checks for any objects derived from vtkObject. This # will force unit tests to fail if they have any of these memory leaks. option(MITK_VTK_DEBUG_LEAKS OFF) mark_as_advanced(MITK_VTK_DEBUG_LEAKS) list(APPEND additional_cmake_args -DVTK_DEBUG_LEAKS:BOOL=${MITK_VTK_DEBUG_LEAKS} ) if(MITK_USE_Qt5) list(APPEND additional_cmake_args -DVTK_GROUP_ENABLE_Qt:STRING=YES -DQt5_DIR:PATH=${Qt5_DIR} ) endif() if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() mitk_query_custom_ep_vars() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/VTK-9.1.0.tar.gz - URL_MD5 96508e51d7c3764cd5aba06fffd9864e + GIT_REPOSITORY https://github.com/Kitware/VTK.git + GIT_TAG v9.1.0 + GIT_SUBMODULES "" CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} -DOpenGL_GL_PREFERENCE:STRING=LEGACY -DVTK_ENABLE_WRAPPING:BOOL=OFF -DVTK_LEGACY_REMOVE:BOOL=ON -DVTK_MODULE_ENABLE_VTK_TestingRendering:STRING=YES -DVTK_MODULE_ENABLE_VTK_RenderingContextOpenGL2:STRING=YES -DVTK_MODULE_ENABLE_VTK_RenderingVolumeOpenGL2:STRING=YES -DVTK_MODULE_ENABLE_VTK_GUISupportQtQuick:STRING=NO ${additional_cmake_args} ${${proj}_CUSTOM_CMAKE_ARGS} CMAKE_CACHE_ARGS ${ep_common_cache_args} ${${proj}_CUSTOM_CMAKE_CACHE_ARGS} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} ${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS} DEPENDS ${proj_DEPENDENCIES} ) set(VTK_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/CMakeExternals/Vigra.cmake b/CMakeExternals/Vigra.cmake index f79b5ff82c..5b2c4ddd22 100644 --- a/CMakeExternals/Vigra.cmake +++ b/CMakeExternals/Vigra.cmake @@ -1,72 +1,71 @@ #----------------------------------------------------------------------------- # VIGRA #----------------------------------------------------------------------------- if(MITK_USE_Vigra) # Sanity checks if(DEFINED Vigra_DIR AND NOT EXISTS ${Vigra_DIR}) message(FATAL_ERROR "Vigra_DIR variable is defined but corresponds to non-existing directory") endif() if(NOT ${MITK_USE_HDF5}) message(FATAL_ERROR "HDF5 is required for Vigra. Please enable it.") endif() set(proj Vigra) set(proj_DEPENDENCIES HDF5) set(Vigra_DEPENDS ${proj}) # If a mac ports installation is present some imaging-io-libraries may interfere with the vigra build. # Hence, we exclude them here. set(mac_additional_cmake_args) if(APPLE) set(mac_additional_cmake_args -DJPEG_INCLUDE_DIR= -DJPEG_LIBRARY= -DTIFF_INCLUDE_DIR= -DTIFF_LIBRARY= -DPNG_LIBRARY_RELEASE= -DPNG_PNG_INCLUDE_DIR= ) endif() if(NOT DEFINED Vigra_DIR) set(additional_cmake_args ) if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() ExternalProject_Add(${proj} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/vigra-1.10.0-src.tar.gz - URL_MD5 4f963f0be4fcb8b06271c2aa40baa9be - PATCH_COMMAND ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/Vigra.patch + GIT_REPOSITORY https://github.com/MITK/vigra.git + GIT_TAG Version-1-10-0-patched CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_cmake_args} ${mac_additional_cmake_args} -DAUTOEXEC_TESTS:BOOL=OFF -DWITH_VIGRANUMPY:BOOL=OFF -DHDF5_DIR:PATH=${HDF5_DIR} -DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=TRUE -DCMAKE_INSTALL_PREFIX:PATH= CMAKE_CACHE_ARGS ${ep_common_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) set(Vigra_DIR ${ep_prefix}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif(MITK_USE_Vigra) diff --git a/CMakeExternals/cpprestsdk.cmake b/CMakeExternals/cpprestsdk.cmake index 93644937b8..ea888903a9 100644 --- a/CMakeExternals/cpprestsdk.cmake +++ b/CMakeExternals/cpprestsdk.cmake @@ -1,39 +1,41 @@ set(proj cpprestsdk) set(proj_DEPENDENCIES Boost ZLIB) if(MITK_USE_${proj}) set(${proj}_DEPENDS ${proj}) if(DEFINED ${proj}_DIR AND NOT EXISTS ${${proj}_DIR}) message(FATAL_ERROR "${proj}_DIR variable is defined but corresponds to non-existing directory!") endif() if(NOT DEFINED ${proj}_DIR) set(cmake_cache_args ${ep_common_cache_args} -DBUILD_SAMPLES:BOOL=OFF -DBUILD_TESTS:BOOL=OFF -DWERROR:BOOL=OFF ) if(OPENSSL_ROOT_DIR) list(APPEND cmake_cache_args -DOPENSSL_ROOT_DIR:PATH=${OPENSSL_ROOT_DIR} ) endif() ExternalProject_Add(${proj} GIT_REPOSITORY https://github.com/microsoft/cpprestsdk.git - GIT_TAG v2.10.16 + GIT_TAG v2.10.18 SOURCE_SUBDIR Release - CMAKE_ARGS ${ep_common_args} + CMAKE_ARGS + "-DBoost_DIR:PATH=${Boost_DIR}" + ${ep_common_args} CMAKE_CACHE_ARGS ${cmake_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) set(${proj}_DIR ${ep_prefix}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/tinyxml2.cmake b/CMakeExternals/tinyxml2.cmake index 8b24f8c184..900956a1b2 100644 --- a/CMakeExternals/tinyxml2.cmake +++ b/CMakeExternals/tinyxml2.cmake @@ -1,52 +1,52 @@ #----------------------------------------------------------------------------- # tinyxml2 #----------------------------------------------------------------------------- # Sanity checks if(DEFINED tinyxml2_DIR AND NOT EXISTS ${tinyxml2_DIR}) message(FATAL_ERROR "tinyxml2_DIR variable is defined but corresponds to non-existing directory") endif() set(proj tinyxml2) set(proj_DEPENDENCIES ) set(${proj}_DEPENDS ${proj}) if(NOT DEFINED tinyxml2_DIR) set(additional_cmake_args ) if(WIN32) set(additional_cmake_args -DBUILD_SHARED_LIBS:BOOL=OFF) endif() if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/tinyxml2-8.0.0.tar.gz - URL_MD5 5dc535c8b34ee621fe2128f072d275b5 + GIT_REPOSITORY https://github.com/leethomason/tinyxml2.git + GIT_TAG 8.0.0 CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_cmake_args} CMAKE_CACHE_ARGS ${ep_common_cache_args} -DBUILD_TESTING:BOOL=OFF -DBUILD_TESTS:BOOL=OFF CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) set(${proj}_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 9abcfe07f0..75ca0cfe03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1436 +1,1385 @@ #[[ When increasing the minimum required version, check if Boost_ADDITIONAL_VERSIONS in CMake/PackageDepends/MITK_Boost_Config.cmake can be removed. See the first long comment in CMakeExternals/Boost.cmake for details. ]] set(MITK_CMAKE_MINIMUM_REQUIRED_VERSION 3.18) cmake_minimum_required(VERSION ${MITK_CMAKE_MINIMUM_REQUIRED_VERSION}) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19 AND CMAKE_VERSION VERSION_LESS 3.19.2) message(FATAL_ERROR "\ CMake v${CMAKE_VERSION} is defective [1]. \ Please either downgrade to v3.18 or upgrade to at least v3.19.2.\n\ [1] https://gitlab.kitware.com/cmake/cmake/-/issues/21529") endif() #----------------------------------------------------------------------------- # Policies #----------------------------------------------------------------------------- #[[ T28060 https://cmake.org/cmake/help/v3.18/policy/CMP0091.html https://cmake.org/cmake/help/v3.18/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html We pass CMP0091 to all external projects as command-line argument: -DCMAKE_POLICY_DEFAULT_CMP0091:STRING=OLD ]] cmake_policy(SET CMP0091 OLD) #----------------------------------------------------------------------------- # Superbuild Option - Enabled by default #----------------------------------------------------------------------------- option(MITK_USE_SUPERBUILD "Build MITK and the projects it depends on via SuperBuild.cmake." ON) if(MITK_USE_SUPERBUILD) project(MITK-superbuild) set(MITK_SOURCE_DIR ${PROJECT_SOURCE_DIR}) set(MITK_BINARY_DIR ${PROJECT_BINARY_DIR}) else() project(MITK VERSION 2022.04.99) include_directories(SYSTEM ${MITK_SUPERBUILD_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # MITK Extension Feature #----------------------------------------------------------------------------- set(MITK_EXTENSION_DIRS "" CACHE STRING "") unset(MITK_ABSOLUTE_EXTENSION_DIRS) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) get_filename_component(MITK_ABSOLUTE_EXTENSION_DIR "${MITK_EXTENSION_DIR}" ABSOLUTE) list(APPEND MITK_ABSOLUTE_EXTENSION_DIRS "${MITK_ABSOLUTE_EXTENSION_DIR}") endforeach() set(MITK_DIR_PLUS_EXTENSION_DIRS "${MITK_SOURCE_DIR}" ${MITK_ABSOLUTE_EXTENSION_DIRS}) #----------------------------------------------------------------------------- # Update CMake module path #----------------------------------------------------------------------------- set(MITK_CMAKE_DIR ${MITK_SOURCE_DIR}/CMake) set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_CMAKE_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake") if(EXISTS "${MITK_CMAKE_EXTENSION_DIR}") list(APPEND CMAKE_MODULE_PATH "${MITK_CMAKE_EXTENSION_DIR}") endif() endforeach() #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- # Standard CMake macros include(FeatureSummary) include(CTest) include(CMakeParseArguments) include(FindPackageHandleStandardArgs) # MITK macros include(mitkFunctionGetGccVersion) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkMacroEmptyExternalProject) include(mitkFunctionEnableBuildConfiguration) include(mitkFunctionWhitelists) include(mitkFunctionAddExternalProject) include(mitkFunctionAddLibrarySearchPaths) SUPPRESS_VC_DEPRECATED_WARNINGS() #----------------------------------------------------------------------------- # Set a default build type if none was specified #----------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() -#----------------------------------------------------------------------------- -# Check miminum macOS version -#----------------------------------------------------------------------------- -# The minimum supported macOS version is 10.14. If you use a version less than -# 10.14, there is no guarantee that the build still works. -if(APPLE) - exec_program(sw_vers ARGS -productVersion OUTPUT_VARIABLE macos_version) - if (macos_version VERSION_LESS "10.14") - message(WARNING "Detected macOS version \"${macos_version}\" is not supported anymore. Minimum required macOS version is at least 10.14.") - endif() - if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.14) - message(WARNING "Detected macOS deployment target \"${CMAKE_OSX_DEPLOYMENT_TARGET}\" is not supported anymore. Minimum required macOS version is at least 10.14.") - endif() -endif() - -#----------------------------------------------------------------------------- -# Check miminum compiler versions -#----------------------------------------------------------------------------- - -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # require at least gcc 4.9 as provided by ppa:ubuntu-toolchain-r/test for Ubuntu 14.04 - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) - message(FATAL_ERROR "GCC version must be at least 4.9 -If you are using Ubuntu 14.04, you can easily install gcc and g++ 4.9 (or any later version available) in addition to your version ${CMAKE_CXX_COMPILER_VERSION}: - sudo add-apt-repository ppa:ubuntu-toolchain-r/test - sudo apt-get update - sudo apt-get install gcc-4.9 g++-4.9 -Make sure to explicitly specify these compilers when configuring MITK: - CMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc-4.9 - CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++-4.9 -For more information on the proposed PPA see the Toolchain Updates section of https://wiki.ubuntu.com/ToolChain.") - endif() -elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - # require at least clang 3.4 - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4) - message(FATAL_ERROR "Clang version must be at least 3.4") - endif() -elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") - # require at least clang 5.0 - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) - message(FATAL_ERROR "Apple Clang version must be at least 5.0") - endif() -elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - # require at least Visual Studio 2017 - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10) - message(FATAL_ERROR "Microsoft Visual Studio 2017 or newer required") - endif() -else() - message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang (Linux or Apple), GCC and MSVC.") -endif() - if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) else() set(GCC_VERSION 0) endif() set(MITK_CXX_STANDARD 17) set(CMAKE_CXX_EXTENSIONS 0) set(CMAKE_CXX_STANDARD ${MITK_CXX_STANDARD}) set(CMAKE_CXX_STANDARD_REQUIRED 1) # This is necessary to avoid problems with compile feature checks. # CMAKE_CXX_STANDARD seems to only set the -std=c++ flag for targets. # However, compile flag checks also need to be done with -std=c++. # The MITK_CXX_FLAG variable is also used for external projects # build during the MITK super-build. mitkFunctionCheckCompilerFlags("-std=c++${MITK_CXX_STANDARD}" MITK_CXX${MITK_CXX_STANDARD}_FLAG) #----------------------------------------------------------------------------- # Warn if source or build path is too long #----------------------------------------------------------------------------- if(WIN32) set(_src_dir_length_max 50) set(_bin_dir_length_max 50) if(MITK_USE_SUPERBUILD) set(_src_dir_length_max 34) # _src_dir_length_max - strlen(ep/src/ITK-build) set(_bin_dir_length_max 40) # _bin_dir_length_max - strlen(MITK-build) endif() string(LENGTH "${MITK_SOURCE_DIR}" _src_n) string(LENGTH "${MITK_BINARY_DIR}" _bin_n) # The warnings should be converted to errors if(_src_n GREATER _src_dir_length_max) message(WARNING "MITK source code directory path length is too long (${_src_n} > ${_src_dir_length_max})." "Please move the MITK source code directory to a directory with a shorter path." ) endif() if(_bin_n GREATER _bin_dir_length_max) message(WARNING "MITK build directory path length is too long (${_bin_n} > ${_bin_dir_length_max})." "Please move the MITK build directory to a directory with a shorter path." ) endif() endif() #----------------------------------------------------------------------------- # Additional MITK Options (also shown during superbuild) #----------------------------------------------------------------------------- # ----------------------------------------- # General build options option(BUILD_SHARED_LIBS "Build MITK with shared libraries" ON) option(WITH_COVERAGE "Enable/Disable coverage" OFF) option(BUILD_TESTING "Test the project" ON) option(MITK_FAST_TESTING "Disable long-running tests like packaging" OFF) option(MITK_XVFB_TESTING "Execute test drivers through xvfb-run" OFF) option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF) option(MITK_BUILD_EXAMPLES "Build the MITK Examples" OFF) mark_as_advanced( MITK_XVFB_TESTING MITK_FAST_TESTING MITK_BUILD_ALL_APPS ) #----------------------------------------------------------------------------- # Set UI testing flags #----------------------------------------------------------------------------- if(MITK_XVFB_TESTING) set(MITK_XVFB_TESTING_COMMAND "xvfb-run" "--auto-servernum" CACHE STRING "Command and options to test through Xvfb") mark_as_advanced(MITK_XVFB_TESTING_COMMAND) endif(MITK_XVFB_TESTING) # ----------------------------------------- # Other options set(MITK_CUSTOM_REVISION_DESC "" CACHE STRING "Override MITK revision description") mark_as_advanced(MITK_CUSTOM_REVISION_DESC) set_property(GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS "") include(CMakeExternals/ExternalProjectList.cmake) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMakeExternals") if(EXISTS "${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake") include("${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake") endif() endforeach() # ----------------------------------------- # Other MITK_USE_* options not related to # external projects build via the # MITK superbuild option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) option(MITK_USE_OpenCL "Use OpenCL GPU-Computing library" OFF) option(MITK_USE_OpenMP "Use OpenMP" OFF) option(MITK_USE_Python3 "Use Python 3" OFF) #----------------------------------------------------------------------------- # Build configurations #----------------------------------------------------------------------------- set(_buildConfigs "Custom") file(GLOB _buildConfigFiles CMake/BuildConfigurations/*.cmake) foreach(_buildConfigFile ${_buildConfigFiles}) get_filename_component(_buildConfigFile ${_buildConfigFile} NAME_WE) list(APPEND _buildConfigs ${_buildConfigFile}) endforeach() foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) file(GLOB _extBuildConfigFiles "${MITK_EXTENSION_DIR}/CMake/BuildConfigurations/*.cmake") foreach(_extBuildConfigFile ${_extBuildConfigFiles}) get_filename_component(_extBuildConfigFile "${_extBuildConfigFile}" NAME_WE) list(APPEND _buildConfigs "${_extBuildConfigFile}") endforeach() list(REMOVE_DUPLICATES _buildConfigs) endforeach() set(MITK_BUILD_CONFIGURATION "Custom" CACHE STRING "Use pre-defined MITK configurations") set_property(CACHE MITK_BUILD_CONFIGURATION PROPERTY STRINGS ${_buildConfigs}) mitkFunctionEnableBuildConfiguration() mitkFunctionCreateWhitelistPaths(MITK) mitkFunctionFindWhitelists(MITK) # ----------------------------------------- # Qt version related variables option(MITK_USE_Qt5 "Use Qt 5 library" ON) if(MITK_USE_Qt5) if(WIN32) set(MITK_QT5_MINIMUM_VERSION 5.12.9) else() set(MITK_QT5_MINIMUM_VERSION 5.12) endif() set(MITK_QT5_COMPONENTS Concurrent OpenGL PrintSupport Script Sql Svg Widgets Xml XmlPatterns WebEngineWidgets UiTools Help LinguistTools) if(APPLE) list(APPEND MITK_QT5_COMPONENTS DBus) elseif(UNIX) list(APPEND MITK_QT5_COMPONENTS X11Extras) endif() # Hint at default install locations of Qt if(NOT Qt5_DIR) if(MSVC) set(_dir_candidates "C:/Qt") if(CMAKE_GENERATOR MATCHES "^Visual Studio [0-9]+ ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") elseif(CMAKE_GENERATOR MATCHES "Ninja") include(mitkFunctionGetMSVCVersion) mitkFunctionGetMSVCVersion() if(VISUAL_STUDIO_PRODUCT_NAME MATCHES "^Visual Studio ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") endif() endif() if(_compilers MATCHES "[0-9]+") if (CMAKE_MATCH_0 EQUAL 2022) list(APPEND _compilers "msvc2019" "msvc2017") # Binary compatible elseif (CMAKE_MATCH_0 EQUAL 2019) list(APPEND _compilers "msvc2017") # Binary compatible endif() endif() else() set(_dir_candidates ~/Qt) if(APPLE) set(_compilers clang) else() list(APPEND _dir_candidates /opt/Qt) set(_compilers gcc) endif() endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) foreach(_compiler ${_compilers}) list(APPEND _compilers64 "${_compiler}_64") endforeach() set(_compilers ${_compilers64}) endif() foreach(_dir_candidate ${_dir_candidates}) get_filename_component(_dir_candidate ${_dir_candidate} REALPATH) foreach(_compiler ${_compilers}) set(_glob_expression "${_dir_candidate}/5.*/${_compiler}") file(GLOB _hints ${_glob_expression}) list(SORT _hints) list(APPEND MITK_QT5_HINTS ${_hints}) endforeach() endforeach() endif() find_package(Qt5 ${MITK_QT5_MINIMUM_VERSION} COMPONENTS ${MITK_QT5_COMPONENTS} REQUIRED HINTS ${MITK_QT5_HINTS}) endif() # ----------------------------------------- # Custom dependency logic if(WIN32 AND Qt5_DIR) set(_dir_candidate "${Qt5_DIR}/../../../../../Tools/OpenSSL/Win_x64") get_filename_component(_dir_candidate ${_dir_candidate} ABSOLUTE) if(EXISTS "${_dir_candidate}") set(OPENSSL_ROOT_DIR "${_dir_candidate}") endif() endif() find_package(OpenSSL) option(MITK_USE_SYSTEM_Boost "Use the system Boost" OFF) set(MITK_USE_Boost_LIBRARIES "" CACHE STRING "A semi-colon separated list of required Boost libraries") if(MITK_USE_cpprestsdk) if(NOT OpenSSL_FOUND) set(openssl_message "Could not find OpenSSL (dependency of C++ REST SDK).\n") if(UNIX) if(APPLE) set(openssl_message "${openssl_message}Please install it using your favorite package management " "system (i.e. Homebrew or MacPorts).\n") else() set(openssl_message "${openssl_message}Please install the dev package of OpenSSL (i.e. libssl-dev).\n") endif() else() set(openssl_message "${openssl_message}Please either install Win32 OpenSSL:\n" " https://slproweb.com/products/Win32OpenSSL.html\n" "Or use the Qt Maintenance tool to install:\n" " Developer and Designer Tools > OpenSSL Toolkit > OpenSSL 64-bit binaries\n") endif() set(openssl_message "${openssl_message}If it still cannot be found, you can hint CMake to find OpenSSL by " "adding/setting the OPENSSL_ROOT_DIR variable to the root directory of an " "OpenSSL installation. Make sure to clear variables of partly found " "versions of OpenSSL before, or they will be mixed up.") message(FATAL_ERROR ${openssl_message}) endif() list(APPEND MITK_USE_Boost_LIBRARIES date_time regex system) if(UNIX) list(APPEND MITK_USE_Boost_LIBRARIES atomic chrono filesystem random thread) endif() list(REMOVE_DUPLICATES MITK_USE_Boost_LIBRARIES) set(MITK_USE_Boost_LIBRARIES ${MITK_USE_Boost_LIBRARIES} CACHE STRING "A semi-colon separated list of required Boost libraries" FORCE) endif() if(MITK_USE_Python3) set(MITK_USE_ZLIB ON CACHE BOOL "" FORCE) if(APPLE AND CMAKE_FRAMEWORK_PATH AND CMAKE_FRAMEWORK_PATH MATCHES "python3\\.?([0-9]+)") find_package(Python3 3.${CMAKE_MATCH_1} EXACT REQUIRED COMPONENTS Interpreter Development NumPy) else() find_package(Python3 REQUIRED COMPONENTS Interpreter Development NumPy) endif() if(WIN32) string(REPLACE "\\" "/" Python3_STDARCH "${Python3_STDARCH}") string(REPLACE "\\" "/" Python3_STDLIB "${Python3_STDLIB}") string(REPLACE "\\" "/" Python3_SITELIB "${Python3_SITELIB}") endif() endif() if(BUILD_TESTING AND NOT MITK_USE_CppUnit) message("> Forcing MITK_USE_CppUnit to ON because BUILD_TESTING=ON") set(MITK_USE_CppUnit ON CACHE BOOL "Use CppUnit for unit tests" FORCE) endif() if(MITK_USE_BLUEBERRY) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) mark_as_advanced(MITK_BUILD_ALL_PLUGINS) if(NOT MITK_USE_CTK) message("> Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY") set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE) endif() endif() #----------------------------------------------------------------------------- # Pixel type multiplexing #----------------------------------------------------------------------------- # Customize the default pixel types for multiplex macros set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, 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(mitkFunctionAddManifest) include(mitkFunctionAddCustomModuleTest) include(mitkFunctionCheckModuleDependencies) include(mitkFunctionCompileSnippets) include(mitkFunctionConfigureVisualStudioUserProjectFile) include(mitkFunctionCreateBlueBerryApplication) include(mitkFunctionCreateCommandLineApp) include(mitkFunctionCreateModule) include(mitkFunctionCreatePlugin) include(mitkFunctionCreateProvisioningFile) include(mitkFunctionGetLibrarySearchPaths) include(mitkFunctionGetVersion) include(mitkFunctionGetVersionDescription) include(mitkFunctionInstallAutoLoadModules) include(mitkFunctionInstallCTKPlugin) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionInstallThirdPartyCTKPlugins) include(mitkFunctionOrganizeSources) include(mitkFunctionUseModules) if( ${MITK_USE_MatchPoint} ) include(mitkFunctionCreateMatchPointDeployedAlgorithm) endif() include(mitkMacroConfigureItkPixelTypes) include(mitkMacroCreateExecutable) include(mitkMacroCreateModuleTests) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetLinuxDistribution) include(mitkMacroGetPMDPlatformString) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroMultiplexPicType) # Deprecated include(mitkMacroCreateCTKPlugin) #----------------------------------------------------------------------------- # Global CMake variables #----------------------------------------------------------------------------- if(NOT DEFINED CMAKE_DEBUG_POSTFIX) # We can't do this yet because the CTK Plugin Framework # cannot cope with a postfix yet. #set(CMAKE_DEBUG_POSTFIX d) endif() #----------------------------------------------------------------------------- # Output directories. #----------------------------------------------------------------------------- set(_default_LIBRARY_output_dir lib) set(_default_RUNTIME_output_dir bin) set(_default_ARCHIVE_output_dir lib) foreach(type LIBRARY RUNTIME ARCHIVE) # Make sure the directory exists if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY AND NOT EXISTS ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) message("Creating directory MITK_CMAKE_${type}_OUTPUT_DIRECTORY: ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") file(MAKE_DIRECTORY "${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") endif() if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) else() set(CMAKE_${type}_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${_default_${type}_output_dir}) set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY}) endif() set(CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY} CACHE INTERNAL "Output directory for ${type} files.") mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY) endforeach() #----------------------------------------------------------------------------- # Set MITK specific options and variables (NOT available during superbuild) #----------------------------------------------------------------------------- if(OpenSSL_FOUND AND WIN32) set(MITK_OPENSSL_SSL_DLL "" CACHE FILEPATH "") set(MITK_OPENSSL_CRYPTO_DLL "" CACHE FILEPATH "") if(MITK_OPENSSL_SSL_DLL AND EXISTS "${MITK_OPENSSL_SSL_DLL}" AND MITK_OPENSSL_CRYPTO_DLL AND EXISTS "${MITK_OPENSSL_CRYPTO_DLL}") foreach(config_type ${CMAKE_CONFIGURATION_TYPES}) execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${MITK_BINARY_DIR}/bin/${config_type}") configure_file("${MITK_OPENSSL_SSL_DLL}" "${MITK_BINARY_DIR}/bin/${config_type}/" COPYONLY) configure_file("${MITK_OPENSSL_CRYPTO_DLL}" "${MITK_BINARY_DIR}/bin/${config_type}/" COPYONLY) endforeach() MITK_INSTALL(FILES "${MITK_OPENSSL_SSL_DLL}" "${MITK_OPENSSL_CRYPTO_DLL}" ) endif() endif() # Look for optional Doxygen package find_package(Doxygen) option(BLUEBERRY_DEBUG_SMARTPOINTER "Enable code for debugging smart pointers" OFF) mark_as_advanced(BLUEBERRY_DEBUG_SMARTPOINTER) # Ask the user to show the console window for applications option(MITK_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting MITK GUI Applications" ON) mark_as_advanced(MITK_SHOW_CONSOLE_WINDOW) # As of Windows 10 Version 1903 (May 2019 Update), applications can use the UTF-8 code page if(WIN32) option(MITK_UTF8 "Use UTF-8 code page in MITK applications on Windows" ON) mark_as_advanced(MITK_UTF8) endif() if(NOT MITK_FAST_TESTING) if(MITK_CTEST_SCRIPT_MODE STREQUAL "Continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "Experimental") set(MITK_FAST_TESTING ON) endif() endif() if(NOT UNIX) set(MITK_WIN32_FORCE_STATIC "STATIC" CACHE INTERNAL "Use this variable to always build static libraries on non-unix platforms") endif() if(MITK_BUILD_ALL_PLUGINS) set(MITK_BUILD_ALL_PLUGINS_OPTION "FORCE_BUILD_ALL") endif() # Configure pixel types used for ITK image access multiplexing mitkMacroConfigureItkPixelTypes() # Configure module naming conventions set(MITK_MODULE_NAME_REGEX_MATCH "^[A-Z].*$") set(MITK_MODULE_NAME_REGEX_NOT_MATCH "^[Mm][Ii][Tt][Kk].*$") set(MITK_DEFAULT_MODULE_NAME_PREFIX "Mitk") set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) set(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME 1) #----------------------------------------------------------------------------- # Get MITK version info #----------------------------------------------------------------------------- mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK) mitkFunctionGetVersionDescription(${MITK_SOURCE_DIR} MITK) # MITK_VERSION set(MITK_VERSION_STRING "${MITK_VERSION_MAJOR}.${MITK_VERSION_MINOR}.${MITK_VERSION_PATCH}") if(MITK_VERSION_PATCH STREQUAL "99") set(MITK_VERSION_STRING "${MITK_VERSION_STRING}-${MITK_REVISION_SHORTID}") endif() #----------------------------------------------------------------------------- # Installation preparation # # These should be set before any MITK install macros are used #----------------------------------------------------------------------------- # on macOS all BlueBerry plugins get copied into every # application bundle (.app directory) specified here if(MITK_USE_BLUEBERRY AND APPLE) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) list(GET target_info_list 0 app_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} Mitk${app_name}) endif() endforeach() endif() endforeach() endif() #----------------------------------------------------------------------------- # Set coverage Flags #----------------------------------------------------------------------------- if(WITH_COVERAGE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG") set(COVERAGE_CXX_FLAGS ${coverage_flags}) set(COVERAGE_C_FLAGS ${coverage_flags}) endif() endif() #----------------------------------------------------------------------------- # MITK C/CXX Flags #----------------------------------------------------------------------------- set(MITK_C_FLAGS "${COVERAGE_C_FLAGS}") set(MITK_C_FLAGS_DEBUG ) set(MITK_C_FLAGS_RELEASE ) set(MITK_CXX_FLAGS "${COVERAGE_CXX_FLAGS} ${MITK_CXX${MITK_CXX_STANDARD}_FLAG}") set(MITK_CXX_FLAGS_DEBUG ) set(MITK_CXX_FLAGS_RELEASE ) set(MITK_EXE_LINKER_FLAGS ) set(MITK_SHARED_LINKER_FLAGS ) if(WIN32) - set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DPOCO_NO_UNWINDOWS -DWIN32_LEAN_AND_MEAN -DNOMINMAX") + set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DWIN32_LEAN_AND_MEAN -DNOMINMAX") mitkFunctionCheckCompilerFlags("/wd4005" MITK_CXX_FLAGS) # warning C4005: macro redefinition mitkFunctionCheckCompilerFlags("/wd4231" MITK_CXX_FLAGS) # warning C4231: nonstandard extension used : 'extern' before template explicit instantiation # the following line should be removed after fixing bug 17637 mitkFunctionCheckCompilerFlags("/wd4316" MITK_CXX_FLAGS) # warning C4316: object alignment on heap mitkFunctionCheckCompilerFlags("/wd4180" MITK_CXX_FLAGS) # warning C4180: qualifier applied to function type has no meaning mitkFunctionCheckCompilerFlags("/wd4251" MITK_CXX_FLAGS) # warning C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' endif() if(APPLE) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DGL_SILENCE_DEPRECATION") # Apple deprecated OpenGL in macOS 10.14 endif() if(NOT MSVC_VERSION) foreach(_flag -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align -Wwrite-strings -Wno-error=gnu -Wno-error=unknown-pragmas # The strict-overflow warning is generated by ITK template code -Wno-error=strict-overflow -Woverloaded-virtual -Wstrict-null-sentinel #-Wold-style-cast #-Wsign-promo -Wno-deprecated-copy -Wno-array-bounds -Wno-cast-function-type -Wno-maybe-uninitialized -Wno-error=stringop-overread -fdiagnostics-show-option ) mitkFunctionCheckCAndCXXCompilerFlags(${_flag} MITK_C_FLAGS MITK_CXX_FLAGS) endforeach() endif() if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE) mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" MITK_SHARED_LINKER_FLAGS) mitkFunctionCheckCompilerFlags("-Wl,--as-needed" MITK_SHARED_LINKER_FLAGS) endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionCheckCAndCXXCompilerFlags("-fstack-protector-all" MITK_C_FLAGS MITK_CXX_FLAGS) set(MITK_CXX_FLAGS_RELEASE "-U_FORTIFY_SOURCES -D_FORTIFY_SOURCE=2 ${MITK_CXX_FLAGS_RELEASE}") endif() set(MITK_MODULE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) set(MITK_EXE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) #----------------------------------------------------------------------------- # MITK Packages #----------------------------------------------------------------------------- set(MITK_MODULES_PACKAGE_DEPENDS_DIR ${MITK_SOURCE_DIR}/CMake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_PACKAGE_DEPENDS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake/PackageDepends") if(EXISTS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}") list(APPEND MODULES_PACKAGE_DEPENDS_DIRS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}") endif() endforeach() if(NOT MITK_USE_SYSTEM_Boost) set(Boost_NO_SYSTEM_PATHS 1) endif() set(Boost_USE_MULTITHREADED 1) set(Boost_USE_STATIC_LIBS 0) set(Boost_USE_STATIC_RUNTIME 0) set(Boost_ADDITIONAL_VERSIONS 1.74 1.74.0) # We need this later for a DCMTK workaround set(_dcmtk_dir_orig ${DCMTK_DIR}) # This property is populated at the top half of this file get_property(MITK_EXTERNAL_PROJECTS GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(MITK_USE_${ep} AND _package) if(_components) find_package(${_package} COMPONENTS ${_components} REQUIRED CONFIG) else() # Prefer config mode first because it finds external # 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) if(${_dcmtk_dir_orig} MATCHES "${MITK_EXTERNAL_PROJECT_PREFIX}.*") # Help our FindDCMTK.cmake script find our super-build DCMTK set(DCMTK_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) else() # Use the original value set(DCMTK_DIR ${_dcmtk_dir_orig}) endif() endif() if(MITK_USE_DCMQI) # Due to the preferred CONFIG mode in find_package calls above, # the DCMQIConfig.cmake file is read, which does not provide useful # package information. We explictly need MODULE mode to find DCMQI. # Help our FindDCMQI.cmake script find our super-build DCMQI set(DCMQI_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) find_package(DCMQI REQUIRED) endif() if(MITK_USE_OpenIGTLink) link_directories(${OpenIGTLink_LIBRARY_DIRS}) endif() if(MITK_USE_OpenCL) find_package(OpenCL REQUIRED) endif() if(MITK_USE_OpenMP) find_package(OpenMP REQUIRED COMPONENTS CXX) else() find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) elseif(APPLE AND OpenMP_libomp_LIBRARY AND NOT OpenMP_CXX_LIB_NAMES) set(OpenMP_CXX_LIB_NAMES libomp CACHE STRING "" FORCE) get_filename_component(openmp_lib_dir "${OpenMP_libomp_LIBRARY}" DIRECTORY) set(openmp_include_dir "${openmp_lib_dir}/../include") if(EXISTS "${openmp_include_dir}") get_filename_component(openmp_include_dir "${openmp_include_dir}" REALPATH) set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${openmp_include_dir}" CACHE STRING "" FORCE) find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) endif() endif() endif() endif() # Qt support if(MITK_USE_Qt5) find_package(Qt5Core ${MITK_QT5_MINIMUM_VERSION} REQUIRED) # at least Core required get_target_property(_qmake_exec Qt5::qmake LOCATION) execute_process(COMMAND ${_qmake_exec} -query QT_INSTALL_BINS RESULT_VARIABLE _result OUTPUT_VARIABLE QT_BINARY_DIR ERROR_VARIABLE _error ) string(STRIP "${QT_BINARY_DIR}" QT_BINARY_DIR) if(_result OR NOT EXISTS "${QT_BINARY_DIR}") message(FATAL_ERROR "Could not determine Qt binary directory: ${_result} ${QT_BINARY_DIR} ${_error}") endif() find_program(QT_HELPGENERATOR_EXECUTABLE NAMES qhelpgenerator qhelpgenerator-qt5 qhelpgenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_COLLECTIONGENERATOR_EXECUTABLE NAMES qcollectiongenerator qcollectiongenerator-qt5 qcollectiongenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_ASSISTANT_EXECUTABLE NAMES assistant assistant-qt5 assistant5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_XMLPATTERNS_EXECUTABLE NAMES xmlpatterns PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) mark_as_advanced(QT_HELPGENERATOR_EXECUTABLE QT_COLLECTIONGENERATOR_EXECUTABLE QT_ASSISTANT_EXECUTABLE QT_XMLPATTERNS_EXECUTABLE ) if(MITK_USE_BLUEBERRY) option(BLUEBERRY_USE_QT_HELP "Enable support for integrating plugin documentation into Qt Help" ${DOXYGEN_FOUND}) mark_as_advanced(BLUEBERRY_USE_QT_HELP) # Sanity checks for in-application BlueBerry plug-in help generation if(BLUEBERRY_USE_QT_HELP) set(_force_blueberry_use_qt_help_to_off 0) if(NOT DOXYGEN_FOUND) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen was not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(DOXYGEN_FOUND AND DOXYGEN_VERSION VERSION_LESS 1.8.7) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_HELPGENERATOR_EXECUTABLE) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because QT_HELPGENERATOR_EXECUTABLE is empty.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT MITK_USE_Qt5) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because MITK_USE_Qt5 is OFF.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_XMLPATTERNS_EXECUTABLE) message("You have enabled Qt Help support, but QT_XMLPATTERNS_EXECUTABLE is empty") set(_force_blueberry_use_qt_help_to_off 1) endif() if(_force_blueberry_use_qt_help_to_off) set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating plugin documentation into Qt Help" FORCE) endif() endif() if(BLUEBERRY_QT_HELP_REQUIRED AND NOT BLUEBERRY_USE_QT_HELP) message(FATAL_ERROR "BLUEBERRY_USE_QT_HELP is required to be set to ON") endif() endif() endif() #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- if(BUILD_TESTING) #[[ See T27701 # Initial cache for ProjectTemplate and PluginGenerator tests configure_file( CMake/mitkTestInitialCache.txt.in ${MITK_BINARY_DIR}/mitkTestInitialCache.txt @ONLY )]] # Configuration for the CMake-generated test driver set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ") set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " try {") set(CMAKE_TESTDRIVER_AFTER_TESTMAIN " } catch (const std::exception& e) { fprintf(stderr, \"%s\\n\", e.what()); return EXIT_FAILURE; } catch (...) { printf(\"Exception caught in the test driver\\n\"); return EXIT_FAILURE; }") set(MITK_TEST_OUTPUT_DIR "${MITK_BINARY_DIR}/test_output") if(NOT EXISTS ${MITK_TEST_OUTPUT_DIR}) file(MAKE_DIRECTORY ${MITK_TEST_OUTPUT_DIR}) endif() # Test the package target include(mitkPackageTest) endif() configure_file(mitkTestingConfig.h.in ${MITK_BINARY_DIR}/mitkTestingConfig.h) #----------------------------------------------------------------------------- # MITK_SUPERBUILD_BINARY_DIR #----------------------------------------------------------------------------- # If MITK_SUPERBUILD_BINARY_DIR isn't defined, it means MITK is *NOT* build using Superbuild. # In that specific case, MITK_SUPERBUILD_BINARY_DIR should default to MITK_BINARY_DIR if(NOT DEFINED MITK_SUPERBUILD_BINARY_DIR) set(MITK_SUPERBUILD_BINARY_DIR ${MITK_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # Set C/CXX and linker flags for MITK code #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MITK_CXX_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MITK_CXX_FLAGS_DEBUG}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MITK_CXX_FLAGS_RELEASE}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MITK_C_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${MITK_C_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${MITK_C_FLAGS_RELEASE}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MITK_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${MITK_SHARED_LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${MITK_MODULE_LINKER_FLAGS}") #----------------------------------------------------------------------------- # Add subdirectories #----------------------------------------------------------------------------- add_subdirectory(Utilities) add_subdirectory(Modules) include("${CMAKE_CURRENT_SOURCE_DIR}/Modules/ModuleList.cmake") mitkFunctionWhitelistModules(MITK MITK_MODULES) set(MITK_ROOT_FOLDER_BACKUP "${MITK_ROOT_FOLDER}") foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) get_filename_component(MITK_ROOT_FOLDER "${MITK_EXTENSION_DIR}" NAME) set(MITK_MODULES_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Modules") if(EXISTS "${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake") set(MITK_MODULES "") include("${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake") foreach(mitk_module ${MITK_MODULES}) add_subdirectory("${MITK_MODULES_EXTENSION_DIR}/${mitk_module}" "Modules/${mitk_module}") endforeach() endif() set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) endforeach() set(MITK_ROOT_FOLDER "${MITK_ROOT_FOLDER_BACKUP}") add_subdirectory(Wrapping) set(MITK_DOXYGEN_OUTPUT_DIR "${PROJECT_BINARY_DIR}/Documentation/Doxygen" CACHE PATH "Output directory for doxygen generated documentation.") if(MITK_USE_BLUEBERRY) include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake") mitkFunctionWhitelistPlugins(MITK MITK_PLUGINS) set(mitk_plugins_fullpath "") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin}) endforeach() set(MITK_PLUGIN_REGEX_LIST "") foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_PLUGINS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Plugins") if(EXISTS "${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake") set(MITK_PLUGINS "") include("${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath "${MITK_PLUGINS_EXTENSION_DIR}/${mitk_plugin}") endforeach() endif() endforeach() if(EXISTS ${MITK_PRIVATE_MODULES}/PluginList.cmake) include(${MITK_PRIVATE_MODULES}/PluginList.cmake) foreach(mitk_plugin ${MITK_PRIVATE_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PRIVATE_MODULES}/${mitk_plugin}) endforeach() endif() if(MITK_BUILD_EXAMPLES) include("${CMAKE_CURRENT_SOURCE_DIR}/Examples/Plugins/PluginList.cmake") set(mitk_example_plugins_fullpath ) foreach(mitk_example_plugin ${MITK_EXAMPLE_PLUGINS}) list(APPEND mitk_example_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) list(APPEND mitk_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) endforeach() endif() # Specify which plug-ins belong to this project macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin_mitk "^org_mitk_[a-zA-Z0-9_]+$") set(re_ctkplugin_bb "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin_mitk re_ctkplugin_bb MITK_PLUGIN_REGEX_LIST OUTPUT_VARIABLE ${varname}) endmacro() # Get infos about application directories and build options set(mitk_apps_fullpath "") foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) if(${option_name}) list(APPEND mitk_apps_fullpath "${MITK_APPLICATIONS_EXTENSION_DIR}/${directory_name}^^${option_name}") endif() endforeach() endif() endforeach() if (mitk_plugins_fullpath) ctkMacroSetupPlugins(${mitk_plugins_fullpath} BUILD_OPTION_PREFIX MITK_BUILD_ APPS ${mitk_apps_fullpath} BUILD_ALL ${MITK_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) endif() set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE}) else() file(REMOVE ${MITK_PLUGIN_USE_FILE}) set(MITK_PLUGIN_USE_FILE ) endif() endif() #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- set(MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS) set(MITK_DOXYGEN_ADDITIONAL_IMAGE_PATHS) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS "${MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS} \"${MITK_EXTENSION_DIR}\"") set(MITK_DOXYGEN_ADDITIONAL_IMAGE_PATHS "${MITK_DOXYGEN_ADDITIONAL_IMAGE_PATHS} \"${MITK_EXTENSION_DIR}\"") endforeach() if(DOXYGEN_FOUND) add_subdirectory(Documentation) endif() #----------------------------------------------------------------------------- # Installation #----------------------------------------------------------------------------- # set MITK cpack variables # These are the default variables, which can be overwritten ( see below ) include(mitkSetupCPack) set(use_default_config ON) set(ALL_MITK_APPS "") set(activated_apps_no 0) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) list(APPEND ALL_MITK_APPS "${MITK_EXTENSION_DIR}/Applications/${directory_name}^^${option_name}^^${executable_name}") if(${option_name} OR MITK_BUILD_ALL_APPS) MATH(EXPR activated_apps_no "${activated_apps_no} + 1") endif() endforeach() endif() endforeach() list(LENGTH ALL_MITK_APPS app_count) if(app_count EQUAL 1 AND (activated_apps_no EQUAL 1 OR MITK_BUILD_ALL_APPS)) # Corner case if there is only one app in total set(use_project_cpack ON) elseif(activated_apps_no EQUAL 1 AND NOT MITK_BUILD_ALL_APPS) # Only one app is enabled (no "build all" flag set) set(use_project_cpack ON) else() # Less or more then one app is enabled set(use_project_cpack OFF) endif() foreach(mitk_app ${ALL_MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) # check whether application specific configuration files will be used if(use_project_cpack) # use files if they exist if(EXISTS "${target_dir}/CPackOptions.cmake") include("${target_dir}/CPackOptions.cmake") endif() if(EXISTS "${target_dir}/CPackConfig.cmake.in") set(CPACK_PROJECT_CONFIG_FILE "${target_dir}/CPackConfig.cmake") configure_file(${target_dir}/CPackConfig.cmake.in ${CPACK_PROJECT_CONFIG_FILE} @ONLY) set(use_default_config OFF) endif() endif() # add link to the list list(APPEND CPACK_CREATE_DESKTOP_LINKS "${executable_name}") endif() endforeach() # if no application specific configuration file was used, use default if(use_default_config) configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in ${MITK_BINARY_DIR}/MITKCPackOptions.cmake @ONLY) set(CPACK_PROJECT_CONFIG_FILE "${MITK_BINARY_DIR}/MITKCPackOptions.cmake") endif() # include CPack model once all variables are set include(CPack) # Additional installation rules include(mitkInstallRules) #----------------------------------------------------------------------------- # Last configuration steps #----------------------------------------------------------------------------- # ---------------- Export targets ----------------- set(MITK_EXPORTS_FILE "${MITK_BINARY_DIR}/MitkExports.cmake") file(REMOVE ${MITK_EXPORTS_FILE}) set(targets_to_export) get_property(module_targets GLOBAL PROPERTY MITK_MODULE_TARGETS) if(module_targets) list(APPEND targets_to_export ${module_targets}) endif() if(MITK_USE_BLUEBERRY) if(MITK_PLUGIN_LIBRARIES) list(APPEND targets_to_export ${MITK_PLUGIN_LIBRARIES}) endif() endif() export(TARGETS ${targets_to_export} APPEND FILE ${MITK_EXPORTS_FILE}) set(MITK_EXPORTED_TARGET_PROPERTIES ) foreach(target_to_export ${targets_to_export}) get_target_property(autoload_targets ${target_to_export} MITK_AUTOLOAD_TARGETS) if(autoload_targets) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_TARGETS \"${autoload_targets}\")") endif() get_target_property(autoload_dir ${target_to_export} MITK_AUTOLOAD_DIRECTORY) if(autoload_dir) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_DIRECTORY \"${autoload_dir}\")") endif() get_target_property(deprecated_module ${target_to_export} MITK_MODULE_DEPRECATED_SINCE) if(deprecated_module) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_MODULE_DEPRECATED_SINCE \"${deprecated_module}\")") endif() endforeach() # ---------------- External projects ----------------- get_property(MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS_CONFIG GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) set(MITK_CONFIG_EXTERNAL_PROJECTS ) #string(REPLACE "^^" ";" _mitk_external_projects ${MITK_EXTERNAL_PROJECTS}) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} set(MITK_USE_${ep} ${MITK_USE_${ep}}) set(MITK_${ep}_DIR \"${${ep}_DIR}\") set(MITK_${ep}_COMPONENTS ${_components}) ") endforeach() foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(_components) set(_components_arg COMPONENTS \${_components}) else() set(_components_arg) endif() if(_package) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} if(MITK_USE_${ep}) set(${ep}_DIR \${MITK_${ep}_DIR}) if(MITK_${ep}_COMPONENTS) mitkMacroFindDependency(${_package} COMPONENTS \${MITK_${ep}_COMPONENTS}) else() mitkMacroFindDependency(${_package}) endif() endif()") endif() endforeach() # ---------------- Tools ----------------- configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) # ---------------- Configure files ----------------- configure_file(mitkVersion.h.in ${MITK_BINARY_DIR}/mitkVersion.h) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) set(IPFUNC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipFunc) set(UTILITIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY) write_basic_config_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake VERSION ${MITK_VERSION_STRING} COMPATIBILITY AnyNewerVersion) #----------------------------------------------------------------------------- # MITK Applications #----------------------------------------------------------------------------- # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Applications) if(MSVC AND TARGET MitkWorkbench) set_directory_properties(PROPERTIES VS_STARTUP_PROJECT MitkWorkbench) endif() foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/CMakeLists.txt") add_subdirectory("${MITK_APPLICATIONS_EXTENSION_DIR}" "Applications") endif() endforeach() #----------------------------------------------------------------------------- # MITK Examples #----------------------------------------------------------------------------- if(MITK_BUILD_EXAMPLES) # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Examples) endif() #----------------------------------------------------------------------------- # Print configuration summary #----------------------------------------------------------------------------- message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL ) diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/SupportedPlatforms.md b/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/SupportedPlatforms.md index 41c0a30b1b..693917cc85 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/SupportedPlatforms.md +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/SupportedPlatforms.md @@ -1,55 +1,55 @@ Supported Platforms {#SupportedPlatformsPage} =================== MITK is a cross-platform framework that is available for the following platforms: - Windows - Linux - macOS Supported Platforms Details --------------------------- The MITK team provides support for the most frequently used platforms and continuously runs testing procedures to ensure compatibility. Due to the large amount of possible combinations of operating systems and compiler versions, we divide platform support into two test categories: Tier 1 and Tier 2. Although MITK may be built on a broader range of platform-compiler combinations, only a subset of these are actively supported by the MITK development team. In general, only 64-bit builds are supported. Tier 1 Platforms ---------------- All Tier 1 platforms are continuously tested by our unit test suite and other internal testing procedures. Errors or bugs discovered in these platforms are prioritized and corrected as soon as possible. | Platform | Compilers | ----------------------------------- | -------------------------------------------------- | Microsoft Windows 10 | Visual Studio 2019 (latest update) +| Linux Ubuntu 22.04 | Default GCC version | Linux Ubuntu 20.04 | Default GCC version -| Linux Ubuntu 18.04 | Default GCC version Tier 2 Platforms ---------------- Tier 2 platforms may or may not be tested on a regular basis. Some Tier 2 platforms are used by individual members of the MITK development team on a daily basis and some only receive occasional testing. While we strive to support these platforms, MITK users should note that errors may be present in released versions as well as in the current master branch. | Platform | Compilers | ----------------------------------- | -------------------------------------------------- | Microsoft Windows 10 | Visual Studio 2022 (latest update) | Apple macOS 11 "Big Sur" | Default Apple Clang version | Apple macOS 10.15 "Catalina" | Default Apple Clang version All platforms not listed above are not officially supported by the MITK team. However, we will happily accept contributions to improve support for other platforms as long as we have the hardware and capacity for maintenance. CI Build Clients ---------------- To get an overview of currently tested platforms, see the build reports on our CDash site. diff --git a/Examples/QtFreeRender/QtFreeRender.cpp b/Examples/QtFreeRender/QtFreeRender.cpp index eb917cbe3b..07e8f8e157 100644 --- a/Examples/QtFreeRender/QtFreeRender.cpp +++ b/Examples/QtFreeRender/QtFreeRender.cpp @@ -1,319 +1,319 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkRenderWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //##Documentation //## @brief Example of a NON QT DEPENDENT MITK RENDERING APPLICATION. mitk::RenderWindow::Pointer mitkWidget1; mitk::RenderWindow::Pointer mitkWidget2; mitk::RenderWindow::Pointer mitkWidget3; mitk::RenderWindow::Pointer mitkWidget4; mitk::DisplayActionEventBroadcast::Pointer m_DisplayActionEventBroadcast; vtkSmartPointer m_RectangleRendering1; vtkSmartPointer m_RectangleRendering2; vtkSmartPointer m_RectangleRendering3; vtkSmartPointer m_RectangleRendering4; mitk::SliceNavigationController *m_TimeNavigationController = nullptr; mitk::DataStorage::Pointer m_DataStorage; mitk::DataNode::Pointer m_PlaneNode1; mitk::DataNode::Pointer m_PlaneNode2; mitk::DataNode::Pointer m_PlaneNode3; mitk::DataNode::Pointer m_Node; void InitializeWindows() { // Set default view directions for SNCs mitkWidget1->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); mitkWidget2->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Sagittal); mitkWidget3->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Coronal); mitkWidget4->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Original); // initialize m_TimeNavigationController: send time via sliceNavigationControllers m_TimeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); - m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget1->GetSliceNavigationController(), false); - m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget2->GetSliceNavigationController(), false); - m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget3->GetSliceNavigationController(), false); - m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget4->GetSliceNavigationController(), false); + m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget1->GetSliceNavigationController()); + m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget2->GetSliceNavigationController()); + m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget3->GetSliceNavigationController()); + m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget4->GetSliceNavigationController()); mitkWidget1->GetSliceNavigationController()->ConnectGeometrySendEvent( mitk::BaseRenderer::GetInstance(mitkWidget4->GetVtkRenderWindow())); // reverse connection between sliceNavigationControllers and m_TimeNavigationController - mitkWidget1->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); - mitkWidget2->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); - mitkWidget3->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); - mitkWidget4->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); + mitkWidget1->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); + mitkWidget2->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); + mitkWidget3->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); + mitkWidget4->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); mitkWidget4->GetRenderer()->GetVtkRenderer()->SetBackground(0.1, 0.1, 0.1); mitkWidget4->GetRenderer()->GetVtkRenderer()->SetBackground(0.5, 0.5, 0.5); mitkWidget4->GetRenderer()->GetVtkRenderer()->GradientBackgroundOn(); m_RectangleRendering1 = vtkSmartPointer::New(); m_RectangleRendering1->SetColor(1.0, 0.0, 0.0); mitkWidget1->GetRenderer()->GetVtkRenderer()->AddViewProp(m_RectangleRendering1); m_RectangleRendering2 = vtkSmartPointer::New(); m_RectangleRendering2->SetColor(0.0, 1.0, 0.0); mitkWidget2->GetRenderer()->GetVtkRenderer()->AddViewProp(m_RectangleRendering2); m_RectangleRendering3 = vtkSmartPointer::New(); m_RectangleRendering3->SetColor(0.0, 0.0, 1.0); mitkWidget3->GetRenderer()->GetVtkRenderer()->AddViewProp(m_RectangleRendering3); m_RectangleRendering4 = vtkSmartPointer::New(); m_RectangleRendering4->SetColor(1.0, 1.0, 0.0); mitkWidget4->GetRenderer()->GetVtkRenderer()->AddViewProp(m_RectangleRendering4); } void AddDisplayPlaneSubTree() { // add the displayed planes of the multiwidget to a node to which the subtree // @a planesSubTree points ... float white[3] = {1.0f, 1.0f, 1.0f}; mitk::PlaneGeometryDataMapper2D::Pointer mapper; mitk::IntProperty::Pointer layer = mitk::IntProperty::New(1000); // ... of widget 1 m_PlaneNode1 = (mitk::BaseRenderer::GetInstance(mitkWidget1->GetVtkRenderWindow()))->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode1->SetColor(white, mitk::BaseRenderer::GetInstance(mitkWidget4->GetVtkRenderWindow())); m_PlaneNode1->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode1->SetProperty("name", mitk::StringProperty::New("widget1Plane")); m_PlaneNode1->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode1->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PlaneNode1->SetProperty("layer", layer); m_PlaneNode1->SetColor(1.0, 0.0, 0.0); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode1->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 2 m_PlaneNode2 = (mitk::BaseRenderer::GetInstance(mitkWidget2->GetVtkRenderWindow()))->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode2->SetColor(white, mitk::BaseRenderer::GetInstance(mitkWidget4->GetVtkRenderWindow())); m_PlaneNode2->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode2->SetProperty("name", mitk::StringProperty::New("widget2Plane")); m_PlaneNode2->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode2->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PlaneNode2->SetProperty("layer", layer); m_PlaneNode2->SetColor(0.0, 1.0, 0.0); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode2->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 3 m_PlaneNode3 = (mitk::BaseRenderer::GetInstance(mitkWidget3->GetVtkRenderWindow()))->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode3->SetColor(white, mitk::BaseRenderer::GetInstance(mitkWidget4->GetVtkRenderWindow())); m_PlaneNode3->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode3->SetProperty("name", mitk::StringProperty::New("widget3Plane")); m_PlaneNode3->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode3->SetProperty("helper object", mitk::BoolProperty::New(true)); m_PlaneNode3->SetProperty("layer", layer); m_PlaneNode3->SetColor(0.0, 0.0, 1.0); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode3->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // AddPlanesToDataStorage if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_Node.IsNotNull()) { if (m_DataStorage.IsNotNull()) { m_DataStorage->Add(m_PlaneNode1); m_DataStorage->Add(m_PlaneNode2); m_DataStorage->Add(m_PlaneNode3); } } } void Fit() { vtkRenderer *vtkrenderer; mitk::BaseRenderer::GetInstance(mitkWidget1->GetVtkRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget2->GetVtkRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget3->GetVtkRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget4->GetVtkRenderWindow())->GetCameraController()->Fit(); int w = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget1->GetVtkRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget2->GetVtkRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget3->GetVtkRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget4->GetVtkRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkObject::SetGlobalWarningDisplay(w); } int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: %s [filename1] [filename2] ...\n\n", ""); return 1; } // Create a DataStorage m_DataStorage = mitk::StandaloneDataStorage::New(); //************************************************************************* // Part II: Create some data by reading files //************************************************************************* int i; for (i = 1; i < argc; ++i) { // For testing if (strcmp(argv[i], "-testing") == 0) continue; std::string filename = argv[i]; try { // Read the file and add it as a data node to the data storage mitk::DataStorage::SetOfObjects::Pointer nodes = mitk::IOUtil::Load(filename, *m_DataStorage); for (mitk::DataStorage::SetOfObjects::Iterator nodeIter = nodes->Begin(), nodeIterEnd = nodes->End(); nodeIter != nodeIterEnd; ++nodeIter) { mitk::DataNode::Pointer node = nodeIter->Value(); mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image.IsNotNull()) { // Set the property "volumerendering" to the Boolean value "true" node->SetProperty("volumerendering", mitk::BoolProperty::New(false)); node->SetProperty("name", mitk::StringProperty::New("testimage")); node->SetProperty("layer", mitk::IntProperty::New(1)); } } } catch (...) { std::cerr << "Could not open file " << filename << std::endl; exit(2); } } //************************************************************************* // Part V: Create window and pass the tree to it //************************************************************************* // Create renderwindows mitkWidget1 = mitk::RenderWindow::New(); mitkWidget2 = mitk::RenderWindow::New(); mitkWidget3 = mitk::RenderWindow::New(); mitkWidget4 = mitk::RenderWindow::New(); mitkWidget1->GetRenderer()->PrepareRender(); mitkWidget2->GetRenderer()->PrepareRender(); mitkWidget3->GetRenderer()->PrepareRender(); // Tell the renderwindow which (part of) the datastorage to render mitkWidget1->GetRenderer()->SetDataStorage(m_DataStorage); mitkWidget2->GetRenderer()->SetDataStorage(m_DataStorage); mitkWidget3->GetRenderer()->SetDataStorage(m_DataStorage); mitkWidget4->GetRenderer()->SetDataStorage(m_DataStorage); // instantiate display interactor if (m_DisplayActionEventBroadcast.IsNull()) { m_DisplayActionEventBroadcast = mitk::DisplayActionEventBroadcast::New(); m_DisplayActionEventBroadcast->LoadStateMachine("DisplayInteraction.xml"); m_DisplayActionEventBroadcast->SetEventConfig("DisplayConfigMITK.xml"); } // Use it as a 2D View mitkWidget1->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard2D); mitkWidget2->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard2D); mitkWidget3->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard2D); mitkWidget4->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard3D); mitkWidget1->SetSize(400, 400); mitkWidget2->GetVtkRenderWindow()->SetPosition(mitkWidget1->GetVtkRenderWindow()->GetPosition()[0] + 420, mitkWidget1->GetVtkRenderWindow()->GetPosition()[1]); mitkWidget2->SetSize(400, 400); mitkWidget3->GetVtkRenderWindow()->SetPosition(mitkWidget1->GetVtkRenderWindow()->GetPosition()[0], mitkWidget1->GetVtkRenderWindow()->GetPosition()[1] + 450); mitkWidget3->SetSize(400, 400); mitkWidget4->GetVtkRenderWindow()->SetPosition(mitkWidget1->GetVtkRenderWindow()->GetPosition()[0] + 420, mitkWidget1->GetVtkRenderWindow()->GetPosition()[1] + 450); mitkWidget4->SetSize(400, 400); InitializeWindows(); AddDisplayPlaneSubTree(); Fit(); // Initialize the RenderWindows auto geo = m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()); mitk::RenderingManager::GetInstance()->InitializeViews(geo); m_DataStorage->Print(std::cout); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // reinit the mitkVTKEventProvider; // this is only necessary once after calling // ForceImmediateUpdateAll() for the first time mitkWidget1->ReinitEventProvider(); mitkWidget2->ReinitEventProvider(); mitkWidget3->ReinitEventProvider(); mitkWidget1->GetVtkRenderWindow()->Render(); mitkWidget2->GetVtkRenderWindow()->Render(); mitkWidget3->GetVtkRenderWindow()->Render(); mitkWidget4->GetVtkRenderWindow()->Render(); mitkWidget4->GetVtkRenderWindowInteractor()->Start(); return 0; } diff --git a/Modules/CameraCalibration/CMakeLists.txt b/Modules/CameraCalibration/CMakeLists.txt index 361c50a164..bf8e292636 100644 --- a/Modules/CameraCalibration/CMakeLists.txt +++ b/Modules/CameraCalibration/CMakeLists.txt @@ -1,7 +1,7 @@ MITK_CREATE_MODULE( DEPENDS MitkIGT - PACKAGE_DEPENDS PUBLIC OpenCV + PACKAGE_DEPENDS PUBLIC OpenCV|calib3d ) # add testing dir add_subdirectory(Testing) diff --git a/Modules/ContourModel/DataManagement/mitkContourElement.cpp b/Modules/ContourModel/DataManagement/mitkContourElement.cpp index 02cfc0c4a3..0bfbe91f9c 100644 --- a/Modules/ContourModel/DataManagement/mitkContourElement.cpp +++ b/Modules/ContourModel/DataManagement/mitkContourElement.cpp @@ -1,489 +1,541 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All 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 bool mitk::ContourElement::ContourModelVertex::operator==(const ContourModelVertex &other) const { return this->Coordinates == other.Coordinates && this->IsControlPoint == other.IsControlPoint; } mitk::ContourElement::ConstVertexIterator mitk::ContourElement::ConstIteratorBegin() const { return this->begin(); } mitk::ContourElement::ConstVertexIterator mitk::ContourElement::ConstIteratorEnd() const { return this->end(); } mitk::ContourElement::VertexIterator mitk::ContourElement::IteratorBegin() { return this->begin(); } mitk::ContourElement::VertexIterator mitk::ContourElement::IteratorEnd() { return this->end(); } mitk::ContourElement::ConstVertexIterator mitk::ContourElement::begin() const { return this->m_Vertices.begin(); } mitk::ContourElement::ConstVertexIterator mitk::ContourElement::end() const { return this->m_Vertices.end(); } mitk::ContourElement::VertexIterator mitk::ContourElement::begin() { return this->m_Vertices.begin(); } mitk::ContourElement::VertexIterator mitk::ContourElement::end() { return this->m_Vertices.end(); } mitk::ContourElement::ContourElement(const mitk::ContourElement &other) : itk::LightObject(), m_IsClosed(other.m_IsClosed) { for (const auto &v : other.m_Vertices) { m_Vertices.push_back(new ContourModelVertex(*v)); } } mitk::ContourElement &mitk::ContourElement::operator=(const ContourElement &other) { if (this != &other) { this->Clear(); for (const auto &v : other.m_Vertices) { m_Vertices.push_back(new ContourModelVertex(*v)); } } this->m_IsClosed = other.m_IsClosed; return *this; } mitk::ContourElement::~ContourElement() { this->Clear(); } mitk::ContourElement::VertexSizeType mitk::ContourElement::GetSize() const { return this->m_Vertices.size(); } void mitk::ContourElement::AddVertex(const mitk::Point3D &vertex, bool isControlPoint) { this->m_Vertices.push_back(new VertexType(vertex, isControlPoint)); } void mitk::ContourElement::AddVertexAtFront(const mitk::Point3D &vertex, bool isControlPoint) { this->m_Vertices.push_front(new VertexType(vertex, isControlPoint)); } void mitk::ContourElement::InsertVertexAtIndex(const mitk::Point3D &vertex, bool isControlPoint, VertexSizeType index) { - if (this->GetSize() > index) + if (this->GetSize() >= index) { auto _where = this->m_Vertices.begin(); _where += index; this->m_Vertices.insert(_where, new VertexType(vertex, isControlPoint)); } } void mitk::ContourElement::SetVertexAt(VertexSizeType pointId, const Point3D &point) { if (this->GetSize() > pointId) { this->m_Vertices[pointId]->Coordinates = point; } } void mitk::ContourElement::SetVertexAt(VertexSizeType pointId, const VertexType *vertex) { if (nullptr == vertex) { mitkThrow() << "Cannot set vertex. Passed vertex instance is invalid. Index to set: " << pointId; } if (this->GetSize() > pointId) { this->m_Vertices[pointId]->Coordinates = vertex->Coordinates; this->m_Vertices[pointId]->IsControlPoint = vertex->IsControlPoint; } } mitk::ContourElement::VertexType *mitk::ContourElement::GetVertexAt(VertexSizeType index) { return this->m_Vertices.at(index); } const mitk::ContourElement::VertexType *mitk::ContourElement::GetVertexAt(VertexSizeType index) const { return this->m_Vertices.at(index); } bool mitk::ContourElement::IsEmpty() const { return this->m_Vertices.empty(); } mitk::ContourElement::VertexType *mitk::ContourElement::GetControlVertexAt(const mitk::Point3D &point, float eps) { /* current version iterates over the whole deque - should some kind of an octree with spatial query*/ if (eps > 0) { // currently no method with better performance is available return BruteForceGetVertexAt(point, eps, true); } // if eps < 0 return nullptr; } mitk::ContourElement::VertexType *mitk::ContourElement::GetVertexAt(const mitk::Point3D &point, float eps) { /* current version iterates over the whole deque - should some kind of an octree with spatial query*/ if (eps > 0) { // currently no method with better performance is available return BruteForceGetVertexAt(point, eps); } // if eps < 0 return nullptr; } mitk::ContourElement::VertexType *mitk::ContourElement::GetNextControlVertexAt(const mitk::Point3D &point, float eps) { /* current version iterates over the whole deque - should some kind of an octree with spatial query*/ if (eps > 0) { // currently no method with better performance is available return BruteForceGetVertexAt(point, eps, true, 1); } // if eps < 0 return nullptr; } mitk::ContourElement::VertexType *mitk::ContourElement::GetPreviousControlVertexAt(const mitk::Point3D &point, float eps) { /* current version iterates over the whole deque - should some kind of an octree with spatial query*/ if (eps > 0) { // currently no method with better performance is available return BruteForceGetVertexAt(point, eps, true, -1); } // if eps < 0 return nullptr; } mitk::ContourElement::VertexType *mitk::ContourElement::BruteForceGetVertexAt(const mitk::Point3D &point, double eps, bool isControlPoint, int offset) { VertexListType verticesList; if (isControlPoint) { verticesList = this->GetControlVertices(); } else { verticesList = *this->GetVertexList(); } int vertexIndex = BruteForceGetVertexIndexAt(point, eps, verticesList); if (vertexIndex!=-1) { vertexIndex += offset; if (vertexIndex < 0) { // for negative offset // if the offset exceeds the first vertex, we start from the end of the vertex list backwards vertexIndex = verticesList.size() + offset; } else if (vertexIndex >= (int) verticesList.size()) { // if the offset exceeds the last vertex, we start from the beginning of the vertex list vertexIndex = vertexIndex - verticesList.size(); } return verticesList[vertexIndex]; } return nullptr; } int mitk::ContourElement::BruteForceGetVertexIndexAt(const mitk::Point3D &point, double eps, VertexListType verticesList) { if (eps < 0) { mitkThrow() << "Distance cannot be negative"; } ConstVertexIterator nearestPointIterator; bool nearestPointIsInitialized = false; double nearestPointDistance = std::numeric_limits::max(); ConstVertexIterator it = verticesList.begin(); ConstVertexIterator end = verticesList.end(); while (it != end) { mitk::Point3D currentPoint = (*it)->Coordinates; double distance = currentPoint.EuclideanDistanceTo(point); if (distance < eps) { if (distance < nearestPointDistance) { nearestPointIterator = it; nearestPointIsInitialized = true; nearestPointDistance = distance; } } // if distance > eps it++; } // while if (nearestPointIsInitialized) { return nearestPointIterator - verticesList.begin(); } return -1; } const mitk::ContourElement::VertexListType *mitk::ContourElement::GetVertexList() const { return &(this->m_Vertices); } bool mitk::ContourElement::IsClosed() const { return this->m_IsClosed; } -bool mitk::ContourElement::IsNearContour(const mitk::Point3D &point, float eps) const +bool mitk::ContourElement::IsNearContour(const mitk::Point3D& point, float eps) const +{ + VertexSizeType segmentStartIndex; + VertexSizeType segmentEndIndex; + mitk::Point3D closestContourPoint; + + return GetLineSegmentForPoint(point, eps, segmentStartIndex, segmentEndIndex, closestContourPoint, false); +} + +bool mitk::ContourElement::GetLineSegmentForPoint(const mitk::Point3D& point, + float eps, VertexSizeType& segmentStartIndex, VertexSizeType& segmentEndIndex, mitk::Point3D& closestContourPoint, bool findClosest) const { ConstVertexIterator it1 = this->m_Vertices.begin(); ConstVertexIterator it2 = this->m_Vertices.begin(); it2++; // it2 runs one position ahead ConstVertexIterator end = this->m_Vertices.end(); - int counter = 0; - - for (; it1 != end; it1++, it2++, counter++) + bool closePointFound = false; + double closestDistance = std::numeric_limits::max(); + for (; it1 != end; it1++, it2++) { if (it2 == end) it2 = this->m_Vertices.begin(); mitk::Point3D v1 = (*it1)->Coordinates; mitk::Point3D v2 = (*it2)->Coordinates; const float l2 = v1.SquaredEuclideanDistanceTo(v2); mitk::Vector3D p_v1 = point - v1; mitk::Vector3D v2_v1 = v2 - v1; double tc = (p_v1 * v2_v1) / l2; // take into account we have line segments and not (infinite) lines if (tc < 0.0) + { tc = 0.0; + } if (tc > 1.0) + { tc = 1.0; + } mitk::Point3D crossPoint = v1 + v2_v1 * tc; double distance = point.SquaredEuclideanDistanceTo(crossPoint); - if (distance < eps) + if (distance < eps && distance < closestDistance) { - return true; + closestDistance = distance; + closePointFound = true; + closestContourPoint = crossPoint; + segmentStartIndex = std::distance(this->m_Vertices.begin(), it1); + segmentEndIndex = std::distance(this->m_Vertices.begin(), it2); + if (!findClosest) + { + return true; + } } } - return false; + return closePointFound; } +bool mitk::ContourElement::GetLineSegmentForPoint(const mitk::Point3D &point, + float eps, + mitk::ContourElement::VertexType *previousVertex, + mitk::ContourElement::VertexType *nextVertex) const +{ + VertexSizeType segmentStartIndex; + VertexSizeType segmentEndIndex; + mitk::Point3D closestContourPoint; + + auto result = GetLineSegmentForPoint(point, eps, segmentStartIndex, segmentEndIndex, closestContourPoint, true); + + if (result) + { + ConstVertexIterator it1 = this->m_Vertices.begin() + segmentStartIndex; + ConstVertexIterator it2 = this->m_Vertices.begin() + segmentEndIndex; + if (previousVertex) + { + previousVertex->Coordinates = (*it1)->Coordinates; + previousVertex->IsControlPoint = (*it1)->IsControlPoint; + } + if (nextVertex) + { + nextVertex->Coordinates = (*it2)->Coordinates; + nextVertex->IsControlPoint = (*it2)->IsControlPoint; + } + } + return result; +} + + void mitk::ContourElement::Close() { this->m_IsClosed = true; } void mitk::ContourElement::Open() { this->m_IsClosed = false; } void mitk::ContourElement::SetClosed(bool isClosed) { isClosed ? this->Close() : this->Open(); } mitk::ContourElement::VertexListType mitk::ContourElement::GetControlVertices() const { VertexListType controlVertices; std::copy_if( this->m_Vertices.begin(), this->m_Vertices.end(), std::back_inserter(controlVertices), [](const VertexType *v) { return v->IsControlPoint; }); return controlVertices; } void mitk::ContourElement::Concatenate(const mitk::ContourElement *other, bool check) { if (other->GetSize() > 0) { for (const auto &sourceVertex : other->m_Vertices) { if (check) { auto finding = std::find_if(this->m_Vertices.begin(), this->m_Vertices.end(), [sourceVertex](const VertexType *v) { return sourceVertex->Coordinates == v->Coordinates; }); if (finding == this->m_Vertices.end()) { this->m_Vertices.push_back(new ContourModelVertex(*sourceVertex)); } } else { this->m_Vertices.push_back(new ContourModelVertex(*sourceVertex)); } } } } mitk::ContourElement::VertexSizeType mitk::ContourElement::GetIndex(const VertexType *vertex) const { VertexSizeType result = NPOS; auto finding = std::find(this->m_Vertices.begin(), this->m_Vertices.end(), vertex); if (finding != this->m_Vertices.end()) { result = finding - this->m_Vertices.begin(); } return result; } bool mitk::ContourElement::RemoveVertex(const VertexType *vertex) { auto finding = std::find(this->m_Vertices.begin(), this->m_Vertices.end(), vertex); return RemoveVertexByIterator(finding); } bool mitk::ContourElement::RemoveVertexAt(VertexSizeType index) { if (index < this->m_Vertices.size()) { auto delIter = this->m_Vertices.begin() + index; return RemoveVertexByIterator(delIter); } return false; } bool mitk::ContourElement::RemoveVertexAt(const mitk::Point3D &point, double eps) { if (eps > 0) { auto finding = std::find_if(this->m_Vertices.begin(), this->m_Vertices.end(), [point, eps](const VertexType *v) { return v->Coordinates.EuclideanDistanceTo(point) < eps; }); return RemoveVertexByIterator(finding); } return false; } bool mitk::ContourElement::RemoveVertexByIterator(VertexListType::iterator &iter) { if (iter != this->m_Vertices.end()) { delete *iter; this->m_Vertices.erase(iter); return true; } return false; } void mitk::ContourElement::Clear() { for (auto vertex : m_Vertices) { delete vertex; } this->m_Vertices.clear(); } //---------------------------------------------------------------------- void mitk::ContourElement::RedistributeControlVertices(const VertexType *selected, int period) { int counter = 0; auto _where = this->m_Vertices.begin(); if (selected != nullptr) { auto finding = std::find(this->m_Vertices.begin(), this->m_Vertices.end(), selected); if (finding != this->m_Vertices.end()) { _where = finding; } } auto _iter = _where; while (_iter != this->m_Vertices.end()) { div_t divresult; divresult = div(counter, period); (*_iter)->IsControlPoint = (divresult.rem == 0); counter++; _iter++; } _iter = _where; counter = 0; while (_iter != this->m_Vertices.begin()) { div_t divresult; divresult = div(counter, period); (*_iter)->IsControlPoint = (divresult.rem == 0); counter++; _iter--; } } diff --git a/Modules/ContourModel/DataManagement/mitkContourElement.h b/Modules/ContourModel/DataManagement/mitkContourElement.h index 4a5e52864e..3175781eba 100644 --- a/Modules/ContourModel/DataManagement/mitkContourElement.h +++ b/Modules/ContourModel/DataManagement/mitkContourElement.h @@ -1,290 +1,308 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _mitkContourElement_H_ #define _mitkContourElement_H_ #include "mitkCommon.h" #include #include #include namespace mitk { /** \brief Represents a contour in 3D space. A ContourElement is consisting of linked vertices implicitely defining the contour. They are stored in a double ended queue making it possible to add vertices at front and end of the contour and to iterate in both directions. To mark a vertex as a special one it can be set as a control point. \note This class assumes that it manages its vertices. So if a vertex instance is added to this class the ownership of the vertex is transfered to the ContourElement instance. The ContourElement instance takes care of deleting vertex instances if needed. It is highly not recommend to use this class directly as it is designed as a internal class of ContourModel. Therefore it is adviced to use ContourModel if contour representations are needed in MITK. */ class MITKCONTOURMODEL_EXPORT ContourElement : public itk::LightObject { public: mitkClassMacroItkParent(ContourElement, itk::LightObject); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** \brief Represents a single vertex of a contour. */ struct MITKCONTOURMODEL_EXPORT ContourModelVertex { ContourModelVertex(const mitk::Point3D& point, bool active = false) : IsControlPoint(active), Coordinates(point) {}; ContourModelVertex(const ContourModelVertex& other) : IsControlPoint(other.IsControlPoint), Coordinates(other.Coordinates) { }; /** \brief Treat point special. */ bool IsControlPoint; /** \brief Coordinates in 3D space. */ mitk::Point3D Coordinates; bool operator ==(const ContourModelVertex& other) const; }; using VertexType = ContourModelVertex; using VertexListType = std::deque; using VertexIterator = VertexListType::iterator; using ConstVertexIterator = VertexListType::const_iterator; using VertexSizeType = VertexListType::size_type; /**Indicates an invalid index. * It is always the maximum of the unsigned int type.*/ static const VertexSizeType NPOS = -1; /** \brief Return a const iterator a the front. */ ConstVertexIterator ConstIteratorBegin() const; /** \brief Return a const iterator a the end. */ ConstVertexIterator ConstIteratorEnd() const; /** \brief Return an iterator a the front. */ VertexIterator IteratorBegin(); /** \brief Return an iterator a the end. */ VertexIterator IteratorEnd(); /** \brief Return a const iterator a the front. * For easier support of stl functionality. */ ConstVertexIterator begin() const; /** \brief Return a const iterator a the end. * For easier support of stl functionality. */ ConstVertexIterator end() const; /** \brief Return an iterator a the front. * For easier support of stl functionality. */ VertexIterator begin(); /** \brief Return an iterator a the end. * For easier support of stl functionality. */ VertexIterator end(); /** \brief Returns the number of contained vertices. */ VertexSizeType GetSize() const; /** \brief Add a vertex at the end of the contour \param point - coordinates in 3D space. \param isControlPoint - is the vertex a special control point. */ void AddVertex(const mitk::Point3D &point, bool isControlPoint); /** \brief Add a vertex at the front of the contour \param point - coordinates in 3D space. \param isControlPoint - is the vertex a control point. */ void AddVertexAtFront(const mitk::Point3D &point, bool isControlPoint); /** \brief Add a vertex at a given index of the contour \param point - coordinates in 3D space. \param isControlPoint - is the vertex a special control point. \param index - the index to be inserted at. */ void InsertVertexAtIndex(const mitk::Point3D &point, bool isControlPoint, VertexSizeType index); /** \brief Set coordinates a given index. \param pointId Index of vertex. \param point Coordinates. */ void SetVertexAt(VertexSizeType pointId, const mitk::Point3D &point); /** \brief Set vertex a given index (by copying the values). \param pointId Index of vertex. \param vertex Vertex. \pre Passed vertex is a valid instance */ void SetVertexAt(VertexSizeType pointId, const VertexType* vertex); /** \brief Returns the vertex a given index \param index \pre index must be valid. */ VertexType* GetVertexAt(VertexSizeType index); const VertexType* GetVertexAt(VertexSizeType index) const; /** \brief Returns the approximate nearest vertex a given position in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ VertexType *GetVertexAt(const mitk::Point3D &point, float eps); /** \brief Returns the next vertex to the approximate nearest vertex of a given position in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ VertexType *GetNextControlVertexAt(const mitk::Point3D &point, float eps); /** \brief Returns the previous vertex to the approximate nearest vertex of a given position in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ VertexType *GetPreviousControlVertexAt(const mitk::Point3D &point, float eps); /** \brief Returns the approximate nearest control vertex a given posoition in 3D space, if the clicked position is within a specific range. \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ VertexType *GetControlVertexAt(const mitk::Point3D &point, float eps); /** \brief Returns the index of the given vertex within the contour. \param vertex - the vertex to be searched. \return index of vertex. Returns ContourElement::NPOS if not found. */ VertexSizeType GetIndex(const VertexType *vertex) const; /** \brief Returns the container of the vertices. */ const VertexListType *GetVertexList() const; /** \brief Returns whether the contour element is empty. */ bool IsEmpty() const; /** \brief Returns if the conour is closed or not. */ bool IsClosed() const; /** \brief Returns whether a given point is near a contour, according to eps. \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ bool IsNearContour(const mitk::Point3D &point, float eps) const; + /** Function that searches for the line segment of the contour that is closest to the passed point + and close enough (distance between point and line segment <= eps). If such an line segment exist, + the starting vertex and closing vertex of the found segment are passed back. + @return True indicates that a line segment was found. False indicates that no segment of the contour + is close enough to the passed point. + @remark previousVertex and nextVertex are only valid if return is true.*/ + bool GetLineSegmentForPoint(const mitk::Point3D &point, + float eps, + mitk::ContourElement::VertexType *previousVertex, + mitk::ContourElement::VertexType *nextVertex) const; + /** Overloaded version that offers additional options when searching for the line segment. + In contrast to the other version it returns the index of the segment start and end as well as the point + on the line segment closest to the passed point. Further one can decide if the function should search for + the first segment that is close enough (see eps) or for the segment that is really the closest (findClosest==true). + @remark segmentStartIndex, segmentEndIndex and closestContourPoint are only valid if return is true.*/ + bool GetLineSegmentForPoint(const mitk::Point3D& point, + float eps, VertexSizeType& segmentStartIndex, VertexSizeType& segmentEndIndex, mitk::Point3D& closestContourPoint, bool findClosest = true) const; + /** \brief Close the contour. Connect first with last element. */ void Close(); /** \brief Open the contour. Disconnect first and last element. */ void Open(); /** \brief Set the contours IsClosed property. \param isClosed - true = closed; false = open; */ void SetClosed(bool isClosed); /** \brief Concatenate the contuor with a another contour. All vertices of the other contour will be cloned and added after last vertex. \param other - the other contour \param check - set it true to avoid adding of vertices that are already in the source contour */ void Concatenate(const mitk::ContourElement *other, bool check); /** \brief Remove the given vertex from the container if exists. \param vertex - the vertex to be removed. */ bool RemoveVertex(const VertexType *vertex); /** \brief Remove a vertex at given index within the container if exists. \param index - the index where the vertex should be removed. */ bool RemoveVertexAt(VertexSizeType index); /** \brief Remove the approximate nearest vertex at given position in 3D space if one exists. \param point - query point in 3D space. \param eps - error bound for search algorithm. */ bool RemoveVertexAt(const mitk::Point3D &point, double eps); /** \brief Clear the storage container. */ void Clear(); /** \brief Returns the approximate nearest vertex a given position in 3D space. With the parameter 'isControlPoint', one can decide if any vertex should be returned, or just control vertices. \param point - query position in 3D space. \param eps - the error bound for search algorithm. It is an open boundary. \param isControlPoint \param offset - a offset to the vertex, e.g. 1 if the next vertex should be returned or -1 for the previous vertex */ VertexType *BruteForceGetVertexAt(const mitk::Point3D &point, double eps, bool isControlPoint = false, int offset = 0); /** \brief Returns the index of the approximate nearest vertex of a given position in 3D space. \param point - query position in 3D space. \param eps - the error bound for search algorithm. It is an open boundary. \param verticesList - the vertex list to search the index in, either only control vertices or all vertices */ int BruteForceGetVertexIndexAt(const mitk::Point3D &point, double eps, VertexListType verticesList); /** Returns a list pointing to all vertices that are indicated to be control points. \remark It is important to note, that the vertex pointers in the returned list directly point to the vertices stored interanlly. So they are still owned by the ContourElement instance that returns the list. If one wants to take over ownership, one has to clone the vertex instances. */ VertexListType GetControlVertices() const; /** \brief Uniformly redistribute control points with a given period (in number of vertices) \param vertex - the vertex around which the redistribution is done. \param period - number of vertices between control points. */ void RedistributeControlVertices(const VertexType *vertex, int period); protected: mitkCloneMacro(Self); ContourElement() = default; ContourElement(const mitk::ContourElement &other); ~ContourElement(); ContourElement& operator = (const ContourElement & other); /** Internal helper function to correctly remove the element indicated by the iterator from the list. After the call the iterator is invalid. Caller of the function must ensure that the iterator is valid!. \result Indicates if the element indicated by the iterator was removed. If iterator points to end it returns false.*/ bool RemoveVertexByIterator(VertexListType::iterator& iter); VertexListType m_Vertices; // double ended queue with vertices bool m_IsClosed = false; }; } // namespace mitk #endif // _mitkContourElement_H_ diff --git a/Modules/ContourModel/DataManagement/mitkContourModel.cpp b/Modules/ContourModel/DataManagement/mitkContourModel.cpp index 518abf4352..1f7c53febc 100644 --- a/Modules/ContourModel/DataManagement/mitkContourModel.cpp +++ b/Modules/ContourModel/DataManagement/mitkContourModel.cpp @@ -1,690 +1,729 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All 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 namespace mitk { itkEventMacroDefinition(ContourModelEvent, itk::AnyEvent); itkEventMacroDefinition(ContourModelShiftEvent, ContourModelEvent); itkEventMacroDefinition(ContourModelSizeChangeEvent, ContourModelEvent); itkEventMacroDefinition(ContourModelAddEvent, ContourModelSizeChangeEvent); itkEventMacroDefinition(ContourModelRemoveEvent, ContourModelSizeChangeEvent); itkEventMacroDefinition(ContourModelExpandTimeBoundsEvent, ContourModelEvent); itkEventMacroDefinition(ContourModelClosedEvent, ContourModelEvent); } mitk::ContourModel::ContourModel() : m_UpdateBoundingBox(true) { // set to initial state this->InitializeEmpty(); } mitk::ContourModel::ContourModel(const ContourModel &other) : BaseData(other), m_ContourSeries(other.m_ContourSeries), m_lineInterpolation(other.m_lineInterpolation) { m_SelectedVertex = nullptr; } mitk::ContourModel::~ContourModel() { m_SelectedVertex = nullptr; this->m_ContourSeries.clear(); // TODO check destruction } void mitk::ContourModel::AddVertex(const Point3D &vertex, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->AddVertex(vertex, false, timestep); } } void mitk::ContourModel::AddVertex(const Point3D &vertex, bool isControlPoint, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertex(vertex, isControlPoint); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::AddVertex(const VertexType &vertex, TimeStepType timestep) { this->AddVertex(vertex.Coordinates, vertex.IsControlPoint, timestep); } void mitk::ContourModel::AddVertexAtFront(const Point3D &vertex, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->AddVertexAtFront(vertex, false, timestep); } } void mitk::ContourModel::AddVertexAtFront(const Point3D &vertex, bool isControlPoint, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertexAtFront(vertex, isControlPoint); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::AddVertexAtFront(const VertexType &vertex, TimeStepType timestep) { this->AddVertexAtFront(vertex.Coordinates, vertex.IsControlPoint, timestep); } bool mitk::ContourModel::SetVertexAt(int pointId, const Point3D &point, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (pointId >= 0 && this->m_ContourSeries[timestep]->GetSize() > ContourElement::VertexSizeType(pointId)) { this->m_ContourSeries[timestep]->SetVertexAt(pointId, point); this->Modified(); this->m_UpdateBoundingBox = true; return true; } return false; } return false; } bool mitk::ContourModel::SetVertexAt(int pointId, const VertexType *vertex, TimeStepType timestep) { if (vertex == nullptr) return false; if (!this->IsEmptyTimeStep(timestep)) { if (pointId >= 0 && this->m_ContourSeries[timestep]->GetSize() > ContourElement::VertexSizeType(pointId)) { this->m_ContourSeries[timestep]->SetVertexAt(pointId, vertex); this->Modified(); this->m_UpdateBoundingBox = true; return true; } return false; } return false; } void mitk::ContourModel::InsertVertexAtIndex(const Point3D &vertex, int index, bool isControlPoint, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { - if (index >= 0 && this->m_ContourSeries[timestep]->GetSize() > ContourElement::VertexSizeType(index)) + if (index >= 0 && this->m_ContourSeries[timestep]->GetSize() >= ContourElement::VertexSizeType(index)) { this->m_ContourSeries[timestep]->InsertVertexAtIndex(vertex, isControlPoint, index); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } } void mitk::ContourModel::UpdateContour(const ContourModel* sourceModel, TimeStepType destinationTimeStep, TimeStepType sourceTimeStep) { if (nullptr == sourceModel) { mitkThrow() << "Cannot update contour. Passed source model is invalid."; } if (!sourceModel->GetTimeGeometry()->IsValidTimeStep(sourceTimeStep)) { mitkThrow() << "Cannot update contour. Source contour time geometry does not support passed time step. Invalid time step: " << sourceTimeStep; } if (!this->GetTimeGeometry()->IsValidTimeStep(destinationTimeStep)) { MITK_WARN << "Cannot update contour. Contour time geometry does not support passed time step. Invalid time step: " << destinationTimeStep; return; } this->Clear(destinationTimeStep); std::for_each(sourceModel->Begin(sourceTimeStep), sourceModel->End(sourceTimeStep), [this, destinationTimeStep](ContourElement::VertexType* vertex) { this->m_ContourSeries[destinationTimeStep]->AddVertex(vertex->Coordinates, vertex->IsControlPoint); }); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } bool mitk::ContourModel::IsEmpty(TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsEmpty(); } return true; } bool mitk::ContourModel::IsEmpty() const { return this->IsEmpty(0); } int mitk::ContourModel::GetNumberOfVertices(TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetSize(); } return -1; } const mitk::ContourModel::VertexType *mitk::ContourModel::GetVertexAt(int index, TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep) && this->m_ContourSeries[timestep]->GetSize() > mitk::ContourElement::VertexSizeType(index)) { return this->m_ContourSeries[timestep]->GetVertexAt(index); } return nullptr; } +const mitk::ContourModel::VertexType *mitk::ContourModel::GetVertexAt(mitk::Point3D &point, + float eps, + TimeStepType timestep) const +{ + if (!this->IsEmptyTimeStep(timestep)) + { + return this->m_ContourSeries[timestep]->GetVertexAt(point, eps); + } + return nullptr; +} + + const mitk::ContourModel::VertexType *mitk::ContourModel::GetNextControlVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetNextControlVertexAt(point, eps); } return nullptr; } const mitk::ContourModel::VertexType *mitk::ContourModel::GetPreviousControlVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetPreviousControlVertexAt(point, eps); } return nullptr; } int mitk::ContourModel::GetIndex(const VertexType *vertex, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetIndex(vertex); } return -1; } void mitk::ContourModel::Close(TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->Close(); this->InvokeEvent(ContourModelClosedEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::Open(TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->Open(); this->InvokeEvent(ContourModelClosedEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::SetClosed(bool isClosed, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->SetClosed(isClosed); this->InvokeEvent(ContourModelClosedEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } bool mitk::ContourModel::IsEmptyTimeStep(unsigned int t) const { return (this->m_ContourSeries.size() <= t); } -bool mitk::ContourModel::IsNearContour(Point3D &point, float eps, TimeStepType timestep) +bool mitk::ContourModel::IsNearContour(Point3D &point, float eps, TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsNearContour(point, eps); } return false; } +bool mitk::ContourModel::GetLineSegmentForPoint(const mitk::Point3D& point, + float eps, TimeStepType timestep, + ContourElement::VertexSizeType& segmentStartIndex, + ContourElement::VertexSizeType& segmentEndIndex, + mitk::Point3D& closestContourPoint, + bool findClosest) const +{ + if (!this->IsEmptyTimeStep(timestep)) + { + return this->m_ContourSeries[timestep]->GetLineSegmentForPoint(point, eps, segmentStartIndex, segmentEndIndex, closestContourPoint, findClosest); + } + return false; +} + +bool mitk::ContourModel::GetLineSegmentForPoint(Point3D &point, + float eps, + TimeStepType timestep, + mitk::ContourElement::VertexType *previousVertex, + mitk::ContourElement::VertexType *nextVertex) +{ + if (!this->IsEmptyTimeStep(timestep)) + { + return this->m_ContourSeries[timestep]->GetLineSegmentForPoint(point, eps, previousVertex, nextVertex); + } + return false; +} + void mitk::ContourModel::Concatenate(ContourModel *other, TimeStepType timestep, bool check) { if (!this->IsEmptyTimeStep(timestep)) { if (!this->m_ContourSeries[timestep]->IsClosed()) { this->m_ContourSeries[timestep]->Concatenate(other->m_ContourSeries[timestep], check); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } } mitk::ContourModel::VertexIterator mitk::ContourModel::Begin(TimeStepType timestep) const { return this->IteratorBegin(timestep); } mitk::ContourModel::VertexIterator mitk::ContourModel::IteratorBegin(TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IteratorBegin(); } else { mitkThrow() << "No iterator at invalid timestep " << timestep << ". There are only " << this->GetTimeSteps() << " timesteps available."; } } mitk::ContourModel::VertexIterator mitk::ContourModel::End(TimeStepType timestep) const { return this->IteratorEnd(timestep); } mitk::ContourModel::VertexIterator mitk::ContourModel::IteratorEnd(TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IteratorEnd(); } else { mitkThrow() << "No iterator at invalid timestep " << timestep << ". There are only " << this->GetTimeSteps() << " timesteps available."; } } bool mitk::ContourModel::IsClosed(int timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsClosed(); } return false; } bool mitk::ContourModel::SelectControlVertexAt(Point3D &point, float eps, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetControlVertexAt(point, eps); } return this->m_SelectedVertex != nullptr; } bool mitk::ContourModel::SelectVertexAt(Point3D &point, float eps, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetVertexAt(point, eps); } return this->m_SelectedVertex != nullptr; } bool mitk::ContourModel::SelectVertexAt(int index, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep) && index >= 0) { return (this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetVertexAt(index)); } return false; } bool mitk::ContourModel::SetControlVertexAt(Point3D &point, float eps, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { VertexType *vertex = this->m_ContourSeries[timestep]->GetVertexAt(point, eps); if (vertex != nullptr) { vertex->IsControlPoint = true; return true; } } return false; } bool mitk::ContourModel::SetControlVertexAt(int index, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep) && index >= 0) { VertexType *vertex = this->m_ContourSeries[timestep]->GetVertexAt(index); if (vertex != nullptr) { vertex->IsControlPoint = true; return true; } } return false; } bool mitk::ContourModel::RemoveVertex(const VertexType *vertex, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (this->m_ContourSeries[timestep]->RemoveVertex(vertex)) { this->Modified(); this->m_UpdateBoundingBox = true; this->InvokeEvent(ContourModelSizeChangeEvent()); return true; } } return false; } bool mitk::ContourModel::RemoveVertexAt(int index, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (this->m_ContourSeries[timestep]->RemoveVertexAt(index)) { this->Modified(); this->m_UpdateBoundingBox = true; this->InvokeEvent(ContourModelSizeChangeEvent()); return true; } } return false; } bool mitk::ContourModel::RemoveVertexAt(Point3D &point, float eps, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (this->m_ContourSeries[timestep]->RemoveVertexAt(point, eps)) { this->Modified(); this->m_UpdateBoundingBox = true; this->InvokeEvent(ContourModelSizeChangeEvent()); return true; } } return false; } void mitk::ContourModel::ShiftSelectedVertex(Vector3D &translate) { if (this->m_SelectedVertex) { this->ShiftVertex(this->m_SelectedVertex, translate); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::ShiftContour(Vector3D &translate, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { // shift all vertices for (auto vertex : *(this->m_ContourSeries[timestep])) { this->ShiftVertex(vertex, translate); } this->Modified(); this->m_UpdateBoundingBox = true; this->InvokeEvent(ContourModelShiftEvent()); } } void mitk::ContourModel::ShiftVertex(VertexType *vertex, Vector3D &vector) { vertex->Coordinates[0] += vector[0]; vertex->Coordinates[1] += vector[1]; vertex->Coordinates[2] += vector[2]; } void mitk::ContourModel::Clear(TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { // clear data at timestep this->m_ContourSeries[timestep]->Clear(); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::Expand(unsigned int timeSteps) { std::size_t oldSize = this->m_ContourSeries.size(); if (static_cast(timeSteps) > oldSize) { Superclass::Expand(timeSteps); // insert contours for each new timestep for (std::size_t i = oldSize; i < static_cast(timeSteps); i++) { m_ContourSeries.push_back(ContourElement::New()); } this->InvokeEvent(ContourModelExpandTimeBoundsEvent()); } } void mitk::ContourModel::SetRequestedRegionToLargestPossibleRegion() { // no support for regions } bool mitk::ContourModel::RequestedRegionIsOutsideOfTheBufferedRegion() { // no support for regions return false; } bool mitk::ContourModel::VerifyRequestedRegion() { // no support for regions return true; } void mitk::ContourModel::SetRequestedRegion(const itk::DataObject * /*data*/) { // no support for regions } void mitk::ContourModel::Clear() { // clear data and set to initial state again this->ClearData(); this->InitializeEmpty(); this->Modified(); this->m_UpdateBoundingBox = true; } void mitk::ContourModel::RedistributeControlVertices(int period, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->RedistributeControlVertices(this->GetSelectedVertex(), period); this->InvokeEvent(ContourModelClosedEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } mitk::ContourModel::VertexListType mitk::ContourModel::GetControlVertices(TimeStepType timestep) { VertexListType controlVertices; if (!this->IsEmptyTimeStep(timestep)) { controlVertices = this->m_ContourSeries[timestep]->GetControlVertices(); } return controlVertices; } mitk::ContourModel::VertexListType mitk::ContourModel::GetVertexList(TimeStepType timestep) { VertexListType controlVertices; if (!this->IsEmptyTimeStep(timestep)) { controlVertices = *this->m_ContourSeries[timestep]->GetVertexList(); } return controlVertices; } void mitk::ContourModel::ClearData() { // call the superclass, this releases the data of BaseData Superclass::ClearData(); // clear out the time resolved contours this->m_ContourSeries.clear(); } void mitk::ContourModel::Initialize() { this->InitializeEmpty(); this->Modified(); this->m_UpdateBoundingBox = true; } void mitk::ContourModel::Initialize(const ContourModel &other) { TimeStepType numberOfTimesteps = other.GetTimeGeometry()->CountTimeSteps(); this->InitializeTimeGeometry(numberOfTimesteps); for (TimeStepType currentTimestep = 0; currentTimestep < numberOfTimesteps; currentTimestep++) { this->m_ContourSeries.push_back(ContourElement::New()); this->SetClosed(other.IsClosed(currentTimestep), currentTimestep); } m_SelectedVertex = nullptr; this->m_lineInterpolation = other.m_lineInterpolation; this->Modified(); this->m_UpdateBoundingBox = true; } void mitk::ContourModel::InitializeEmpty() { // clear data at timesteps this->m_ContourSeries.resize(0); this->m_ContourSeries.push_back(ContourElement::New()); // set number of timesteps to one this->InitializeTimeGeometry(1); m_SelectedVertex = nullptr; this->m_lineInterpolation = ContourModel::LINEAR; } void mitk::ContourModel::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } if (this->m_UpdateBoundingBox) { // update the bounds of the geometry according to the stored vertices ScalarType mitkBounds[6]; // calculate the boundingbox at each timestep typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::PointsContainer PointsContainer; int timesteps = this->GetTimeSteps(); // iterate over the timesteps for (int currenTimeStep = 0; currenTimeStep < timesteps; currenTimeStep++) { if (dynamic_cast(this->GetGeometry(currenTimeStep))) { // do not update bounds for 2D geometries, as they are unfortunately defined with min bounds 0! return; } else { // we have a 3D geometry -> let's update bounds // only update bounds if the contour was modified if (this->GetMTime() > this->GetGeometry(currenTimeStep)->GetBoundingBox()->GetMTime()) { mitkBounds[0] = 0.0; mitkBounds[1] = 0.0; mitkBounds[2] = 0.0; mitkBounds[3] = 0.0; mitkBounds[4] = 0.0; mitkBounds[5] = 0.0; BoundingBoxType::Pointer boundingBox = BoundingBoxType::New(); PointsContainer::Pointer points = PointsContainer::New(); auto it = this->IteratorBegin(currenTimeStep); auto end = this->IteratorEnd(currenTimeStep); // fill the boundingbox with the points while (it != end) { Point3D currentP = (*it)->Coordinates; BoundingBoxType::PointType p; p.CastFrom(currentP); points->InsertElement(points->Size(), p); it++; } // construct the new boundingBox boundingBox->SetPoints(points); boundingBox->ComputeBoundingBox(); BoundingBoxType::BoundsArrayType tmp = boundingBox->GetBounds(); mitkBounds[0] = tmp[0]; mitkBounds[1] = tmp[1]; mitkBounds[2] = tmp[2]; mitkBounds[3] = tmp[3]; mitkBounds[4] = tmp[4]; mitkBounds[5] = tmp[5]; // set boundingBox at current timestep BaseGeometry *geometry3d = this->GetGeometry(currenTimeStep); geometry3d->SetBounds(mitkBounds); } } } this->m_UpdateBoundingBox = false; } GetTimeGeometry()->Update(); } void mitk::ContourModel::ExecuteOperation(Operation * /*operation*/) { // not supported yet } diff --git a/Modules/ContourModel/DataManagement/mitkContourModel.h b/Modules/ContourModel/DataManagement/mitkContourModel.h index f88e13b358..6fb6c7bb7a 100644 --- a/Modules/ContourModel/DataManagement/mitkContourModel.h +++ b/Modules/ContourModel/DataManagement/mitkContourModel.h @@ -1,467 +1,489 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _MITK_CONTOURMODEL_H_ #define _MITK_CONTOURMODEL_H_ #include "mitkBaseData.h" #include "mitkCommon.h" #include #include namespace mitk { /** \brief ContourModel is a structure of linked vertices defining a contour in 3D space. The vertices are stored in a mitk::ContourElement is stored for each timestep. The contour line segments are implicitly defined by the given linked vertices. By default two control points are are linked by a straight line.It is possible to add vertices at front and end of the contour and to iterate in both directions. Points are specified containing coordinates and additional (data) information, see mitk::ContourElement. For accessing a specific vertex either an index or a position in 3D Space can be used. The vertices are best accessed by using a VertexIterator. Interaction with the contour is thus available without any mitk interactor class using the api of ContourModel. It is possible to shift single vertices also as shifting the whole contour. A contour can be either open like a single curved line segment or closed. A closed contour can for example represent a jordan curve. \section mitkContourModelDisplayOptions Display Options The default mappers for this data structure are mitk::ContourModelGLMapper2D and mitk::ContourModelMapper3D. See these classes for display options which can can be set via properties. */ class MITKCONTOURMODEL_EXPORT ContourModel : public BaseData { public: mitkClassMacro(ContourModel, BaseData); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /*+++++++++++++++ typedefs +++++++++++++++++++++++++++++++*/ typedef ContourElement::VertexType VertexType; typedef ContourElement::VertexListType VertexListType; typedef ContourElement::VertexIterator VertexIterator; typedef ContourElement::ConstVertexIterator ConstVertexIterator; typedef std::vector ContourModelSeries; /*+++++++++++++++ END typedefs ++++++++++++++++++++++++++++*/ /** \brief Possible interpolation of the line segments between control points */ enum LineSegmentInterpolation { LINEAR, B_SPLINE }; /*++++++++++++++++ inline methods +++++++++++++++++++++++*/ /** \brief Get the current selected vertex. */ VertexType *GetSelectedVertex() { return this->m_SelectedVertex; } /** \brief Deselect vertex. */ void Deselect() { this->m_SelectedVertex = nullptr; } /** \brief Set selected vertex as control point */ void SetSelectedVertexAsControlPoint(bool isControlPoint = true) { if (this->m_SelectedVertex) { m_SelectedVertex->IsControlPoint = isControlPoint; this->Modified(); } } /** \brief Set the interpolation of the line segments between control points. */ void SetLineSegmentInterpolation(LineSegmentInterpolation interpolation) { this->m_lineInterpolation = interpolation; this->Modified(); } /** \brief Get the interpolation of the line segments between control points. */ LineSegmentInterpolation GetLineSegmentInterpolation() { return this->m_lineInterpolation; } /*++++++++++++++++ END inline methods +++++++++++++++++++++++*/ /** \brief Add a vertex to the contour at given timestep. The vertex is added at the end of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertex(const Point3D &vertex, TimeStepType timestep = 0); /** \brief Add a vertex to the contour at given timestep. A copy of the passed vertex is added at the end of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertex(const VertexType &vertex, TimeStepType timestep = 0); /** \brief Add a vertex to the contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) \param isControlPoint - specifies the vertex to be handled in a special way (e.g. control points will be rendered). @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertex(const Point3D& vertex, bool isControlPoint, TimeStepType timestep = 0); /** Clears the contour of destinationTimeStep and copies the contour of the passed source model at the sourceTimeStep. @pre soureModel must point to a valid instance @pre sourceTimePoint must be valid @note Updateing a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void UpdateContour(const ContourModel* sourceModel, TimeStepType destinationTimeStep, TimeStepType sourceTimeStep); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. The vertex is added at the FRONT of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertexAtFront(const Point3D &vertex, TimeStepType timestep = 0); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. The vertex is added at the FRONT of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertexAtFront(const VertexType &vertex, TimeStepType timestep = 0); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) \param isControlPoint - specifies the vertex to be handled in a special way (e.g. control points will be rendered). @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertexAtFront(const Point3D &vertex, bool isControlPoint, TimeStepType timestep = 0); /** \brief Insert a vertex at given index. */ void InsertVertexAtIndex(const Point3D &vertex, int index, bool isControlPoint = false, TimeStepType timestep = 0); /** \brief Set a coordinates for point at given index. */ bool SetVertexAt(int pointId, const Point3D &point, TimeStepType timestep = 0); /** \brief Set a coordinates and control state for point at given index. */ bool SetVertexAt(int pointId, const VertexType *vertex, TimeStepType timestep = 0); /** \brief Return if the contour is closed or not. */ bool IsClosed(int timestep = 0) const; /** \brief Concatenate two contours. The starting control point of the other will be added at the end of the contour. \param other \param timestep - the timestep at which the vertex will be add ( default 0) \param check - check for intersections ( default false) */ void Concatenate(ContourModel *other, TimeStepType timestep = 0, bool check = false); /** \brief Returns a const VertexIterator at the start element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator Begin(TimeStepType timestep = 0) const; /** \brief Returns a const VertexIterator at the start element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator IteratorBegin(TimeStepType timestep = 0) const; /** \brief Returns a const VertexIterator at the end element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator End(TimeStepType timestep = 0) const; /** \brief Returns a const VertexIterator at the end element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator IteratorEnd(TimeStepType timestep = 0) const; /** \brief Close the contour. The last control point will be linked with the first point. */ virtual void Close(TimeStepType timestep = 0); /** \brief Set isClosed to false contour. The link between the last control point the first point will be removed. */ virtual void Open(TimeStepType timestep = 0); /** \brief Set closed property to given boolean. false - The link between the last control point the first point will be removed. true - The last control point will be linked with the first point. */ virtual void SetClosed(bool isClosed, TimeStepType timestep = 0); /** \brief Returns the number of vertices at a given timestep. \param timestep - default = 0 */ int GetNumberOfVertices(TimeStepType timestep = 0) const; /** \brief Returns whether the contour model is empty at a given timestep. \param timestep - default = 0 */ virtual bool IsEmpty(TimeStepType timestep) const; /** \brief Returns whether the contour model is empty. */ bool IsEmpty() const override; /** \brief Returns the vertex at the index position within the container. * If the index or timestep is invalid a nullptr will be returned. */ virtual const VertexType *GetVertexAt(int index, TimeStepType timestep = 0) const; + const VertexType *GetVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const; + /** Returns the next control vertex to the approximate nearest vertex of a given position in 3D space * If the timestep is invalid a nullptr will be returned. */ virtual const VertexType *GetNextControlVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const; /** Returns the previous control vertex to the approximate nearest vertex of a given position in 3D space * If the timestep is invalid a nullptr will be returned. */ virtual const VertexType *GetPreviousControlVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const; /** \brief Remove a vertex at given timestep within the container. \return index of vertex. -1 if not found. */ int GetIndex(const VertexType *vertex, TimeStepType timestep = 0); /** \brief Check if there isn't something at this timestep. */ bool IsEmptyTimeStep(unsigned int t) const override; /** \brief Check if mouse cursor is near the contour. */ - virtual bool IsNearContour(Point3D &point, float eps, TimeStepType timestep); - + bool IsNearContour(Point3D &point, float eps, TimeStepType timestep) const; + + /** Function that searches for the line segment of the contour that is closest to the passed point + and close enough (distance between point and line segment <= eps). If such an line segment exist, + the starting vertex and closing vertex of the found segment are passed back. + @return True indicates that a line segment was found. False indicates that no segment of the contour + is close enough to the passed point. + @remark previousVertex and nextVertex are only valid if return is true.*/ + bool GetLineSegmentForPoint(Point3D &point, + float eps, + TimeStepType timestep, + mitk::ContourElement::VertexType *previousVertex = nullptr, + mitk::ContourElement::VertexType *nextVertex = nullptr); + + /**Overloaded version that returns additional information (start and end vertix of the line + closest to the passed point and the closest point on the contour). + @remark segmentStart, segmentStop and closestContourPoint are only valid if the function returns true. + */ + bool GetLineSegmentForPoint(const mitk::Point3D& point, + float eps, TimeStepType timestep, ContourElement::VertexSizeType& segmentStartIndex, + ContourElement::VertexSizeType& segmentEndIndex, mitk::Point3D& closestContourPoint, + bool findClosest = true) const; /** \brief Mark a vertex at an index in the container as selected. */ bool SelectVertexAt(int index, TimeStepType timestep = 0); /** \brief Mark a vertex at an index in the container as control point. */ bool SetControlVertexAt(int index, TimeStepType timestep = 0); /** \brief Mark a control vertex at a given position in 3D space. \param point - query point in 3D space \param eps - radius for nearest neighbour search (error bound). \param timestep - search at this timestep @return true = vertex found; false = no vertex found */ bool SelectControlVertexAt(Point3D &point, float eps, TimeStepType timestep = 0); /** \brief Mark a vertex at a given position in 3D space. \param point - query point in 3D space \param eps - radius for nearest neighbour search (error bound). \param timestep - search at this timestep @return true = vertex found; false = no vertex found */ bool SelectVertexAt(Point3D &point, float eps, TimeStepType timestep = 0); /* \pararm point - query point in 3D space \pararm eps - radius for nearest neighbour search (error bound). \pararm timestep - search at this timestep @return true = vertex found; false = no vertex found */ bool SetControlVertexAt(Point3D &point, float eps, TimeStepType timestep = 0); /** \brief Remove a vertex at given index within the container. @return true = the vertex was successfuly removed; false = wrong index. */ bool RemoveVertexAt(int index, TimeStepType timestep = 0); /** \brief Remove a vertex at given timestep within the container. @return true = the vertex was successfuly removed. */ bool RemoveVertex(const VertexType *vertex, TimeStepType timestep = 0); /** \brief Remove a vertex at a query position in 3D space. The vertex to be removed will be search by nearest neighbour search. Note that possibly no vertex at this position and eps is stored inside the contour. @return true = the vertex was successfuly removed; false = no vertex found. */ bool RemoveVertexAt(Point3D &point, float eps, TimeStepType timestep = 0); /** \brief Shift the currently selected vertex by a translation vector. \param translate - the translation vector. */ void ShiftSelectedVertex(Vector3D &translate); /** \brief Shift the whole contour by a translation vector at given timestep. \param translate - the translation vector. \param timestep - at this timestep the contour will be shifted. */ void ShiftContour(Vector3D &translate, TimeStepType timestep = 0); /** \brief Clear the storage container at given timestep. All control points are removed at timestep. */ virtual void Clear(TimeStepType timestep); /** \brief Initialize all data objects */ void Initialize() override; /** \brief Initialize object with specs of other contour. Note: No data will be copied. */ void Initialize(const ContourModel &other); /** \brief Returns a list pointing to all vertices that are indicated to be control points. */ VertexListType GetControlVertices(TimeStepType timestep); /** \brief Returns the container of the vertices. */ VertexListType GetVertexList(TimeStepType timestep); /*++++++++++++++++++ method inherit from base data +++++++++++++++++++++++++++*/ /** \brief Inherit from base data - no region support available for contourModel objects. */ void SetRequestedRegionToLargestPossibleRegion() override; /** \brief Inherit from base data - no region support available for contourModel objects. */ bool RequestedRegionIsOutsideOfTheBufferedRegion() override; /** \brief Inherit from base data - no region support available for contourModel objects. */ bool VerifyRequestedRegion() override; /** \brief Inherit from base data - no region support available for contourModel objects. */ void SetRequestedRegion(const itk::DataObject *data) override; /** \brief Expand the contour model and its TimeGeometry to given number of timesteps. */ void Expand(unsigned int timeSteps) override; /** \brief Update the OutputInformation of a ContourModel object The BoundingBox of the contour will be updated, if necessary. */ void UpdateOutputInformation() override; /** \brief Clear the storage container. The object is set to initial state. All control points are removed and the number of timesteps are set to 1. */ void Clear() override; /** \brief overwrite if the Data can be called by an Interactor (StateMachine). */ void ExecuteOperation(Operation *operation) override; /** \brief Redistributes ontrol vertices with a given period (as number of vertices) \param period - the number of vertices between control points. \param timestep - at this timestep all lines will be rebuilt. */ virtual void RedistributeControlVertices(int period, TimeStepType timestep); protected: mitkCloneMacro(Self); ContourModel(); ContourModel(const ContourModel &other); ~ContourModel() override; // inherit from BaseData. called by Clear() void ClearData() override; // inherit from BaseData. Initial state of a contour with no vertices and a single timestep. void InitializeEmpty() override; // Shift a vertex static void ShiftVertex(VertexType *vertex, Vector3D &vector); // Storage with time resolved support. ContourModelSeries m_ContourSeries; // The currently selected vertex. VertexType *m_SelectedVertex; // The interpolation of the line segment between control points. LineSegmentInterpolation m_lineInterpolation; // only update the bounding geometry if necessary bool m_UpdateBoundingBox; }; itkEventMacroDeclaration(ContourModelEvent, itk::AnyEvent); itkEventMacroDeclaration(ContourModelShiftEvent, ContourModelEvent); itkEventMacroDeclaration(ContourModelSizeChangeEvent, ContourModelEvent); itkEventMacroDeclaration(ContourModelAddEvent, ContourModelSizeChangeEvent); itkEventMacroDeclaration(ContourModelRemoveEvent, ContourModelSizeChangeEvent); itkEventMacroDeclaration(ContourModelExpandTimeBoundsEvent, ContourModelEvent); itkEventMacroDeclaration(ContourModelClosedEvent, ContourModelEvent); } #endif diff --git a/Modules/ContourModel/Testing/mitkContourElementTest.cpp b/Modules/ContourModel/Testing/mitkContourElementTest.cpp index b75a434883..2e8b8eb1e8 100644 --- a/Modules/ContourModel/Testing/mitkContourElementTest.cpp +++ b/Modules/ContourModel/Testing/mitkContourElementTest.cpp @@ -1,349 +1,351 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkContourElement.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include class mitkContourElementTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkContourElementTestSuite); // Test the append method MITK_TEST(Iterator); MITK_TEST(AddVertex); MITK_TEST(AddVertexAtFront); MITK_TEST(InsertVertexAtIndex); MITK_TEST(GetSetVertexAt); MITK_TEST(OpenAndClose); MITK_TEST(OtherGetters); MITK_TEST(Concatenate); MITK_TEST(RemoveVertex); MITK_TEST(Clear); MITK_TEST(GetControlVertices); MITK_TEST(RedistributeControlVertices); MITK_TEST(Others); CPPUNIT_TEST_SUITE_END(); private: mitk::ContourElement::Pointer m_Contour1to4; mitk::ContourElement::Pointer m_Contour5to6; mitk::ContourElement::Pointer m_Contour_empty; mitk::Point3D m_p1; mitk::Point3D m_p2; mitk::Point3D m_p3; mitk::Point3D m_p4; mitk::Point3D m_p5; mitk::Point3D m_p6; mitk::Point3D m_p7; public: static mitk::Point3D GeneratePoint(double val) { return mitk::Point3D(val); } void setUp() override { m_p1 = GeneratePoint(1); m_p2 = GeneratePoint(2); m_p3 = GeneratePoint(3); m_p4 = GeneratePoint(4); m_p5 = GeneratePoint(5); m_p6 = GeneratePoint(6); m_p7 = GeneratePoint(7); m_Contour1to4 = mitk::ContourElement::New(); m_Contour5to6 = mitk::ContourElement::New(); m_Contour_empty = mitk::ContourElement::New(); m_Contour1to4->AddVertex(m_p1, true); m_Contour1to4->AddVertex(m_p2, false); m_Contour1to4->AddVertex(m_p3, true); m_Contour1to4->AddVertex(m_p4, false); m_Contour5to6->AddVertex(m_p5, false); m_Contour5to6->AddVertex(m_p6, true); } void tearDown() override {} void Iterator() { mitk::ContourElement::ConstPointer constcontour = m_Contour5to6.GetPointer(); CPPUNIT_ASSERT_MESSAGE("Begin does not point to correct element", m_Contour1to4->IteratorBegin().operator*()->Coordinates == m_p1); CPPUNIT_ASSERT_MESSAGE("Begin does not point to correct element", m_Contour1to4->ConstIteratorBegin().operator*()->Coordinates == m_p1); CPPUNIT_ASSERT_MESSAGE("Begin does not point to correct element", m_Contour5to6->begin().operator*()->Coordinates == m_p5); CPPUNIT_ASSERT_MESSAGE("Begin does not point to correct element", constcontour->begin().operator*()->Coordinates == m_p5); CPPUNIT_ASSERT_MESSAGE("End does not point to correct element", m_Contour_empty->ConstIteratorBegin() == m_Contour_empty->ConstIteratorEnd()); CPPUNIT_ASSERT_MESSAGE("End does not point to correct element", m_Contour_empty->IteratorBegin() == m_Contour_empty->IteratorEnd()); CPPUNIT_ASSERT_MESSAGE("End does not point to correct element", m_Contour_empty->begin() == m_Contour_empty->end()); } void AddVertex() { m_Contour_empty->AddVertex(m_p1, false); CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->Coordinates == m_p1); CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour_empty->GetSize() == 1); m_Contour_empty->AddVertex(m_p7, true); CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->Coordinates == m_p1); CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(1)->Coordinates == m_p7); CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(1)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour_empty->GetSize() == 2); } void AddVertexAtFront() { m_Contour_empty->AddVertexAtFront(m_p1, false); CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->Coordinates == m_p1); CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour_empty->GetSize() == 1); m_Contour_empty->AddVertexAtFront(m_p7, true); CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(1)->Coordinates == m_p1); CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(1)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->Coordinates == m_p7); CPPUNIT_ASSERT(m_Contour_empty->GetVertexAt(0)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour_empty->GetSize() == 2); } void InsertVertexAtIndex() { - mitk::Point3D outOfBountPoint; + mitk::Point3D outOfBountPoint = GeneratePoint(99); m_Contour1to4->InsertVertexAtIndex(m_p5, false, 0); m_Contour1to4->InsertVertexAtIndex(m_p7, true, 2); m_Contour1to4->InsertVertexAtIndex(outOfBountPoint, true, 6); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->Coordinates == m_p5); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1)->Coordinates == m_p1); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(2)->Coordinates == m_p7); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(2)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(3)->Coordinates == m_p2); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(3)->IsControlPoint == false); - CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 6); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(6)->Coordinates == outOfBountPoint); + CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(6)->IsControlPoint == true); + CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 7); } void GetSetVertexAt() { mitk::ContourElement::ConstPointer constcontour = m_Contour1to4.GetPointer(); auto v0 = m_Contour1to4->GetVertexAt(0); auto v1 = m_Contour1to4->GetVertexAt(1); CPPUNIT_ASSERT(v0->Coordinates == m_p1); CPPUNIT_ASSERT(v0->IsControlPoint == true); CPPUNIT_ASSERT(v1->Coordinates == m_p2); CPPUNIT_ASSERT(v1->IsControlPoint == false); CPPUNIT_ASSERT(constcontour->GetVertexAt(0)->Coordinates == m_p1); CPPUNIT_ASSERT(constcontour->GetVertexAt(0)->IsControlPoint == true); CPPUNIT_ASSERT(constcontour->GetVertexAt(1)->Coordinates == m_p2); CPPUNIT_ASSERT(constcontour->GetVertexAt(1)->IsControlPoint == false); m_Contour1to4->SetVertexAt(0, m_p7); CPPUNIT_ASSERT(v0->Coordinates == m_p7); CPPUNIT_ASSERT(v0->IsControlPoint == true); m_Contour1to4->SetVertexAt(1, m_Contour5to6->GetVertexAt(1)); CPPUNIT_ASSERT(v1->Coordinates == m_Contour5to6->GetVertexAt(1)->Coordinates); CPPUNIT_ASSERT(v1->IsControlPoint == m_Contour5to6->GetVertexAt(1)->IsControlPoint); CPPUNIT_ASSERT(v1 != m_Contour5to6->GetVertexAt(1)); mitk::Point3D search = GeneratePoint(6.05); auto finding = m_Contour1to4->GetVertexAt(search, 0.); CPPUNIT_ASSERT(nullptr == finding); finding = m_Contour1to4->GetVertexAt(search, 0.1); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1) == finding); } void OpenAndClose() { CPPUNIT_ASSERT(!m_Contour1to4->IsClosed()); CPPUNIT_ASSERT(!m_Contour_empty->IsClosed()); m_Contour1to4->Close(); m_Contour_empty->SetClosed(true); CPPUNIT_ASSERT(m_Contour1to4->IsClosed()); CPPUNIT_ASSERT(m_Contour_empty->IsClosed()); m_Contour1to4->Open(); m_Contour_empty->SetClosed(false); CPPUNIT_ASSERT(!m_Contour1to4->IsClosed()); CPPUNIT_ASSERT(!m_Contour_empty->IsClosed()); } void OtherGetters() { CPPUNIT_ASSERT(m_Contour1to4->GetIndex(m_Contour1to4->GetVertexAt(0)) == 0); CPPUNIT_ASSERT(m_Contour1to4->GetIndex(m_Contour1to4->GetVertexAt(1)) == 1); CPPUNIT_ASSERT(m_Contour1to4->GetIndex(m_Contour1to4->GetVertexAt(3)) == 3); CPPUNIT_ASSERT(m_Contour1to4->GetIndex(m_Contour5to6->GetVertexAt(0)) == mitk::ContourElement::NPOS); } void Concatenate() { m_Contour5to6->Concatenate(m_Contour1to4, true); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->Coordinates == m_p5); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(1)->Coordinates == m_p6); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(1)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(2)->Coordinates == m_p1); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(2)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(3)->Coordinates == m_p2); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(3)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(4)->Coordinates == m_p3); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(4)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(5)->Coordinates == m_p4); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(5)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour5to6->GetSize() == 6); m_Contour_empty->AddVertex(m_p1, false); m_Contour5to6->Concatenate(m_Contour_empty, true); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->Coordinates == m_p5); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(1)->Coordinates == m_p6); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(1)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(2)->Coordinates == m_p1); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(2)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(3)->Coordinates == m_p2); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(3)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(4)->Coordinates == m_p3); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(4)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(5)->Coordinates == m_p4); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(5)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour5to6->GetSize() == 6); m_Contour5to6->Concatenate(m_Contour_empty, false); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->Coordinates == m_p5); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(1)->Coordinates == m_p6); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(1)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(2)->Coordinates == m_p1); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(2)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(3)->Coordinates == m_p2); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(3)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(4)->Coordinates == m_p3); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(4)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(5)->Coordinates == m_p4); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(5)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(6)->Coordinates == m_p1); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(6)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour5to6->GetSize() == 7); } void RemoveVertex() { CPPUNIT_ASSERT(m_Contour1to4->RemoveVertex(m_Contour1to4->GetVertexAt(0))); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->Coordinates == m_p2); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 3); CPPUNIT_ASSERT(!m_Contour1to4->RemoveVertex(m_Contour5to6->GetVertexAt(0))); CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 3); CPPUNIT_ASSERT(m_Contour1to4->RemoveVertexAt(1)); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->Coordinates == m_p2); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1)->Coordinates == m_p4); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 2); CPPUNIT_ASSERT(!m_Contour1to4->RemoveVertexAt(2)); CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 2); mitk::Point3D search = GeneratePoint(6.05); CPPUNIT_ASSERT(!m_Contour1to4->RemoveVertexAt(search,0.1)); CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 2); CPPUNIT_ASSERT(m_Contour5to6->RemoveVertexAt(search, 0.1)); CPPUNIT_ASSERT(m_Contour5to6->GetSize() == 1); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->Coordinates == m_p5); CPPUNIT_ASSERT(m_Contour5to6->GetVertexAt(0)->IsControlPoint == false); } void Clear() { m_Contour1to4->Clear(); CPPUNIT_ASSERT(m_Contour1to4->IsEmpty()); m_Contour_empty->Clear(); CPPUNIT_ASSERT(m_Contour_empty->IsEmpty()); } void GetControlVertices() { auto controlVs = m_Contour1to4->GetControlVertices(); CPPUNIT_ASSERT(controlVs.size() == 2); CPPUNIT_ASSERT(controlVs[0] == m_Contour1to4->GetVertexAt(0)); CPPUNIT_ASSERT(controlVs[1] == m_Contour1to4->GetVertexAt(2)); controlVs = m_Contour_empty->GetControlVertices(); CPPUNIT_ASSERT(controlVs.empty()); } void RedistributeControlVertices() { //just adding more nodes to better check the redistribution m_Contour1to4->Concatenate(m_Contour1to4, false); m_Contour1to4->Concatenate(m_Contour1to4, false); m_Contour1to4->RedistributeControlVertices(nullptr, 3); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(2)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(3)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(4)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(5)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(6)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(7)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(8)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(9)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(10)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(11)->IsControlPoint == false); m_Contour1to4->RedistributeControlVertices(m_Contour1to4->GetVertexAt(4), 4); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(0)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(1)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(2)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(3)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(4)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(5)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(6)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(7)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(8)->IsControlPoint == true); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(9)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(10)->IsControlPoint == false); CPPUNIT_ASSERT(m_Contour1to4->GetVertexAt(11)->IsControlPoint == false); } void Others() { CPPUNIT_ASSERT(m_Contour1to4->GetSize() == 4); CPPUNIT_ASSERT(m_Contour_empty->GetSize() == 0); CPPUNIT_ASSERT(!m_Contour1to4->IsEmpty()); CPPUNIT_ASSERT(m_Contour_empty->IsEmpty()); mitk::ContourElement::Pointer copyConstructed = m_Contour5to6->Clone(); CPPUNIT_ASSERT(*(m_Contour5to6->GetVertexAt(0)) == *(copyConstructed->GetVertexAt(0))); CPPUNIT_ASSERT(*(m_Contour5to6->GetVertexAt(1)) == *(copyConstructed->GetVertexAt(1))); CPPUNIT_ASSERT(m_Contour5to6->GetSize() == copyConstructed->GetSize()); } }; MITK_TEST_SUITE_REGISTRATION(mitkContourElement) diff --git a/Modules/Core/CMakeLists.txt b/Modules/Core/CMakeLists.txt index 7ffa8c7796..a4e065c8c4 100644 --- a/Modules/Core/CMakeLists.txt +++ b/Modules/Core/CMakeLists.txt @@ -1,87 +1,74 @@ set(TOOL_CPPS "") # temporary suppress warnings in the following files until image accessors are fully integrated. set_source_files_properties( src/DataManagement/mitkImage.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS ) set_source_files_properties( src/Controllers/mitkSliceNavigationController.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS ) -#if(MSVC) -# set(optional_private_package_depends psapi) -#endif() - -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1) - set(optional_public_target_depends stdc++fs) -elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) - set(optional_public_target_depends c++fs) -endif() - mitk_create_module( INCLUDE_DIRS PUBLIC ${MITK_BINARY_DIR} PRIVATE src/Algorithms src/Controllers src/DataManagement src/Interactions src/IO src/Rendering DEPENDS PUBLIC mbilog CppMicroServices PACKAGE_DEPENDS PUBLIC Boost ITK|IOImageBase+SpatialObjects+Statistics #ITK|Statistics+Transform VTK|FiltersTexture+FiltersParallel+ImagingStencil+ImagingMath+InteractionStyle+RenderingOpenGL2+RenderingVolumeOpenGL2+RenderingFreeType+RenderingLabel+InteractionWidgets+IOGeometry+IOXML PRIVATE ITK|IOBioRad+IOBMP+IOBruker+IOCSV+IOGDCM+IOGE+IOGIPL+IOHDF5+IOIPL+IOJPEG+IOJPEG2000+IOLSM+IOMesh+IOMeta+IOMINC+IOMRC+IONIFTI+IONRRD+IOPNG+IOSiemens+IOSpatialObjects+IOStimulate+IOTIFF+IOTransformBase+IOTransformHDF5+IOTransformInsightLegacy+IOTransformMatlab+IOVTK+IOXML nlohmann_json tinyxml2 ${optional_private_package_depends} - TARGET_DEPENDS - PUBLIC - ${optional_public_target_depends} # Do not automatically create CppMicroServices initialization code. # Because the VTK "auto-init" functionality injects file-local static # initialization code in every cpp file which includes a VTK header, # static initialization order becomes an issue again. For the Mitk # core library, we need to ensure that the VTK static initialization stuff # happens before the CppMicroServices initialization, since the latter # might already use VTK code which needs to access VTK object factories. # Hence, CppMicroServices initialization code is placed manually within # the mitkCoreActivator.cpp file. NO_INIT ) if(NOT TARGET ${MODULE_TARGET}) message(SEND_ERROR "Core target ${MODULE_TARGET} does not exist") endif() function(_itk_create_factory_register_manager) # In MITK_ITK_Config.cmake, we do *not* include ITK_USE_FILE, which # prevents multiple registrations/unregistrations of ITK IO factories # during library loading/unloading (of MITK libraries). However, we need # "one" place where the IO factories are registered at # least once. This could be the application executable, but every executable would # need to take care of that itself. Instead, we allow the auto registration in the # Mitk Core library. set(NO_DIRECTORY_SCOPED_ITK_COMPILE_DEFINITION 1) find_package(ITK) include(${ITK_USE_FILE}) if(NOT ITK_NO_IO_FACTORY_REGISTER_MANAGER) # We manually add the define which will be of target scope. MITK # patches ITK_USE_FILE to remove the directory scoped compile # definition since it would be propagated to other targets in the # same directory scope but these targets might want to *not* # use the ITK factory manager stuff. target_compile_definitions(${MODULE_TARGET} PRIVATE ITK_IO_FACTORY_REGISTER_MANAGER) endif() endfunction() _itk_create_factory_register_manager() if(BUILD_TESTING) add_subdirectory(TestingHelper) add_subdirectory(test) endif() diff --git a/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp b/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp index e908256b1f..aedf8ae4ed 100644 --- a/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp +++ b/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp @@ -1,436 +1,436 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // MITK #include #include #include #include #include // VTK #include #include // us #include #include mitk::InteractionTestHelper::InteractionTestHelper(const std::string &interactionXmlFilePath) : m_InteractionFilePath(interactionXmlFilePath) { this->Initialize(interactionXmlFilePath); } void mitk::InteractionTestHelper::Initialize(const std::string &interactionXmlFilePath) { tinyxml2::XMLDocument document; if (tinyxml2::XML_SUCCESS == document.LoadFile(interactionXmlFilePath.c_str())) { // get RenderingManager instance auto rm = mitk::RenderingManager::GetInstance(); // create data storage m_DataStorage = mitk::StandaloneDataStorage::New(); // for each renderer found create a render window and configure for (auto *element = document.FirstChildElement(mitk::InteractionEventConst::xmlTagInteractions().c_str()) ->FirstChildElement(mitk::InteractionEventConst::xmlTagConfigRoot().c_str()) ->FirstChildElement(mitk::InteractionEventConst::xmlTagRenderer().c_str()); element != nullptr; element = element->NextSiblingElement(mitk::InteractionEventConst::xmlTagRenderer().c_str())) { // get name of renderer const char *rendererName = element->Attribute(mitk::InteractionEventConst::xmlEventPropertyRendererName().c_str()); // get view direction mitk::SliceNavigationController::ViewDirection viewDirection = mitk::SliceNavigationController::Axial; if (element->Attribute(mitk::InteractionEventConst::xmlEventPropertyViewDirection().c_str()) != nullptr) { int viewDirectionNum = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyViewDirection().c_str())); viewDirection = static_cast(viewDirectionNum); } // get mapper slot id - mitk::BaseRenderer::MapperSlotId mapperID = mitk::BaseRenderer::Standard2D; + MapperSlotId mapperID = mitk::BaseRenderer::Standard2D; if (element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID().c_str()) != nullptr) { int mapperIDNum = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID().c_str())); - mapperID = static_cast(mapperIDNum); + mapperID = static_cast(mapperIDNum); } // Get Size of Render Windows int size[3]; size[0] = size[1] = size[2] = 0; if (element->Attribute(mitk::InteractionEventConst::xmlRenderSizeX().c_str()) != nullptr) { size[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeX().c_str())); } if (element->Attribute(mitk::InteractionEventConst::xmlRenderSizeY().c_str()) != nullptr) { size[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeY().c_str())); } if (element->Attribute(mitk::InteractionEventConst::xmlRenderSizeZ().c_str()) != nullptr) { size[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeZ().c_str())); } // create renderWindow, renderer and dispatcher auto rw = RenderWindow::New(nullptr, rendererName); // VtkRenderWindow is created within constructor if nullptr if (size[0] != 0 && size[1] != 0) { rw->SetSize(size[0], size[1]); rw->GetRenderer()->Resize(size[0], size[1]); } // set storage of renderer rw->GetRenderer()->SetDataStorage(m_DataStorage); // set view direction to axial rw->GetSliceNavigationController()->SetDefaultViewDirection(viewDirection); // set renderer to render 2D rw->GetRenderer()->SetMapperID(mapperID); rw->GetRenderer()->PrepareRender(); // Some more magic for the 3D render window case: // Camera view direction, position and focal point if (mapperID == mitk::BaseRenderer::Standard3D) { if (element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX().c_str()) != nullptr) { double cameraFocalPoint[3]; cameraFocalPoint[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX().c_str())); cameraFocalPoint[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointY().c_str())); cameraFocalPoint[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointZ().c_str())); rw->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(cameraFocalPoint); } if (element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX().c_str()) != nullptr) { double cameraPosition[3]; cameraPosition[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX().c_str())); cameraPosition[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionY().c_str())); cameraPosition[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionZ().c_str())); rw->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetPosition(cameraPosition); } if (element->Attribute(mitk::InteractionEventConst::xmlViewUpX().c_str()) != nullptr) { double viewUp[3]; viewUp[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpX().c_str())); viewUp[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpY().c_str())); viewUp[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpZ().c_str())); rw->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetViewUp(viewUp); } } rw->GetVtkRenderWindow()->Render(); rw->GetVtkRenderWindow()->WaitForCompletion(); // connect SliceNavigationControllers to timestep changed event of TimeNavigationController - rw->GetSliceNavigationController()->ConnectGeometryTimeEvent(rm->GetTimeNavigationController(), false); - rm->GetTimeNavigationController()->ConnectGeometryTimeEvent(rw->GetSliceNavigationController(), false); + rw->GetSliceNavigationController()->ConnectGeometryTimeEvent(rm->GetTimeNavigationController()); + rm->GetTimeNavigationController()->ConnectGeometryTimeEvent(rw->GetSliceNavigationController()); // add to list of known render windows m_RenderWindowList.push_back(rw); } // TODO: check the following lines taken from QmitkStdMultiWidget and adapt them to be executed in our code here. // mitkWidget1->GetSliceNavigationController() // ->ConnectGeometrySendEvent(mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())); // register interaction event obserer to handle scroll events InitializeDisplayActionEventHandling(); } else { mitkThrow() << "Can not load interaction xml file <" << m_InteractionFilePath << ">"; } // WARNING assumes a 3D window exists !!!! this->AddDisplayPlaneSubTree(); } void mitk::InteractionTestHelper::InitializeDisplayActionEventHandling() { m_DisplayActionEventBroadcast = mitk::DisplayActionEventBroadcast::New(); m_DisplayActionEventBroadcast->LoadStateMachine("DisplayInteraction.xml"); m_DisplayActionEventBroadcast->SetEventConfig("DisplayConfigMITKBase.xml"); m_DisplayActionEventBroadcast->AddEventConfig("DisplayConfigCrosshair.xml"); } mitk::InteractionTestHelper::~InteractionTestHelper() { mitk::RenderingManager *rm = mitk::RenderingManager::GetInstance(); // unregister renderers auto it = m_RenderWindowList.begin(); auto end = m_RenderWindowList.end(); for (; it != end; ++it) { rm->GetTimeNavigationController()->Disconnect((*it)->GetSliceNavigationController()); (*it)->GetSliceNavigationController()->Disconnect(rm->GetTimeNavigationController()); mitk::BaseRenderer::RemoveInstance((*it)->GetVtkRenderWindow()); } rm->RemoveAllObservers(); } mitk::DataStorage::Pointer mitk::InteractionTestHelper::GetDataStorage() { return m_DataStorage; } void mitk::InteractionTestHelper::AddNodeToStorage(mitk::DataNode::Pointer node) { this->m_DataStorage->Add(node); this->Set3dCameraSettings(); } void mitk::InteractionTestHelper::PlaybackInteraction() { mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(m_DataStorage); // load events if not loaded yet if (m_Events.empty()) this->LoadInteraction(); auto it = m_RenderWindowList.begin(); auto end = m_RenderWindowList.end(); for (; it != end; ++it) { (*it)->GetRenderer()->PrepareRender(); (*it)->GetVtkRenderWindow()->Render(); (*it)->GetVtkRenderWindow()->WaitForCompletion(); } mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(m_DataStorage); it = m_RenderWindowList.begin(); for (; it != end; ++it) { (*it)->GetVtkRenderWindow()->Render(); (*it)->GetVtkRenderWindow()->WaitForCompletion(); } // mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); // playback all events in queue for (unsigned long i = 0; i < m_Events.size(); ++i) { // let dispatcher of sending renderer process the event m_Events.at(i)->GetSender()->GetDispatcher()->ProcessEvent(m_Events.at(i)); } if (false) { it--; (*it)->GetVtkRenderWindow()->GetInteractor()->Start(); } } void mitk::InteractionTestHelper::LoadInteraction() { // load interaction pattern from xml file std::ifstream xmlStream(m_InteractionFilePath.c_str()); mitk::XML2EventParser parser(xmlStream); m_Events = parser.GetInteractions(); xmlStream.close(); // Avoid VTK warning: Trying to delete object with non-zero reference count. parser.SetReferenceCount(0); } void mitk::InteractionTestHelper::SetTimeStep(int newTimeStep) { mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(m_DataStorage); bool timeStepIsvalid = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetCreatedWorldGeometry()->IsValidTimeStep( newTimeStep); if (timeStepIsvalid) { mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetTime()->SetPos(newTimeStep); } } mitk::RenderWindow *mitk::InteractionTestHelper::GetRenderWindowByName(const std::string &name) { auto it = m_RenderWindowList.begin(); auto end = m_RenderWindowList.end(); for (; it != end; ++it) { if (name.compare((*it)->GetRenderer()->GetName()) == 0) return (*it).GetPointer(); } return nullptr; } mitk::RenderWindow *mitk::InteractionTestHelper::GetRenderWindowByDefaultViewDirection( mitk::SliceNavigationController::ViewDirection viewDirection) { auto it = m_RenderWindowList.begin(); auto end = m_RenderWindowList.end(); for (; it != end; ++it) { if (viewDirection == (*it)->GetSliceNavigationController()->GetDefaultViewDirection()) return (*it).GetPointer(); } return nullptr; } mitk::RenderWindow *mitk::InteractionTestHelper::GetRenderWindow(unsigned int index) { if (index < m_RenderWindowList.size()) { return m_RenderWindowList.at(index).GetPointer(); } else { return nullptr; } } void mitk::InteractionTestHelper::AddDisplayPlaneSubTree() { // add the displayed planes of the multiwidget to a node to which the subtree // @a planesSubTree points ... mitk::PlaneGeometryDataMapper2D::Pointer mapper; mitk::IntProperty::Pointer layer = mitk::IntProperty::New(1000); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetProperty("name", mitk::StringProperty::New("Widgets")); node->SetProperty("helper object", mitk::BoolProperty::New(true)); m_DataStorage->Add(node); for (auto it : m_RenderWindowList) { if (it->GetRenderer()->GetMapperID() == BaseRenderer::Standard3D) continue; // ... of widget 1 mitk::DataNode::Pointer planeNode1 = (mitk::BaseRenderer::GetInstance(it->GetVtkRenderWindow()))->GetCurrentWorldPlaneGeometryNode(); planeNode1->SetProperty("visible", mitk::BoolProperty::New(true)); planeNode1->SetProperty("name", mitk::StringProperty::New("widget1Plane")); planeNode1->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); planeNode1->SetProperty("helper object", mitk::BoolProperty::New(true)); planeNode1->SetProperty("layer", layer); planeNode1->SetColor(1.0, 0.0, 0.0); mapper = mitk::PlaneGeometryDataMapper2D::New(); planeNode1->SetMapper(mitk::BaseRenderer::Standard2D, mapper); m_DataStorage->Add(planeNode1, node); } } void mitk::InteractionTestHelper::Set3dCameraSettings() { tinyxml2::XMLDocument document; if (tinyxml2::XML_SUCCESS == document.LoadFile(m_InteractionFilePath.c_str())) { // for each renderer found create a render window and configure for (auto *element = document.FirstChildElement(mitk::InteractionEventConst::xmlTagInteractions().c_str()) ->FirstChildElement(mitk::InteractionEventConst::xmlTagConfigRoot().c_str()) ->FirstChildElement(mitk::InteractionEventConst::xmlTagRenderer().c_str()); element != nullptr; element = element->NextSiblingElement(mitk::InteractionEventConst::xmlTagRenderer().c_str())) { // get name of renderer const char *rendererName = element->Attribute(mitk::InteractionEventConst::xmlEventPropertyRendererName().c_str()); // get mapper slot id - mitk::BaseRenderer::MapperSlotId mapperID = mitk::BaseRenderer::Standard2D; + MapperSlotId mapperID = mitk::BaseRenderer::Standard2D; if (element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID().c_str()) != nullptr) { int mapperIDNum = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID().c_str())); - mapperID = static_cast(mapperIDNum); + mapperID = static_cast(mapperIDNum); } if (mapperID == mitk::BaseRenderer::Standard3D) { RenderWindow *namedRenderer = nullptr; for (const auto &it : m_RenderWindowList) { if (strcmp(it->GetRenderer()->GetName(), rendererName) == 0) { namedRenderer = it.GetPointer(); break; } } if (namedRenderer == nullptr) { MITK_ERROR << "No match for render window was found."; return; } namedRenderer->GetRenderer()->PrepareRender(); if (element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX().c_str()) != nullptr) { double cameraFocalPoint[3]; cameraFocalPoint[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX().c_str())); cameraFocalPoint[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointY().c_str())); cameraFocalPoint[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointZ().c_str())); namedRenderer->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(cameraFocalPoint); } if (element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX().c_str()) != nullptr) { double cameraPosition[3]; cameraPosition[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX().c_str())); cameraPosition[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionY().c_str())); cameraPosition[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionZ().c_str())); namedRenderer->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetPosition(cameraPosition); } if (element->Attribute(mitk::InteractionEventConst::xmlViewUpX().c_str()) != nullptr) { double viewUp[3]; viewUp[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpX().c_str())); viewUp[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpY().c_str())); viewUp[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpZ().c_str())); namedRenderer->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetViewUp(viewUp); } namedRenderer->GetVtkRenderWindow()->Render(); } } } } diff --git a/Modules/Core/include/mitkBaseRenderer.h b/Modules/Core/include/mitkBaseRenderer.h index 430721aee6..d91f6a2b13 100644 --- a/Modules/Core/include/mitkBaseRenderer.h +++ b/Modules/Core/include/mitkBaseRenderer.h @@ -1,523 +1,467 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#ifndef BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 -#define BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 +#ifndef MITKBASERENDERER_H +#define MITKBASERENDERER_H -#include "mitkCameraRotationController.h" #include "mitkDataStorage.h" #include "mitkPlaneGeometry.h" #include "mitkPlaneGeometryData.h" -#include "mitkSliceNavigationController.h" #include "mitkTimeGeometry.h" +#include "mitkCameraController.h" +#include "mitkCameraRotationController.h" +#include "mitkSliceNavigationController.h" + #include "mitkBindDispatcherInteractor.h" #include "mitkDispatcher.h" #include #include #include #include namespace mitk { - class NavigationController; - class SliceNavigationController; - class CameraRotationController; - class CameraController; - class DataStorage; class Mapper; class BaseLocalStorageHandler; - class KeyEvent; #pragma GCC visibility push(default) itkEventMacroDeclaration(RendererResetEvent, itk::AnyEvent); #pragma GCC visibility pop - //##Documentation - //## @brief Organizes the rendering process - //## - //## Organizes the rendering process. A Renderer contains a reference to a - //## DataStorage and asks the mappers of the data objects to render - //## the data into the renderwindow it is associated to. - //## - //## \#Render() checks if rendering is currently allowed by calling - //## RenderWindow::PrepareRendering(). Initialization of a rendering context - //## can also be performed in this method. - //## - //## The actual rendering code has been moved to \#Repaint() - //## Both \#Repaint() and \#Update() are declared protected now. - //## - //## Note: Separation of the Repaint and Update processes (rendering vs - //## creating a vtk prop tree) still needs to be worked on. The whole - //## rendering process also should be reworked to use VTK based classes for - //## both 2D and 3D rendering. - //## @ingroup Renderer + /* + * \brief Organizes the rendering process + * + * A BaseRenderer contains a reference to a given vtkRenderWindow + * and a corresponding vtkRenderer. + * The BaseRenderer defines which mapper should be used (2D / 3D) + * and which view direction should be rendered. + * + * All existing BaseRenderer are stored in a static variable + * that can be accessed / modified via the static functions. + * VtkPropRenderer is a concrete implementation of a BaseRenderer. + */ class MITKCORE_EXPORT BaseRenderer : public itk::Object { public: - typedef std::map BaseRendererMapType; + + typedef std::map BaseRendererMapType; static BaseRendererMapType baseRendererMap; - static BaseRenderer *GetInstance(vtkRenderWindow *renWin); - static void AddInstance(vtkRenderWindow *renWin, BaseRenderer *baseRenderer); - static void RemoveInstance(vtkRenderWindow *renWin); + static BaseRenderer* GetInstance(vtkRenderWindow* renderWindow); + static void AddInstance(vtkRenderWindow* renderWindow, BaseRenderer* baseRenderer); + static void RemoveInstance(vtkRenderWindow* renderWindow); - static BaseRenderer *GetByName(const std::string &name); - static vtkRenderWindow *GetRenderWindowByName(const std::string &name); + static BaseRenderer* GetByName(const std::string& name); + static vtkRenderWindow* GetRenderWindowByName(const std::string& name); - /** Standard class typedefs. */ mitkClassMacroItkParent(BaseRenderer, itk::Object); - BaseRenderer(const char *name = nullptr, vtkRenderWindow *renWin = nullptr); - - //##Documentation - //## @brief MapperSlotId defines which kind of mapper (e.g. 2D or 3D) should be used. - typedef int MapperSlotId; + BaseRenderer(const char* name = nullptr, vtkRenderWindow* renderWindow = nullptr); + /** + * \brief Defines which kind of mapper (e.g. 2D or 3D) should be used. + */ enum StandardMapperSlot { Standard2D = 1, Standard3D = 2 }; - //##Documentation - //## @brief Possible view directions for render windows. + /** + * \brief Defines which view direction should be rendered. + */ enum class ViewDirection { AXIAL = 0, SAGITTAL, CORONAL, THREE_D }; - virtual void SetDataStorage(DataStorage *storage); ///< set the datastorage that will be used for rendering - - //##Documentation - //## return the DataStorage that is used for rendering - virtual DataStorage::Pointer GetDataStorage() const { return m_DataStorage.GetPointer(); } - //##Documentation - //## @brief Access the RenderWindow into which this renderer renders. - vtkRenderWindow *GetRenderWindow() const { return m_RenderWindow; } - vtkRenderer *GetVtkRenderer() const { return m_VtkRenderer; } - //##Documentation - //## @brief Returns the Dispatcher which handles Events for this BaseRenderer - Dispatcher::Pointer GetDispatcher() const; + void RemoveAllLocalStorages(); + void RegisterLocalStorageHandler(BaseLocalStorageHandler* lsh); + void UnregisterLocalStorageHandler(BaseLocalStorageHandler* lsh); - //##Documentation - //## @brief Default mapper id to use. - static const MapperSlotId defaultMapper; + virtual void SetDataStorage(DataStorage* storage); - //##Documentation - //## @brief Do the rendering and flush the result. - virtual void Paint(); + virtual DataStorage::Pointer GetDataStorage() const + { + return m_DataStorage.GetPointer(); + } - //##Documentation - //## @brief Initialize the RenderWindow. Should only be called from RenderWindow. - virtual void Initialize(); + vtkRenderWindow* GetRenderWindow() const + { + return m_RenderWindow; + } + + vtkRenderer* GetVtkRenderer() const + { + return m_VtkRenderer; + } + + /** + * \brief Get the dispatcher, which handles events for this base renderer. + */ + Dispatcher::Pointer GetDispatcher() const; - //##Documentation - //## @brief Called to inform the renderer that the RenderWindow has been resized. + /** + * \brief Set a new size for the render window. + */ virtual void Resize(int w, int h); - //##Documentation - //## @brief Initialize the renderer with a RenderWindow (@a renderwindow). - virtual void InitRenderer(vtkRenderWindow *renderwindow); + /** + * \brief Initialize the base renderer with a vtk render window. + * Set the new renderer for the camera controller. + */ + virtual void InitRenderer(vtkRenderWindow* renderwindow); - //##Documentation - //## @brief Set the initial size. Called by RenderWindow after it has become - //## visible for the first time. + /** + * \brief Set the initial size for the render window. + */ virtual void InitSize(int w, int h); - //##Documentation - //## @brief Draws a point on the widget. - //## Should be used during conferences to show the position of the remote mouse - virtual void DrawOverlayMouse(Point2D &p2d); - - //##Documentation - //## @brief Set/Get the WorldGeometry (m_WorldGeometry) for 3D and 2D rendering, that describing the - //## (maximal) area to be rendered. - //## - //## Depending of the type of the passed BaseGeometry more or less information can be extracted: - //## \li if it is a PlaneGeometry (which is a sub-class of BaseGeometry), m_CurrentWorldPlaneGeometry is - //## also set to point to it. m_WorldTimeGeometry is set to nullptr. - //## \li if it is a TimeGeometry, m_WorldTimeGeometry is also set to point to it. - //## If m_WorldTimeGeometry contains instances of SlicedGeometry3D, m_CurrentWorldPlaneGeometry is set to - //## one of geometries stored in the SlicedGeometry3D according to the value of m_Slice; otherwise - //## a PlaneGeometry describing the top of the bounding-box of the BaseGeometry is set as the - //## m_CurrentWorldPlaneGeometry. - //## \li otherwise a PlaneGeometry describing the top of the bounding-box of the BaseGeometry - //## is set as the m_CurrentWorldPlaneGeometry. m_WorldTimeGeometry is set to nullptr. - //## @todo add calculation of PlaneGeometry describing the top of the bounding-box of the BaseGeometry - //## when the passed BaseGeometry is not sliced. - //## \sa m_WorldGeometry - //## \sa m_WorldTimeGeometry - //## \sa m_CurrentWorldPlaneGeometry - virtual void SetWorldGeometry3D(const BaseGeometry *geometry); - virtual void SetWorldTimeGeometry(const mitk::TimeGeometry *geometry); + virtual void DrawOverlayMouse(Point2D&) + { + MITK_INFO << "BaseRenderer::DrawOverlayMouse() should be in concret implementation OpenGLRenderer." << std::endl; + } + /** + * \brief Set the world time geometry using the given TimeGeometry. + * + * Setting a new world time geometry updates the current world geometry and the + * curent world plane geometry, using the currently selected slice and timestep. + */ + virtual void SetWorldTimeGeometry(const mitk::TimeGeometry* geometry); itkGetConstObjectMacro(WorldTimeGeometry, TimeGeometry); - //##Documentation - //## @brief Get the current 3D-worldgeometry (m_CurrentWorldGeometry) used for 3D-rendering - itkGetConstObjectMacro(CurrentWorldGeometry, BaseGeometry); - - //##Documentation - //## @brief Get the current 2D-worldgeometry (m_CurrentWorldPlaneGeometry) used for 2D-rendering - itkGetConstObjectMacro(CurrentWorldPlaneGeometry, PlaneGeometry) - /** - * \deprecatedSince{2014_10} Please use GetCurrentWorldPlaneGeometry - */ - DEPRECATED(const PlaneGeometry *GetCurrentWorldGeometry2D()) + /** + * \brief Get the current time-extracted 3D-geometry. + */ + itkGetConstObjectMacro(CurrentWorldGeometry, BaseGeometry); + + /** + * \brief Get the current slice-extracted 2D-geometry. + */ + itkGetConstObjectMacro(CurrentWorldPlaneGeometry, PlaneGeometry); + + virtual bool SetWorldGeometryToDataStorageBounds() { - return GetCurrentWorldPlaneGeometry(); - }; + return false; + } - //##Documentation - //## Calculates the bounds of the DataStorage (if it contains any valid data), - //## creates a geometry from these bounds and sets it as world geometry of the renderer. - //## - //## Call this method to re-initialize the renderer to the current DataStorage - //## (e.g. after loading an additional dataset), to ensure that the view is - //## aligned correctly. - //## \warning This is not implemented yet. - virtual bool SetWorldGeometryToDataStorageBounds() { return false; } - //##Documentation - //## @brief Set/Get m_Slice which defines together with m_TimeStep the 2D geometry - //## stored in m_WorldTimeGeometry used as m_CurrentWorldPlaneGeometry - //## - //## \sa m_Slice + /** + * \brief Set the slice that should be used for geometry extraction. + * + * The slice defines the current slice-extracted 2D-geometry (CurrentWorldPlaneGeometry). + * Setting a new slice will update the current world geometry and the + * curent world plane geometry. + */ virtual void SetSlice(unsigned int slice); itkGetConstMacro(Slice, unsigned int); - //##Documentation - //## @brief Set/Get m_TimeStep which defines together with m_Slice the 2D geometry - //## stored in m_WorldTimeGeometry used as m_CurrentWorldPlaneGeometry - //## - //## \sa m_TimeStep - virtual void SetTimeStep(unsigned int timeStep); + /** + * \brief Set the timestep that should be used for geometry extraction. + * + * The timestep defines the current time-extracted 3D-geometry (CurrentWorldGeometry). + * Setting a new timestep will update the current world geometry and the + * curent world plane geometry. + */ + virtual void SetTimeStep(unsigned int timeStep); itkGetConstMacro(TimeStep, unsigned int); - //##Documentation - //## @brief Get the time-step of a BaseData object which - //## exists at the time of the currently displayed content - //## - //## Returns -1 or mitk::BaseData::m_TimeSteps if there - //## is no data at the current time. - //## \sa GetTimeStep, m_TimeStep - TimeStepType GetTimeStep(const BaseData *data) const; - - //##Documentation - //## @brief Get the time in ms of the currently displayed content - //## - //## \sa GetTimeStep, m_TimeStep + /** + * \brief Get the timestep of a BaseData object which + * exists at the time of the currently displayed content. + * + * Returns -1 if there is no data at the current time. + */ + TimeStepType GetTimeStep(const BaseData* data) const; + + /** + * \brief Get the time in ms of the currently display content (geometry). + */ ScalarType GetTime() const; - //##Documentation - //## @brief SetWorldGeometry is called according to the geometrySliceEvent, - //## which is supposed to be a SliceNavigationController::GeometrySendEvent - virtual void SetGeometry(const itk::EventObject &geometrySliceEvent); - - //##Documentation - //## @brief UpdateWorldGeometry is called to re-read the 2D geometry from the - //## slice navigation controller - virtual void UpdateGeometry(const itk::EventObject &geometrySliceEvent); - - //##Documentation - //## @brief SetSlice is called according to the geometrySliceEvent, - //## which is supposed to be a SliceNavigationController::GeometrySliceEvent - virtual void SetGeometrySlice(const itk::EventObject &geometrySliceEvent); - - //##Documentation - //## @brief SetTimeStep is called according to the geometrySliceEvent, - //## which is supposed to be a SliceNavigationController::GeometryTimeEvent - virtual void SetGeometryTime(const itk::EventObject &geometryTimeEvent); - - //##Documentation - //## @brief Get a DataNode pointing to a data object containing the current 2D-worldgeometry - // m_CurrentWorldPlaneGeometry (for 2D rendering) - itkGetObjectMacro(CurrentWorldPlaneGeometryNode, DataNode) - /** - * \deprecatedSince{2014_10} Please use GetCurrentWorldPlaneGeometryNode - */ - DEPRECATED(DataNode *GetCurrentWorldGeometry2DNode()) - { - return GetCurrentWorldPlaneGeometryNode(); - }; + /** + * \brief Set the world time geometry using the geometry of the given event. + * + * The function is triggered by a SliceNavigationController::GeometrySendEvent. + */ + virtual void SetGeometry(const itk::EventObject& geometrySliceEvent); + + /** + * \brief Set the current world plane geometry using the existing current world geometry. + * + * The function is triggered by a SliceNavigationController::GeometryUpdateEvent. + */ + virtual void UpdateGeometry(const itk::EventObject& geometrySliceEvent); - //##Documentation - //## @brief Sets timestamp of CurrentWorldPlaneGeometry and forces so reslicing in that renderwindow + /** + * \brief Set the current slice using "SetSlice" and update the current world geometry + * and the current world plane geometry. + * + * The function is triggered by a SliceNavigationController::GeometrySliceEvent. + */ + virtual void SetGeometrySlice(const itk::EventObject& geometrySliceEvent); + + /** + * \brief Set the current time using "SetTimeStep" and update the current world geometry + * and the current world plane geometry. + * + * The function is triggered by a TimeNavigationController::TimeEvent. + */ + virtual void SetGeometryTime(const itk::EventObject& geometryTimeEvent); + + itkGetObjectMacro(CurrentWorldPlaneGeometryNode, DataNode); + + /** + * \brief Modify the update time of the current world plane geometry and force reslicing. + */ void SendUpdateSlice(); - //##Documentation - //## @brief Get timestamp of last call of SetCurrentWorldPlaneGeometry - unsigned long GetCurrentWorldPlaneGeometryUpdateTime() { return m_CurrentWorldPlaneGeometryUpdateTime; } /** - * \deprecatedSince{2014_10} Please use GetCurrentWorldPlaneGeometryUpdateTime - */ - DEPRECATED(unsigned long GetCurrentWorldGeometry2DUpdateTime()) - { - return GetCurrentWorldPlaneGeometryUpdateTime(); - }; - //##Documentation - //## @brief Get timestamp of last change of current TimeStep - unsigned long GetTimeStepUpdateTime() { return m_TimeStepUpdateTime; } - //##Documentation - //## @brief Perform a picking: find the x,y,z world coordinate of a - //## display x,y coordinate. - //## @warning Has to be overwritten in subclasses for the 3D-case. - //## - //## Implemented here only for 2D-rendering - virtual void PickWorldPoint(const Point2D &diplayPosition, Point3D &worldPosition) const = 0; - - /** \brief Determines the object (mitk::DataNode) closest to the current - * position by means of picking - * - * \warning Implementation currently empty for 2D rendering; intended to be - * implemented for 3D renderers */ - virtual DataNode *PickObject(const Point2D & /*displayPosition*/, Point3D & /*worldPosition*/) const + * \brief Get timestamp of the update time of the current world plane geometry. + */ + itkGetMacro(CurrentWorldPlaneGeometryUpdateTime, unsigned long); + + /** + * \brief Get timestamp of the update time of the current timestep. + */ + itkGetMacro(TimeStepUpdateTime, unsigned long); + + /** + * \brief Pick a world coordinate (x,y,z) given a display coordinate (x,y). + * + * \warning Not implemented; has to be overwritten in subclasses. + */ + virtual void PickWorldPoint(const Point2D& diplayPosition, Point3D& worldPosition) const = 0; + + /** + * \brief Determines the object (mitk::DataNode) closest to the current + * position by means of picking. + * + * \warning Implementation currently empty for 2D rendering; intended to be + * implemented for 3D renderers. + */ + virtual DataNode* PickObject(const Point2D& /*displayPosition*/, Point3D& /*worldPosition*/) const { return nullptr; } - //##Documentation - //## @brief Get the MapperSlotId to use. + /** + * \brief Get the currently used mapperID. + */ itkGetMacro(MapperID, MapperSlotId); itkGetConstMacro(MapperID, MapperSlotId); - //##Documentation - //## @brief Set the MapperSlotId to use. - virtual void SetMapperID(MapperSlotId id); + /** + * \brief Set the used mapperID. + */ + virtual void SetMapperID(MapperSlotId id); - virtual int *GetSize() const; - virtual int *GetViewportSize() const; + virtual int* GetSize() const; + virtual int* GetViewportSize() const; - void SetSliceNavigationController(SliceNavigationController *SlicenavigationController); + void SetSliceNavigationController(SliceNavigationController* SlicenavigationController); itkGetObjectMacro(CameraController, CameraController); itkGetObjectMacro(SliceNavigationController, SliceNavigationController); itkGetObjectMacro(CameraRotationController, CameraRotationController); itkGetMacro(EmptyWorldGeometry, bool); - //##Documentation - //## @brief Tells if the displayed region is shifted and rescaled if the render window is resized. - itkGetMacro(KeepDisplayedRegion, bool) - //##Documentation - //## @brief Tells if the displayed region should be shifted and rescaled if the render window is resized. - itkSetMacro(KeepDisplayedRegion, bool); - - //##Documentation - //## @brief get the name of the Renderer - //## @note - const char *GetName() const + /** + * \brief Getter/Setter for defining if the displayed region should be shifted + * or rescaled if the render window is resized. + */ + itkGetMacro(KeepDisplayedRegion, bool); + itkSetMacro(KeepDisplayedRegion, bool); + + /** + * \brief Return the name of the base renderer + */ + const char* GetName() const { return m_Name.c_str(); } - //##Documentation - //## @brief get the x_size of the RendererWindow - //## @note - int GetSizeX() const { return GetSize()[0]; } - //##Documentation - //## @brief get the y_size of the RendererWindow - //## @note - int GetSizeY() const { return GetSize()[1]; } - const double *GetBounds() const; + /** + * \brief Return the size in x-direction of the base renderer. + */ + int GetSizeX() const + { + return this->GetSize()[0]; + } + + /** + * \brief Return the size in y-direction of the base renderer. + */ + int GetSizeY() const + { + return this->GetSize()[1]; + } + + /** + * \brief Return the bounds of the bounding box of the + * current world geometry (time-extracted 3D-geometry). + * + * If the geometry is empty, the bounds are set to zero. + */ + const double* GetBounds() const; void RequestUpdate(); void ForceImmediateUpdate(); - /** Returns number of mappers which are visible and have level-of-detail - * rendering enabled */ + /** + * \brief Return the number of mappers which are visible and have + * level-of-detail rendering enabled. + */ unsigned int GetNumberOfVisibleLODEnabledMappers() const; - //##Documentation - //## @brief This method converts a display point to the 3D world index - //## using the geometry of the renderWindow. - void DisplayToWorld(const Point2D &displayPoint, Point3D &worldIndex) const; - - //##Documentation - //## @brief This method converts a display point to the 2D world index, mapped onto the display plane - //## using the geometry of the renderWindow. - void DisplayToPlane(const Point2D &displayPoint, Point2D &planePointInMM) const; + /** + * \brief Convert a display point to the 3D world index + * using the geometry of the renderWindow. + */ + void DisplayToWorld(const Point2D& displayPoint, Point3D& worldIndex) const; - //##Documentation - //## @brief This method converts a 3D world index to the display point - //## using the geometry of the renderWindow. - void WorldToDisplay(const Point3D &worldIndex, Point2D &displayPoint) const; + /** + * \brief Convert a display point to the 2D world index, mapped onto the display plane + * using the geometry of the renderWindow. + */ + void DisplayToPlane(const Point2D& displayPoint, Point2D& planePointInMM) const; - //##Documentation - //## @brief This method converts a 3D world index to the point on the viewport - //## using the geometry of the renderWindow. - void WorldToView(const Point3D &worldIndex, Point2D &viewPoint) const; + /** + * \brief Convert a 3D world index to the display point + * using the geometry of the renderWindow. + */ + void WorldToDisplay(const Point3D& worldIndex, Point2D& displayPoint) const; - //##Documentation - //## @brief This method converts a 2D plane coordinate to the display point - //## using the geometry of the renderWindow. - void PlaneToDisplay(const Point2D &planePointInMM, Point2D &displayPoint) const; + /** + * \brief Convert a 3D world index to the point on the viewport + * using the geometry of the renderWindow. + */ + void WorldToView(const Point3D& worldIndex, Point2D& viewPoint) const; - //##Documentation - //## @brief This method converts a 2D plane coordinate to the point on the viewport - //## using the geometry of the renderWindow. - void PlaneToView(const Point2D &planePointInMM, Point2D &viewPoint) const; + /** + * \brief Convert a 2D plane coordinate to the display point + * using the geometry of the renderWindow. + */ + void PlaneToDisplay(const Point2D& planePointInMM, Point2D& displayPoint) const; + /** + * \brief Convert a 2D plane coordinate to the point on the viewport + * using the geometry of the renderWindow. + */ + void PlaneToView(const Point2D& planePointInMM, Point2D& viewPoint) const; double GetScaleFactorMMPerDisplayUnit() const; Point2D GetDisplaySizeInMM() const; Point2D GetViewportSizeInMM() const; Point2D GetOriginInMM() const; - itkGetConstMacro(ConstrainZoomingAndPanning, bool) virtual void SetConstrainZoomingAndPanning(bool constrain); - - /** - * \brief Provides (1) world coordinates for a given mouse position and (2) - * translates mousePosition to Display coordinates - * \deprecated Map2DRendererPositionTo3DWorldPosition is deprecated. Please use DisplayToWorld instead. - */ - DEPRECATED(virtual Point3D Map2DRendererPositionTo3DWorldPosition(const Point2D &mousePosition) const); + itkGetConstMacro(ConstrainZoomingAndPanning, bool) + virtual void SetConstrainZoomingAndPanning(bool constrain); protected: + ~BaseRenderer() override; - //##Documentation - //## @brief Call update of all mappers. To be implemented in subclasses. virtual void Update() = 0; - vtkRenderWindow *m_RenderWindow; - vtkRenderer *m_VtkRenderer; + vtkRenderWindow* m_RenderWindow; + vtkRenderer* m_VtkRenderer; - //##Documentation - //## @brief MapperSlotId to use. Defines which kind of mapper (e.g., 2D or 3D) should be used. MapperSlotId m_MapperID; - - //##Documentation - //## @brief The DataStorage that is used for rendering. DataStorage::Pointer m_DataStorage; - - //##Documentation - //## @brief Timestamp of last call of Update(). unsigned long m_LastUpdateTime; - //##Documentation - //## @brief CameraController for 3D rendering - //## @note preliminary. - itk::SmartPointer m_CameraController; + CameraController::Pointer m_CameraController; SliceNavigationController::Pointer m_SliceNavigationController; CameraRotationController::Pointer m_CameraRotationController; - //##Documentation - //## @brief Sets m_CurrentWorldPlaneGeometry - virtual void SetCurrentWorldPlaneGeometry(const PlaneGeometry *geometry2d); - /** - * \deprecatedSince{2014_10} Please use SetCurrentWorldPlaneGeometry - */ - DEPRECATED(void SetCurrentWorldGeometry2D(PlaneGeometry *geometry2d)) { SetCurrentWorldPlaneGeometry(geometry2d); }; - //##Documentation - //## @brief Sets m_CurrentWorldGeometry + void UpdateCurrentGeometries(); + virtual void SetCurrentWorldPlaneGeometry(const PlaneGeometry* geometry2d); virtual void SetCurrentWorldGeometry(const BaseGeometry *geometry); private: - //##Documentation - //## m_WorldTimeGeometry is set by SetWorldGeometry if the passed BaseGeometry is a - //## TimeGeometry (or a sub-class of it). If it contains instances of SlicedGeometry3D, - //## m_Slice and m_TimeStep (set via SetSlice and SetTimeStep, respectively) define - //## which 2D geometry stored in m_WorldTimeGeometry (if available) - //## is used as m_CurrentWorldPlaneGeometry. - //## \sa m_CurrentWorldPlaneGeometry + + /** + * \brief Pointer to the current TimeGeometry. + * + * This WorldTimeGeometry is used to extract a SlicedGeometry3D, + * using the current timestep (set via SetTimeStep). + * The time-extracted 3D-geometry is used as the "CurrentWorldgeometry". + * It will be set using the "SetCurrentWorldGeometry"-function. + * A PlaneGeometry can further be extracted using the current slice (set via SetSlice). + * The slice-extracted 2D-geometry is used as the "CurrentWorldPlaneGeometry". + * It will be set using the "SetCurrentWorldPlaneGeometry"-function. + */ TimeGeometry::ConstPointer m_WorldTimeGeometry; - //##Documentation - //## Pointer to the current 3D-worldgeometry. + /** + * \brief Pointer to the current time-extracted 3D-geometry. + * + * This CurrentWorldGeometry is used to define the bounds for this + * BaseRenderer. + * It will be set using the "SetCurrentWorldGeometry"-function. + */ BaseGeometry::ConstPointer m_CurrentWorldGeometry; - //##Documentation - //## Pointer to the current 2D-worldgeometry. The 2D-worldgeometry - //## describes the maximal area (2D manifold) to be rendered in case we - //## are doing 2D-rendering. - //## It is const, since we are not allowed to change it (it may be taken - //## directly from the geometry of an image-slice and thus it would be - //## very strange when suddenly the image-slice changes its geometry). + /** + * \brief Pointer to the current slice-extracted 2D-geometry. + * + * This CurrentWorldPlaneGeometry is used to define the maximal + * area (2D manifold) to be rendered in case we are doing 2D-rendering. + * It will be set using the "SetCurrentWorldPlaneGeometry"-function. + */ PlaneGeometry::Pointer m_CurrentWorldPlaneGeometry; - //##Documentation - //## Defines together with m_Slice which 2D geometry stored in m_WorldTimeGeometry - //## is used as m_CurrentWorldPlaneGeometry: m_WorldTimeGeometry->GetPlaneGeometry(m_Slice, m_TimeStep). - //## \sa m_WorldTimeGeometry unsigned int m_Slice; - //##Documentation - //## Defines together with m_TimeStep which 2D geometry stored in m_WorldTimeGeometry - //## is used as m_CurrentWorldPlaneGeometry: m_WorldTimeGeometry->GetPlaneGeometry(m_Slice, m_TimeStep). - //## \sa m_WorldTimeGeometry unsigned int m_TimeStep; - //##Documentation - //## @brief timestamp of last call of SetWorldGeometry itk::TimeStamp m_CurrentWorldPlaneGeometryUpdateTime; - - //##Documentation - //## @brief timestamp of last change of the current time step itk::TimeStamp m_TimeStepUpdateTime; - //##Documentation - //## @brief Helper class which establishes connection between Interactors and Dispatcher via a common DataStorage. - BindDispatcherInteractor *m_BindDispatcherInteractor; + BindDispatcherInteractor* m_BindDispatcherInteractor; - //##Documentation - //## @brief Tells if the displayed region should be shifted or rescaled if the render window is resized. bool m_KeepDisplayedRegion; protected: - void PrintSelf(std::ostream &os, itk::Indent indent) const override; - //##Documentation - //## Data object containing the m_CurrentWorldPlaneGeometry defined above. - PlaneGeometryData::Pointer m_CurrentWorldPlaneGeometryData; + void PrintSelf(std::ostream& os, itk::Indent indent) const override; - //##Documentation - //## DataNode objects containing the m_CurrentWorldPlaneGeometryData defined above. + PlaneGeometryData::Pointer m_CurrentWorldPlaneGeometryData; DataNode::Pointer m_CurrentWorldPlaneGeometryNode; - - //##Documentation - //## @brief test only unsigned long m_CurrentWorldPlaneGeometryTransformTime; std::string m_Name; double m_Bounds[6]; bool m_EmptyWorldGeometry; - typedef std::set LODEnabledMappersType; + typedef std::set LODEnabledMappersType; - /** Number of mappers which are visible and have level-of-detail - * rendering enabled */ unsigned int m_NumberOfVisibleLODEnabledMappers; - // Local Storage Handling for mappers - - protected: - std::list m_RegisteredLocalStorageHandlers; + std::list m_RegisteredLocalStorageHandlers; bool m_ConstrainZoomingAndPanning; - - public: - void RemoveAllLocalStorages(); - void RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh); - void UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh); }; } // namespace mitk -#endif /* BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 */ +#endif // MITKBASERENDERER_H diff --git a/Modules/Core/include/mitkIOUtil.h b/Modules/Core/include/mitkIOUtil.h index 9f395d13f3..50425bdc57 100644 --- a/Modules/Core/include/mitkIOUtil.h +++ b/Modules/Core/include/mitkIOUtil.h @@ -1,440 +1,444 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKIOUTIL_H #define MITKIOUTIL_H #include #include #include #include #include #include #include #include #include #include +#if !defined(MITK_WINDOWS_NO_UNDEF) && defined(GetTempPath) + #undef GetTempPath +#endif + namespace us { class ModuleResource; } namespace mitk { class PropertyList; /** * \ingroup IO * * \brief A utility class to load and save data from/to the local file system. * * \see QmitkIOUtil */ class MITKCORE_EXPORT IOUtil { public: /**Struct that contains information regarding the current loading process. (e.g. Path that should be loaded, all found readers for the load path,...). It is set be IOUtil and used to pass information via the option callback in load operations. */ struct MITKCORE_EXPORT LoadInfo { LoadInfo(const std::string &path); std::string m_Path; std::vector m_Output; FileReaderSelector m_ReaderSelector; bool m_Cancel; const PropertyList* m_Properties; }; /**Struct that is the base class for option callbacks used in load operations. The callback is used by IOUtil, if more than one suitable reader was found or the a reader contains options that can be set. The callback allows to change option settings and select the reader that should be used (via loadInfo). */ struct MITKCORE_EXPORT ReaderOptionsFunctorBase { virtual bool operator()(LoadInfo &loadInfo) const = 0; }; struct MITKCORE_EXPORT SaveInfo { SaveInfo(const BaseData *baseData, const MimeType &mimeType, const std::string &path); bool operator<(const SaveInfo &other) const; /// The BaseData object to save. const BaseData *m_BaseData; /// Contains a set of IFileWriter objects. FileWriterSelector m_WriterSelector; /// The selected mime-type, used to restrict results from FileWriterSelector. MimeType m_MimeType; /// The path to write the BaseData object to. std::string m_Path; /// Flag indicating if sub-sequent save operations are to be canceled. bool m_Cancel; }; /**Struct that is the base class for option callbacks used in save operations. The callback is used by IOUtil, if more than one suitable writer was found or the a writer contains options that can be set. The callback allows to change option settings and select the writer that should be used (via saveInfo). */ struct MITKCORE_EXPORT WriterOptionsFunctorBase { virtual bool operator()(SaveInfo &saveInfo) const = 0; }; /** * Get the file system path where the running executable is located. * * @return The location of the currently running executable, without the filename. */ static std::string GetProgramPath(); /** * Get the default temporary path. * * @return The default path for temporary data. */ static std::string GetTempPath(); /** * Returns the Directory Seperator for the current OS. * * @return the Directory Seperator for the current OS, i.e. "\\" for Windows and "/" otherwise. */ static char GetDirectorySeparator(); /** * Create and open a temporary file. * * This method generates a unique temporary filename from \c templateName, creates * and opens the file using the output stream \c tmpStream and returns the name of * the newly create file. * * The \c templateName argument must contain six consecutive 'X' characters ("XXXXXX") * and these are replaced with a string that makes the filename unique. * * The file is created with read and write permissions for owner only. * * @param tmpStream The output stream for writing to the temporary file. * @param templateName An optional template for the filename. * @param path An optional path where the temporary file should be created. Defaults * to the default temp path as returned by GetTempPath(). * @return The filename of the created temporary file. * * @throw mitk::Exception if the temporary file could not be created. */ static std::string CreateTemporaryFile(std::ofstream &tmpStream, const std::string &templateName = "XXXXXX", std::string path = std::string()); /** * Create and open a temporary file. * * This method generates a unique temporary filename from \c templateName, creates * and opens the file using the output stream \c tmpStream and the specified open * mode \c mode and returns the name of the newly create file. The open mode is always * OR'd with std::ios_base::out | std::ios_base::trunc. * * The \c templateName argument must contain six consecutive 'X' characters ("XXXXXX") * and these are replaced with a string that makes the filename unique. * * The file is created with read and write permissions for owner only. * * @param tmpStream The output stream for writing to the temporary file. * @param mode The open mode for the temporary file stream. * @param templateName An optional template for the filename. * @param path An optional path where the temporary file should be created. Defaults * to the default temp path as returned by GetTempPath(). * @return The filename of the created temporary file. * * @throw mitk::Exception if the temporary file could not be created. */ static std::string CreateTemporaryFile(std::ofstream &tmpStream, std::ios_base::openmode mode, const std::string &templateName = "XXXXXX", std::string path = std::string()); /** * Creates an empty temporary file. * * This method generates a unique temporary filename from \c templateName and creates * this file. * * The file is created with read and write permissions for owner only. * * --- * This version is potentially unsafe because the created temporary file is not kept open * and could be used by another process between calling this method and opening the returned * file path for reading or writing. * --- * * @return The filename of the created temporary file. * @param templateName An optional template for the filename. * @param path An optional path where the temporary file should be created. Defaults * to the default temp path as returned by GetTempPath(). * @throw mitk::Exception if the temporary file could not be created. */ static std::string CreateTemporaryFile(const std::string &templateName = "XXXXXX", std::string path = std::string()); /** * Create a temporary directory. * * This method generates a uniquely named temporary directory from \c templateName. * The last set of six consecutive 'X' characters in \c templateName is replaced * with a string that makes the directory name unique. * * The directory is created with read, write and executable permissions for owner only. * * @param templateName An optional template for the directory name. * @param path An optional path where the temporary directory should be created. Defaults * to the default temp path as returned by GetTempPath(). * @return The filename of the created temporary file. * * @throw mitk::Exception if the temporary directory could not be created. */ static std::string CreateTemporaryDirectory(const std::string &templateName = "XXXXXX", std::string path = std::string()); /** * @brief Load a file into the given DataStorage. * * This method calls Load(const std::vector&, DataStorage&) with a * one-element vector. * * @param path The absolute file name including the file extension. * @param storage A DataStorage object to which the loaded data will be added. * @param optionsCallback Pointer to a callback instance. The callback is used by * the load operation if more the suitable reader was found or the reader has options * that can be set. * @return The set of added DataNode objects. * @throws mitk::Exception if \c path could not be loaded. * * @sa Load(const std::vector&, DataStorage&) */ static DataStorage::SetOfObjects::Pointer Load(const std::string &path, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback = nullptr); /** * @brief Load a file into the given DataStorage given user defined IFileReader::Options. * * This method calls Load(const std::vector&, DataStorage&) with a * one-element vector. * * @param path The absolute file name including the file extension. * @param options IFileReader option instance that should be used if selected reader * has options. * @param storage A DataStorage object to which the loaded data will be added. * @return The set of added DataNode objects. * @throws mitk::Exception if \c path could not be loaded. * * @sa Load(const std::vector&, DataStorage&) */ static DataStorage::SetOfObjects::Pointer Load(const std::string &path, const IFileReader::Options &options, DataStorage &storage); /** * @brief Load a file and return the loaded data. * * This method calls Load(const std::vector&) with a * one-element vector. * * @param path The absolute file name including the file extension. * @param optionsCallback Pointer to a callback instance. The callback is used by * the load operation if more the suitable reader was found or the reader has options * that can be set. * @return The set of added DataNode objects. * @throws mitk::Exception if \c path could not be loaded. * * @sa Load(const std::vector&, DataStorage&) */ static std::vector Load(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback = nullptr); template static typename T::Pointer Load(const std::string& path, const ReaderOptionsFunctorBase *optionsCallback = nullptr) { return dynamic_cast(Load(path, optionsCallback).at(0).GetPointer()); } /** * @brief Load a file and return the loaded data. * * This method calls Load(const std::vector&) with a * one-element vector. * * @param path The absolute file name including the file extension. * @param options IFileReader option instance that should be used if selected reader * has options. * @return The set of added DataNode objects. * @throws mitk::Exception if \c path could not be loaded. * * @sa Load(const std::vector&, DataStorage&) */ static std::vector Load(const std::string &path, const IFileReader::Options &options); template static typename T::Pointer Load(const std::string& path, const IFileReader::Options &options) { return dynamic_cast(Load(path, options).at(0).GetPointer()); } /** * @brief Loads a list of file paths into the given DataStorage. * * If an entry in \c paths cannot be loaded, this method will continue to load * the remaining entries into \c storage and throw an exception afterwards. * * @param paths A list of absolute file names including the file extension. * @param storage A DataStorage object to which the loaded data will be added. * @param optionsCallback Pointer to a callback instance. The callback is used by * the load operation if more the suitable reader was found or the reader has options * that can be set. * @return The set of added DataNode objects. * @throws mitk::Exception if an entry in \c paths could not be loaded. */ static DataStorage::SetOfObjects::Pointer Load(const std::vector &paths, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback = nullptr); static std::vector Load(const std::vector &paths, const ReaderOptionsFunctorBase *optionsCallback = nullptr); /** * @brief Loads the contents of a us::ModuleResource and returns the corresponding mitk::BaseData * @param usResource a ModuleResource, representing a BaseData object * @param mode Optional parameter to set the openmode of the stream * @return The set of loaded BaseData objects. \c Should contain either one or zero elements, since a resource * stream * respresents one object. * @throws mitk::Exception if no reader was found for the stream. */ static std::vector Load(const us::ModuleResource &usResource, std::ios_base::openmode mode = std::ios_base::in); template static typename T::Pointer Load(const us::ModuleResource &usResource, std::ios_base::openmode mode = std::ios_base::in) { return dynamic_cast(Load(usResource, mode).at(0).GetPointer()); } static BaseData::Pointer Load(const std::string& path, const PropertyList* properties); /** * @brief Save a mitk::BaseData instance. * @param data The data to save. * @param path The path to the image including file name and and optional file extension. * If no extension is set, the default extension and mime-type for the * BaseData type of \c data is used. * @param setPathProperty * @throws mitk::Exception if no writer for \c data is available or the writer * is not able to write the image. */ static void Save(const mitk::BaseData *data, const std::string &path, bool setPathProperty = false); /** * @brief Save a mitk::BaseData instance. * @param data The data to save. * @param path The path to the image including file name and an optional file extension. * If no extension is set, the default extension and mime-type for the * BaseData type of \c data is used. * @param options The IFileWriter options to use for the selected writer. * @param setPathProperty * @throws mitk::Exception if no writer for \c data is available or the writer * is not able to write the image. */ static void Save(const mitk::BaseData *data, const std::string &path, const IFileWriter::Options &options, bool setPathProperty = false); /** * @brief Save a mitk::BaseData instance. * @param data The data to save. * @param mimeType The mime-type to use for writing \c data. * @param path The path to the image including file name and an optional file extension. * @param addExtension If \c true, an extension according to the given \c mimeType * is added to \c path if it does not contain one. If \c path already contains * a file name extension, it is not checked for compatibility with \c mimeType. * @param setPathProperty * * @throws mitk::Exception if no writer for the combination of \c data and \c mimeType is * available or the writer is not able to write the image. */ static void Save(const mitk::BaseData *data, const std::string &mimeType, const std::string &path, bool addExtension = true, bool setPathProperty = false); /** * @brief Save a mitk::BaseData instance. * @param data The data to save. * @param mimeType The mime-type to use for writing \c data. * @param path The path to the image including file name and an optional file extension. * @param options Configuration data for the used IFileWriter instance. * @param addExtension If \c true, an extension according to the given \c mimeType * is added to \c path if it does not contain one. If \c path already contains * a file name extension, it is not checked for compatibility with \c mimeType. * @param setPathProperty * * @throws mitk::Exception if no writer for the combination of \c data and \c mimeType is * available or the writer is not able to write the image. */ static void Save(const mitk::BaseData *data, const std::string &mimeType, const std::string &path, const mitk::IFileWriter::Options &options, bool addExtension = true, bool setPathProperty = false); /** * @brief Use SaveInfo objects to save BaseData instances. * * This is a low-level method for directly working with SaveInfo objects. Usually, * the Save() methods taking a BaseData object as an argument are more appropriate. * * @param saveInfos A list of SaveInfo objects for saving contained BaseData objects. * @param setPathProperty * * @see Save(const mitk::BaseData*, const std::string&) */ static void Save(std::vector &saveInfos, bool setPathProperty = false); protected: static std::string Load(std::vector &loadInfos, DataStorage::SetOfObjects *nodeResult, DataStorage *ds, const ReaderOptionsFunctorBase *optionsCallback); static std::string Save(const BaseData *data, const std::string &mimeType, const std::string &path, WriterOptionsFunctorBase *optionsCallback, bool addExtension, bool setPathProperty); static std::string Save(std::vector &saveInfos, WriterOptionsFunctorBase *optionsCallback, bool setPathProperty); private: struct Impl; }; } #endif // MITKIOUTIL_H diff --git a/Modules/Core/include/mitkSliceNavigationController.h b/Modules/Core/include/mitkSliceNavigationController.h index dd2548c92e..c45673095e 100644 --- a/Modules/Core/include/mitkSliceNavigationController.h +++ b/Modules/Core/include/mitkSliceNavigationController.h @@ -1,472 +1,439 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#ifndef SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F -#define SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F +#ifndef MITKSLICENAVIGATIONCONTROLLER_H +#define MITKSLICENAVIGATIONCONTROLLER_H -#include "mitkBaseController.h" -#include "mitkMessage.h" -#include "mitkRenderingManager.h" -#include "mitkTimeGeometry.h" #include + +#include +#include +#include +#include +#include + #pragma GCC visibility push(default) #include #pragma GCC visibility pop -#include "mitkDataStorage.h" -#include "mitkRestorePlanePositionOperation.h" + #include -#include namespace mitk { #define mitkTimeGeometryEventMacro(classname, super) \ class MITKCORE_EXPORT classname : public super \ { \ public: \ typedef classname Self; \ typedef super Superclass; \ classname(TimeGeometry *aTimeGeometry, unsigned int aPos) : Superclass(aTimeGeometry, aPos) {} \ virtual ~classname() {} \ virtual const char *GetEventName() const { return #classname; } \ virtual bool CheckEvent(const ::itk::EventObject *e) const { return dynamic_cast(e); } \ virtual ::itk::EventObject *MakeObject() const { return new Self(GetTimeGeometry(), GetPos()); } \ private: \ void operator=(const Self &); \ } class PlaneGeometry; class BaseGeometry; class BaseRenderer; /** * \brief Controls the selection of the slice the associated BaseRenderer * will display * * A SliceNavigationController takes a BaseGeometry or a TimeGeometry as input world geometry * (TODO what are the exact requirements?) and generates a TimeGeometry * as output. The TimeGeometry holds a number of SlicedGeometry3Ds and * these in turn hold a series of PlaneGeometries. One of these PlaneGeometries is * selected as world geometry for the BaseRenderers associated to 2D views. * * The SliceNavigationController holds has Steppers (one for the slice, a * second for the time step), which control the selection of a single * PlaneGeometry from the TimeGeometry. SliceNavigationController generates - * ITK events to tell observers, like a BaseRenderer, when the selected slice + * ITK events to tell observers, like a BaseRenderer, when the selected slice * or timestep changes. * * Example: * \code * // Initialization * sliceCtrl = mitk::SliceNavigationController::New(); * * // Tell the navigator the geometry to be sliced (with geometry a * // BaseGeometry::ConstPointer) - * sliceCtrl->SetInputWorldGeometry3D(geometry.GetPointer()); + * sliceCtrl->SetInputWorldTimeGeometry(geometry.GetPointer()); * * // Tell the navigator in which direction it shall slice the data * sliceCtrl->SetViewDirection(mitk::SliceNavigationController::Axial); * * // Connect one or more BaseRenderer to this navigator, i.e.: events sent * // by the navigator when stepping through the slices (e.g. by * // sliceCtrl->GetSlice()->Next()) will be received by the BaseRenderer * // (in this example only slice-changes, see also ConnectGeometryTimeEvent * // and ConnectGeometryEvents.) * sliceCtrl->ConnectGeometrySliceEvent(renderer.GetPointer()); * * //create a world geometry and send the information to the connected renderer(s) * sliceCtrl->Update(); * \endcode * * * You can connect visible navigators to a SliceNavigationController, e.g., a * QmitkSliderNavigator (for Qt): * * \code * // Create the visible navigator (a slider with a spin-box) * QmitkSliderNavigator* navigator = * new QmitkSliderNavigator(parent, "slidernavigator"); * * // Connect the navigator to the slice-stepper of the * // SliceNavigationController. For initialization (position, minimal and * // maximal values) the values of the SliceNavigationController are used. * // Thus, accessing methods of a navigator is normally not necessary, since * // everything can be set via the (Qt-independent) SliceNavigationController. * // The QmitkStepperAdapter converts the Qt-signals to Qt-independent * // itk-events. * new QmitkStepperAdapter(navigator, sliceCtrl->GetSlice(), "navigatoradaptor"); * \endcode * * If you do not want that all renderwindows are updated when a new slice is * selected, you can use a specific RenderingManager, which updates only those * renderwindows that should be updated. This is sometimes useful when a 3D view * does not need to be updated when the slices in some 2D views are changed. * QmitkSliderNavigator (for Qt): * * \code * // create a specific RenderingManager * mitk::RenderingManager::Pointer myManager = mitk::RenderingManager::New(); * * // tell the RenderingManager to update only renderwindow1 and renderwindow2 * myManager->AddRenderWindow(renderwindow1); * myManager->AddRenderWindow(renderwindow2); * * // tell the SliceNavigationController of renderwindow1 and renderwindow2 * // to use the specific RenderingManager instead of the global one * renderwindow1->GetSliceNavigationController()->SetRenderingManager(myManager); * renderwindow2->GetSliceNavigationController()->SetRenderingManager(myManager); * \endcode * * \todo implement for non-evenly-timed geometry! * \ingroup NavigationControl */ class MITKCORE_EXPORT SliceNavigationController : public BaseController { public: + mitkClassMacro(SliceNavigationController, BaseController); - // itkFactorylessNewMacro(Self) - // mitkNewMacro1Param(Self, const char *); itkNewMacro(Self); - // itkCloneMacro(Self) /** - * \brief Possible view directions, \a Original will uses + * \brief Possible view directions, \a Original will use * the PlaneGeometry instances in a SlicedGeometry3D provided - * as input world geometry (by SetInputWorldGeometry3D). + * as input world geometry (by SetInputWorldTimeGeometry). */ enum ViewDirection { Axial, Sagittal, Coronal, Original }; /** - * \brief Set the input world geometry3D out of which the + * \brief Set the input world time geometry out of which the * geometries for slicing will be created. * * Any previous previous set input geometry (3D or Time) will * be ignored in future. */ - void SetInputWorldGeometry3D(const mitk::BaseGeometry *geometry); - itkGetConstObjectMacro(InputWorldGeometry3D, mitk::BaseGeometry); - - void SetInputWorldTimeGeometry(const mitk::TimeGeometry *geometry); - itkGetConstObjectMacro(InputWorldTimeGeometry, mitk::TimeGeometry); + void SetInputWorldTimeGeometry(const TimeGeometry* geometry); + itkGetConstObjectMacro(InputWorldTimeGeometry, TimeGeometry); /** * \brief Access the created geometry */ - itkGetConstObjectMacro(CreatedWorldGeometry, mitk::TimeGeometry); + itkGetConstObjectMacro(CreatedWorldGeometry, TimeGeometry); + itkGetObjectMacro(CreatedWorldGeometry, TimeGeometry); /** * \brief Set the desired view directions * * \sa ViewDirection * \sa Update(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(ViewDirection, ViewDirection); itkGetEnumMacro(ViewDirection, ViewDirection); /** * \brief Set the default view direction * * This is used to re-initialize the view direction of the SNC to the * default value with SetViewDirectionToDefault() * * \sa ViewDirection * \sa Update(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(DefaultViewDirection, ViewDirection); itkGetEnumMacro(DefaultViewDirection, ViewDirection); const char *GetViewDirectionAsString() const; virtual void SetViewDirectionToDefault(); /** * \brief Do the actual creation and send it to the connected * observers (renderers) * */ virtual void Update(); /** * \brief Extended version of Update, additionally allowing to * specify the direction/orientation of the created geometry. * */ virtual void Update(ViewDirection viewDirection, bool top = true, bool frontside = true, bool rotated = false); /** * \brief Send the created geometry to the connected * observers (renderers) * * Called by Update(). */ virtual void SendCreatedWorldGeometry(); /** * \brief Tell observers to re-read the currently selected 2D geometry * */ virtual void SendCreatedWorldGeometryUpdate(); /** * \brief Send the currently selected slice to the connected * observers (renderers) * * Called by Update(). */ virtual void SendSlice(); /** * \brief Send the currently selected time to the connected * observers (renderers) * * Called by Update(). */ virtual void SendTime(); class MITKCORE_EXPORT TimeGeometryEvent : public itk::AnyEvent { public: typedef TimeGeometryEvent Self; typedef itk::AnyEvent Superclass; - TimeGeometryEvent(TimeGeometry *aTimeGeometry, unsigned int aPos) : m_TimeGeometry(aTimeGeometry), m_Pos(aPos) {} + TimeGeometryEvent(TimeGeometry* aTimeGeometry, unsigned int aPos) : m_TimeGeometry(aTimeGeometry), m_Pos(aPos) {} ~TimeGeometryEvent() override {} - const char *GetEventName() const override { return "TimeGeometryEvent"; } - bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast(e); } - ::itk::EventObject *MakeObject() const override { return new Self(m_TimeGeometry, m_Pos); } - TimeGeometry *GetTimeGeometry() const { return m_TimeGeometry; } + const char* GetEventName() const override { return "TimeGeometryEvent"; } + bool CheckEvent(const ::itk::EventObject* e) const override { return dynamic_cast(e); } + ::itk::EventObject* MakeObject() const override { return new Self(m_TimeGeometry, m_Pos); } + TimeGeometry* GetTimeGeometry() const { return m_TimeGeometry; } unsigned int GetPos() const { return m_Pos; } + private: TimeGeometry::Pointer m_TimeGeometry; unsigned int m_Pos; // TimeGeometryEvent(const Self&); - void operator=(const Self &); // just hide + void operator=(const Self&); // just hide }; mitkTimeGeometryEventMacro(GeometrySendEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometryUpdateEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometryTimeEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometrySliceEvent, TimeGeometryEvent); template - void ConnectGeometrySendEvent(T *receiver) + void ConnectGeometrySendEvent(T* receiver) { - typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; - ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); + auto eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometry); unsigned long tag = AddObserver(GeometrySendEvent(nullptr, 0), eventReceptorCommand); - m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); + m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template - void ConnectGeometryUpdateEvent(T *receiver) + void ConnectGeometryUpdateEvent(T* receiver) { - typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; - ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); + auto eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::UpdateGeometry); unsigned long tag = AddObserver(GeometryUpdateEvent(nullptr, 0), eventReceptorCommand); - m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); + m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template - void ConnectGeometrySliceEvent(T *receiver, bool connectSendEvent = true) + void ConnectGeometrySliceEvent(T* receiver) { - typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; - ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); + auto eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometrySlice); unsigned long tag = AddObserver(GeometrySliceEvent(nullptr, 0), eventReceptorCommand); - m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); - if (connectSendEvent) - ConnectGeometrySendEvent(receiver); + m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template - void ConnectGeometryTimeEvent(T *receiver, bool connectSendEvent = true) + void ConnectGeometryTimeEvent(T* receiver) { - typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; - ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); + auto eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometryTime); unsigned long tag = AddObserver(GeometryTimeEvent(nullptr, 0), eventReceptorCommand); - m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); - if (connectSendEvent) - ConnectGeometrySendEvent(receiver); + m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template - void ConnectGeometryEvents(T *receiver) + void ConnectGeometryEvents(T* receiver) { // connect sendEvent only once ConnectGeometrySliceEvent(receiver, false); ConnectGeometryTimeEvent(receiver); } // use a templated method to get the right offset when casting to void* template - void Disconnect(T *receiver) + void Disconnect(T* receiver) { - auto i = m_ReceiverToObserverTagsMap.find(static_cast(receiver)); + auto i = m_ReceiverToObserverTagsMap.find(static_cast(receiver)); if (i == m_ReceiverToObserverTagsMap.end()) return; - const std::list &tags = i->second; + const std::list& tags = i->second; for (auto tagIter = tags.begin(); tagIter != tags.end(); ++tagIter) { RemoveObserver(*tagIter); } m_ReceiverToObserverTagsMap.erase(i); } - Message1 SetCrosshairEvent; + Message1 SetCrosshairEvent; /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface * \warning not implemented */ - virtual void SetGeometry(const itk::EventObject &geometrySliceEvent); + virtual void SetGeometry(const itk::EventObject& geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ - virtual void SetGeometrySlice(const itk::EventObject &geometrySliceEvent); + virtual void SetGeometrySlice(const itk::EventObject& geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ - virtual void SetGeometryTime(const itk::EventObject &geometryTimeEvent); + virtual void SetGeometryTime(const itk::EventObject& geometryTimeEvent); /** \brief Positions the SNC according to the specified point */ - void SelectSliceByPoint(const mitk::Point3D &point); - - /** \brief Returns the TimeGeometry created by the SNC. */ - mitk::TimeGeometry *GetCreatedWorldGeometry(); + void SelectSliceByPoint(const Point3D& point); /** \brief Returns the BaseGeometry of the currently selected time step. */ - const mitk::BaseGeometry *GetCurrentGeometry3D(); + const BaseGeometry* GetCurrentGeometry3D(); /** \brief Returns the currently selected Plane in the current * BaseGeometry (if existent). */ - const mitk::PlaneGeometry *GetCurrentPlaneGeometry(); - - /** \brief Sets the BaseRenderer associated with this SNC (if any). While - * the BaseRenderer is not directly used by SNC, this is a convenience - * method to enable BaseRenderer access via the SNC. */ - void SetRenderer(BaseRenderer *renderer); + const PlaneGeometry* GetCurrentPlaneGeometry(); - /** \brief Gets the BaseRenderer associated with this SNC (if any). While - * the BaseRenderer is not directly used by SNC, this is a convenience - * method to enable BaseRenderer access via the SNC. Returns nullptr if no - * BaseRenderer has been specified*/ - BaseRenderer *GetRenderer() const; + /** \brief Sets / gets the BaseRenderer associated with this SNC (if any). + * While the BaseRenderer is not directly used by SNC, this is a convenience + * method to enable BaseRenderer access via the SNC. + */ + itkSetObjectMacro(Renderer, BaseRenderer); + itkGetMacro(Renderer, BaseRenderer*); /** \brief Re-orients the slice stack. All slices will be oriented to the given normal vector. The given point (world coordinates) defines the selected slice. Careful: The resulting axis vectors are not clearly defined this way. If you want to define them clearly, use - ReorientSlices (const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1). + ReorientSlices (const Point3D &point, const Vector3D &axisVec0, const Vector3D &axisVec1). */ - void ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &normal); + void ReorientSlices(const Point3D& point, const Vector3D& normal); /** \brief Re-orients the slice stack so that all planes are oriented according to the - * given axis vectors. The given Point eventually defines selected slice. - */ - void ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1); + * given axis vectors. The given Point eventually defines selected slice. + */ + void ReorientSlices(const Point3D& point, const Vector3D& axisVec0, const Vector3D& axisVec1); - void ExecuteOperation(Operation *operation) override; + void ExecuteOperation(Operation* operation) override; /** * \brief Feature option to lock planes during mouse interaction. * This option flag disables the mouse event which causes the center * cross to move near by. */ itkSetMacro(SliceLocked, bool); itkGetMacro(SliceLocked, bool); itkBooleanMacro(SliceLocked); /** * \brief Feature option to lock slice rotation. * * This option flag disables separately the rotation of a slice which is * implemented in mitkSliceRotator. */ itkSetMacro(SliceRotationLocked, bool); itkGetMacro(SliceRotationLocked, bool); itkBooleanMacro(SliceRotationLocked); /** * \brief Adjusts the numerical range of the slice stepper according to * the current geometry orientation of this SNC's SlicedGeometry. */ void AdjustSliceStepperRange(); /** \brief Convenience method that returns the time step currently selected by the controller.*/ TimeStepType GetSelectedTimeStep() const; /** \brief Convenience method that returns the time point that corresponds to the selected * time step. The conversion is done using the time geometry of the SliceNavigationController. * If the time geometry is not yet set, this function will always return 0.0.*/ TimePointType GetSelectedTimePoint() const; protected: + SliceNavigationController(); ~SliceNavigationController() override; - mitk::BaseGeometry::ConstPointer m_InputWorldGeometry3D; - mitk::TimeGeometry::ConstPointer m_InputWorldTimeGeometry; + void CreateWorldGeometry(bool top, bool frontside, bool rotated); - mitk::TimeGeometry::Pointer m_CreatedWorldGeometry; + TimeGeometry::ConstPointer m_InputWorldTimeGeometry; + TimeGeometry::Pointer m_CreatedWorldGeometry; ViewDirection m_ViewDirection; ViewDirection m_DefaultViewDirection; - mitk::RenderingManager::Pointer m_RenderingManager; - - mitk::BaseRenderer *m_Renderer; - - itkSetMacro(Top, bool); - itkGetMacro(Top, bool); - itkBooleanMacro(Top); - - itkSetMacro(FrontSide, bool); - itkGetMacro(FrontSide, bool); - itkBooleanMacro(FrontSide); - - itkSetMacro(Rotated, bool); - itkGetMacro(Rotated, bool); - itkBooleanMacro(Rotated); + RenderingManager::Pointer m_RenderingManager; - bool m_Top; - bool m_FrontSide; - bool m_Rotated; + BaseRenderer* m_Renderer; bool m_BlockUpdate; bool m_SliceLocked; bool m_SliceRotationLocked; - unsigned int m_OldPos; - typedef std::map> ObserverTagsMapType; + typedef std::map> ObserverTagsMapType; ObserverTagsMapType m_ReceiverToObserverTagsMap; }; } // namespace mitk -#endif /* SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F */ +#endif // MITKSLICENAVIGATIONCONTROLLER_H diff --git a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp index 65ac9ae5ad..d99c58a6e6 100644 --- a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp +++ b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp @@ -1,656 +1,599 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSliceNavigationController.h" -#include "mitkAction.h" + #include "mitkBaseRenderer.h" #include "mitkCrosshairPositionEvent.h" -#include "mitkInteractionConst.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkPlaneGeometry.h" #include "mitkProportionalTimeGeometry.h" #include "mitkArbitraryTimeGeometry.h" -#include "mitkRenderingManager.h" #include "mitkSlicedGeometry3D.h" #include "mitkVtkPropRenderer.h" #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkInteractionConst.h" #include "mitkNodePredicateDataType.h" #include "mitkOperationEvent.h" #include "mitkPixelTypeMultiplex.h" #include "mitkPlaneOperation.h" #include "mitkPointOperation.h" #include "mitkStatusBar.h" -#include "mitkUndoController.h" #include "mitkApplyTransformMatrixOperation.h" -#include "mitkMemoryUtilities.h" - -#include - namespace mitk { + + PlaneGeometry::PlaneOrientation ViewDirectionToPlaneOrientation(SliceNavigationController::ViewDirection viewDiretion) + { + switch (viewDiretion) + { + case SliceNavigationController::Axial: + return PlaneGeometry::Axial; + case SliceNavigationController::Sagittal: + return PlaneGeometry::Sagittal; + case SliceNavigationController::Coronal: + return PlaneGeometry::Coronal; + case SliceNavigationController::Original: + default: + return PlaneGeometry::None; + } + } + SliceNavigationController::SliceNavigationController() - : BaseController(), - m_InputWorldGeometry3D( mitk::BaseGeometry::ConstPointer() ), - m_InputWorldTimeGeometry( mitk::TimeGeometry::ConstPointer() ), - m_CreatedWorldGeometry( mitk::TimeGeometry::Pointer() ), - m_ViewDirection(Axial), - m_DefaultViewDirection(Axial), - m_RenderingManager( mitk::RenderingManager::Pointer() ), - m_Renderer( nullptr ), - m_Top(false), - m_FrontSide(false), - m_Rotated(false), - m_BlockUpdate(false), - m_SliceLocked(false), - m_SliceRotationLocked(false), - m_OldPos(0) + : BaseController() + , m_InputWorldTimeGeometry(TimeGeometry::ConstPointer()) + , m_CreatedWorldGeometry(TimeGeometry::Pointer()) + , m_ViewDirection(Axial) + , m_DefaultViewDirection(Axial) + , m_RenderingManager(RenderingManager::Pointer()) + , m_Renderer(nullptr) + , m_BlockUpdate(false) + , m_SliceLocked(false) + , m_SliceRotationLocked(false) { typedef itk::SimpleMemberCommand SNCCommandType; SNCCommandType::Pointer sliceStepperChangedCommand, timeStepperChangedCommand; sliceStepperChangedCommand = SNCCommandType::New(); timeStepperChangedCommand = SNCCommandType::New(); sliceStepperChangedCommand->SetCallbackFunction(this, &SliceNavigationController::SendSlice); timeStepperChangedCommand->SetCallbackFunction(this, &SliceNavigationController::SendTime); m_Slice->AddObserver(itk::ModifiedEvent(), sliceStepperChangedCommand); m_Time->AddObserver(itk::ModifiedEvent(), timeStepperChangedCommand); m_Slice->SetUnitName("mm"); m_Time->SetUnitName("ms"); - - m_Top = false; - m_FrontSide = false; - m_Rotated = false; } - SliceNavigationController::~SliceNavigationController() {} - void SliceNavigationController::SetInputWorldGeometry3D(const BaseGeometry *geometry) + SliceNavigationController::~SliceNavigationController() { - if ( geometry != nullptr ) - { - if (geometry->GetBoundingBox()->GetDiagonalLength2() < eps) - { - itkWarningMacro("setting an empty bounding-box"); - geometry = nullptr; - } - } - if (m_InputWorldGeometry3D != geometry) - { - m_InputWorldGeometry3D = geometry; - m_InputWorldTimeGeometry = mitk::TimeGeometry::ConstPointer(); - this->Modified(); - } + // nothing here } - void SliceNavigationController::SetInputWorldTimeGeometry(const TimeGeometry *geometry) + void SliceNavigationController::SetInputWorldTimeGeometry(const TimeGeometry* geometry) { - if ( geometry != nullptr ) + if (nullptr != geometry) { if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() < eps) { itkWarningMacro("setting an empty bounding-box"); geometry = nullptr; } } + if (m_InputWorldTimeGeometry != geometry) { m_InputWorldTimeGeometry = geometry; - m_InputWorldGeometry3D = mitk::BaseGeometry::ConstPointer(); this->Modified(); } } - void SliceNavigationController::SetViewDirectionToDefault() { m_ViewDirection = m_DefaultViewDirection; } - const char *SliceNavigationController::GetViewDirectionAsString() const + void SliceNavigationController::SetViewDirectionToDefault() { - const char *viewDirectionString; + m_ViewDirection = m_DefaultViewDirection; + } + + const char* SliceNavigationController::GetViewDirectionAsString() const + { + const char* viewDirectionString; switch (m_ViewDirection) { case SliceNavigationController::Axial: viewDirectionString = "Axial"; break; case SliceNavigationController::Sagittal: viewDirectionString = "Sagittal"; break; case SliceNavigationController::Coronal: viewDirectionString = "Coronal"; break; case SliceNavigationController::Original: viewDirectionString = "Original"; break; default: viewDirectionString = "No View Direction Available"; break; } return viewDirectionString; } void SliceNavigationController::Update() { if (!m_BlockUpdate) { if (m_ViewDirection == Sagittal) { this->Update(Sagittal, true, true, false); } else if (m_ViewDirection == Coronal) { this->Update(Coronal, false, true, false); } else if (m_ViewDirection == Axial) { this->Update(Axial, false, false, true); } else { this->Update(m_ViewDirection); } } } void SliceNavigationController::Update(SliceNavigationController::ViewDirection viewDirection, bool top, bool frontside, bool rotated) { - TimeGeometry::ConstPointer worldTimeGeometry = m_InputWorldTimeGeometry; - - if (m_BlockUpdate || (m_InputWorldTimeGeometry.IsNull() && m_InputWorldGeometry3D.IsNull()) || - ((worldTimeGeometry.IsNotNull()) && (worldTimeGeometry->CountTimeSteps() == 0))) + if (m_BlockUpdate) { return; } - m_BlockUpdate = true; + if (m_InputWorldTimeGeometry.IsNull()) + { + return; + } - if (m_InputWorldTimeGeometry.IsNotNull() && m_LastUpdateTime < m_InputWorldTimeGeometry->GetMTime()) + if (0 == m_InputWorldTimeGeometry->CountTimeSteps()) { - Modified(); + return; } - if (m_InputWorldGeometry3D.IsNotNull() && m_LastUpdateTime < m_InputWorldGeometry3D->GetMTime()) + + m_BlockUpdate = true; + + if (m_LastUpdateTime < m_InputWorldTimeGeometry->GetMTime()) { Modified(); } + this->SetViewDirection(viewDirection); - this->SetTop(top); - this->SetFrontSide(frontside); - this->SetRotated(rotated); if (m_LastUpdateTime < GetMTime()) { m_LastUpdateTime = GetMTime(); - // initialize the viewplane - SlicedGeometry3D::Pointer slicedWorldGeometry = SlicedGeometry3D::Pointer(); - BaseGeometry::ConstPointer currentGeometry = BaseGeometry::ConstPointer(); - if (m_InputWorldTimeGeometry.IsNotNull()) - if (m_InputWorldTimeGeometry->IsValidTimeStep(GetTime()->GetPos())) - currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(GetTime()->GetPos()); - else - currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(0); - else - currentGeometry = m_InputWorldGeometry3D; - - m_CreatedWorldGeometry = mitk::TimeGeometry::Pointer(); - switch (viewDirection) - { - case Original: - if (worldTimeGeometry.IsNotNull()) - { - m_CreatedWorldGeometry = worldTimeGeometry->Clone(); - - worldTimeGeometry = m_CreatedWorldGeometry.GetPointer(); - - slicedWorldGeometry = dynamic_cast( - m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()).GetPointer()); - - if (slicedWorldGeometry.IsNotNull()) - { - break; - } - } - else - { - const auto *worldSlicedGeometry = - dynamic_cast(currentGeometry.GetPointer()); - - if ( worldSlicedGeometry != nullptr ) - { - slicedWorldGeometry = static_cast(currentGeometry->Clone().GetPointer()); - break; - } - } - slicedWorldGeometry = SlicedGeometry3D::New(); - slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::None, top, frontside, rotated); - slicedWorldGeometry->SetSliceNavigationController(this); - break; - - case Axial: - slicedWorldGeometry = SlicedGeometry3D::New(); - slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Axial, top, frontside, rotated); - slicedWorldGeometry->SetSliceNavigationController(this); - break; - - case Coronal: - slicedWorldGeometry = SlicedGeometry3D::New(); - slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Coronal, top, frontside, rotated); - slicedWorldGeometry->SetSliceNavigationController(this); - break; - - case Sagittal: - slicedWorldGeometry = SlicedGeometry3D::New(); - slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Sagittal, top, frontside, rotated); - slicedWorldGeometry->SetSliceNavigationController(this); - break; - default: - itkExceptionMacro("unknown ViewDirection"); - } - - m_Slice->SetPos(0); - m_Slice->SetSteps((int)slicedWorldGeometry->GetSlices()); - - if ( worldTimeGeometry.IsNull() ) - { - auto createdTimeGeometry = ProportionalTimeGeometry::New(); - createdTimeGeometry->Initialize( slicedWorldGeometry, 1 ); - m_CreatedWorldGeometry = createdTimeGeometry; - - m_Time->SetSteps(0); - m_Time->SetPos(0); - m_Time->InvalidateRange(); - } - else - { - m_BlockUpdate = true; - m_Time->SetSteps(worldTimeGeometry->CountTimeSteps()); - m_Time->SetPos(0); - - const TimeBounds &timeBounds = worldTimeGeometry->GetTimeBounds(); - m_Time->SetRange(timeBounds[0], timeBounds[1]); - - m_BlockUpdate = false; - - const auto currentTemporalPosition = this->GetTime()->GetPos(); - assert( worldTimeGeometry->GetGeometryForTimeStep( currentTemporalPosition ).IsNotNull() ); - - if ( dynamic_cast( worldTimeGeometry.GetPointer() ) != nullptr ) - { - const TimePointType minimumTimePoint = - worldTimeGeometry->TimeStepToTimePoint( currentTemporalPosition ); - - const TimePointType stepDuration = - worldTimeGeometry->TimeStepToTimePoint( currentTemporalPosition + 1 ) - minimumTimePoint; - - auto createdTimeGeometry = ProportionalTimeGeometry::New(); - createdTimeGeometry->Initialize( slicedWorldGeometry, worldTimeGeometry->CountTimeSteps() ); - createdTimeGeometry->SetFirstTimePoint( minimumTimePoint ); - createdTimeGeometry->SetStepDuration( stepDuration ); - - m_CreatedWorldGeometry = createdTimeGeometry; - } - else - { - auto createdTimeGeometry = mitk::ArbitraryTimeGeometry::New(); - const TimeStepType numberOfTimeSteps = worldTimeGeometry->CountTimeSteps(); - createdTimeGeometry->ReserveSpaceForGeometries( numberOfTimeSteps ); - - for ( TimeStepType i = 0; i < numberOfTimeSteps; ++i ) - { - const BaseGeometry::Pointer clonedGeometry = slicedWorldGeometry->Clone().GetPointer(); - const auto bounds = worldTimeGeometry->GetTimeBounds( i ); - createdTimeGeometry->AppendNewTimeStep( clonedGeometry, - bounds[0], bounds[1]); - } - createdTimeGeometry->Update(); - - m_CreatedWorldGeometry = createdTimeGeometry; - } - } + this->CreateWorldGeometry(top, frontside, rotated); } // unblock update; we may do this now, because if m_BlockUpdate was already // true before this method was entered, then we will never come here. m_BlockUpdate = false; // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry and time/slice data. this->SendCreatedWorldGeometry(); this->SendSlice(); this->SendTime(); // Adjust the stepper range of slice stepper according to geometry this->AdjustSliceStepperRange(); } void SliceNavigationController::SendCreatedWorldGeometry() { - // Send the geometry. Do this even if nothing was changed, because maybe - // Update() was only called to re-send the old geometry. if (!m_BlockUpdate) { this->InvokeEvent(GeometrySendEvent(m_CreatedWorldGeometry, 0)); } } void SliceNavigationController::SendCreatedWorldGeometryUpdate() { if (!m_BlockUpdate) { this->InvokeEvent(GeometryUpdateEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); } } void SliceNavigationController::SendSlice() { if (!m_BlockUpdate) { if (m_CreatedWorldGeometry.IsNotNull()) { this->InvokeEvent(GeometrySliceEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); RenderingManager::GetInstance()->RequestUpdateAll(); } } } void SliceNavigationController::SendTime() { if (!m_BlockUpdate) { if (m_CreatedWorldGeometry.IsNotNull()) { this->InvokeEvent(GeometryTimeEvent(m_CreatedWorldGeometry, m_Time->GetPos())); RenderingManager::GetInstance()->RequestUpdateAll(); } } } - void SliceNavigationController::SetGeometry(const itk::EventObject &) {} - void SliceNavigationController::SetGeometryTime(const itk::EventObject &geometryTimeEvent) + void SliceNavigationController::SetGeometry(const itk::EventObject&) + { + // not implemented + } + + void SliceNavigationController::SetGeometryTime(const itk::EventObject& geometryTimeEvent) { if (m_CreatedWorldGeometry.IsNull()) { return; } - const auto *timeEvent = - dynamic_cast< const SliceNavigationController::GeometryTimeEvent * >(&geometryTimeEvent); - assert( timeEvent != nullptr ); + const auto* timeEvent = dynamic_cast(&geometryTimeEvent); + assert(timeEvent != nullptr); - TimeGeometry *timeGeometry = timeEvent->GetTimeGeometry(); - assert( timeGeometry != nullptr ); + TimeGeometry* timeGeometry = timeEvent->GetTimeGeometry(); + assert(timeGeometry != nullptr); auto timeStep = (int)timeEvent->GetPos(); ScalarType timeInMS; timeInMS = timeGeometry->TimeStepToTimePoint(timeStep); timeStep = m_CreatedWorldGeometry->TimePointToTimeStep(timeInMS); this->GetTime()->SetPos(timeStep); } - void SliceNavigationController::SetGeometrySlice(const itk::EventObject &geometrySliceEvent) + void SliceNavigationController::SetGeometrySlice(const itk::EventObject& geometrySliceEvent) { - const auto *sliceEvent = - dynamic_cast(&geometrySliceEvent); - assert(sliceEvent!=nullptr); + const auto* sliceEvent = dynamic_cast(&geometrySliceEvent); + assert(sliceEvent != nullptr); this->GetSlice()->SetPos(sliceEvent->GetPos()); } - void SliceNavigationController::SelectSliceByPoint(const Point3D &point) + void SliceNavigationController::SelectSliceByPoint(const Point3D& point) { if (m_CreatedWorldGeometry.IsNull()) { return; } //@todo add time to PositionEvent and use here!! - SlicedGeometry3D *slicedWorldGeometry = dynamic_cast( + const auto* slicedWorldGeometry = dynamic_cast( m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()).GetPointer()); - if (slicedWorldGeometry) + if (nullptr == slicedWorldGeometry) { - int bestSlice = -1; - double bestDistance = itk::NumericTraits::max(); + return; + } - int s, slices; - slices = slicedWorldGeometry->GetSlices(); - if (slicedWorldGeometry->GetEvenlySpaced()) - { - mitk::PlaneGeometry *plane = slicedWorldGeometry->GetPlaneGeometry(0); + int bestSlice = -1; + double bestDistance = itk::NumericTraits::max(); - const Vector3D &direction = slicedWorldGeometry->GetDirectionVector(); + if (slicedWorldGeometry->GetEvenlySpaced()) + { + PlaneGeometry* plane = slicedWorldGeometry->GetPlaneGeometry(0); - Point3D projectedPoint; - plane->Project(point, projectedPoint); + const Vector3D& direction = slicedWorldGeometry->GetDirectionVector(); - // Check whether the point is somewhere within the slice stack volume; - // otherwise, the default slice (0) will be selected - if (direction[0] * (point[0] - projectedPoint[0]) + direction[1] * (point[1] - projectedPoint[1]) + - direction[2] * (point[2] - projectedPoint[2]) >= - 0) - { - bestSlice = (int)(plane->Distance(point) / slicedWorldGeometry->GetSpacing()[2] + 0.5); - } - } - else - { - Point3D projectedPoint; - for (s = 0; s < slices; ++s) - { - slicedWorldGeometry->GetPlaneGeometry(s)->Project(point, projectedPoint); - const Vector3D distance = projectedPoint - point; - ScalarType currentDistance = distance.GetSquaredNorm(); + Point3D projectedPoint; + plane->Project(point, projectedPoint); - if (currentDistance < bestDistance) - { - bestDistance = currentDistance; - bestSlice = s; - } - } - } - if (bestSlice >= 0) + // Check whether the point is somewhere within the slice stack volume; + // otherwise, the default slice (0) will be selected + if (direction[0] * (point[0] - projectedPoint[0]) + direction[1] * (point[1] - projectedPoint[1]) + + direction[2] * (point[2] - projectedPoint[2]) >= 0) { - this->GetSlice()->SetPos(bestSlice); + bestSlice = static_cast(plane->Distance(point) / slicedWorldGeometry->GetSpacing()[2] + 0.5); } - else + } + else + { + int numberOfSlices = slicedWorldGeometry->GetSlices(); + Point3D projectedPoint; + for (int slice = 0; slice < numberOfSlices; ++slice) { - this->GetSlice()->SetPos(0); + slicedWorldGeometry->GetPlaneGeometry(slice)->Project(point, projectedPoint); + const Vector3D distance = projectedPoint - point; + ScalarType currentDistance = distance.GetSquaredNorm(); + + if (currentDistance < bestDistance) + { + bestDistance = currentDistance; + bestSlice = slice; + } } - this->SendCreatedWorldGeometryUpdate(); - // send crosshair event - SetCrosshairEvent.Send(point); } + + if (bestSlice >= 0) + { + this->GetSlice()->SetPos(bestSlice); + } + else + { + this->GetSlice()->SetPos(0); + } + + this->SendCreatedWorldGeometryUpdate(); + // send crosshair event + SetCrosshairEvent.Send(point); } - void SliceNavigationController::ReorientSlices(const Point3D &point, const Vector3D &normal) + void SliceNavigationController::ReorientSlices(const Point3D& point, const Vector3D& normal) { if (m_CreatedWorldGeometry.IsNull()) { return; } PlaneOperation op(OpORIENT, point, normal); - m_CreatedWorldGeometry->ExecuteOperation(&op); this->SendCreatedWorldGeometryUpdate(); } - void SliceNavigationController::ReorientSlices(const mitk::Point3D &point, - const mitk::Vector3D &axisVec0, - const mitk::Vector3D &axisVec1) + void SliceNavigationController::ReorientSlices(const Point3D& point, + const Vector3D& axisVec0, + const Vector3D& axisVec1) { - if (m_CreatedWorldGeometry) + if (m_CreatedWorldGeometry.IsNull()) { - PlaneOperation op(OpORIENT, point, axisVec0, axisVec1); - m_CreatedWorldGeometry->ExecuteOperation(&op); - - this->SendCreatedWorldGeometryUpdate(); + return; } + + PlaneOperation op(OpORIENT, point, axisVec0, axisVec1); + m_CreatedWorldGeometry->ExecuteOperation(&op); + + this->SendCreatedWorldGeometryUpdate(); } - mitk::TimeGeometry *SliceNavigationController::GetCreatedWorldGeometry() { return m_CreatedWorldGeometry; } - const mitk::BaseGeometry *SliceNavigationController::GetCurrentGeometry3D() + const BaseGeometry* SliceNavigationController::GetCurrentGeometry3D() { - if (m_CreatedWorldGeometry.IsNotNull()) - { - return m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()); - } - else + if (m_CreatedWorldGeometry.IsNull()) { return nullptr; } + + return m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()); } - const mitk::PlaneGeometry *SliceNavigationController::GetCurrentPlaneGeometry() + const PlaneGeometry* SliceNavigationController::GetCurrentPlaneGeometry() { - const auto *slicedGeometry = - dynamic_cast(this->GetCurrentGeometry3D()); + const auto* slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); - if (slicedGeometry) - { - const mitk::PlaneGeometry *planeGeometry = (slicedGeometry->GetPlaneGeometry(this->GetSlice()->GetPos())); - return planeGeometry; - } - else + if (nullptr == slicedGeometry) { return nullptr; } + + return slicedGeometry->GetPlaneGeometry(this->GetSlice()->GetPos()); } - void SliceNavigationController::SetRenderer(BaseRenderer *renderer) { m_Renderer = renderer; } - BaseRenderer *SliceNavigationController::GetRenderer() const { return m_Renderer; } void SliceNavigationController::AdjustSliceStepperRange() { - const auto *slicedGeometry = - dynamic_cast(this->GetCurrentGeometry3D()); + const auto* slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); - const Vector3D &direction = slicedGeometry->GetDirectionVector(); + const Vector3D& direction = slicedGeometry->GetDirectionVector(); int c = 0; int i, k = 0; for (i = 0; i < 3; ++i) { if (fabs(direction[i]) < 0.000000001) { ++c; } else { k = i; } } if (c == 2) { ScalarType min = slicedGeometry->GetOrigin()[k]; ScalarType max = min + slicedGeometry->GetExtentInMM(k); m_Slice->SetRange(min, max); } else { m_Slice->InvalidateRange(); } } - void SliceNavigationController::ExecuteOperation(Operation *operation) + void SliceNavigationController::ExecuteOperation(Operation* operation) { // switch on type // - select best slice for a given point // - rotate created world geometry according to Operation->SomeInfo() if (!operation || m_CreatedWorldGeometry.IsNull()) { return; } switch (operation->GetOperationType()) { case OpMOVE: // should be a point operation { if (!m_SliceLocked) // do not move the cross position { // select a slice - auto *po = dynamic_cast(operation); + auto* po = dynamic_cast(operation); if (po && po->GetIndex() == -1) { this->SelectSliceByPoint(po->GetPoint()); } else if (po && po->GetIndex() != -1) // undo case because index != -1, index holds the old position of this slice { this->GetSlice()->SetPos(po->GetIndex()); } } break; } case OpRESTOREPLANEPOSITION: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } case OpAPPLYTRANSFORMMATRIX: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } default: { // do nothing break; } } } TimeStepType SliceNavigationController::GetSelectedTimeStep() const { return this->GetTime()->GetPos(); } TimePointType SliceNavigationController::GetSelectedTimePoint() const { auto timeStep = this->GetSelectedTimeStep(); if (m_CreatedWorldGeometry.IsNull()) { return 0.0; } if (!m_CreatedWorldGeometry->IsValidTimeStep(timeStep)) { mitkThrow() << "SliceNavigationController is in an invalid state. It has a time step" - << "selected that is not covered by its time geometry. Selected time step: " - << timeStep << "; TimeGeometry steps count: " << m_CreatedWorldGeometry->CountTimeSteps(); + << "selected that is not covered by its time geometry. Selected time step: " << timeStep + << "; TimeGeometry steps count: " << m_CreatedWorldGeometry->CountTimeSteps(); } return m_CreatedWorldGeometry->TimeStepToTimePoint(timeStep); } -} // namespace + void SliceNavigationController::CreateWorldGeometry(bool top, bool frontside, bool rotated) + { + // initialize the viewplane + SlicedGeometry3D::Pointer slicedWorldGeometry; + BaseGeometry::ConstPointer currentGeometry; + + // get the BaseGeometry (ArbitraryTimeGeometry or ProportionalTimeGeometry) of the current time step + auto currentTimeStep = this->GetTime()->GetPos(); + if (m_InputWorldTimeGeometry->IsValidTimeStep(currentTimeStep)) + { + currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(currentTimeStep); + } + else + { + currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(0); + } + + if (Original == m_ViewDirection) + { + slicedWorldGeometry = dynamic_cast( + m_InputWorldTimeGeometry->GetGeometryForTimeStep(currentTimeStep).GetPointer()); + if (slicedWorldGeometry.IsNull()) + { + slicedWorldGeometry = SlicedGeometry3D::New(); + slicedWorldGeometry->InitializePlanes( + currentGeometry, PlaneGeometry::None, top, frontside, rotated); + slicedWorldGeometry->SetSliceNavigationController(this); + } + } + else + { + slicedWorldGeometry = SlicedGeometry3D::New(); + slicedWorldGeometry->InitializePlanes( + currentGeometry, ViewDirectionToPlaneOrientation(m_ViewDirection), top, frontside, rotated); + slicedWorldGeometry->SetSliceNavigationController(this); + } + + // reset the stepper + m_Slice->SetSteps(slicedWorldGeometry->GetSlices()); + m_Slice->SetPos(0); + + TimeStepType inputTimeSteps = m_InputWorldTimeGeometry->CountTimeSteps(); + const TimeBounds& timeBounds = m_InputWorldTimeGeometry->GetTimeBounds(); + m_Time->SetSteps(inputTimeSteps); + m_Time->SetPos(0); + m_Time->SetRange(timeBounds[0], timeBounds[1]); + + currentTimeStep = this->GetTime()->GetPos(); + assert(m_InputWorldTimeGeometry->GetGeometryForTimeStep(currentTimeStep).IsNotNull()); + + // create new time geometry and initialize it according to the type of the 'm_InputWorldTimeGeometry' + // the created world geometry will either have equidistant timesteps (ProportionalTimeGeometry) + // or individual time bounds for each timestep (ArbitraryTimeGeometry) + m_CreatedWorldGeometry = mitk::TimeGeometry::Pointer(); + if (nullptr != dynamic_cast(m_InputWorldTimeGeometry.GetPointer())) + { + const TimePointType minimumTimePoint = m_InputWorldTimeGeometry->TimeStepToTimePoint(currentTimeStep); + const TimePointType stepDuration = + m_InputWorldTimeGeometry->TimeStepToTimePoint(currentTimeStep + 1) - minimumTimePoint; + + auto createdTimeGeometry = ProportionalTimeGeometry::New(); + createdTimeGeometry->Initialize(slicedWorldGeometry, inputTimeSteps); + createdTimeGeometry->SetFirstTimePoint(minimumTimePoint); + createdTimeGeometry->SetStepDuration(stepDuration); + + m_CreatedWorldGeometry = createdTimeGeometry; + } + else + { + auto createdTimeGeometry = mitk::ArbitraryTimeGeometry::New(); + createdTimeGeometry->ReserveSpaceForGeometries(inputTimeSteps); + const BaseGeometry::Pointer clonedGeometry = slicedWorldGeometry->Clone(); + + for (TimeStepType i = 0; i < inputTimeSteps; ++i) + { + const auto bounds = m_InputWorldTimeGeometry->GetTimeBounds(i); + createdTimeGeometry->AppendNewTimeStep(clonedGeometry, bounds[0], bounds[1]); + } + + createdTimeGeometry->Update(); + m_CreatedWorldGeometry = createdTimeGeometry; + } + } +} // namespace mitk diff --git a/Modules/Core/src/IO/mitkIOUtil.cpp b/Modules/Core/src/IO/mitkIOUtil.cpp index d9b7a72510..2e05ce7d71 100644 --- a/Modules/Core/src/IO/mitkIOUtil.cpp +++ b/Modules/Core/src/IO/mitkIOUtil.cpp @@ -1,1015 +1,1015 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkIOUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ITK #include // VTK #include #include #include #include #include static std::string GetLastErrorStr() { #ifdef US_PLATFORM_POSIX return std::string(strerror(errno)); #else // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, nullptr); std::string errMsg((LPCTSTR)lpMsgBuf); LocalFree(lpMsgBuf); return errMsg; #endif } #ifdef US_PLATFORM_WINDOWS #include #include // make the posix flags point to the obsolete bsd types on windows #define S_IRUSR S_IREAD #define S_IWUSR S_IWRITE #else #include #include #include #endif #include #include static const char validLetters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // A cross-platform version of the mkstemps function static int mkstemps_compat(char *tmpl, int suffixlen) { static unsigned long long value = 0; int savedErrno = errno; // Lower bound on the number of temporary files to attempt to generate. #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX const unsigned int attempts = TMP_MAX; #else const unsigned int attempts = ATTEMPTS_MIN; #endif const int len = strlen(tmpl); if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6)) { errno = EINVAL; return -1; } /* This is where the Xs start. */ char *XXXXXX = &tmpl[len - 6 - suffixlen]; /* Get some more or less random data. */ #ifdef US_PLATFORM_WINDOWS { SYSTEMTIME stNow; FILETIME ftNow; // get system time GetSystemTime(&stNow); stNow.wMilliseconds = 500; if (!SystemTimeToFileTime(&stNow, &ftNow)) { errno = -1; return -1; } unsigned long long randomTimeBits = ((static_cast(ftNow.dwHighDateTime) << 32) | static_cast(ftNow.dwLowDateTime)); value = randomTimeBits ^ static_cast(GetCurrentThreadId()); } #else { struct timeval tv; gettimeofday(&tv, nullptr); unsigned long long randomTimeBits = ((static_cast(tv.tv_usec) << 32) | static_cast(tv.tv_sec)); value = randomTimeBits ^ static_cast(getpid()); } #endif for (unsigned int count = 0; count < attempts; value += 7777, ++count) { unsigned long long v = value; /* Fill in the random bits. */ XXXXXX[0] = validLetters[v % 62]; v /= 62; XXXXXX[1] = validLetters[v % 62]; v /= 62; XXXXXX[2] = validLetters[v % 62]; v /= 62; XXXXXX[3] = validLetters[v % 62]; v /= 62; XXXXXX[4] = validLetters[v % 62]; v /= 62; XXXXXX[5] = validLetters[v % 62]; int fd = open(tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd >= 0) { errno = savedErrno; return fd; } else if (errno != EEXIST) { return -1; } } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return -1; } // A cross-platform version of the POSIX mkdtemp function static char *mkdtemps_compat(char *tmpl, int suffixlen) { static unsigned long long value = 0; int savedErrno = errno; // Lower bound on the number of temporary dirs to attempt to generate. #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary dir. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX const unsigned int attempts = TMP_MAX; #else const unsigned int attempts = ATTEMPTS_MIN; #endif const int len = strlen(tmpl); if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6)) { errno = EINVAL; return nullptr; } /* This is where the Xs start. */ char *XXXXXX = &tmpl[len - 6 - suffixlen]; /* Get some more or less random data. */ #ifdef US_PLATFORM_WINDOWS { SYSTEMTIME stNow; FILETIME ftNow; // get system time GetSystemTime(&stNow); stNow.wMilliseconds = 500; if (!SystemTimeToFileTime(&stNow, &ftNow)) { errno = -1; return nullptr; } unsigned long long randomTimeBits = ((static_cast(ftNow.dwHighDateTime) << 32) | static_cast(ftNow.dwLowDateTime)); value = randomTimeBits ^ static_cast(GetCurrentThreadId()); } #else { struct timeval tv; gettimeofday(&tv, nullptr); unsigned long long randomTimeBits = ((static_cast(tv.tv_usec) << 32) | static_cast(tv.tv_sec)); value = randomTimeBits ^ static_cast(getpid()); } #endif unsigned int count = 0; for (; count < attempts; value += 7777, ++count) { unsigned long long v = value; /* Fill in the random bits. */ XXXXXX[0] = validLetters[v % 62]; v /= 62; XXXXXX[1] = validLetters[v % 62]; v /= 62; XXXXXX[2] = validLetters[v % 62]; v /= 62; XXXXXX[3] = validLetters[v % 62]; v /= 62; XXXXXX[4] = validLetters[v % 62]; v /= 62; XXXXXX[5] = validLetters[v % 62]; #ifdef US_PLATFORM_WINDOWS int fd = _mkdir(tmpl); //, _S_IREAD | _S_IWRITE | _S_IEXEC); #else int fd = mkdir(tmpl, S_IRUSR | S_IWUSR | S_IXUSR); #endif if (fd >= 0) { errno = savedErrno; return tmpl; } else if (errno != EEXIST) { return nullptr; } } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return nullptr; } //#endif //************************************************************** // mitk::IOUtil method definitions namespace mitk { struct IOUtil::Impl { struct FixedReaderOptionsFunctor : public ReaderOptionsFunctorBase { FixedReaderOptionsFunctor(const IFileReader::Options &options) : m_Options(options) {} bool operator()(LoadInfo &loadInfo) const override { IFileReader *reader = loadInfo.m_ReaderSelector.GetSelected().GetReader(); if (reader) { reader->SetOptions(m_Options); } return false; } private: const IFileReader::Options &m_Options; }; struct FixedWriterOptionsFunctor : public WriterOptionsFunctorBase { FixedWriterOptionsFunctor(const IFileReader::Options &options) : m_Options(options) {} bool operator()(SaveInfo &saveInfo) const override { IFileWriter *writer = saveInfo.m_WriterSelector.GetSelected().GetWriter(); if (writer) { writer->SetOptions(m_Options); } return false; } private: const IFileWriter::Options &m_Options; }; static BaseData::Pointer LoadBaseDataFromFile(const std::string &path, const ReaderOptionsFunctorBase* optionsCallback = nullptr); }; BaseData::Pointer IOUtil::Impl::LoadBaseDataFromFile(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback) { std::vector baseDataList = Load(path, optionsCallback); // The Load(path) call above should throw an exception if nothing could be loaded assert(!baseDataList.empty()); return baseDataList.front(); } #ifdef US_PLATFORM_WINDOWS std::string IOUtil::GetProgramPath() { char path[512]; std::size_t index = std::string(path, GetModuleFileName(nullptr, path, 512)).find_last_of('\\'); return std::string(path, index); } #elif defined(US_PLATFORM_APPLE) #include std::string IOUtil::GetProgramPath() { char path[512]; uint32_t size = sizeof(path); if (_NSGetExecutablePath(path, &size) == 0) { std::size_t index = std::string(path).find_last_of('/'); std::string strPath = std::string(path, index); // const char* execPath = strPath.c_str(); // mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch(execPath,false); return strPath; } return std::string(); } #else #include #include #include std::string IOUtil::GetProgramPath() { std::stringstream ss; ss << "/proc/" << getpid() << "/exe"; char proc[512] = {0}; ssize_t ch = readlink(ss.str().c_str(), proc, 512); if (ch == -1) return std::string(); std::size_t index = std::string(proc).find_last_of('/'); return std::string(proc, index); } #endif char IOUtil::GetDirectorySeparator() { #ifdef US_PLATFORM_WINDOWS return '\\'; #else return '/'; #endif } std::string IOUtil::GetTempPath() { static std::string result; if (result.empty()) { #ifdef US_PLATFORM_WINDOWS char tempPathTestBuffer[1]; - DWORD bufferLength = ::GetTempPath(1, tempPathTestBuffer); + DWORD bufferLength = GetTempPathA(1, tempPathTestBuffer); if (bufferLength == 0) { mitkThrow() << GetLastErrorStr(); } std::vector tempPath(bufferLength); - bufferLength = ::GetTempPath(bufferLength, &tempPath[0]); + bufferLength = GetTempPathA(bufferLength, &tempPath[0]); if (bufferLength == 0) { mitkThrow() << GetLastErrorStr(); } result.assign(tempPath.begin(), tempPath.begin() + static_cast(bufferLength)); #else result = "/tmp/"; #endif } return result; } std::string IOUtil::CreateTemporaryFile(const std::string &templateName, std::string path) { std::ofstream tmpOutputStream; std::string returnValue = CreateTemporaryFile(tmpOutputStream, templateName, path); tmpOutputStream.close(); return returnValue; } std::string IOUtil::CreateTemporaryFile(std::ofstream &f, const std::string &templateName, std::string path) { return CreateTemporaryFile(f, std::ios_base::out | std::ios_base::trunc, templateName, path); } std::string IOUtil::CreateTemporaryFile(std::ofstream &f, std::ios_base::openmode mode, const std::string &templateName, std::string path) { if (path.empty()) { path = GetTempPath(); } path += templateName; std::vector dst_path(path.begin(), path.end()); dst_path.push_back('\0'); std::size_t lastX = path.find_last_of('X'); std::size_t firstX = path.find_last_not_of('X', lastX); int firstNonX = firstX == std::string::npos ? -1 : firstX - 1; while (lastX != std::string::npos && (lastX - firstNonX) < 6) { lastX = path.find_last_of('X', firstX); firstX = path.find_last_not_of('X', lastX); firstNonX = firstX == std::string::npos ? -1 : firstX - 1; } std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1; int fd = mkstemps_compat(&dst_path[0], suffixlen); if (fd != -1) { path.assign(dst_path.begin(), dst_path.end() - 1); f.open(path.c_str(), mode | std::ios_base::out | std::ios_base::trunc); close(fd); } else { mitkThrow() << "Creating temporary file " << &dst_path[0] << " failed: " << GetLastErrorStr(); } return path; } std::string IOUtil::CreateTemporaryDirectory(const std::string &templateName, std::string path) { if (path.empty()) { path = GetTempPath(); } path += GetDirectorySeparator() + templateName; std::vector dst_path(path.begin(), path.end()); dst_path.push_back('\0'); std::size_t lastX = path.find_last_of('X'); std::size_t firstX = path.find_last_not_of('X', lastX); int firstNonX = firstX == std::string::npos ? -1 : firstX - 1; while (lastX != std::string::npos && (lastX - firstNonX) < 6) { lastX = path.find_last_of('X', firstX); firstX = path.find_last_not_of('X', lastX); firstNonX = firstX == std::string::npos ? -1 : firstX - 1; } std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1; if (mkdtemps_compat(&dst_path[0], suffixlen) == nullptr) { mitkThrow() << "Creating temporary directory " << &dst_path[0] << " failed: " << GetLastErrorStr(); } path.assign(dst_path.begin(), dst_path.end() - 1); return path; } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string &path, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback) { std::vector paths; paths.push_back(path); return Load(paths, storage, optionsCallback); } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string &path, const IFileReader::Options &options, DataStorage &storage) { std::vector loadInfos; loadInfos.push_back(LoadInfo(path)); DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New(); Impl::FixedReaderOptionsFunctor optionsCallback(options); std::string errMsg = Load(loadInfos, nodeResult, &storage, &optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return nodeResult; } std::vector IOUtil::Load(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback) { std::vector paths; paths.push_back(path); return Load(paths, optionsCallback); } std::vector IOUtil::Load(const std::string &path, const IFileReader::Options &options) { std::vector loadInfos; loadInfos.push_back(LoadInfo(path)); Impl::FixedReaderOptionsFunctor optionsCallback(options); std::string errMsg = Load(loadInfos, nullptr, nullptr, &optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return loadInfos.front().m_Output; } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::vector &paths, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback) { DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New(); std::vector loadInfos; for (const auto &loadInfo : paths) { loadInfos.emplace_back(loadInfo); } std::string errMsg = Load(loadInfos, nodeResult, &storage, optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return nodeResult; } std::vector IOUtil::Load(const std::vector &paths, const ReaderOptionsFunctorBase *optionsCallback) { std::vector result; std::vector loadInfos; for (const auto &loadInfo : paths) { loadInfos.emplace_back(loadInfo); } std::string errMsg = Load(loadInfos, nullptr, nullptr, optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } for (std::vector::const_iterator iter = loadInfos.begin(), iterEnd = loadInfos.end(); iter != iterEnd; ++iter) { result.insert(result.end(), iter->m_Output.begin(), iter->m_Output.end()); } return result; } std::string IOUtil::Load(std::vector &loadInfos, DataStorage::SetOfObjects *nodeResult, DataStorage *ds, const ReaderOptionsFunctorBase *optionsCallback) { if (loadInfos.empty()) { return "No input files given"; } int filesToRead = loadInfos.size(); mitk::ProgressBar::GetInstance()->AddStepsToDo(2 * filesToRead); std::string errMsg; std::map usedReaderItems; std::vector< std::string > read_files; for (auto &loadInfo : loadInfos) { if(std::find(read_files.begin(), read_files.end(), loadInfo.m_Path) != read_files.end()) continue; std::vector readers = loadInfo.m_ReaderSelector.Get(); if (readers.empty()) { if (!itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(loadInfo.m_Path).c_str())) { errMsg += "File '" + loadInfo.m_Path + "' does not exist\n"; } else { errMsg += "No reader available for '" + loadInfo.m_Path + "'\n"; } continue; } bool callOptionsCallback = readers.size() > 1 || !readers.front().GetReader()->GetOptions().empty(); // check if we already used a reader which should be re-used std::vector currMimeTypes = loadInfo.m_ReaderSelector.GetMimeTypes(); std::string selectedMimeType; for (std::vector::const_iterator mimeTypeIter = currMimeTypes.begin(), mimeTypeIterEnd = currMimeTypes.end(); mimeTypeIter != mimeTypeIterEnd; ++mimeTypeIter) { std::map::const_iterator oldSelectedItemIter = usedReaderItems.find(mimeTypeIter->GetName()); if (oldSelectedItemIter != usedReaderItems.end()) { // we found an already used item for a mime-type which is contained // in the current reader set, check all current readers if there service // id equals the old reader for (std::vector::const_iterator currReaderItem = readers.begin(), currReaderItemEnd = readers.end(); currReaderItem != currReaderItemEnd; ++currReaderItem) { if (currReaderItem->GetMimeType().GetName() == mimeTypeIter->GetName() && currReaderItem->GetServiceId() == oldSelectedItemIter->second.GetServiceId() && currReaderItem->GetConfidenceLevel() >= oldSelectedItemIter->second.GetConfidenceLevel()) { // okay, we used the same reader already, re-use its options selectedMimeType = mimeTypeIter->GetName(); callOptionsCallback = false; loadInfo.m_ReaderSelector.Select(oldSelectedItemIter->second.GetServiceId()); loadInfo.m_ReaderSelector.GetSelected().GetReader()->SetOptions( oldSelectedItemIter->second.GetReader()->GetOptions()); break; } } if (!selectedMimeType.empty()) break; } } if (callOptionsCallback && optionsCallback) { callOptionsCallback = (*optionsCallback)(loadInfo); if (!callOptionsCallback && !loadInfo.m_Cancel) { usedReaderItems.erase(selectedMimeType); FileReaderSelector::Item selectedItem = loadInfo.m_ReaderSelector.GetSelected(); usedReaderItems.insert(std::make_pair(selectedItem.GetMimeType().GetName(), selectedItem)); } } if (loadInfo.m_Cancel) { errMsg += "Reading operation(s) cancelled."; break; } IFileReader *reader = loadInfo.m_ReaderSelector.GetSelected().GetReader(); if (reader == nullptr) { errMsg += "Unexpected nullptr reader."; break; } reader->SetProperties(loadInfo.m_Properties); // Do the actual reading try { DataStorage::SetOfObjects::Pointer nodes; if (ds != nullptr) { nodes = reader->Read(*ds); std::vector< std::string > new_files = reader->GetReadFiles(); read_files.insert( read_files.end(), new_files.begin(), new_files.end() ); } else { nodes = DataStorage::SetOfObjects::New(); std::vector baseData = reader->Read(); for (auto iter = baseData.begin(); iter != baseData.end(); ++iter) { if (iter->IsNotNull()) { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(*iter); nodes->InsertElement(nodes->Size(), node); } } std::vector< std::string > new_files = reader->GetReadFiles(); read_files.insert( read_files.end(), new_files.begin(), new_files.end() ); } for (DataStorage::SetOfObjects::ConstIterator nodeIter = nodes->Begin(), nodeIterEnd = nodes->End(); nodeIter != nodeIterEnd; ++nodeIter) { const mitk::DataNode::Pointer &node = nodeIter->Value(); mitk::BaseData::Pointer data = node->GetData(); if (data.IsNull()) { continue; } data->SetProperty("path", mitk::StringProperty::New(Utf8Util::Local8BitToUtf8(loadInfo.m_Path))); loadInfo.m_Output.push_back(data); if (nodeResult) { nodeResult->push_back(nodeIter->Value()); } } if (loadInfo.m_Output.empty() || (nodeResult && nodeResult->Size() == 0)) { errMsg += "Unknown read error occurred reading " + loadInfo.m_Path; } } catch (const std::exception &e) { errMsg += "Exception occurred when reading file " + loadInfo.m_Path + ":\n" + e.what() + "\n\n"; } mitk::ProgressBar::GetInstance()->Progress(2); --filesToRead; } if (!errMsg.empty()) { MITK_ERROR << errMsg; } mitk::ProgressBar::GetInstance()->Progress(2 * filesToRead); return errMsg; } std::vector IOUtil::Load(const us::ModuleResource &usResource, std::ios_base::openmode mode) { us::ModuleResourceStream resStream(usResource, mode); mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); std::vector mimetypes = mimeTypeProvider->GetMimeTypesForFile(usResource.GetResourcePath()); std::vector data; if (mimetypes.empty()) { mitkThrow() << "No mimetype for resource stream: " << usResource.GetResourcePath(); return data; } mitk::FileReaderRegistry fileReaderRegistry; std::vector> refs = fileReaderRegistry.GetReferences(mimetypes[0]); if (refs.empty()) { mitkThrow() << "No reader available for resource stream: " << usResource.GetResourcePath(); return data; } mitk::IFileReader *reader = fileReaderRegistry.GetReader(refs[0]); reader->SetInput(usResource.GetResourcePath(), &resStream); data = reader->Read(); return data; } BaseData::Pointer IOUtil::Load(const std::string& path, const PropertyList* properties) { LoadInfo loadInfo(path); loadInfo.m_Properties = properties; std::vector loadInfos; loadInfos.push_back(loadInfo); auto errMsg = Load(loadInfos, nullptr, nullptr, nullptr); if (!errMsg.empty()) mitkThrow() << errMsg; return loadInfos.front().m_Output.front(); } void IOUtil::Save(const BaseData *data, const std::string &path, bool setPathProperty) { Save(data, path, IFileWriter::Options(), setPathProperty); } void IOUtil::Save(const BaseData *data, const std::string &path, const IFileWriter::Options &options, bool setPathProperty) { Save(data, std::string(), path, options, setPathProperty); } void IOUtil::Save(const BaseData *data, const std::string &mimeType, const std::string &path, bool addExtension, bool setPathProperty) { Save(data, mimeType, path, IFileWriter::Options(), addExtension, setPathProperty); } void IOUtil::Save(const BaseData *data, const std::string &mimeType, const std::string &path, const IFileWriter::Options &options, bool addExtension, bool setPathProperty) { if ((data == nullptr) || (data->IsEmpty())) mitkThrow() << "BaseData cannotbe null or empty for save methods in IOUtil.h."; std::string errMsg; if (options.empty()) { errMsg = Save(data, mimeType, path, nullptr, addExtension, setPathProperty); } else { Impl::FixedWriterOptionsFunctor optionsCallback(options); errMsg = Save(data, mimeType, path, &optionsCallback, addExtension, setPathProperty); } if (!errMsg.empty()) { mitkThrow() << errMsg; } } void IOUtil::Save(std::vector &saveInfos, bool setPathProperty) { std::string errMsg = Save(saveInfos, nullptr, setPathProperty); if (!errMsg.empty()) { mitkThrow() << errMsg; } } std::string IOUtil::Save(const BaseData *data, const std::string &mimeTypeName, const std::string &path, WriterOptionsFunctorBase *optionsCallback, bool addExtension, bool setPathProperty) { if (path.empty()) { return "No output filename given"; } mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); MimeType mimeType = mimeTypeProvider->GetMimeTypeForName(mimeTypeName); SaveInfo saveInfo(data, mimeType, path); std::string ext = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetFilenameExtension(Utf8Util::Local8BitToUtf8(path))); if (saveInfo.m_WriterSelector.IsEmpty()) { return std::string("No suitable writer found for the current data of type ") + data->GetNameOfClass() + (mimeType.IsValid() ? (std::string(" and mime-type ") + mimeType.GetName()) : std::string()) + (ext.empty() ? std::string() : (std::string(" with extension ") + ext)); } // Add an extension if not already specified if (ext.empty() && addExtension) { ext = saveInfo.m_MimeType.GetExtensions().empty() ? std::string() : "." + saveInfo.m_MimeType.GetExtensions().front(); saveInfo.m_Path += ext; } std::vector infos; infos.push_back(saveInfo); return Save(infos, optionsCallback, setPathProperty); } std::string IOUtil::Save(std::vector &saveInfos, WriterOptionsFunctorBase *optionsCallback, bool setPathProperty) { if (saveInfos.empty()) { return "No data for saving available"; } int filesToWrite = saveInfos.size(); mitk::ProgressBar::GetInstance()->AddStepsToDo(2 * filesToWrite); std::string errMsg; std::set usedSaveInfos; for (auto &saveInfo : saveInfos) { const std::string baseDataType = saveInfo.m_BaseData->GetNameOfClass(); std::vector writers = saveInfo.m_WriterSelector.Get(); // Error out if no compatible Writer was found if (writers.empty()) { errMsg += std::string("No writer available for ") + baseDataType + " data.\n"; continue; } bool callOptionsCallback = writers.size() > 1 || !writers[0].GetWriter()->GetOptions().empty(); // check if we already used a writer for this base data type // which should be re-used auto oldSaveInfoIter = usedSaveInfos.find(saveInfo); if (oldSaveInfoIter != usedSaveInfos.end()) { // we previously saved a base data object of the same data with the same mime-type, // check if the same writer is contained in the current writer set and if the // confidence level matches FileWriterSelector::Item oldSelectedItem = oldSaveInfoIter->m_WriterSelector.Get(oldSaveInfoIter->m_WriterSelector.GetSelectedId()); for (std::vector::const_iterator currWriterItem = writers.begin(), currWriterItemEnd = writers.end(); currWriterItem != currWriterItemEnd; ++currWriterItem) { if (currWriterItem->GetServiceId() == oldSelectedItem.GetServiceId() && currWriterItem->GetConfidenceLevel() >= oldSelectedItem.GetConfidenceLevel()) { // okay, we used the same writer already, re-use its options callOptionsCallback = false; saveInfo.m_WriterSelector.Select(oldSaveInfoIter->m_WriterSelector.GetSelectedId()); saveInfo.m_WriterSelector.GetSelected().GetWriter()->SetOptions(oldSelectedItem.GetWriter()->GetOptions()); break; } } } if (callOptionsCallback && optionsCallback) { callOptionsCallback = (*optionsCallback)(saveInfo); if (!callOptionsCallback && !saveInfo.m_Cancel) { usedSaveInfos.erase(saveInfo); usedSaveInfos.insert(saveInfo); } } if (saveInfo.m_Cancel) { errMsg += "Writing operation(s) cancelled."; break; } IFileWriter *writer = saveInfo.m_WriterSelector.GetSelected().GetWriter(); if (writer == nullptr) { errMsg += "Unexpected nullptr writer."; break; } // Do the actual writing try { writer->SetOutputLocation(saveInfo.m_Path); writer->Write(); } catch (const std::exception &e) { errMsg += std::string("Exception occurred when writing to ") + saveInfo.m_Path + ":\n" + e.what() + "\n"; } if (setPathProperty) saveInfo.m_BaseData->GetPropertyList()->SetStringProperty("path", Utf8Util::Local8BitToUtf8(saveInfo.m_Path).c_str()); mitk::ProgressBar::GetInstance()->Progress(2); --filesToWrite; } if (!errMsg.empty()) { MITK_ERROR << errMsg; } mitk::ProgressBar::GetInstance()->Progress(2 * filesToWrite); return errMsg; } IOUtil::SaveInfo::SaveInfo(const BaseData *baseData, const MimeType &mimeType, const std::string &path) : m_BaseData(baseData), m_WriterSelector(baseData, mimeType.GetName(), path), m_MimeType(mimeType.IsValid() ? mimeType // use the original mime-type : (m_WriterSelector.IsEmpty() ? mimeType // no writer found, use the original invalid mime-type : m_WriterSelector.GetDefault().GetMimeType() // use the found default mime-type )), m_Path(path), m_Cancel(false) { } bool IOUtil::SaveInfo::operator<(const IOUtil::SaveInfo &other) const { int r = strcmp(m_BaseData->GetNameOfClass(), other.m_BaseData->GetNameOfClass()); if (r == 0) { return m_WriterSelector.GetSelected().GetMimeType() < other.m_WriterSelector.GetSelected().GetMimeType(); } return r < 0; } IOUtil::LoadInfo::LoadInfo(const std::string &path) : m_Path(path), m_ReaderSelector(path), m_Cancel(false), m_Properties(nullptr) { } } diff --git a/Modules/Core/src/Interactions/mitkEventRecorder.cpp b/Modules/Core/src/Interactions/mitkEventRecorder.cpp index 11bdf36c00..429f00c5bd 100644 --- a/Modules/Core/src/Interactions/mitkEventRecorder.cpp +++ b/Modules/Core/src/Interactions/mitkEventRecorder.cpp @@ -1,188 +1,188 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkEventRecorder.h" #include "mitkEventFactory.h" #include "mitkInteractionEvent.h" #include "mitkInteractionEventConst.h" #include "vtkCamera.h" #include "mitkBaseRenderer.h" static void WriteEventXMLHeader(std::ofstream &stream) { stream << mitk::InteractionEventConst::xmlHead() << "\n"; } static void WriteEventXMLConfig(std::ofstream &stream) { // stream << " <" << mitk::InteractionEventConst::xmlTagConfigRoot() << ">\n"; // write renderer config // for all registered 2D renderers write name and viewdirection. auto rendererIterator = mitk::BaseRenderer::baseRendererMap.begin(); auto end = mitk::BaseRenderer::baseRendererMap.end(); for (; rendererIterator != end; ++rendererIterator) { std::string rendererName = (*rendererIterator).second->GetName(); mitk::SliceNavigationController::ViewDirection viewDirection = (*rendererIterator).second->GetSliceNavigationController()->GetDefaultViewDirection(); - mitk::BaseRenderer::MapperSlotId mapperID = (*rendererIterator).second->GetMapperID(); + MapperSlotId mapperID = (*rendererIterator).second->GetMapperID(); // stream << " <" << mitk::InteractionEventConst::xmlTagRenderer() << " " << mitk::InteractionEventConst::xmlEventPropertyRendererName() << "=\"" << rendererName << "\" " << mitk::InteractionEventConst::xmlEventPropertyViewDirection() << "=\"" << viewDirection << "\" " << mitk::InteractionEventConst::xmlEventPropertyMapperID() << "=\"" << mapperID << "\" " << mitk::InteractionEventConst::xmlRenderSizeX() << "=\"" << (*rendererIterator).second->GetSize()[0] << "\" " << mitk::InteractionEventConst::xmlRenderSizeY() << "=\"" << (*rendererIterator).second->GetSize()[1] << "\" " << mitk::InteractionEventConst::xmlRenderSizeZ() << "=\"" << (*rendererIterator).second->GetSize()[2] << "\" "; ; if ((*rendererIterator).second->GetMapperID() == mitk::BaseRenderer::Standard3D) { // For a 3D render window, rotation and zoom settings are determined by the vtkCamera parameters // these are recorded here: stream << mitk::InteractionEventConst::xmlViewUpX() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetViewUp()[0] << "\" " << mitk::InteractionEventConst::xmlViewUpY() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetViewUp()[1] << "\" " << mitk::InteractionEventConst::xmlViewUpZ() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetViewUp()[2] << "\" " << mitk::InteractionEventConst::xmlCameraFocalPointX() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint()[0] << "\" " << mitk::InteractionEventConst::xmlCameraFocalPointY() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint()[1] << "\" " << mitk::InteractionEventConst::xmlCameraFocalPointZ() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint()[2] << "\" " << mitk::InteractionEventConst::xmlCameraPositionX() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetPosition()[0] << "\" " << mitk::InteractionEventConst::xmlCameraPositionY() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetPosition()[1] << "\" " << mitk::InteractionEventConst::xmlCameraPositionZ() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetPosition()[2] << "\" "; } stream << "/>\n"; } // stream << " \n"; } static void WriteEventXMLEventsOpen(std::ofstream &stream) { stream << " <" << mitk::InteractionEventConst::xmlTagEvents() << ">\n"; } static void WriteEventXMLEventsClose(std::ofstream &stream) { stream << " \n"; } static void WriteEventXMLInteractionsOpen(std::ofstream &stream) { stream << "<" << mitk::InteractionEventConst::xmlTagInteractions() << ">\n"; } static void WriteEventXMLInteractionsClose(std::ofstream &stream) { stream << ""; } static void WriteEventXMLClose(std::ofstream &stream) { WriteEventXMLEventsClose(stream); WriteEventXMLInteractionsClose(stream); } mitk::EventRecorder::EventRecorder() : m_Active(false) { } mitk::EventRecorder::~EventRecorder() { if (m_FileStream.is_open()) { m_FileStream.flush(); m_FileStream.close(); } } void mitk::EventRecorder::Notify(mitk::InteractionEvent *interactionEvent, bool /*isHandled*/) { if (m_FileStream.is_open()) m_FileStream << EventFactory::EventToXML(interactionEvent) << "\n"; } void mitk::EventRecorder::SetEventIgnoreList(std::vector list) { m_IgnoreList = list; } void mitk::EventRecorder::StartRecording() { if (m_FileName == "") { MITK_ERROR << "EventRecorder::StartRecording - Filename needs to be set first."; return; } if (m_FileStream.is_open()) { MITK_ERROR << "EventRecorder::StartRecording - Still recording. Stop recording before starting it again."; return; } m_FileStream.open(m_FileName.c_str(), std::ofstream::out); if (!m_FileStream.good()) { MITK_ERROR << "File " << m_FileName << " could not be opened!"; m_FileStream.close(); return; } m_Active = true; // write head and config // // // // // // ... // // WriteEventXMLHeader(m_FileStream); WriteEventXMLInteractionsOpen(m_FileStream); WriteEventXMLConfig(m_FileStream); WriteEventXMLEventsOpen(m_FileStream); } void mitk::EventRecorder::StopRecording() { if (m_FileStream.is_open()) { // write end tag // // WriteEventXMLClose(m_FileStream); m_FileStream.flush(); m_FileStream.close(); m_Active = false; } } diff --git a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp index 6439d9025e..6c75786446 100644 --- a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp +++ b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp @@ -1,789 +1,724 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkBaseRenderer.h" #include "mitkMapper.h" #include "mitkResliceMethodProperty.h" // Geometries -#include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" -// Controllers -#include "mitkCameraController.h" -#include "mitkCameraRotationController.h" -#include "mitkSliceNavigationController.h" - #include "mitkVtkLayerController.h" #include "mitkInteractionConst.h" #include "mitkProperties.h" #include "mitkWeakPointerProperty.h" // VTK #include #include -#include -#include #include #include #include namespace mitk { itkEventMacroDefinition(RendererResetEvent, itk::AnyEvent); } mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::baseRendererMap; mitk::BaseRenderer *mitk::BaseRenderer::GetInstance(vtkRenderWindow *renWin) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).first == renWin) return (*mapit).second; } return nullptr; } void mitk::BaseRenderer::AddInstance(vtkRenderWindow *renWin, BaseRenderer *baseRenderer) { if (renWin == nullptr || baseRenderer == nullptr) return; // ensure that no BaseRenderer is managed twice mitk::BaseRenderer::RemoveInstance(renWin); baseRendererMap.insert(BaseRendererMapType::value_type(renWin, baseRenderer)); } void mitk::BaseRenderer::RemoveInstance(vtkRenderWindow *renWin) { auto mapit = baseRendererMap.find(renWin); if (mapit != baseRendererMap.end()) baseRendererMap.erase(mapit); } mitk::BaseRenderer *mitk::BaseRenderer::GetByName(const std::string &name) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).second; } return nullptr; } vtkRenderWindow *mitk::BaseRenderer::GetRenderWindowByName(const std::string &name) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).first; } return nullptr; } mitk::BaseRenderer::BaseRenderer(const char *name, vtkRenderWindow *renWin) : m_RenderWindow(nullptr), m_VtkRenderer(nullptr), - m_MapperID(defaultMapper), + m_MapperID(StandardMapperSlot::Standard2D), m_DataStorage(nullptr), m_LastUpdateTime(0), m_CameraController(nullptr), m_SliceNavigationController(nullptr), m_CameraRotationController(nullptr), m_WorldTimeGeometry(nullptr), m_CurrentWorldGeometry(nullptr), m_CurrentWorldPlaneGeometry(nullptr), m_Slice(0), m_TimeStep(), m_CurrentWorldPlaneGeometryUpdateTime(), m_TimeStepUpdateTime(), m_KeepDisplayedRegion(true), m_CurrentWorldPlaneGeometryData(nullptr), m_CurrentWorldPlaneGeometryNode(nullptr), m_CurrentWorldPlaneGeometryTransformTime(0), m_Name(name), m_EmptyWorldGeometry(true), m_NumberOfVisibleLODEnabledMappers(0) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; if (name != nullptr) { m_Name = name; } else { m_Name = "unnamed renderer"; itkWarningMacro(<< "Created unnamed renderer. Bad for serialization. Please choose a name."); } if (renWin != nullptr) { m_RenderWindow = renWin; m_RenderWindow->Register(nullptr); } else { itkWarningMacro(<< "Created mitkBaseRenderer without vtkRenderWindow present."); } // instances.insert( this ); // adding this BaseRenderer to the List of all BaseRenderer m_BindDispatcherInteractor = new mitk::BindDispatcherInteractor(GetName()); WeakPointerProperty::Pointer rendererProp = WeakPointerProperty::New((itk::Object *)this); m_CurrentWorldPlaneGeometry = mitk::PlaneGeometry::New(); m_CurrentWorldPlaneGeometryData = mitk::PlaneGeometryData::New(); m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryNode = mitk::DataNode::New(); m_CurrentWorldPlaneGeometryNode->SetData(m_CurrentWorldPlaneGeometryData); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("renderer", rendererProp); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("layer", IntProperty::New(1000)); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(1)); m_CurrentWorldPlaneGeometryTransformTime = m_CurrentWorldPlaneGeometryNode->GetVtkTransform()->GetMTime(); mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::SliceNavigationController::New(); sliceNavigationController->SetRenderer(this); - sliceNavigationController->ConnectGeometrySliceEvent(this); + sliceNavigationController->ConnectGeometrySendEvent(this); sliceNavigationController->ConnectGeometryUpdateEvent(this); - sliceNavigationController->ConnectGeometryTimeEvent(this, false); + sliceNavigationController->ConnectGeometrySliceEvent(this); + sliceNavigationController->ConnectGeometryTimeEvent(this); m_SliceNavigationController = sliceNavigationController; m_CameraRotationController = mitk::CameraRotationController::New(); m_CameraRotationController->SetRenderWindow(m_RenderWindow); m_CameraRotationController->AcquireCamera(); m_CameraController = mitk::CameraController::New(); m_CameraController->SetRenderer(this); m_VtkRenderer = vtkRenderer::New(); m_VtkRenderer->SetMaximumNumberOfPeels(16); if (AntiAliasing::FastApproximate == RenderingManager::GetInstance()->GetAntiAliasing()) m_VtkRenderer->UseFXAAOn(); if (nullptr == mitk::VtkLayerController::GetInstance(m_RenderWindow)) mitk::VtkLayerController::AddInstance(m_RenderWindow, m_VtkRenderer); mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer); } mitk::BaseRenderer::~BaseRenderer() { if (m_VtkRenderer != nullptr) { m_VtkRenderer->Delete(); m_VtkRenderer = nullptr; } if (m_CameraController.IsNotNull()) m_CameraController->SetRenderer(nullptr); mitk::VtkLayerController::RemoveInstance(m_RenderWindow); RemoveAllLocalStorages(); m_DataStorage = nullptr; if (m_BindDispatcherInteractor != nullptr) { delete m_BindDispatcherInteractor; } if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); m_RenderWindow = nullptr; } } -void mitk::BaseRenderer::SetMapperID(MapperSlotId id) -{ - if (m_MapperID != id) - { - bool useDepthPeeling = Standard3D == id; - m_VtkRenderer->SetUseDepthPeeling(useDepthPeeling); - m_VtkRenderer->SetUseDepthPeelingForVolumes(useDepthPeeling); - - m_MapperID = id; - this->Modified(); - } -} - void mitk::BaseRenderer::RemoveAllLocalStorages() { this->InvokeEvent(RendererResetEvent()); std::list::iterator it; for (it = m_RegisteredLocalStorageHandlers.begin(); it != m_RegisteredLocalStorageHandlers.end(); ++it) (*it)->ClearLocalStorage(this, false); m_RegisteredLocalStorageHandlers.clear(); } void mitk::BaseRenderer::RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.push_back(lsh); } -mitk::Dispatcher::Pointer mitk::BaseRenderer::GetDispatcher() const -{ - return m_BindDispatcherInteractor->GetDispatcher(); -} - void mitk::BaseRenderer::UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.remove(lsh); } void mitk::BaseRenderer::SetDataStorage(DataStorage *storage) { if (storage != m_DataStorage && storage != nullptr) { m_DataStorage = storage; m_BindDispatcherInteractor->SetDataStorage(m_DataStorage); this->Modified(); } } -const mitk::BaseRenderer::MapperSlotId mitk::BaseRenderer::defaultMapper = 1; - -void mitk::BaseRenderer::Paint() -{ -} - -void mitk::BaseRenderer::Initialize() +mitk::Dispatcher::Pointer mitk::BaseRenderer::GetDispatcher() const { + return m_BindDispatcherInteractor->GetDispatcher(); } void mitk::BaseRenderer::Resize(int w, int h) { - this->m_RenderWindow->SetSize(w, h); + m_RenderWindow->SetSize(w, h); } void mitk::BaseRenderer::InitRenderer(vtkRenderWindow *renderwindow) { if (m_RenderWindow != renderwindow) { if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); } m_RenderWindow = renderwindow; if (m_RenderWindow != nullptr) { m_RenderWindow->Register(nullptr); } } RemoveAllLocalStorages(); if (m_CameraController.IsNotNull()) { m_CameraController->SetRenderer(this); } } void mitk::BaseRenderer::InitSize(int w, int h) { - this->m_RenderWindow->SetSize(w, h); + m_RenderWindow->SetSize(w, h); +} + +void mitk::BaseRenderer::SetWorldTimeGeometry(const mitk::TimeGeometry* geometry) +{ + if (m_WorldTimeGeometry == geometry) + { + return; + } + + m_WorldTimeGeometry = geometry; + + this->UpdateCurrentGeometries(); } void mitk::BaseRenderer::SetSlice(unsigned int slice) { - if (m_Slice != slice) + if (m_Slice == slice) { - m_Slice = slice; - if (m_WorldTimeGeometry.IsNotNull()) - { - // get world geometry which may be rotated, for the current time step - SlicedGeometry3D *slicedWorldGeometry = - dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); - if (slicedWorldGeometry != nullptr) - { - // if slice position is part of the world geometry... - if (m_Slice >= slicedWorldGeometry->GetSlices()) - // set the current worldplanegeometry as the selected 2D slice of the world geometry - m_Slice = slicedWorldGeometry->GetSlices() - 1; - SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); - SetCurrentWorldGeometry(slicedWorldGeometry); - } - } - else - Modified(); + return; } + + m_Slice = slice; + + this->UpdateCurrentGeometries(); } void mitk::BaseRenderer::SetTimeStep(unsigned int timeStep) { - if (m_TimeStep != timeStep) + if (m_TimeStep == timeStep) { - m_TimeStep = timeStep; - m_TimeStepUpdateTime.Modified(); - - if (m_WorldTimeGeometry.IsNotNull()) - { - if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) - m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; - SlicedGeometry3D *slicedWorldGeometry = - dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); - if (slicedWorldGeometry != nullptr) - { - SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); - SetCurrentWorldGeometry(slicedWorldGeometry); - } - } - else - Modified(); + return; } + + m_TimeStep = timeStep; + m_TimeStepUpdateTime.Modified(); + + this->UpdateCurrentGeometries(); } -mitk::TimeStepType mitk::BaseRenderer::GetTimeStep(const mitk::BaseData *data) const +mitk::TimeStepType mitk::BaseRenderer::GetTimeStep(const mitk::BaseData* data) const { if ((data == nullptr) || (data->IsInitialized() == false)) { return -1; } return data->GetTimeGeometry()->TimePointToTimeStep(GetTime()); } mitk::ScalarType mitk::BaseRenderer::GetTime() const { if (m_WorldTimeGeometry.IsNull()) { return 0; } else { ScalarType timeInMS = m_WorldTimeGeometry->TimeStepToTimePoint(GetTimeStep()); if (timeInMS == itk::NumericTraits::NonpositiveMin()) return 0; else return timeInMS; } } -void mitk::BaseRenderer::SetWorldTimeGeometry(const mitk::TimeGeometry *geometry) +void mitk::BaseRenderer::SetGeometry(const itk::EventObject& geometrySendEvent) { - assert(geometry != nullptr); + const auto* sendEvent = dynamic_cast(&geometrySendEvent); - itkDebugMacro("setting WorldTimeGeometry to " << geometry); - if (m_WorldTimeGeometry != geometry) + if (nullptr == sendEvent) { - if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() == 0) - return; - - m_WorldTimeGeometry = geometry; - itkDebugMacro("setting WorldTimeGeometry to " << m_WorldTimeGeometry); - - if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) - m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; - - BaseGeometry *geometry3d; - geometry3d = m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep); - SetWorldGeometry3D(geometry3d); + return; } + + SetWorldTimeGeometry(sendEvent->GetTimeGeometry()); } -void mitk::BaseRenderer::SetWorldGeometry3D(const mitk::BaseGeometry *geometry) +void mitk::BaseRenderer::UpdateGeometry(const itk::EventObject& geometryUpdateEvent) { - itkDebugMacro("setting WorldGeometry3D to " << geometry); + const auto* updateEvent = dynamic_cast(&geometryUpdateEvent); - if (geometry->GetBoundingBox()->GetDiagonalLength2() == 0) + if (nullptr == updateEvent) + { return; - const SlicedGeometry3D *slicedWorldGeometry; - slicedWorldGeometry = dynamic_cast(geometry); + } - PlaneGeometry::ConstPointer geometry2d; - if (slicedWorldGeometry != nullptr) + if (m_CurrentWorldGeometry.IsNull()) { - if (m_Slice >= slicedWorldGeometry->GetSlices() && (m_Slice != 0)) - m_Slice = slicedWorldGeometry->GetSlices() - 1; - geometry2d = slicedWorldGeometry->GetPlaneGeometry(m_Slice); - if (geometry2d.IsNull()) - { - PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); - plane->InitializeStandardPlane(slicedWorldGeometry); - geometry2d = plane; - } - SetCurrentWorldGeometry(slicedWorldGeometry); + return; } - else + + const auto* slicedWorldGeometry = dynamic_cast(m_CurrentWorldGeometry.GetPointer()); + if (slicedWorldGeometry) { - geometry2d = dynamic_cast(geometry); - if (geometry2d.IsNull()) - { - PlaneGeometry::Pointer plane = PlaneGeometry::New(); - plane->InitializeStandardPlane(geometry); - geometry2d = plane; - } - SetCurrentWorldGeometry(geometry); - } - SetCurrentWorldPlaneGeometry(geometry2d); // calls Modified() + PlaneGeometry* geometry2D = slicedWorldGeometry->GetPlaneGeometry(m_Slice); - if (m_CurrentWorldPlaneGeometry.IsNull()) - itkWarningMacro("m_CurrentWorldPlaneGeometry is nullptr"); + SetCurrentWorldPlaneGeometry(geometry2D); // calls Modified() + } } -void mitk::BaseRenderer::SetCurrentWorldPlaneGeometry(const mitk::PlaneGeometry *geometry2d) +void mitk::BaseRenderer::SetGeometrySlice(const itk::EventObject& geometrySliceEvent) { - if (m_CurrentWorldPlaneGeometry != geometry2d) + const auto* sliceEvent = dynamic_cast(&geometrySliceEvent); + + if (nullptr == sliceEvent) { - m_CurrentWorldPlaneGeometry = geometry2d->Clone(); - m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); - m_CurrentWorldPlaneGeometryUpdateTime.Modified(); - Modified(); + return; } -} -void mitk::BaseRenderer::SendUpdateSlice() -{ - m_CurrentWorldPlaneGeometryUpdateTime.Modified(); + this->SetSlice(sliceEvent->GetPos()); } -int *mitk::BaseRenderer::GetSize() const +void mitk::BaseRenderer::SetGeometryTime(const itk::EventObject& geometryTimeEvent) { - return this->m_RenderWindow->GetSize(); -} + const auto* timeEvent = dynamic_cast(&geometryTimeEvent); -int *mitk::BaseRenderer::GetViewportSize() const -{ - return this->m_VtkRenderer->GetSize(); -} - -void mitk::BaseRenderer::SetCurrentWorldGeometry(const mitk::BaseGeometry *geometry) -{ - m_CurrentWorldGeometry = geometry; - if (geometry == nullptr) + if (nullptr == timeEvent) { - m_Bounds[0] = 0; - m_Bounds[1] = 0; - m_Bounds[2] = 0; - m_Bounds[3] = 0; - m_Bounds[4] = 0; - m_Bounds[5] = 0; - m_EmptyWorldGeometry = true; return; } - BoundingBox::Pointer boundingBox = m_CurrentWorldGeometry->CalculateBoundingBoxRelativeToTransform(nullptr); - const BoundingBox::BoundsArrayType &worldBounds = boundingBox->GetBounds(); - m_Bounds[0] = worldBounds[0]; - m_Bounds[1] = worldBounds[1]; - m_Bounds[2] = worldBounds[2]; - m_Bounds[3] = worldBounds[3]; - m_Bounds[4] = worldBounds[4]; - m_Bounds[5] = worldBounds[5]; - if (boundingBox->GetDiagonalLength2() <= mitk::eps) - m_EmptyWorldGeometry = true; - else - m_EmptyWorldGeometry = false; + + this->SetTimeStep(timeEvent->GetPos()); } -void mitk::BaseRenderer::SetGeometry(const itk::EventObject &geometrySendEvent) +void mitk::BaseRenderer::SendUpdateSlice() { - const auto *sendEvent = - dynamic_cast(&geometrySendEvent); - - assert(sendEvent != nullptr); - SetWorldTimeGeometry(sendEvent->GetTimeGeometry()); + m_CurrentWorldPlaneGeometryUpdateTime.Modified(); } -void mitk::BaseRenderer::UpdateGeometry(const itk::EventObject &geometryUpdateEvent) +void mitk::BaseRenderer::SetMapperID(MapperSlotId id) { - const auto *updateEvent = - dynamic_cast(&geometryUpdateEvent); - - if (updateEvent == nullptr) - return; - - if (m_CurrentWorldGeometry.IsNotNull()) + if (m_MapperID != id) { - auto *slicedWorldGeometry = dynamic_cast(m_CurrentWorldGeometry.GetPointer()); - if (slicedWorldGeometry) - { - PlaneGeometry *geometry2D = slicedWorldGeometry->GetPlaneGeometry(m_Slice); + bool useDepthPeeling = Standard3D == id; + m_VtkRenderer->SetUseDepthPeeling(useDepthPeeling); + m_VtkRenderer->SetUseDepthPeelingForVolumes(useDepthPeeling); - SetCurrentWorldPlaneGeometry(geometry2D); // calls Modified() - } + m_MapperID = id; + this->Modified(); } } -void mitk::BaseRenderer::SetGeometrySlice(const itk::EventObject &geometrySliceEvent) +int* mitk::BaseRenderer::GetSize() const { - const auto *sliceEvent = - dynamic_cast(&geometrySliceEvent); - - assert(sliceEvent != nullptr); - SetSlice(sliceEvent->GetPos()); + return m_RenderWindow->GetSize(); } -void mitk::BaseRenderer::SetGeometryTime(const itk::EventObject &geometryTimeEvent) +int* mitk::BaseRenderer::GetViewportSize() const { - const auto *timeEvent = - dynamic_cast(&geometryTimeEvent); - - assert(timeEvent != nullptr); - SetTimeStep(timeEvent->GetPos()); + return m_VtkRenderer->GetSize(); } -const double *mitk::BaseRenderer::GetBounds() const +const double* mitk::BaseRenderer::GetBounds() const { return m_Bounds; } -void mitk::BaseRenderer::DrawOverlayMouse(mitk::Point2D &itkNotUsed(p2d)) -{ - MITK_INFO << "BaseRenderer::DrawOverlayMouse()- should be inconcret implementation OpenGLRenderer." << std::endl; -} - void mitk::BaseRenderer::RequestUpdate() { SetConstrainZoomingAndPanning(true); - RenderingManager::GetInstance()->RequestUpdate(this->m_RenderWindow); + RenderingManager::GetInstance()->RequestUpdate(m_RenderWindow); } void mitk::BaseRenderer::ForceImmediateUpdate() { - RenderingManager::GetInstance()->ForceImmediateUpdate(this->m_RenderWindow); + RenderingManager::GetInstance()->ForceImmediateUpdate(m_RenderWindow); } unsigned int mitk::BaseRenderer::GetNumberOfVisibleLODEnabledMappers() const { return m_NumberOfVisibleLODEnabledMappers; } -/*! - Sets the new Navigation controller - */ void mitk::BaseRenderer::SetSliceNavigationController(mitk::SliceNavigationController *SlicenavigationController) { if (SlicenavigationController == nullptr) return; // copy worldgeometry SlicenavigationController->SetInputWorldTimeGeometry(SlicenavigationController->GetCreatedWorldGeometry()); SlicenavigationController->Update(); // set new m_SliceNavigationController = SlicenavigationController; m_SliceNavigationController->SetRenderer(this); if (m_SliceNavigationController.IsNotNull()) { - m_SliceNavigationController->ConnectGeometrySliceEvent(this); + m_SliceNavigationController->ConnectGeometrySendEvent(this); m_SliceNavigationController->ConnectGeometryUpdateEvent(this); - m_SliceNavigationController->ConnectGeometryTimeEvent(this, false); + m_SliceNavigationController->ConnectGeometrySliceEvent(this); + m_SliceNavigationController->ConnectGeometryTimeEvent(this); } } -void mitk::BaseRenderer::DisplayToWorld(const Point2D &displayPoint, Point3D &worldIndex) const +void mitk::BaseRenderer::DisplayToWorld(const Point2D& displayPoint, Point3D& worldIndex) const { if (m_MapperID == BaseRenderer::Standard2D) { - double display[3], *world; + double display[3], * world; // For the right z-position in display coordinates, take the focal point, convert it to display and use it for // correct depth. - double *displayCoord; + double* displayCoord; double cameraFP[4]; // Get camera focal point and position. Convert to display (screen) // coordinates. We need a depth value for z-buffer. this->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint(cameraFP); cameraFP[3] = 0.0; this->GetVtkRenderer()->SetWorldPoint(cameraFP[0], cameraFP[1], cameraFP[2], cameraFP[3]); this->GetVtkRenderer()->WorldToDisplay(); displayCoord = this->GetVtkRenderer()->GetDisplayPoint(); // now convert the display point to world coordinates display[0] = displayPoint[0]; display[1] = displayPoint[1]; display[2] = displayCoord[2]; this->GetVtkRenderer()->SetDisplayPoint(display); this->GetVtkRenderer()->DisplayToWorld(); world = this->GetVtkRenderer()->GetWorldPoint(); for (int i = 0; i < 3; i++) { worldIndex[i] = world[i] / world[3]; } } else if (m_MapperID == BaseRenderer::Standard3D) { - PickWorldPoint( - displayPoint, - worldIndex); // Seems to be the same code as above, but subclasses may contain different implementations. + // Seems to be the same code as above, but subclasses may contain different implementations. + PickWorldPoint(displayPoint, worldIndex); } - return; } void mitk::BaseRenderer::DisplayToPlane(const Point2D &displayPoint, Point2D &planePointInMM) const { if (m_MapperID == BaseRenderer::Standard2D) { Point3D worldPoint; this->DisplayToWorld(displayPoint, worldPoint); - this->m_CurrentWorldPlaneGeometry->Map(worldPoint, planePointInMM); + m_CurrentWorldPlaneGeometry->Map(worldPoint, planePointInMM); } else if (m_MapperID == BaseRenderer::Standard3D) { MITK_WARN << "No conversion possible with 3D mapper."; return; } return; } void mitk::BaseRenderer::WorldToDisplay(const Point3D &worldIndex, Point2D &displayPoint) const { double world[4], *display; world[0] = worldIndex[0]; world[1] = worldIndex[1]; world[2] = worldIndex[2]; world[3] = 1.0; this->GetVtkRenderer()->SetWorldPoint(world); this->GetVtkRenderer()->WorldToDisplay(); display = this->GetVtkRenderer()->GetDisplayPoint(); displayPoint[0] = display[0]; displayPoint[1] = display[1]; return; } void mitk::BaseRenderer::WorldToView(const mitk::Point3D &worldIndex, mitk::Point2D &viewPoint) const { double world[4], *view; world[0] = worldIndex[0]; world[1] = worldIndex[1]; world[2] = worldIndex[2]; world[3] = 1.0; this->GetVtkRenderer()->SetWorldPoint(world); this->GetVtkRenderer()->WorldToView(); view = this->GetVtkRenderer()->GetViewPoint(); this->GetVtkRenderer()->ViewToNormalizedViewport(view[0], view[1], view[2]); viewPoint[0] = view[0] * this->GetViewportSize()[0]; viewPoint[1] = view[1] * this->GetViewportSize()[1]; return; } void mitk::BaseRenderer::PlaneToDisplay(const Point2D &planePointInMM, Point2D &displayPoint) const { Point3D worldPoint; - this->m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); + m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); this->WorldToDisplay(worldPoint, displayPoint); return; } void mitk::BaseRenderer::PlaneToView(const Point2D &planePointInMM, Point2D &viewPoint) const { Point3D worldPoint; - this->m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); + m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); this->WorldToView(worldPoint,viewPoint); return; } double mitk::BaseRenderer::GetScaleFactorMMPerDisplayUnit() const { if (this->GetMapperID() == BaseRenderer::Standard2D) { // GetParallelScale returns half of the height of the render window in mm. // Divided by the half size of the Display size in pixel givest the mm per pixel. return this->GetVtkRenderer()->GetActiveCamera()->GetParallelScale() * 2.0 / GetViewportSize()[1]; } else return 1.0; } mitk::Point2D mitk::BaseRenderer::GetDisplaySizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetSizeX() * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetSizeY() * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetViewportSizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetViewportSize()[0] * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetViewportSize()[1] * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetOriginInMM() const { Point2D originPx; originPx[0] = m_VtkRenderer->GetOrigin()[0]; originPx[1] = m_VtkRenderer->GetOrigin()[1]; Point2D displayGeometryOriginInMM; DisplayToPlane(originPx, displayGeometryOriginInMM); // top left of the render window (Origin) return displayGeometryOriginInMM; } void mitk::BaseRenderer::SetConstrainZoomingAndPanning(bool constrain) { m_ConstrainZoomingAndPanning = constrain; if (m_ConstrainZoomingAndPanning) { this->GetCameraController()->AdjustCameraToPlane(); } } -mitk::Point3D mitk::BaseRenderer::Map2DRendererPositionTo3DWorldPosition(const Point2D &mousePosition) const +void mitk::BaseRenderer::UpdateCurrentGeometries() { - // DEPRECATED: Map2DRendererPositionTo3DWorldPosition is deprecated. use DisplayToWorldInstead - Point3D position; - DisplayToWorld(mousePosition, position); - return position; + if (m_WorldTimeGeometry.IsNull()) + { + // simply mark the base renderer as modified + Modified(); + } + + if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) + { + m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; + } + + auto slicedWorldGeometry = + dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); + if (slicedWorldGeometry != nullptr) + { + if (m_Slice >= slicedWorldGeometry->GetSlices()) + { + m_Slice = slicedWorldGeometry->GetSlices() - 1; + } + + SetCurrentWorldGeometry(slicedWorldGeometry); + SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); + } +} + +void mitk::BaseRenderer::SetCurrentWorldPlaneGeometry(const mitk::PlaneGeometry* geometry2d) +{ + if (m_CurrentWorldPlaneGeometry != geometry2d) + { + m_CurrentWorldPlaneGeometry = geometry2d->Clone(); + m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); + m_CurrentWorldPlaneGeometryUpdateTime.Modified(); + Modified(); + } +} + +void mitk::BaseRenderer::SetCurrentWorldGeometry(const mitk::BaseGeometry* geometry) +{ + m_CurrentWorldGeometry = geometry; + if (geometry == nullptr) + { + m_Bounds[0] = 0; + m_Bounds[1] = 0; + m_Bounds[2] = 0; + m_Bounds[3] = 0; + m_Bounds[4] = 0; + m_Bounds[5] = 0; + m_EmptyWorldGeometry = true; + return; + } + BoundingBox::Pointer boundingBox = m_CurrentWorldGeometry->CalculateBoundingBoxRelativeToTransform(nullptr); + const BoundingBox::BoundsArrayType& worldBounds = boundingBox->GetBounds(); + m_Bounds[0] = worldBounds[0]; + m_Bounds[1] = worldBounds[1]; + m_Bounds[2] = worldBounds[2]; + m_Bounds[3] = worldBounds[3]; + m_Bounds[4] = worldBounds[4]; + m_Bounds[5] = worldBounds[5]; + if (boundingBox->GetDiagonalLength2() <= mitk::eps) + { + m_EmptyWorldGeometry = true; + } + else + { + m_EmptyWorldGeometry = false; + } } void mitk::BaseRenderer::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << " MapperID: " << m_MapperID << std::endl; os << indent << " Slice: " << m_Slice << std::endl; os << indent << " TimeStep: " << m_TimeStep << std::endl; os << indent << " CurrentWorldPlaneGeometry: "; if (m_CurrentWorldPlaneGeometry.IsNull()) os << "nullptr" << std::endl; else m_CurrentWorldPlaneGeometry->Print(os, indent); os << indent << " CurrentWorldPlaneGeometryUpdateTime: " << m_CurrentWorldPlaneGeometryUpdateTime << std::endl; os << indent << " CurrentWorldPlaneGeometryTransformTime: " << m_CurrentWorldPlaneGeometryTransformTime << std::endl; Superclass::PrintSelf(os, indent); } diff --git a/Modules/Core/test/mitkSliceNavigationControllerTest.cpp b/Modules/Core/test/mitkSliceNavigationControllerTest.cpp index 232eeb6a33..3cae978167 100644 --- a/Modules/Core/test/mitkSliceNavigationControllerTest.cpp +++ b/Modules/Core/test/mitkSliceNavigationControllerTest.cpp @@ -1,205 +1,196 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include // T22254 class mitkSliceNavigationControllerTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSliceNavigationControllerTestSuite); CPPUNIT_TEST(validateAxialViewDirection); CPPUNIT_TEST(validateCoronalViewDirection); CPPUNIT_TEST(validateSagittalViewDirection); CPPUNIT_TEST(GetSelectedTimePoint); CPPUNIT_TEST_SUITE_END(); mitk::Geometry3D::Pointer m_Geometry3D; mitk::ArbitraryTimeGeometry::Pointer m_TimeGeometry; public: void setUp() override { mitk::Point3D origin; mitk::FillVector3D(origin, 10.0, 20.0, 30.0); mitk::Vector3D firstAxisVector; mitk::FillVector3D(firstAxisVector, 100.0, 0.0, 0.0); mitk::Vector3D secondAxisVector; mitk::FillVector3D(secondAxisVector, 0.0, 50.0, 0.0); mitk::Vector3D spacing; mitk::FillVector3D(spacing, 1.0, 1.0, 2.0); auto planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializeStandardPlane(firstAxisVector, secondAxisVector, &spacing); planeGeometry->SetOrigin(origin); unsigned int numberOfSlices = 100U; auto slicedGeometry3D = mitk::SlicedGeometry3D::New(); slicedGeometry3D->InitializeEvenlySpaced(planeGeometry, numberOfSlices); m_Geometry3D = mitk::Geometry3D::New(); m_Geometry3D->SetBounds(slicedGeometry3D->GetBounds()); m_Geometry3D->SetIndexToWorldTransform(slicedGeometry3D->GetIndexToWorldTransform()); m_TimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_TimeGeometry->AppendNewTimeStepClone(m_Geometry3D, 0.5, 10.); m_TimeGeometry->AppendNewTimeStepClone(m_Geometry3D, 10., 30.); m_TimeGeometry->AppendNewTimeStepClone(m_Geometry3D, 30., 50.); m_TimeGeometry->AppendNewTimeStepClone(m_Geometry3D, 50., 60.); m_TimeGeometry->Update(); } void tearDown() override { } void validateAxialViewDirection() { auto sliceNavigationController = mitk::SliceNavigationController::New(); - sliceNavigationController->SetInputWorldGeometry3D(m_Geometry3D); + sliceNavigationController->SetInputWorldTimeGeometry(m_TimeGeometry); sliceNavigationController->SetViewDirection(mitk::SliceNavigationController::Axial); sliceNavigationController->Update(); mitk::Point3D origin; mitk::FillVector3D(origin, 10.0, 70.0, 229.0); mitk::Vector3D firstAxisVector; mitk::FillVector3D(firstAxisVector, 100.0, 0.0, 0.0); mitk::Vector3D secondAxisVector; mitk::FillVector3D(secondAxisVector, 0.0, -50.0, 0.0); mitk::Vector3D thirdAxisVector; mitk::FillVector3D(thirdAxisVector, 0.0, 0.0, -200.0); std::cout << "Axial view direction" << std::endl; CPPUNIT_ASSERT(this->validateGeometry(sliceNavigationController->GetCurrentGeometry3D(), origin, firstAxisVector, secondAxisVector, thirdAxisVector)); } void validateCoronalViewDirection() { auto sliceNavigationController = mitk::SliceNavigationController::New(); - sliceNavigationController->SetInputWorldGeometry3D(m_Geometry3D); + sliceNavigationController->SetInputWorldTimeGeometry(m_TimeGeometry); sliceNavigationController->SetViewDirection(mitk::SliceNavigationController::Coronal); sliceNavigationController->Update(); mitk::Point3D origin; mitk::FillVector3D(origin, 10.0, 69.5, 30.0); mitk::Vector3D firstAxisVector; mitk::FillVector3D(firstAxisVector, 100.0, 0.0, 0.0); mitk::Vector3D secondAxisVector; mitk::FillVector3D(secondAxisVector, 0.0, 0.0, 200.0); mitk::Vector3D thirdAxisVector; mitk::FillVector3D(thirdAxisVector, 0.0, -50.0, 0.0); std::cout << "Coronal view direction" << std::endl; CPPUNIT_ASSERT(this->validateGeometry(sliceNavigationController->GetCurrentGeometry3D(), origin, firstAxisVector, secondAxisVector, thirdAxisVector)); } void validateSagittalViewDirection() { auto sliceNavigationController = mitk::SliceNavigationController::New(); - sliceNavigationController->SetInputWorldGeometry3D(m_Geometry3D); + sliceNavigationController->SetInputWorldTimeGeometry(m_TimeGeometry); sliceNavigationController->SetViewDirection(mitk::SliceNavigationController::Sagittal); sliceNavigationController->Update(); mitk::Point3D origin; mitk::FillVector3D(origin, 10.5, 20.0, 30.0); mitk::Vector3D firstAxisVector; mitk::FillVector3D(firstAxisVector, 0.0, 50.0, 0.0); mitk::Vector3D secondAxisVector; mitk::FillVector3D(secondAxisVector, 0.0, 0.0, 200.0); mitk::Vector3D thirdAxisVector; mitk::FillVector3D(thirdAxisVector, 100.0, 0.0, 0.0); std::cout << "Sagittal view direction" << std::endl; CPPUNIT_ASSERT(this->validateGeometry(sliceNavigationController->GetCurrentGeometry3D(), origin, firstAxisVector, secondAxisVector, thirdAxisVector)); } void GetSelectedTimePoint() { auto sliceNavigationController = mitk::SliceNavigationController::New(); CPPUNIT_ASSERT(sliceNavigationController->GetSelectedTimePoint() == 0.); sliceNavigationController->SetInputWorldTimeGeometry(m_TimeGeometry); sliceNavigationController->SetViewDirection(mitk::SliceNavigationController::Sagittal); sliceNavigationController->Update(); CPPUNIT_ASSERT(sliceNavigationController->GetSelectedTimeStep() == 0); CPPUNIT_ASSERT(sliceNavigationController->GetSelectedTimePoint() == 0.5); sliceNavigationController->GetTime()->SetPos(2); CPPUNIT_ASSERT(sliceNavigationController->GetSelectedTimeStep() == 2); CPPUNIT_ASSERT(sliceNavigationController->GetSelectedTimePoint() == 30.0); - - auto sliceNavigationController2 = mitk::SliceNavigationController::New(); - - sliceNavigationController2->SetInputWorldGeometry3D(m_Geometry3D); - sliceNavigationController2->SetViewDirection(mitk::SliceNavigationController::Sagittal); - sliceNavigationController2->Update(); - - CPPUNIT_ASSERT(sliceNavigationController2->GetSelectedTimeStep() == 0); - CPPUNIT_ASSERT(sliceNavigationController2->GetSelectedTimePoint() == 0.0); } private: bool validateGeometry(mitk::BaseGeometry::ConstPointer geometry, const mitk::Point3D &origin, const mitk::Vector3D &firstAxisVector, const mitk::Vector3D &secondAxisVector, const mitk::Vector3D &thirdAxisVector) { bool result = true; std::cout << " Origin" << std::endl; if (!mitk::Equal(geometry->GetOrigin(), origin, mitk::eps, true)) result = false; std::cout << " First axis vector" << std::endl; if (!mitk::Equal(geometry->GetAxisVector(0), firstAxisVector, mitk::eps, true)) result = false; std::cout << " Second axis vector" << std::endl; if (!mitk::Equal(geometry->GetAxisVector(1), secondAxisVector, mitk::eps, true)) result = false; std::cout << " Third axis vector" << std::endl; if (!mitk::Equal(geometry->GetAxisVector(2), thirdAxisVector, mitk::eps, true)) result = false; return result; } }; MITK_TEST_SUITE_REGISTRATION(mitkSliceNavigationController) diff --git a/Modules/DICOM/CMakeLists.txt b/Modules/DICOM/CMakeLists.txt index 36966c5338..7a333dad07 100644 --- a/Modules/DICOM/CMakeLists.txt +++ b/Modules/DICOM/CMakeLists.txt @@ -1,9 +1,11 @@ MITK_CREATE_MODULE( DEPENDS MitkCore PACKAGE_DEPENDS - PUBLIC GDCM|MSFF tinyxml2 - PRIVATE DCMTK ITK|IOGDCM + PUBLIC tinyxml2 + PRIVATE DCMTK|dcmdata+ofstd ITK|IOGDCM + TARGET_DEPENDS + PUBLIC gdcmMSFF ) add_subdirectory(test) add_subdirectory(autoload/DICOMImageIO) diff --git a/Modules/DICOMPM/CMakeLists.txt b/Modules/DICOMPM/CMakeLists.txt index aa17f28473..42f6d9fda7 100644 --- a/Modules/DICOMPM/CMakeLists.txt +++ b/Modules/DICOMPM/CMakeLists.txt @@ -1,6 +1,4 @@ MITK_CREATE_MODULE(DICOMPM DEPENDS MitkCore MitkDICOM MitkModelFit MitkDICOMQI - PACKAGE_DEPENDS - PRIVATE DCMTK ) add_subdirectory(autoload/DICOMPMIO) diff --git a/Modules/DICOMPM/autoload/DICOMPMIO/CMakeLists.txt b/Modules/DICOMPM/autoload/DICOMPMIO/CMakeLists.txt index 3dd397dbaf..361ba5ce5f 100644 --- a/Modules/DICOMPM/autoload/DICOMPMIO/CMakeLists.txt +++ b/Modules/DICOMPM/autoload/DICOMPMIO/CMakeLists.txt @@ -1,6 +1,6 @@ MITK_CREATE_MODULE(DICOMPMIO DEPENDS MitkModelFit MitkDICOMQI MitkDICOMPM PACKAGE_DEPENDS - PRIVATE DCMQI DCMTK + PRIVATE DCMQI DCMTK|dcmpmap+dcmsr AUTOLOAD_WITH MitkCore ) diff --git a/Modules/DICOMQI/CMakeLists.txt b/Modules/DICOMQI/CMakeLists.txt index eee1639517..d0e9e8421c 100644 --- a/Modules/DICOMQI/CMakeLists.txt +++ b/Modules/DICOMQI/CMakeLists.txt @@ -1,5 +1,3 @@ MITK_CREATE_MODULE( DEPENDS MitkCore MitkDICOM - PACKAGE_DEPENDS - PRIVATE DCMTK ) diff --git a/Modules/DICOMTesting/CMakeLists.txt b/Modules/DICOMTesting/CMakeLists.txt index 857f5a39a1..8bc304edae 100644 --- a/Modules/DICOMTesting/CMakeLists.txt +++ b/Modules/DICOMTesting/CMakeLists.txt @@ -1,43 +1,41 @@ if(BUILD_TESTING) -if(GDCM_DIR) # clear variables from prior files.cmake # Else CMake would use the content of these variables and would try to create tests (which are not present in DICOMTesting). set(MODULE_TESTS) set(MODULE_IMAGE_TESTS) set(MODULE_SURFACE_TESTS) set(MODULE_TESTIMAGE) set(MODULE_TESTSURFACE) set(MODULE_CUSTOM_TESTS) set(H_FILES) set(CPP_FILES) # now create a new module only for testing purposes MITK_CREATE_MODULE( DEPENDS MitkDICOM PACKAGE_DEPENDS - PRIVATE GDCM DCMTK ITK|IOGDCM + PRIVATE DCMTK|config ITK|IOGDCM ) mitk_check_module_dependencies(MODULES MitkDICOMTesting MISSING_DEPENDENCIES_VAR _missing_deps) if(_missing_deps) message(STATUS "mitkDICOMTesting module helper applications won't be built. Missing: ${_missing_deps}") else(_missing_deps) # dumps out image information add_executable(DumpDICOMMitkImage src/DumpDICOMMitkImage.cpp) mitk_use_modules(TARGET DumpDICOMMitkImage MODULES MitkDICOMTesting) # compares dumped out image information against reference dump add_executable(VerifyDICOMMitkImageDump src/VerifyDICOMMitkImageDump.cpp) mitk_use_modules(TARGET VerifyDICOMMitkImageDump MODULES MitkDICOMTesting) set_property(TARGET DumpDICOMMitkImage VerifyDICOMMitkImageDump PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules/Tests") add_subdirectory(test) endif() endif() -endif() diff --git a/Modules/DICOMUI/CMakeLists.txt b/Modules/DICOMUI/CMakeLists.txt index 50dfc8a723..ad06378441 100644 --- a/Modules/DICOMUI/CMakeLists.txt +++ b/Modules/DICOMUI/CMakeLists.txt @@ -1,8 +1,7 @@ include_directories(${CTK_INCLUDE_DIRS}) MITK_CREATE_MODULE( DEPENDS MitkCore PACKAGE_DEPENDS - PUBLIC CTK|CTKDICOMWidgets Qt5|Sql - PRIVATE Qt5|OpenGL+Widgets+Xml + PUBLIC CTK|CTKDICOMWidgets ) diff --git a/Modules/DICOMUI/include/QmitkDicomExternalDataWidget.h b/Modules/DICOMUI/include/QmitkDicomExternalDataWidget.h index afc501c30c..8c80a4b33f 100644 --- a/Modules/DICOMUI/include/QmitkDicomExternalDataWidget.h +++ b/Modules/DICOMUI/include/QmitkDicomExternalDataWidget.h @@ -1,119 +1,117 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkDicomExternalDataWidget_h #define QmitkDicomExternalDataWidget_h #include "ui_QmitkDicomExternalDataWidgetControls.h" #include -// include ctk -#include -#include - // include QT #include #include #include #include #include #include #include class ctkFileDialog; +class ctkDICOMDatabase; +class ctkDICOMIndexer; /** * \brief QmitkDicomExternalDataWidget is a QWidget providing functionality for dicom import. * * \ingroup Functionalities */ class MITKDICOMUI_EXPORT QmitkDicomExternalDataWidget : public QWidget { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string Widget_ID; /** * \brief QmitkDicomExternalDataWidget(QWidget *parent) constructor. * * \param parent is a pointer to the parent widget */ QmitkDicomExternalDataWidget(QWidget *parent); /** * \brief QmitkDicomExternalDataWidget destructor. */ ~QmitkDicomExternalDataWidget() override; /** * \brief CreateQtPartControl(QWidget *parent) sets the view objects from ui_QmitkDicomExternalDataWidgetControls.h. * * \param parent is a pointer to the parent widget */ virtual void CreateQtPartControl(QWidget *parent); /** * \brief Initializes the widget. This method has to be called before widget can start. */ void Initialize(); signals: /// @brief emitted when import into database is finished. void SignalStartDicomImport(const QStringList &); /// @brief emitted when view button is clicked. void SignalDicomToDataManager(QHash); public slots: /// @brief Called when download button was clicked. void OnDownloadButtonClicked(); /// @brief Called when view button was clicked. void OnViewButtonClicked(); /// @brief Called when adding a dicom directory. Starts a thread adding the directory. void OnStartDicomImport(const QString &); void OnSeriesSelectionChanged(const QStringList &s); protected slots: void OnProgressStep(const QString&); void OnProgressDetail(const QString&); protected: /// \brief Get the list of filepath from current selected index in TreeView. All file paths referring to the index /// will be returned. QStringList GetFileNamesFromIndex(); /// \brief SetupImportDialog Sets up import dialog. void SetupImportDialog(); void SetupProgressDialog(); ctkDICOMDatabase *m_ExternalDatabase; ctkDICOMIndexer *m_ExternalIndexer; ctkFileDialog *m_ImportDialog; QProgressDialog *m_ProgressDialog; QString m_LastImportDirectory; QString m_ProgressStep; Ui::QmitkDicomExternalDataWidgetControls *m_Controls; }; #endif // _QmitkDicomExternalDataWidget_H_INCLUDED diff --git a/Modules/DICOMUI/include/QmitkDicomLocalStorageWidget.h b/Modules/DICOMUI/include/QmitkDicomLocalStorageWidget.h index b5da4acd91..20c5157ba6 100644 --- a/Modules/DICOMUI/include/QmitkDicomLocalStorageWidget.h +++ b/Modules/DICOMUI/include/QmitkDicomLocalStorageWidget.h @@ -1,115 +1,113 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkDicomLocalStorageWidget_h #define QmitkDicomLocalStorageWidget_h #include "ui_QmitkDicomLocalStorageWidgetControls.h" #include -// include ctk -#include -#include -#include - // include QT #include #include #include #include #include class QProgressDialog; class QLabel; +class ctkDICOMDatabase; +class ctkDICOMIndexer; + /** * \brief QmitkDicomLocalStorageWidget is a QWidget providing functionality for dicom storage and import. * * \ingroup Functionalities */ class MITKDICOMUI_EXPORT QmitkDicomLocalStorageWidget : public QWidget { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string Widget_ID; /** * \brief QmitkDicomLocalStorageWidget(QWidget *parent) constructor. * * \param parent is a pointer to the parent widget */ QmitkDicomLocalStorageWidget(QWidget *parent); /** * \brief QmitkDicomExternalDataWidget destructor. */ ~QmitkDicomLocalStorageWidget() override; /** * \brief CreateQtPartControl(QWidget *parent) sets the view objects from ui_QmitkDicomExternalDataWidgetControls.h. * * \param parent is a pointer to the parent widget */ virtual void CreateQtPartControl(QWidget *parent); /** * \brief SetDatabaseDirectory sets database directory. * * \param newDatabaseDirectory contains path to new database directoy. */ void SetDatabaseDirectory(QString newDatabaseDirectory); signals: /// @brief emitted when import into database is finished. void SignalFinishedImport(); /** * @brief emitted when view button is clicked. * @param _t1 containing dicom UIDs properties. */ void SignalDicomToDataManager(QHash _t1); /// \brief emitted if cancel button is pressed. void SignalCancelImport(); public slots: /// @brief Called when view button was clicked. void OnViewButtonClicked(); /// @brief Called delete button was clicked. void OnDeleteButtonClicked(); /// @brief Called when adding a dicom directory. Starts a thread adding the directory. void OnStartDicomImport(const QString &dicomData); /// @brief Called when adding a list of dicom files. Starts a thread adding the dicom files. void OnStartDicomImport(const QStringList &dicomData); /// @brief Called when the selection in the series table has changed void OnSeriesSelectionChanged(const QStringList &); protected: void SetDatabase(QString databaseFile); bool DeletePatients(); bool DeleteStudies(); bool DeleteSeries(); ctkDICOMDatabase *m_LocalDatabase; ctkDICOMIndexer *m_LocalIndexer; Ui::QmitkDicomLocalStorageWidgetControls *m_Controls; }; #endif // _QmitkDicomLocalStorageWidget_H_INCLUDED diff --git a/Modules/DICOMUI/src/QmitkDicomExternalDataWidget.cpp b/Modules/DICOMUI/src/QmitkDicomExternalDataWidget.cpp index 783ecb4f4e..414ecfb82b 100644 --- a/Modules/DICOMUI/src/QmitkDicomExternalDataWidget.cpp +++ b/Modules/DICOMUI/src/QmitkDicomExternalDataWidget.cpp @@ -1,229 +1,230 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Qmitk #include "QmitkDicomExternalDataWidget.h" #include // CTK +#include #include // Qt #include #include #include const std::string QmitkDicomExternalDataWidget::Widget_ID = "org.mitk.Widgets.QmitkDicomExternalDataWidget"; QmitkDicomExternalDataWidget::QmitkDicomExternalDataWidget(QWidget *parent) : QWidget(parent), m_ProgressDialog(nullptr), m_Controls(nullptr) { Initialize(); CreateQtPartControl(this); } QmitkDicomExternalDataWidget::~QmitkDicomExternalDataWidget() { } void QmitkDicomExternalDataWidget::CreateQtPartControl(QWidget *parent) { // build up qt Widget, unless already done if (!m_Controls) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkDicomExternalDataWidgetControls; m_Controls->setupUi(parent); m_Controls->viewExternalDataButton->setVisible(true); m_Controls->ctkDICOMBrowser->setTableOrientation(Qt::Vertical); m_Controls->ctkDICOMBrowser->setDICOMDatabase(m_ExternalDatabase); SetupImportDialog(); SetupProgressDialog(); // connect buttons connect(m_Controls->downloadButton, SIGNAL(clicked()), this, SLOT(OnDownloadButtonClicked())); connect(m_Controls->viewExternalDataButton, SIGNAL(clicked()), this, SLOT(OnViewButtonClicked())); connect(m_Controls->directoryButton, SIGNAL(clicked()), m_ImportDialog, SLOT(show())); connect(m_Controls->ctkDICOMBrowser, SIGNAL(seriesSelectionChanged(const QStringList &)), this, SLOT(OnSeriesSelectionChanged(const QStringList &))); connect( m_Controls->ctkDICOMBrowser, SIGNAL(seriesDoubleClicked(const QModelIndex &)), this, SLOT(OnViewButtonClicked())); connect(m_ImportDialog, SIGNAL(fileSelected(QString)), this, SLOT(OnStartDicomImport(QString))); connect(m_ExternalIndexer, SIGNAL(progressStep(QString)), this, SLOT(OnProgressStep(const QString&))); connect(m_ExternalIndexer, SIGNAL(progressDetail(QString)), this, SLOT(OnProgressDetail(const QString &))); connect(m_ExternalIndexer, SIGNAL(progress(int)), m_ProgressDialog, SLOT(setValue(int))); // actually the progress dialog closes if the maximum value is reached, BUT // the following line is needed since the external indexer wont reach maximum value (100 % progress) connect(m_ExternalIndexer, SIGNAL(indexingComplete(int, int, int, int)), m_ProgressDialog, SLOT(close())); connect(m_ProgressDialog, SIGNAL(canceled()), m_ExternalIndexer, SLOT(cancel())); } } void QmitkDicomExternalDataWidget::Initialize() { m_ExternalDatabase = new ctkDICOMDatabase(this); try { // this used to be an in-memory database, but latest CTK enhancements made it difficult // to maintain this mechanism QTemporaryFile tmpDatabaseFile; tmpDatabaseFile.open(); tmpDatabaseFile.setAutoRemove(false); m_ExternalDatabase->openDatabase(tmpDatabaseFile.fileName(), QString("EXTERNAL-DB")); } catch (const std::exception&) { MITK_ERROR << "Database error: " << m_ExternalDatabase->lastError().toStdString(); m_ExternalDatabase->closeDatabase(); return; } m_ExternalIndexer = new ctkDICOMIndexer(this); } void QmitkDicomExternalDataWidget::OnDownloadButtonClicked() { QStringList filesToDownload = GetFileNamesFromIndex(); if (filesToDownload.size() == 0) { QMessageBox info; info.setText("You have to select an entry in the DICOM browser for import."); info.exec(); return; } emit SignalStartDicomImport(GetFileNamesFromIndex()); } void QmitkDicomExternalDataWidget::OnViewButtonClicked() { QStringList uids = m_Controls->ctkDICOMBrowser->currentSeriesSelection(); QString uid; foreach (uid, uids) { QStringList filesForSeries = m_ExternalDatabase->filesForSeries(uid); QHash eventProperty; eventProperty.insert("FilesForSeries", filesForSeries); if (!filesForSeries.isEmpty()) { QString modality = m_ExternalDatabase->fileValue(filesForSeries.at(0), "0008,0060"); eventProperty.insert("Modality", modality); } emit SignalDicomToDataManager(eventProperty); } } void QmitkDicomExternalDataWidget::OnProgressStep(const QString& step) { m_ProgressStep = step; m_ProgressDialog->setLabelText(step); } void QmitkDicomExternalDataWidget::OnProgressDetail(const QString& detail) { m_ProgressDialog->setLabelText(m_ProgressStep+"\n"+detail); } QStringList QmitkDicomExternalDataWidget::GetFileNamesFromIndex() { QStringList filePaths; QString uid; QStringList seriesUIDs = m_Controls->ctkDICOMBrowser->currentSeriesSelection(); foreach (uid, seriesUIDs) { filePaths.append(m_ExternalDatabase->filesForSeries(uid)); } if (!filePaths.empty()) return filePaths; QStringList studyUIDs = m_Controls->ctkDICOMBrowser->currentStudiesSelection(); foreach (uid, studyUIDs) { seriesUIDs = m_ExternalDatabase->seriesForStudy(uid); foreach (uid, seriesUIDs) { filePaths.append(m_ExternalDatabase->filesForSeries(uid)); } } if (!filePaths.empty()) return filePaths; QStringList patientsUIDs = m_Controls->ctkDICOMBrowser->currentPatientsSelection(); foreach (uid, patientsUIDs) { studyUIDs = m_ExternalDatabase->studiesForPatient(uid); foreach (uid, studyUIDs) { seriesUIDs = m_ExternalDatabase->seriesForStudy(uid); foreach (uid, seriesUIDs) { filePaths.append(m_ExternalDatabase->filesForSeries(uid)); } } } return filePaths; } void QmitkDicomExternalDataWidget::OnStartDicomImport(const QString &directory) { m_ImportDialog->close(); // no need to show / start the progress dialog, as the dialog // appears by receiving the progress signal from the external indexer m_LastImportDirectory = directory; m_ExternalIndexer->addDirectory(m_ExternalDatabase, m_LastImportDirectory); } void QmitkDicomExternalDataWidget::OnSeriesSelectionChanged(const QStringList &s) { m_Controls->viewExternalDataButton->setEnabled((s.size() != 0)); } void QmitkDicomExternalDataWidget::SetupImportDialog() { // Initialize import widget m_ImportDialog = new ctkFileDialog(this); // Since copy on import is not working at the moment // this feature is disabled // QCheckBox* importCheckbox = new QCheckBox("Copy on import", m_ImportDialog); // m_ImportDialog->setBottomWidget(importCheckbox); m_ImportDialog->setFileMode(QFileDialog::Directory); m_ImportDialog->setLabelText(QFileDialog::Accept, "Import"); m_ImportDialog->setWindowTitle("Import DICOM files from directory"); m_ImportDialog->setWindowModality(Qt::ApplicationModal); } void QmitkDicomExternalDataWidget::SetupProgressDialog() { m_ProgressDialog = new QProgressDialog("Initialization ...", "Cancel", 0, 100, this); m_ProgressDialog->setWindowTitle("DICOM Import"); m_ProgressDialog->setWindowModality(Qt::ApplicationModal); m_ProgressDialog->setMinimumDuration(0); // FIX T20008: immediately set the progress dialog value to maximum --> will close the dialog m_ProgressDialog->setValue(100); } diff --git a/Modules/DICOMUI/src/QmitkDicomLocalStorageWidget.cpp b/Modules/DICOMUI/src/QmitkDicomLocalStorageWidget.cpp index 975912427f..460b412664 100644 --- a/Modules/DICOMUI/src/QmitkDicomLocalStorageWidget.cpp +++ b/Modules/DICOMUI/src/QmitkDicomLocalStorageWidget.cpp @@ -1,228 +1,230 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Qmitk #include "QmitkDicomLocalStorageWidget.h" //#include // Qt #include #include #include #include +#include + const std::string QmitkDicomLocalStorageWidget::Widget_ID = "org.mitk.Widgets.QmitkDicomLocalStorageWidget"; QmitkDicomLocalStorageWidget::QmitkDicomLocalStorageWidget(QWidget *parent) : QWidget(parent), m_LocalIndexer(new ctkDICOMIndexer(parent)), m_Controls(nullptr) { CreateQtPartControl(this); } QmitkDicomLocalStorageWidget::~QmitkDicomLocalStorageWidget() { m_LocalDatabase->closeDatabase(); } void QmitkDicomLocalStorageWidget::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { m_Controls = new Ui::QmitkDicomLocalStorageWidgetControls; m_Controls->setupUi(parent); connect(m_Controls->deleteButton, SIGNAL(clicked()), this, SLOT(OnDeleteButtonClicked())); connect(m_Controls->viewInternalDataButton, SIGNAL(clicked()), this, SLOT(OnViewButtonClicked())); connect(m_Controls->ctkDicomBrowser, SIGNAL(seriesSelectionChanged(const QStringList &)), this, SLOT(OnSeriesSelectionChanged(const QStringList &))); connect(m_Controls->ctkDicomBrowser, SIGNAL(seriesSelectionChanged(const QStringList &)), this, SLOT(OnSeriesSelectionChanged(const QStringList &))); connect( m_Controls->ctkDicomBrowser, SIGNAL(seriesDoubleClicked(const QModelIndex &)), this, SLOT(OnViewButtonClicked())); connect(m_LocalIndexer, SIGNAL(indexingComplete(int, int, int, int)), this, SIGNAL(SignalFinishedImport())); m_Controls->ctkDicomBrowser->setTableOrientation(Qt::Vertical); } } void QmitkDicomLocalStorageWidget::OnStartDicomImport(const QString &dicomData) { if (m_LocalDatabase->isOpen()) { m_LocalIndexer->addDirectory(dicomData); } } void QmitkDicomLocalStorageWidget::OnStartDicomImport(const QStringList &dicomData) { if (m_LocalDatabase->isOpen()) { m_LocalIndexer->addListOfFiles( dicomData); } } void QmitkDicomLocalStorageWidget::OnDeleteButtonClicked() { if (!this->DeletePatients()) { if (!this->DeleteStudies()) { this->DeleteSeries(); } } m_Controls->ctkDicomBrowser->updateTableViews(); } bool QmitkDicomLocalStorageWidget::DeletePatients() { auto selectedPatientUIDs = m_Controls->ctkDicomBrowser->currentPatientsSelection(); if (!selectedPatientUIDs.empty()) { QStringList studyUIDs; for (const auto &patientUID : qAsConst(selectedPatientUIDs)) studyUIDs.append(m_LocalDatabase->studiesForPatient(patientUID)); QStringList seriesUIDs; for (const auto &studyUID : studyUIDs) seriesUIDs.append(m_LocalDatabase->seriesForStudy(studyUID)); auto answer = QMessageBox::question(nullptr, "Delete Patients", QString("Do you really want to delete %1 %2, containing %3 series in %4 %5?") .arg(selectedPatientUIDs.count()) .arg(selectedPatientUIDs.count() != 1 ? "patients" : "patient") .arg(seriesUIDs.count()) .arg(studyUIDs.count()) .arg(studyUIDs.count() != 1 ? "studies" : "study"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (answer == QMessageBox::Yes) { for (const auto &patientUID : qAsConst(selectedPatientUIDs)) m_LocalDatabase->removePatient(patientUID); } return true; } return false; } bool QmitkDicomLocalStorageWidget::DeleteStudies() { auto selectedStudyUIDs = m_Controls->ctkDicomBrowser->currentStudiesSelection(); if (!selectedStudyUIDs.empty()) { QStringList seriesUIDs; for (const auto &studyUID : qAsConst(selectedStudyUIDs)) seriesUIDs.append(m_LocalDatabase->seriesForStudy(studyUID)); auto answer = QMessageBox::question(nullptr, "Delete Studies", QString("Do you really want to delete %1 %2, containing %3 series?") .arg(selectedStudyUIDs.count()) .arg(selectedStudyUIDs.count() != 1 ? "studies" : "study") .arg(seriesUIDs.count()), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (answer == QMessageBox::Yes) { for (const auto &studyUID : qAsConst(selectedStudyUIDs)) m_LocalDatabase->removeStudy(studyUID); } return true; } return false; } bool QmitkDicomLocalStorageWidget::DeleteSeries() { auto selectedSeriesUIDs = m_Controls->ctkDicomBrowser->currentSeriesSelection(); if (!selectedSeriesUIDs.empty()) { auto answer = QMessageBox::question(nullptr, "Delete Series", QString("Do you really want to delete %1 series?").arg(selectedSeriesUIDs.count()), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (answer == QMessageBox::Yes) { for (const auto &seriesUID : qAsConst(selectedSeriesUIDs)) m_LocalDatabase->removeSeries(seriesUID); } return true; } return false; } void QmitkDicomLocalStorageWidget::OnViewButtonClicked() { QStringList uids = m_Controls->ctkDicomBrowser->currentSeriesSelection(); QString uid; foreach (uid, uids) { QStringList filesForSeries = m_LocalDatabase->filesForSeries(uid); QHash eventProperty; eventProperty.insert("FilesForSeries", filesForSeries); if (!filesForSeries.isEmpty()) { QString modality = m_LocalDatabase->fileValue(filesForSeries.at(0), "0008,0060"); eventProperty.insert("Modality", modality); } emit SignalDicomToDataManager(eventProperty); } } void QmitkDicomLocalStorageWidget::SetDatabaseDirectory(QString newDatatbaseDirectory) { QDir databaseDirecory = QDir(newDatatbaseDirectory); if (!databaseDirecory.exists()) { databaseDirecory.mkpath(databaseDirecory.absolutePath()); } QString newDatatbaseFile = databaseDirecory.absolutePath() + QString("/ctkDICOM.sql"); this->SetDatabase(newDatatbaseFile); } void QmitkDicomLocalStorageWidget::SetDatabase(QString databaseFile) { m_LocalDatabase = new ctkDICOMDatabase(databaseFile); m_LocalDatabase->setParent(this); m_Controls->ctkDicomBrowser->setDICOMDatabase(m_LocalDatabase); m_LocalIndexer->setDatabase(m_LocalDatabase); } void QmitkDicomLocalStorageWidget::OnSeriesSelectionChanged(const QStringList &s) { m_Controls->viewInternalDataButton->setEnabled((s.size() != 0)); } diff --git a/Modules/IGT/Common/mitkSerialCommunication.cpp b/Modules/IGT/Common/mitkSerialCommunication.cpp index 3d6104df87..00f9f75a78 100644 --- a/Modules/IGT/Common/mitkSerialCommunication.cpp +++ b/Modules/IGT/Common/mitkSerialCommunication.cpp @@ -1,510 +1,510 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSerialCommunication.h" #ifdef WIN32 //#include #include #else // Posix #include #include #include #include #include #include #include #include #define INVALID_HANDLE_VALUE -1 #endif #define OK 1 #define ERROR_VALUE 0 mitk::SerialCommunication::SerialCommunication() : itk::Object(), m_DeviceName(""), m_PortNumber(COM1), m_BaudRate(BaudRate9600), m_DataBits(DataBits8), m_Parity(None), m_StopBits(StopBits1), m_HardwareHandshake(HardwareHandshakeOff), m_ReceiveTimeout(500), m_SendTimeout(500), m_Connected(false) { #ifdef WIN32 // Windows m_ComPortHandle = INVALID_HANDLE_VALUE; #else // Posix m_FileDescriptor = INVALID_HANDLE_VALUE; #endif } mitk::SerialCommunication::~SerialCommunication() { CloseConnection(); } bool mitk::SerialCommunication::IsConnected() { return m_Connected; } int mitk::SerialCommunication::OpenConnection() { if (m_Connected) return ERROR_VALUE; #ifdef WIN32 std::stringstream ss; if (m_DeviceName.empty()) ss << "\\\\.\\COM" << static_cast(m_PortNumber); // use m_PortNumber else ss << "\\\\.\\" << m_DeviceName; // use m_DeviceName - m_ComPortHandle = CreateFile(ss.str().c_str(), GENERIC_READ | GENERIC_WRITE, + m_ComPortHandle = CreateFileA(ss.str().c_str(), GENERIC_READ | GENERIC_WRITE, 0, // no sharing 0, // no security flags OPEN_EXISTING, // open com port, don't create it 0, // no flags 0); // no template if (m_ComPortHandle == INVALID_HANDLE_VALUE) return ERROR_VALUE; GetCommState(m_ComPortHandle, &m_PreviousDeviceControlBlock); GetCommTimeouts(m_ComPortHandle, &m_PreviousTimeout); GetCommMask(m_ComPortHandle, &m_PreviousMask); if (this->ApplyConfiguration() != OK) // set interface parameters { CloseHandle(m_ComPortHandle); m_ComPortHandle = INVALID_HANDLE_VALUE; return ERROR_VALUE; } m_Connected = true; return OK; #else // Posix std::stringstream ss; if (m_DeviceName.empty()) ss << "/dev/ttyS" << static_cast(m_PortNumber) - 1; // use m_PortNumber, COM1 = ttyS0 else ss << m_DeviceName; // use m_DeviceName //m_FileDescriptor = open(ss.str().c_str(), O_RDWR | O_NONBLOCK | O_NDELAY | O_NOCTTY | O_EXCL); // open device file m_FileDescriptor = open(ss.str().c_str(), O_RDWR|O_NONBLOCK|O_EXCL); // open device file if (m_FileDescriptor < 0) return ERROR_VALUE; fcntl(m_FileDescriptor, F_SETFL, 0); // change to blocking mode tcflush(m_FileDescriptor, TCIOFLUSH); // flush buffers if (this->ApplyConfiguration() != OK) // set interface parameters { close(m_FileDescriptor); m_FileDescriptor = INVALID_HANDLE_VALUE; return ERROR_VALUE; } m_Connected = true; return OK; #endif } void mitk::SerialCommunication::CloseConnection() { #ifdef WIN32 if (m_ComPortHandle == INVALID_HANDLE_VALUE) return; ClearReceiveBuffer(); ClearSendBuffer(); SetCommState(m_ComPortHandle, &m_PreviousDeviceControlBlock); // restore previous settings SetCommTimeouts(m_ComPortHandle, &m_PreviousTimeout); // restore previous timeout values SetCommMask(m_ComPortHandle, m_PreviousMask); // restore previous mask value PurgeComm(m_ComPortHandle, PURGE_TXCLEAR | PURGE_RXCLEAR); // empty buffers CloseHandle(m_ComPortHandle); // close handle m_ComPortHandle = INVALID_HANDLE_VALUE; m_Connected = false; return; #else // Posix if (m_FileDescriptor == INVALID_HANDLE_VALUE) return; ClearReceiveBuffer(); ClearSendBuffer(); close(m_FileDescriptor); m_FileDescriptor = INVALID_HANDLE_VALUE; m_Connected = false; return; #endif } int mitk::SerialCommunication::Receive(std::string& answer, unsigned int numberOfBytes, const char *eol) { if (numberOfBytes == 0) return OK; if (m_Connected == false) return ERROR_VALUE; #ifdef WIN32 if (m_ComPortHandle == INVALID_HANDLE_VALUE) return ERROR_VALUE; DWORD numberOfBytesRead = 0; char* buffer = new char[numberOfBytes]; if (ReadFile(m_ComPortHandle, buffer, numberOfBytes, &numberOfBytesRead, nullptr) != 0) { if (numberOfBytesRead > 0) // data read { answer.assign(buffer, numberOfBytesRead); // copy buffer to answer delete buffer; if (numberOfBytesRead == numberOfBytes) { return OK; // everything was received } else { return ERROR_VALUE; // some data was received, but not as much as expected } } else // error { answer = ""; delete buffer; return ERROR_VALUE; } } delete buffer; return OK; #else // Posix if (m_FileDescriptor == INVALID_HANDLE_VALUE) return ERROR_VALUE; unsigned long bytesRead = 0; unsigned long bytesLeft = numberOfBytes; auto buffer = new char[numberOfBytes]; while ((bytesLeft > 0) && (bytesRead < numberOfBytes)) { int num = read(m_FileDescriptor, &buffer[bytesRead], 1); // read one byte if (num == -1) // ERROR_VALUE { if (errno == EAGAIN) // nonblocking, no byte there right now, but maybe next time continue; else break; // ERROR_VALUE, stop trying to read } if (num == 0) // timeout or eof(?) break; bytesLeft -= num; // n is number of chars left to read bytesRead += num; // i is the number of chars read if (eol && *eol == buffer[bytesRead-1]) // end of line char reached break; } if (bytesRead > 0) answer.assign(buffer, bytesRead); // copy buffer to answer delete[] buffer; if ( bytesRead == numberOfBytes || // everything was received (eol && answer.size() > 0 && *eol == answer.at(answer.size()-1)) ) // end of line char reached return OK; else return ERROR_VALUE; // some data was received, but not as much as expected #endif } int mitk::SerialCommunication::Send(const std::string& input, bool block) { //long retval = E2ERR_OPENFAILED; if (input.empty()) return OK; if (m_Connected == false) return ERROR_VALUE; #ifdef WIN32 if (m_ComPortHandle == INVALID_HANDLE_VALUE) return ERROR_VALUE; DWORD bytesWritten = 0; if (WriteFile(m_ComPortHandle, input.data(), static_cast(input.size()), &bytesWritten, nullptr) == TRUE) return OK; else return ERROR_VALUE; #else // Posix if (m_FileDescriptor == INVALID_HANDLE_VALUE) return ERROR_VALUE; long bytesWritten = 0; long bytesLeft = input.size(); while (bytesLeft > 0) { bytesWritten = write(m_FileDescriptor, input.data() + bytesWritten, bytesLeft); if (bytesWritten <= 0) return ERROR_VALUE; //return ERROR_VALUE bytesLeft -= bytesWritten; } if (block) { // wait for output to be physically sent if (tcdrain(m_FileDescriptor) == -1) return ERROR_VALUE; } return OK; #endif } int mitk::SerialCommunication::ApplyConfiguration() { #ifdef WIN32 // Windows implementation return ApplyConfigurationWin(); #else // Posix return ApplyConfigurationUnix(); #endif } /** * The Code for Baud rates is highly platform specific and divided into separate subroutines for readability. */ #ifdef WIN32 int mitk::SerialCommunication::ApplyConfigurationWin() { if (m_ComPortHandle == INVALID_HANDLE_VALUE) return ERROR_VALUE; DCB controlSettings; if (GetCommState(m_ComPortHandle, &controlSettings) == 0) { return ERROR_VALUE; } std::ostringstream o; o << "baud=" << m_BaudRate << " parity=" << static_cast(m_Parity) << " data=" << m_DataBits << " stop=" << m_StopBits; if (BuildCommDCBA(o.str().c_str(), &controlSettings) == 0) // Build device-control block return ERROR_VALUE; if (m_HardwareHandshake == HardwareHandshakeOn) // Modify hardware handshake values { controlSettings.fDtrControl = DTR_CONTROL_ENABLE; controlSettings.fRtsControl = RTS_CONTROL_ENABLE; controlSettings.fOutxCtsFlow = TRUE; controlSettings.fRtsControl = RTS_CONTROL_HANDSHAKE; } else { controlSettings.fDtrControl = DTR_CONTROL_DISABLE; controlSettings.fRtsControl = RTS_CONTROL_DISABLE; controlSettings.fOutxCtsFlow = FALSE; controlSettings.fRtsControl = RTS_CONTROL_DISABLE; } if (SetCommState(m_ComPortHandle, &controlSettings) == FALSE) // Configure com port return ERROR_VALUE; COMMTIMEOUTS timeouts; timeouts.ReadIntervalTimeout = m_ReceiveTimeout; timeouts.ReadTotalTimeoutMultiplier = 0; timeouts.ReadTotalTimeoutConstant = m_ReceiveTimeout; timeouts.WriteTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutConstant = m_SendTimeout; if (SetCommTimeouts(m_ComPortHandle, &timeouts) == FALSE) // set timeout values return ERROR_VALUE; PurgeComm(m_ComPortHandle, PURGE_TXCLEAR | PURGE_RXCLEAR); // clear read and write buffers return OK; } #else /** * \brief Applies the configuration for Linux */ int mitk::SerialCommunication::ApplyConfigurationUnix() { if ( m_FileDescriptor == INVALID_HANDLE_VALUE ) return ERROR_VALUE; struct termios termIOStructure; if ( tcgetattr(m_FileDescriptor, &termIOStructure) != 0 ) // retrieve parameters from com port return ERROR_VALUE; cfmakeraw(&termIOStructure); // set flags to raw mode termIOStructure.c_cflag |= CLOCAL; if (m_HardwareHandshake == HardwareHandshakeOn) { // enable termIOStructure.c_cflag |= CRTSCTS; termIOStructure.c_iflag &= ~(IXON|IXOFF); } else { // disable termIOStructure.c_cflag &= ~CRTSCTS; termIOStructure.c_iflag &= ~(IXON|IXOFF); } termIOStructure.c_cflag &= ~CSIZE; // set number of data bits switch (m_DataBits) { case DataBits7: termIOStructure.c_cflag |= CS7; break; case DataBits8: default: termIOStructure.c_cflag |= CS8; } switch (m_StopBits) // set number of stop bits { case StopBits2: termIOStructure.c_cflag |= CSTOPB; break; case StopBits1: default: termIOStructure.c_cflag &= ~CSTOPB; } switch (m_Parity) // set parity { case Odd: termIOStructure.c_cflag |= (PARENB|PARODD); break; case Even: termIOStructure.c_cflag |= PARENB; termIOStructure.c_cflag &= ~PARODD; // TODO: check if this is intended // FALLTHRU case None: // FALLTHRU default: termIOStructure.c_cflag &= ~PARENB; break; } speed_t baudrate; // set baudrate switch (m_BaudRate) { case BaudRate9600: baudrate = B9600; break; case BaudRate14400: baudrate = B9600; //14400 is not defined for posix, use 9600 instead break; case BaudRate19200: baudrate = B19200; break; case BaudRate38400: baudrate = B38400; break; case BaudRate57600: baudrate = B57600; break; case BaudRate115200: baudrate = B115200; break; case BaudRate230400: baudrate = B230400; break; // the following baud rates do not work for apple #ifndef __APPLE__ case BaudRate460800: baudrate = B460800; break; case BaudRate500000: baudrate = B500000; break; case BaudRate576000: baudrate = B576000; break; case BaudRate921600: baudrate = B921600; break; case BaudRate1000000: baudrate = B1000000; break; case BaudRate1152000: baudrate = B1152000; break; //case BaudRate1228739: //baudrate = B1228739; //break; case BaudRate1500000: baudrate = B1500000; break; case BaudRate2000000: baudrate = B2000000; break; case BaudRate2500000: baudrate = B2500000; break; case BaudRate3000000: baudrate = B3000000; break; case BaudRate3500000: baudrate = B3500000; break; case BaudRate4000000: baudrate = B4000000; break; #endif default: MITK_WARN("mitk::SerialCommunication") << "Baud rate not recognized, using default of 9600 Baud."; baudrate = B9600; break; } cfsetispeed(&termIOStructure, baudrate); cfsetospeed(&termIOStructure, baudrate); termIOStructure.c_cc[VMIN] = 0; termIOStructure.c_cc[VTIME] = m_ReceiveTimeout / 100; // timeout in 1/10 sec, not in ms. Rounded down. if (tcsetattr(m_FileDescriptor, TCSANOW, &termIOStructure) == 0) return OK; else return ERROR_VALUE; } #endif void mitk::SerialCommunication::SendBreak(unsigned int ms) { #ifdef WIN32 if (m_ComPortHandle == INVALID_HANDLE_VALUE) return; SetCommBreak(m_ComPortHandle); itksys::SystemTools::Delay(ms); ClearCommBreak(m_ComPortHandle); return; #else // Posix if (m_FileDescriptor == INVALID_HANDLE_VALUE) return; tcsendbreak(m_FileDescriptor, ms); return; #endif } void mitk::SerialCommunication::ClearReceiveBuffer() { #ifdef WIN32 if (m_ComPortHandle != INVALID_HANDLE_VALUE) PurgeComm(m_ComPortHandle, PURGE_RXCLEAR); #else // Posix if (m_FileDescriptor != INVALID_HANDLE_VALUE) tcflush(m_FileDescriptor, TCIFLUSH); #endif } void mitk::SerialCommunication::ClearSendBuffer() { #ifdef WIN32 if ( m_ComPortHandle != INVALID_HANDLE_VALUE ) PurgeComm(m_ComPortHandle, PURGE_TXCLEAR); #else // Posix if ( m_FileDescriptor != INVALID_HANDLE_VALUE ) tcflush(m_FileDescriptor, TCOFLUSH); #endif } diff --git a/Modules/Multilabel/autoload/DICOMSegIO/CMakeLists.txt b/Modules/Multilabel/autoload/DICOMSegIO/CMakeLists.txt index de9ed9e7cc..899a24f3d3 100644 --- a/Modules/Multilabel/autoload/DICOMSegIO/CMakeLists.txt +++ b/Modules/Multilabel/autoload/DICOMSegIO/CMakeLists.txt @@ -1,6 +1,6 @@ MITK_CREATE_MODULE( DICOMSegIO DEPENDS MitkMultilabel MitkDICOMQI PACKAGE_DEPENDS - PRIVATE DCMQI DCMTK + PRIVATE DCMQI DCMTK|dcmseg+dcmsr AUTOLOAD_WITH MitkCore ) diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp index 25f2bd524d..78d241cf19 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp @@ -1,654 +1,657 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __mitkLabelSetImageWriter__cpp #define __mitkLabelSetImageWriter__cpp #include "mitkLabelSetImageIO.h" #include "mitkBasePropertySerializer.h" #include "mitkIOMimeTypes.h" #include "mitkImageAccessByItk.h" #include "mitkLabelSetIOHelper.h" #include "mitkLabelSetImageConverter.h" #include #include #include #include #include #include // itk #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include namespace mitk { const char* const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char* const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char* const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char* const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; const char* const PROPERTY_KEY_UID = "org_mitk_uid"; LabelSetImageIO::LabelSetImageIO() : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), IOMimeTypes::NRRD_MIMETYPE(), "MITK Multilabel Image") { + this->InitializeDefaultMetaDataKeys(); AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); } IFileIO::ConfidenceLevel LabelSetImageIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const auto *input = static_cast(this->GetInput()); if (input) return Supported; else return Unsupported; } void LabelSetImageIO::Write() { ValidateOutputLocation(); auto input = dynamic_cast(this->GetInput()); mitk::LocaleSwitch localeSwitch("C"); mitk::Image::Pointer inputVector = mitk::ConvertLabelSetImageToImage(input); // image write if (inputVector.IsNull()) { mitkThrow() << "Cannot write non-image data"; } itk::NrrdImageIO::Pointer nrrdImageIo = itk::NrrdImageIO::New(); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = inputVector->GetGeometry()->Clone(); // Check if geometry information will be lost if (inputVector->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = inputVector->GetDimension(); const unsigned int *const dimensions = inputVector->GetDimensions(); const mitk::PixelType pixelType = inputVector->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO nrrdImageIo->SetNumberOfDimensions(dimension); nrrdImageIo->SetPixelType(pixelType.GetPixelType()); nrrdImageIo->SetComponentType(static_cast(pixelType.GetComponentType()) < PixelComponentUserType ? pixelType.GetComponentType() : itk::IOComponentEnum::UNKNOWNCOMPONENTTYPE); nrrdImageIo->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { nrrdImageIo->SetDimensions(i, dimensions[i]); nrrdImageIo->SetSpacing(i, spacing4D[i]); nrrdImageIo->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection(0.0); mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref()); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } nrrdImageIo->SetDirection(i, axisDirection); ioRegion.SetSize(i, inputVector->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, inputVector->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available nrrdImageIo->UseCompressionOn(); nrrdImageIo->SetIORegion(ioRegion); nrrdImageIo->SetFileName(path); // label set specific meta data char keybuffer[512]; char valbuffer[512]; sprintf(keybuffer, "modality"); sprintf(valbuffer, "org.mitk.image.multilabel"); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); sprintf(keybuffer, "layers"); sprintf(valbuffer, "%1d", input->GetNumberOfLayers()); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); for (unsigned int layerIdx = 0; layerIdx < input->GetNumberOfLayers(); layerIdx++) { sprintf(keybuffer, "layer_%03u", layerIdx); // layer idx sprintf(valbuffer, "%1u", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); auto iter = input->GetLabelSet(layerIdx)->IteratorConstBegin(); unsigned int count(0); while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd()) { tinyxml2::XMLDocument document; document.InsertEndChild(document.NewDeclaration()); auto *labelElem = mitk::LabelSetIOHelper::GetLabelAsXMLElement(document, iter->second); document.InsertEndChild(labelElem); tinyxml2::XMLPrinter printer; document.Print(&printer); sprintf(keybuffer, "org.mitk.label_%03u_%05u", layerIdx, count); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), printer.CStr()); ++iter; ++count; } } // end label set specific meta data // Handle time geometry const auto* arbitraryTG = dynamic_cast(input->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG); nrrdImageIo->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = input->GetPropertyList(); for (const auto& property : *imagePropertyList->GetMap()) { mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = infoList.front()->GetSerializationFunction()(property.second); if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), key, value); } // Handle UID itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_UID, input->GetUID()); ImageReadAccessor imageAccess(inputVector); nrrdImageIo->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } // end image write } IFileIO::ConfidenceLevel LabelSetImageIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; const std::string fileName = this->GetLocalFileName(); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); io->SetFileName(fileName); io->ReadImageInformation(); itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary(); std::string value(""); itk::ExposeMetaData(imgMetaDataDictionary, "modality", value); if (value.compare("org.mitk.image.multilabel") == 0) { return Supported; } else return Unsupported; } std::vector LabelSetImageIO::DoRead() { mitk::LocaleSwitch localeSwitch("C"); // begin regular image loading, adapted from mitkItkImageIO itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New(); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. nrrdImageIO->SetFileName(path); nrrdImageIO->ReadImageInformation(); unsigned int ndim = nrrdImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = nrrdImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = nrrdImageIO->GetDimensions(i); spacing[i] = nrrdImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = nrrdImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; nrrdImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[nrrdImageIO->GetImageSizeInBytes()]; nrrdImageIO->Read(buffer); image->Initialize(MakePixelType(nrrdImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = nrrdImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary(); TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast*>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass(); typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.empty()) { MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback"; } else if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback"; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass(); ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents(); // end regular image loading LabelSetImage::Pointer output = ConvertImageToLabelSetImage(image); // get labels and add them as properties to the image char keybuffer[256]; unsigned int numberOfLayers = GetIntByKey(dictionary, "layers"); std::string _xmlStr; mitk::Label::Pointer label; for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++) { sprintf(keybuffer, "layer_%03u", layerIdx); int numberOfLabels = GetIntByKey(dictionary, keybuffer); mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New(); for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++) { tinyxml2::XMLDocument doc; sprintf(keybuffer, "label_%03u_%05d", layerIdx, labelIdx); _xmlStr = GetStringByKey(dictionary, keybuffer); doc.Parse(_xmlStr.c_str(), _xmlStr.size()); auto *labelElem = doc.FirstChildElement("Label"); if (labelElem == nullptr) mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO"; label = mitk::LabelSetIOHelper::LoadLabelFromXMLDocument(labelElem); if (label->GetValue() == 0) // set exterior label is needed to hold exterior information output->SetExteriorLabel(label); labelSet->AddLabel(label); labelSet->SetLayer(layerIdx); } output->AddLabelSetToLayer(layerIdx, labelSet); } for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string& key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key); auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer& x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer& x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info auto newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast*>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); if (loadedProp.IsNull()) { MITK_ERROR << "Property cannot be correctly deserialized and is skipped. Check if data format is valid. Problematic property value string: \"" << value << "\"; Property info used to deserialized: " << info; break; } output->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey = false; for (const auto& defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { propPersistenceService->AddInfo(info); } } } // Handle UID if (dictionary.HasKey(PROPERTY_KEY_UID)) { itk::MetaDataObject::ConstPointer uidData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_UID)); if (uidData.IsNotNull()) { mitk::UIDManipulator uidManipulator(output); uidManipulator.SetUID(uidData->GetMetaDataObjectValue()); } } MITK_INFO << "...finished!"; std::vector result; result.push_back(output.GetPointer()); return result; } int LabelSetImageIO::GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return atoi(metaString.c_str()); } } return 0; } std::string LabelSetImageIO::GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return metaString; } } return metaString; } LabelSetImageIO *LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); } void LabelSetImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); - this->m_DefaultMetaDataKeys.push_back("label_"); - this->m_DefaultMetaDataKeys.push_back("layer_"); + this->m_DefaultMetaDataKeys.push_back("label."); + this->m_DefaultMetaDataKeys.push_back("layer."); + this->m_DefaultMetaDataKeys.push_back("layers"); + this->m_DefaultMetaDataKeys.push_back("org.mitk.label."); } } // namespace #endif //__mitkLabelSetImageWriter__cpp diff --git a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp index ce8653ac5d..231f8d4aed 100644 --- a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp +++ b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp @@ -1,75 +1,75 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkMultilabelIOMimeTypes.h" #include -#include +#include #include #include mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::MitkSegmentationTaskListMimeType() : CustomMimeType(SEGMENTATIONTASKLIST_MIMETYPE_NAME()) { this->AddExtension("json"); this->SetCategory("MITK Segmentation Task List"); this->SetComment("MITK Segmentation Task List"); } bool mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::AppliesTo(const std::string& path) const { bool result = CustomMimeType::AppliesTo(path); - if (!fs::exists(path)) // T18572 + if (!std::filesystem::exists(path)) // T18572 return result; std::ifstream file(path); if (!file.is_open()) return false; auto json = nlohmann::json::parse(file, nullptr, false); if (json.is_discarded() || !json.is_object()) return false; if ("MITK Segmentation Task List" != json.value("FileFormat", "")) return false; if (1 != json.value("Version", 0)) return false; return true; } mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType* mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::Clone() const { return new MitkSegmentationTaskListMimeType(*this); } mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType mitk::MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE() { return MitkSegmentationTaskListMimeType(); } std::string mitk::MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE_NAME() { return IOMimeTypes::DEFAULT_BASE_NAME() + ".segmentationtasklist"; } std::vector mitk::MitkMultilabelIOMimeTypes::Get() { std::vector mimeTypes; mimeTypes.push_back(SEGMENTATIONTASKLIST_MIMETYPE().Clone()); return mimeTypes; } diff --git a/Modules/Multilabel/autoload/IO/mitkSegmentationTaskListIO.cpp b/Modules/Multilabel/autoload/IO/mitkSegmentationTaskListIO.cpp index e883c20426..6afa7c5a81 100644 --- a/Modules/Multilabel/autoload/IO/mitkSegmentationTaskListIO.cpp +++ b/Modules/Multilabel/autoload/IO/mitkSegmentationTaskListIO.cpp @@ -1,267 +1,267 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSegmentationTaskListIO.h" #include "mitkMultilabelIOMimeTypes.h" #include #include -#include +#include #include namespace mitk { void to_json(nlohmann::json& json, const SegmentationTaskList::Task& task) { if (task.HasName()) json["Name"] = task.GetName(); if (task.HasDescription()) json["Description"] = task.GetDescription(); if (task.HasImage()) json["Image"] = task.GetImage().string(); if (task.HasSegmentation()) json["Segmentation"] = task.GetSegmentation().string(); if (task.HasLabelName()) json["LabelName"] = task.GetLabelName(); if (task.HasLabelNameSuggestions()) json["LabelNameSuggestions"] = task.GetLabelNameSuggestions().string(); if (task.HasPreset()) json["Preset"] = task.GetPreset().string(); if (task.HasResult()) json["Result"] = task.GetResult().string(); if (task.HasDynamic()) json["Dynamic"] = task.GetDynamic(); } void from_json(const nlohmann::json& json, SegmentationTaskList::Task& task) { auto iter = json.find("Name"); if (iter != json.end()) task.SetName(json["Name"].get()); iter = json.find("Description"); if (iter != json.end()) task.SetDescription(json["Description"].get()); iter = json.find("Image"); if (iter != json.end()) task.SetImage(json["Image"].get()); iter = json.find("Segmentation"); if (iter != json.end()) task.SetSegmentation(json["Segmentation"].get()); iter = json.find("LabelName"); if (iter != json.end()) task.SetLabelName(json["LabelName"].get()); iter = json.find("LabelNameSuggestions"); if (iter != json.end()) task.SetLabelNameSuggestions(json["LabelNameSuggestions"].get()); iter = json.find("Preset"); if (iter != json.end()) task.SetPreset(json["Preset"].get()); iter = json.find("Result"); if (iter != json.end()) task.SetResult(json["Result"].get()); iter = json.find("Dynamic"); if (iter != json.end()) task.SetDynamic(json["Dynamic"].get()); } } mitk::SegmentationTaskListIO::SegmentationTaskListIO() : AbstractFileIO(SegmentationTaskList::GetStaticNameOfClass(), MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE(), "MITK Segmentation Task List") { this->RegisterService(); } std::vector mitk::SegmentationTaskListIO::DoRead() { auto* stream = this->GetInputStream(); std::ifstream fileStream; if (nullptr == stream) { auto filename = this->GetInputLocation(); - if (filename.empty() || !fs::exists(filename)) + if (filename.empty() || !std::filesystem::exists(filename)) mitkThrow() << "Invalid or nonexistent filename: \"" << filename << "\"!"; fileStream.open(filename); if (!fileStream.is_open()) mitkThrow() << "Could not open file \"" << filename << "\" for reading!"; stream = &fileStream; } nlohmann::json json; try { json = nlohmann::json::parse(*stream); } catch (const nlohmann::json::exception& e) { mitkThrow() << e.what(); } if (!json.is_object()) mitkThrow() << "Unknown file format (expected JSON object as root)!"; if ("MITK Segmentation Task List" != json.value("FileFormat", "")) mitkThrow() << "Unknown file format (expected \"MITK Segmentation Task List\")!"; if (1 != json.value("Version", 0)) mitkThrow() << "Unknown file format version (expected \"1\")!"; if (!json.contains("Tasks") || !json["Tasks"].is_array()) mitkThrow() << "Tasks array not found!"; auto segmentationTaskList = SegmentationTaskList::New(); if (json.contains("Name")) segmentationTaskList->SetProperty("name", StringProperty::New(json["Name"].get())); try { if (json.contains("Defaults")) { segmentationTaskList->SetDefaults(json["Defaults"].get()); if (segmentationTaskList->GetDefaults().HasResult()) mitkThrow() << "Defaults must not contain \"Result\"!"; } for (const auto& task : json["Tasks"]) { auto i = segmentationTaskList->AddTask(task.get()); if (!segmentationTaskList->HasImage(i)) mitkThrow() << "Task " << i << " must contain \"Image\"!"; - fs::path imagePath(segmentationTaskList->GetImage(i)); + std::filesystem::path imagePath(segmentationTaskList->GetImage(i)); if (imagePath.is_relative()) { auto inputLocation = this->GetInputLocation(); /* If we have access to properties, we are reading from an MITK scene * file. In this case, paths are still relative to the original input * location, which is preserved in the properties. */ const auto* properties = this->GetProperties(); if (properties != nullptr) properties->GetStringProperty("MITK.IO.reader.inputlocation", inputLocation); - imagePath = fs::path(inputLocation).remove_filename() / imagePath; + imagePath = std::filesystem::path(inputLocation).remove_filename() / imagePath; } - if (!fs::exists(imagePath)) + if (!std::filesystem::exists(imagePath)) mitkThrow() << "Referenced image \"" << imagePath << "\" in task " << i << " does not exist!"; if (!segmentationTaskList->HasResult(i)) mitkThrow() << "Task " << i << " must contain \"Result\"!"; } } catch (const nlohmann::json::type_error& e) { mitkThrow() << e.what(); } std::vector result; result.push_back(segmentationTaskList.GetPointer()); return result; } void mitk::SegmentationTaskListIO::Write() { auto segmentationTaskList = dynamic_cast(this->GetInput()); if (nullptr == segmentationTaskList) mitkThrow() << "Invalid input for writing!"; if (segmentationTaskList->GetNumberOfTasks() == 0) mitkThrow() << "No tasks found!"; auto* stream = this->GetOutputStream(); std::ofstream fileStream; if (nullptr == stream) { auto filename = this->GetOutputLocation(); if (filename.empty()) mitkThrow() << "Neither an output stream nor an output filename was specified!"; fileStream.open(filename); if (!fileStream.is_open()) mitkThrow() << "Could not open file \"" << filename << "\" for writing!"; stream = &fileStream; } if (!stream->good()) mitkThrow() << "Stream for writing is not good!"; nlohmann::ordered_json json = { { "FileFormat", "MITK Segmentation Task List" }, { "Version", 1 }, { "Name", segmentationTaskList->GetProperty("name")->GetValueAsString() } }; nlohmann::json defaults = segmentationTaskList->GetDefaults(); if (!defaults.is_null()) json["Defaults"] = defaults; nlohmann::json tasks; for (const auto& task : *segmentationTaskList) tasks.push_back(task); json["Tasks"] = tasks; *stream << std::setw(2) << json << std::endl; } mitk::SegmentationTaskListIO* mitk::SegmentationTaskListIO::IOClone() const { return new SegmentationTaskListIO(*this); } diff --git a/Modules/Multilabel/mitkLabelSet.cpp b/Modules/Multilabel/mitkLabelSet.cpp index 47f5472fdf..71d5c4908f 100644 --- a/Modules/Multilabel/mitkLabelSet.cpp +++ b/Modules/Multilabel/mitkLabelSet.cpp @@ -1,360 +1,355 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkLabelSet.h" #include "mitkDICOMSegmentationPropertyHelper.h" #include mitk::LabelSet::LabelSet() : m_ActiveLabelValue(0), m_Layer(0) { m_LookupTable = mitk::LookupTable::New(); m_LookupTable->SetType(mitk::LookupTable::MULTILABEL); } mitk::LabelSet::~LabelSet() { m_LabelContainer.clear(); } mitk::LabelSet::LabelSet(const LabelSet &other) : itk::Object(), m_LookupTable(other.GetLookupTable()->Clone()), m_ActiveLabelValue(other.GetActiveLabel()->GetValue()), m_Layer(other.GetLayer()) { // clone Labels auto otherIt = other.IteratorConstBegin(); for (; otherIt != other.IteratorConstEnd(); ++otherIt) { m_LabelContainer[otherIt->first] = otherIt->second->Clone(); itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &LabelSet::OnLabelModified); m_LabelContainer[otherIt->first]->AddObserver(itk::ModifiedEvent(), command); } } void mitk::LabelSet::OnLabelModified() { ModifyLabelEvent.Send(); Superclass::Modified(); } mitk::LabelSet::LabelContainerConstIteratorType mitk::LabelSet::IteratorConstEnd() const { return m_LabelContainer.end(); } mitk::LabelSet::LabelContainerConstIteratorType mitk::LabelSet::IteratorConstBegin() const { return m_LabelContainer.begin(); } mitk::LabelSet::LabelContainerIteratorType mitk::LabelSet::IteratorEnd() { return m_LabelContainer.end(); } mitk::LabelSet::LabelContainerIteratorType mitk::LabelSet::IteratorBegin() { return m_LabelContainer.begin(); } unsigned int mitk::LabelSet::GetNumberOfLabels() const { return m_LabelContainer.size(); } void mitk::LabelSet::SetLayer(unsigned int layer) { m_Layer = layer; Modified(); } void mitk::LabelSet::SetActiveLabel(PixelType pixelValue) { m_ActiveLabelValue = pixelValue; ActiveLabelEvent.Send(pixelValue); Modified(); } bool mitk::LabelSet::ExistLabel(PixelType pixelValue) { return m_LabelContainer.count(pixelValue) > 0 ? true : false; } // TODO Parameter as Smartpointer void mitk::LabelSet::AddLabel(mitk::Label *label) { unsigned int max_size = mitk::Label::MAX_LABEL_VALUE + 1; if (m_LabelContainer.size() >= max_size) return; mitk::Label::Pointer newLabel(label->Clone()); // TODO use layer of label parameter newLabel->SetLayer(m_Layer); PixelType pixelValue; if (m_LabelContainer.empty()) { pixelValue = newLabel->GetValue(); } else { pixelValue = m_LabelContainer.rbegin()->first; if (pixelValue >= newLabel->GetValue() && m_LabelContainer.find(newLabel->GetValue()) != m_LabelContainer.end()) { ++pixelValue; newLabel->SetValue(pixelValue); } else { pixelValue = newLabel->GetValue(); } } // new map entry m_LabelContainer[pixelValue] = newLabel; UpdateLookupTable(pixelValue); // add DICOM information of the label DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(newLabel); itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &LabelSet::OnLabelModified); newLabel->AddObserver(itk::ModifiedEvent(), command); // newLabel->AddObserver(itk::ModifiedEvent(),command); SetActiveLabel(newLabel->GetValue()); AddLabelEvent.Send(); Modified(); } void mitk::LabelSet::AddLabel(const std::string &name, const mitk::Color &color) { mitk::Label::Pointer newLabel = mitk::Label::New(); newLabel->SetName(name); newLabel->SetColor(color); AddLabel(newLabel); } void mitk::LabelSet::RenameLabel(PixelType pixelValue, const std::string &name, const mitk::Color &color) { mitk::Label *label = GetLabel(pixelValue); label->SetName(name); label->SetColor(color); // change DICOM information of the label DICOMSegmentationPropertyHelper::SetDICOMSegmentProperties(label); } void mitk::LabelSet::SetLookupTable(mitk::LookupTable *lut) { m_LookupTable = lut; Modified(); } void mitk::LabelSet::PrintSelf(std::ostream & /*os*/, itk::Indent /*indent*/) const { } void mitk::LabelSet::RemoveLabel(PixelType pixelValue) { auto it = m_LabelContainer.rbegin(); PixelType nextActivePixelValue = it->first; for (; it != m_LabelContainer.rend(); ++it) { if (it->first == pixelValue) { it->second->RemoveAllObservers(); m_LabelContainer.erase(pixelValue); break; } nextActivePixelValue = it->first; } if (m_ActiveLabelValue == pixelValue) { if (ExistLabel(nextActivePixelValue)) SetActiveLabel(nextActivePixelValue); else SetActiveLabel(m_LabelContainer.rbegin()->first); } RemoveLabelEvent.Send(); Modified(); } void mitk::LabelSet::RemoveAllLabels() { auto _it = IteratorBegin(); for (; _it != IteratorConstEnd();) { RemoveLabelEvent.Send(); m_LabelContainer.erase(_it++); } AllLabelsModifiedEvent.Send(); } void mitk::LabelSet::SetNextActiveLabel() { - auto it = m_LabelContainer.begin(); + auto it = m_LabelContainer.find(m_ActiveLabelValue); - for (; it != m_LabelContainer.end(); ++it) + if (it != m_LabelContainer.end()) + ++it; + + if (it == m_LabelContainer.end()) { - if (it->first == m_ActiveLabelValue) - { - // go to next label - ++it; - if (it == m_LabelContainer.end()) - { - // end of container; next label is first label - it = m_LabelContainer.begin(); - } - break; // found the active label; finish loop - } + it = m_LabelContainer.begin(); + if (m_LabelContainer.size() > 1) + ++it; // ...skip background label! } SetActiveLabel(it->first); } void mitk::LabelSet::SetAllLabelsLocked(bool value) { auto _end = m_LabelContainer.end(); auto _it = m_LabelContainer.begin(); for (; _it != _end; ++_it) _it->second->SetLocked(value); AllLabelsModifiedEvent.Send(); Modified(); } void mitk::LabelSet::SetAllLabelsVisible(bool value) { auto _end = m_LabelContainer.end(); auto _it = m_LabelContainer.begin(); for (; _it != _end; ++_it) { _it->second->SetVisible(value); UpdateLookupTable(_it->first); } AllLabelsModifiedEvent.Send(); Modified(); } void mitk::LabelSet::UpdateLookupTable(PixelType pixelValue) { const mitk::Color &color = GetLabel(pixelValue)->GetColor(); double rgba[4]; m_LookupTable->GetTableValue(static_cast(pixelValue), rgba); rgba[0] = color.GetRed(); rgba[1] = color.GetGreen(); rgba[2] = color.GetBlue(); if (GetLabel(pixelValue)->GetVisible()) rgba[3] = GetLabel(pixelValue)->GetOpacity(); else rgba[3] = 0.0; m_LookupTable->SetTableValue(static_cast(pixelValue), rgba); } mitk::Label *mitk::LabelSet::GetLabel(PixelType pixelValue) { if (m_LabelContainer.find(pixelValue) == m_LabelContainer.end()) return nullptr; return m_LabelContainer[pixelValue]; } const mitk::Label *mitk::LabelSet::GetLabel(PixelType pixelValue) const { auto it = m_LabelContainer.find(pixelValue); if (it == m_LabelContainer.end()) return nullptr; return it->second.GetPointer(); } bool mitk::Equal(const mitk::LabelSet &leftHandSide, const mitk::LabelSet &rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; // LabelSetmembers MITK_INFO(verbose) << "--- LabelSet Equal ---"; // m_LookupTable; const mitk::LookupTable *lhsLUT = leftHandSide.GetLookupTable(); const mitk::LookupTable *rhsLUT = rightHandSide.GetLookupTable(); returnValue = *lhsLUT == *rhsLUT; if (!returnValue) { MITK_INFO(verbose) << "Lookup tabels not equal."; return returnValue; ; } // m_ActiveLabel; returnValue = mitk::Equal(*leftHandSide.GetActiveLabel(), *rightHandSide.GetActiveLabel(), eps, verbose); if (!returnValue) { MITK_INFO(verbose) << "Active label not equal."; return returnValue; ; } // m_Layer; returnValue = leftHandSide.GetLayer() == rightHandSide.GetLayer(); if (!returnValue) { MITK_INFO(verbose) << "Layer index not equal."; return returnValue; ; } // container size; returnValue = leftHandSide.GetNumberOfLabels() == rightHandSide.GetNumberOfLabels(); if (!returnValue) { MITK_INFO(verbose) << "Number of labels not equal."; return returnValue; ; } // Label container (map) // m_LabelContainer; auto lhsit = leftHandSide.IteratorConstBegin(); auto rhsit = rightHandSide.IteratorConstBegin(); for (; lhsit != leftHandSide.IteratorConstEnd(); ++lhsit, ++rhsit) { returnValue = rhsit->first == lhsit->first; if (!returnValue) { MITK_INFO(verbose) << "Label in label container not equal."; return returnValue; ; } returnValue = mitk::Equal(*(rhsit->second), *(lhsit->second), eps, verbose); if (!returnValue) { MITK_INFO(verbose) << "Label in label container not equal."; return returnValue; ; } } return returnValue; } diff --git a/Modules/Multilabel/mitkSegmentationTaskList.cpp b/Modules/Multilabel/mitkSegmentationTaskList.cpp index 3722445949..07a102f943 100644 --- a/Modules/Multilabel/mitkSegmentationTaskList.cpp +++ b/Modules/Multilabel/mitkSegmentationTaskList.cpp @@ -1,196 +1,196 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSegmentationTaskList.h" #include #include mitk::SegmentationTaskList::Task::Task() : m_Defaults(nullptr) { } mitk::SegmentationTaskList::Task::~Task() { } void mitk::SegmentationTaskList::Task::SetDefaults(const Task* defaults) { m_Defaults = defaults; } mitk::SegmentationTaskList::SegmentationTaskList() { // A base data cannot be serialized if empty. To be not considered empty its // geometry must consist of at least one time step. However, a segmentation // task would then appear as invisible spacial object in a scene. This can // be prevented by excluding it from the scene's bounding box calculations. this->GetTimeGeometry()->Expand(1); this->SetProperty("includeInBoundingBox", BoolProperty::New(false)); } mitk::SegmentationTaskList::SegmentationTaskList(const Self& other) : BaseData(other) { } mitk::SegmentationTaskList::~SegmentationTaskList() { } size_t mitk::SegmentationTaskList::GetNumberOfTasks() const { return m_Tasks.size(); } size_t mitk::SegmentationTaskList::AddTask(const Task& subtask) { m_Tasks.push_back(subtask); m_Tasks.back().SetDefaults(&m_Defaults); return m_Tasks.size() - 1; } const mitk::SegmentationTaskList::Task* mitk::SegmentationTaskList::GetTask(size_t index) const { return &m_Tasks.at(index); } mitk::SegmentationTaskList::Task* mitk::SegmentationTaskList::GetTask(size_t index) { return &m_Tasks.at(index); } const mitk::SegmentationTaskList::Task& mitk::SegmentationTaskList::GetDefaults() const { return m_Defaults; } void mitk::SegmentationTaskList::SetDefaults(const Task& defaults) { m_Defaults = defaults; for (auto& subtask : m_Tasks) subtask.SetDefaults(&m_Defaults); } bool mitk::SegmentationTaskList::IsDone() const { for (size_t i = 0; i < m_Tasks.size(); ++i) { if (!this->IsDone(i)) return false; } return true; } bool mitk::SegmentationTaskList::IsDone(size_t index) const { - return fs::exists(this->GetAbsolutePath(m_Tasks.at(index).GetResult())); + return std::filesystem::exists(this->GetAbsolutePath(m_Tasks.at(index).GetResult())); } -fs::path mitk::SegmentationTaskList::GetInputLocation() const +std::filesystem::path mitk::SegmentationTaskList::GetInputLocation() const { std::string inputLocation; this->GetPropertyList()->GetStringProperty("MITK.IO.reader.inputlocation", inputLocation); return !inputLocation.empty() - ? fs::path(inputLocation)/*.lexically_normal()*/ // See T29246 - : fs::path(); + ? std::filesystem::path(inputLocation).lexically_normal() + : std::filesystem::path(); } -fs::path mitk::SegmentationTaskList::GetBasePath() const +std::filesystem::path mitk::SegmentationTaskList::GetBasePath() const { return this->GetInputLocation().remove_filename(); } -fs::path mitk::SegmentationTaskList::GetAbsolutePath(const fs::path& path) const +std::filesystem::path mitk::SegmentationTaskList::GetAbsolutePath(const std::filesystem::path& path) const { if (path.empty()) return path; - auto normalizedPath = path/*.lexically_normal()*/; // See T29246 + auto normalizedPath = path.lexically_normal(); return !normalizedPath.is_absolute() ? this->GetBasePath() / normalizedPath : normalizedPath; } -fs::path mitk::SegmentationTaskList::GetInterimPath(const fs::path& path) const +std::filesystem::path mitk::SegmentationTaskList::GetInterimPath(const std::filesystem::path& path) const { if (path.empty() || !path.has_filename()) return path; auto interimPath = path; return interimPath.replace_extension(".interim" + path.extension().string()); } void mitk::SegmentationTaskList::SaveTask(size_t index, const BaseData* segmentation, bool saveAsIntermediateResult) { if (segmentation == nullptr) return; auto path = this->GetAbsolutePath(this->GetResult(index)); auto interimPath = this->GetInterimPath(path); - if (fs::exists(path)) + if (std::filesystem::exists(path)) saveAsIntermediateResult = false; IOUtil::Save(segmentation, saveAsIntermediateResult ? interimPath.string() : path.string()); - if (!saveAsIntermediateResult && fs::exists(interimPath)) + if (!saveAsIntermediateResult && std::filesystem::exists(interimPath)) { std::error_code ec; - fs::remove(interimPath, ec); + std::filesystem::remove(interimPath, ec); } } std::vector::const_iterator mitk::SegmentationTaskList::begin() const { return m_Tasks.begin(); } std::vector::const_iterator mitk::SegmentationTaskList::end() const { return m_Tasks.end(); } std::vector::iterator mitk::SegmentationTaskList::begin() { return m_Tasks.begin(); } std::vector::iterator mitk::SegmentationTaskList::end() { return m_Tasks.end(); } void mitk::SegmentationTaskList::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::SegmentationTaskList::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::SegmentationTaskList::VerifyRequestedRegion() { return true; } void mitk::SegmentationTaskList::SetRequestedRegion(const itk::DataObject*) { } diff --git a/Modules/Multilabel/mitkSegmentationTaskList.h b/Modules/Multilabel/mitkSegmentationTaskList.h index d1513ab3f3..f1f739d9f5 100644 --- a/Modules/Multilabel/mitkSegmentationTaskList.h +++ b/Modules/Multilabel/mitkSegmentationTaskList.h @@ -1,111 +1,111 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkSegmentationTaskList_h #define mitkSegmentationTaskList_h #include #include #include -#include +#include #include namespace mitk { /** \brief A list of segmentation tasks. * * See \ref MITKSegmentationTaskListsPage for more information. */ class MITKMULTILABEL_EXPORT SegmentationTaskList : public BaseData { public: class MITKMULTILABEL_EXPORT Task { public: Task(); ~Task(); void SetDefaults(const Task* defaults); mitkSegmentationTaskValueMacro(std::string, Name) mitkSegmentationTaskValueMacro(std::string, Description) - mitkSegmentationTaskValueMacro(fs::path, Image) - mitkSegmentationTaskValueMacro(fs::path, Segmentation) + mitkSegmentationTaskValueMacro(std::filesystem::path, Image) + mitkSegmentationTaskValueMacro(std::filesystem::path, Segmentation) mitkSegmentationTaskValueMacro(std::string, LabelName) - mitkSegmentationTaskValueMacro(fs::path, LabelNameSuggestions) - mitkSegmentationTaskValueMacro(fs::path, Preset) - mitkSegmentationTaskValueMacro(fs::path, Result) + mitkSegmentationTaskValueMacro(std::filesystem::path, LabelNameSuggestions) + mitkSegmentationTaskValueMacro(std::filesystem::path, Preset) + mitkSegmentationTaskValueMacro(std::filesystem::path, Result) mitkSegmentationTaskValueMacro(bool, Dynamic) private: const Task* m_Defaults; }; mitkClassMacro(SegmentationTaskList, BaseData) itkFactorylessNewMacro(Self) itkCloneMacro(Self) mitkSegmentationTaskListValueMacro(std::string, Name) mitkSegmentationTaskListValueMacro(std::string, Description) - mitkSegmentationTaskListValueMacro(fs::path, Image) - mitkSegmentationTaskListValueMacro(fs::path, Segmentation) + mitkSegmentationTaskListValueMacro(std::filesystem::path, Image) + mitkSegmentationTaskListValueMacro(std::filesystem::path, Segmentation) mitkSegmentationTaskListValueMacro(std::string, LabelName) - mitkSegmentationTaskListValueMacro(fs::path, LabelNameSuggestions) - mitkSegmentationTaskListValueMacro(fs::path, Preset) - mitkSegmentationTaskListValueMacro(fs::path, Result) + mitkSegmentationTaskListValueMacro(std::filesystem::path, LabelNameSuggestions) + mitkSegmentationTaskListValueMacro(std::filesystem::path, Preset) + mitkSegmentationTaskListValueMacro(std::filesystem::path, Result) mitkSegmentationTaskListValueMacro(bool, Dynamic) size_t GetNumberOfTasks() const; size_t AddTask(const Task& subtask); const Task* GetTask(size_t index) const; Task* GetTask(size_t index); const Task& GetDefaults() const; void SetDefaults(const Task& defaults); bool IsDone() const; bool IsDone(size_t index) const; - fs::path GetInputLocation() const; - fs::path GetBasePath() const; - fs::path GetAbsolutePath(const fs::path& path) const; - fs::path GetInterimPath(const fs::path& path) const; + std::filesystem::path GetInputLocation() const; + std::filesystem::path GetBasePath() const; + std::filesystem::path GetAbsolutePath(const std::filesystem::path& path) const; + std::filesystem::path GetInterimPath(const std::filesystem::path& path) const; void SaveTask(size_t index, const BaseData* segmentation, bool saveAsIntermediateResult = false); std::vector::const_iterator begin() const; std::vector::const_iterator end() const; std::vector::iterator begin(); std::vector::iterator end(); void SetRequestedRegionToLargestPossibleRegion() override; bool RequestedRegionIsOutsideOfTheBufferedRegion() override; bool VerifyRequestedRegion() override; void SetRequestedRegion(const itk::DataObject*) override; protected: mitkCloneMacro(Self) SegmentationTaskList(); SegmentationTaskList(const Self& other); ~SegmentationTaskList() override; private: Task m_Defaults; std::vector m_Tasks; }; } #endif diff --git a/Modules/OpenCVVideoSupport/CMakeLists.txt b/Modules/OpenCVVideoSupport/CMakeLists.txt index 2040203b4a..10db853b78 100644 --- a/Modules/OpenCVVideoSupport/CMakeLists.txt +++ b/Modules/OpenCVVideoSupport/CMakeLists.txt @@ -1,19 +1,24 @@ -set(dependencies OpenCV) +set(dependencies OpenCV|videoio+calib3d) + +# adding option for videoinput library on windows (for directshow based frame grabbing) +if(WIN32 AND MITK_USE_OpenCV) + option(MITK_USE_videoInput "Use videoInput (DirectShow wrapper) library" OFF) +endif() + if(MITK_USE_videoInput) - set(dependencies ${dependencies} videoInput) -endif(MITK_USE_videoInput) + list(APPEND dependencies videoInput) +endif() mitk_create_module( INCLUDE_DIRS Commands DEPENDS MitkAlgorithmsExt PACKAGE_DEPENDS PUBLIC ${dependencies} - ADDITIONAL_LIBS ${OPENCVVIDEOSUPPORT_ADDITIONAL_LIBS} ) if(MODULE_IS_ENABLED) if(MITK_USE_Qt5) add_subdirectory(UI) endif() endif() add_subdirectory(Testing) diff --git a/Modules/OpenCVVideoSupport/Commands/mitkConvertGrayscaleOpenCVImageFilter.cpp b/Modules/OpenCVVideoSupport/Commands/mitkConvertGrayscaleOpenCVImageFilter.cpp index 56a6241f47..91385c18c3 100644 --- a/Modules/OpenCVVideoSupport/Commands/mitkConvertGrayscaleOpenCVImageFilter.cpp +++ b/Modules/OpenCVVideoSupport/Commands/mitkConvertGrayscaleOpenCVImageFilter.cpp @@ -1,33 +1,34 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkConvertGrayscaleOpenCVImageFilter.h" #include +#include namespace mitk { bool ConvertGrayscaleOpenCVImageFilter::OnFilterImage( cv::Mat& image ) { // there is nothing to do if the image is grayscale already if (image.channels() == 1) { return true; } cv::Mat buffer; cv::cvtColor(image, buffer, CV_RGB2GRAY, 1); // content of buffer should now be the content of image buffer.copyTo(image); return true; } } // namespace mitk diff --git a/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.cpp b/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.cpp index 294c802013..dd911780a9 100644 --- a/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.cpp +++ b/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.cpp @@ -1,391 +1,392 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // mitk headers #include "mitkGrabCutOpenCVImageFilter.h" #include "mitkPointSet.h" #include +#include // This is a magic number defined in "grabcut.cpp" of OpenCV. // GrabCut function crashes if less than this number of model // points are given. There must be at least as much model points // as components of the Gaussian Mixture Model. #define GMM_COMPONENTS_COUNT 5 mitk::GrabCutOpenCVImageFilter::GrabCutOpenCVImageFilter() : m_ModelPointsDilationSize(0), m_UseOnlyRegionAroundModelPoints(false), m_CurrentProcessImageNum(0), m_InputImageId(AbstractOpenCVImageFilter::INVALID_IMAGE_ID), m_ResultImageId(AbstractOpenCVImageFilter::INVALID_IMAGE_ID), m_StopThread(false) { m_Thread = std::thread(&GrabCutOpenCVImageFilter::SegmentationWorker, this); } mitk::GrabCutOpenCVImageFilter::~GrabCutOpenCVImageFilter() { // terminate worker thread on destruction m_StopThread = true; m_WorkerBarrier.notify_all(); if (m_Thread.joinable()) m_Thread.detach(); } bool mitk::GrabCutOpenCVImageFilter::OnFilterImage( cv::Mat& image ) { if ( image.empty() ) { MITK_WARN << "Filtering empty image?"; return false; } // make sure that the image is an rgb image as needed // by the GrabCut algorithm if (image.type() != CV_8UC3) { cv::Mat tmp = image.clone(); cv::cvtColor(tmp, image, CV_GRAY2RGB); } // set image as the current input image, guarded by // a mutex as the worker thread reads this imagr m_ImageMutex.lock(); m_InputImage = image.clone(); m_InputImageId = this->GetCurrentImageId(); m_ImageMutex.unlock(); // wake up the worker thread if there was an image set // and foreground model points are available if ( ! m_ForegroundPoints.empty()) { m_WorkerBarrier.notify_all(); } return true; } void mitk::GrabCutOpenCVImageFilter::SetModelPoints(ModelPointsList foregroundPoints) { m_PointSetsMutex.lock(); m_ForegroundPoints = foregroundPoints; m_PointSetsMutex.unlock(); } void mitk::GrabCutOpenCVImageFilter::SetModelPoints(ModelPointsList foregroundPoints, ModelPointsList backgroundPoints) { m_PointSetsMutex.lock(); m_BackgroundPoints = backgroundPoints; m_ForegroundPoints = foregroundPoints; m_PointSetsMutex.unlock(); } void mitk::GrabCutOpenCVImageFilter::SetModelPoints(cv::Mat foregroundMask) { m_PointSetsMutex.lock(); m_ForegroundPoints = this->ConvertMaskToModelPointsList(foregroundMask); m_PointSetsMutex.unlock(); } void mitk::GrabCutOpenCVImageFilter::SetModelPoints(cv::Mat foregroundMask, cv::Mat backgroundMask) { m_PointSetsMutex.lock(); m_ForegroundPoints = this->ConvertMaskToModelPointsList(foregroundMask); m_BackgroundPoints = this->ConvertMaskToModelPointsList(backgroundMask); m_PointSetsMutex.unlock(); } void mitk::GrabCutOpenCVImageFilter::SetModelPointsDilationSize(int modelPointsDilationSize) { if ( modelPointsDilationSize < 0 ) { MITK_ERROR("AbstractOpenCVImageFilter")("GrabCutOpenCVImageFilter") << "Model points dilation size must not be smaller then zero."; mitkThrow() << "Model points dilation size must not be smaller then zero."; } m_ModelPointsDilationSize = modelPointsDilationSize; } void mitk::GrabCutOpenCVImageFilter::SetUseOnlyRegionAroundModelPoints(unsigned int additionalWidth) { m_UseOnlyRegionAroundModelPoints = true; m_AdditionalWidth = additionalWidth; } void mitk::GrabCutOpenCVImageFilter::SetUseFullImage() { m_UseOnlyRegionAroundModelPoints = false; } cv::Rect mitk::GrabCutOpenCVImageFilter::GetRegionAroundModelPoints() { return m_BoundingBox; } int mitk::GrabCutOpenCVImageFilter::GetResultImageId() { return m_ResultImageId; } cv::Mat mitk::GrabCutOpenCVImageFilter::GetResultMask() { cv::Mat result; m_ResultMutex.lock(); result = m_ResultMask.clone(); m_ResultMutex.unlock(); return result; } std::vector mitk::GrabCutOpenCVImageFilter::GetResultContours() { std::vector > cvContours; std::vector hierarchy; std::vector contourPoints; cv::Mat resultMask = this->GetResultMask(); if (resultMask.empty()) { return contourPoints; } cv::findContours(resultMask, cvContours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_NONE); // convert cvContours to vector of ModelPointsLists for ( unsigned int i = 0; i < cvContours.size(); ++i ) { mitk::GrabCutOpenCVImageFilter::ModelPointsList curContourPoints; for ( auto it = cvContours[i].begin(); it != cvContours[i].end(); ++it) { itk::Index<2> index; index.SetElement(0, it->x); index.SetElement(1, it->y); curContourPoints.push_back(index); } contourPoints.push_back(curContourPoints); } return contourPoints; } mitk::GrabCutOpenCVImageFilter::ModelPointsList mitk::GrabCutOpenCVImageFilter::GetResultContourWithPixel(itk::Index<2> pixelIndex) { cv::Mat mask = this->GetResultMask(); if (mask.empty()) { return mitk::GrabCutOpenCVImageFilter::ModelPointsList(); } // return empty model point list if given pixel is outside the image borders if (pixelIndex.GetElement(0) < 0 || pixelIndex.GetElement(0) >= mask.size().height || pixelIndex.GetElement(1) < 0 || pixelIndex.GetElement(1) >= mask.size().width) { MITK_WARN("AbstractOpenCVImageFilter")("GrabCutOpenCVImageFilter") << "Given pixel index ("<< pixelIndex.GetElement(0) << ", " << pixelIndex.GetElement(1) << ") is outside the image (" << mask.size().height << ", " << mask.size().width << ")."; return mitk::GrabCutOpenCVImageFilter::ModelPointsList(); } // create a mask where the segmentation around the given pixel index is // set (done by flood filling the result mask using the pixel as seed) cv::floodFill(mask, cv::Point(pixelIndex.GetElement(0), pixelIndex.GetElement(1)), 5); cv::Mat foregroundMask; cv::compare(mask, 5, foregroundMask, cv::CMP_EQ); // find the contour on the flood filled image (there can be only one now) std::vector > cvContours; std::vector hierarchy; cv::findContours(foregroundMask, cvContours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_NONE); ModelPointsList contourPoints; // convert cvContours to ModelPointsList for ( auto it = cvContours[0].begin(); it != cvContours[0].end(); ++it) { itk::Index<2> index; index.SetElement(0, it->x); index.SetElement(1, it->y); contourPoints.push_back(index); } return contourPoints; } cv::Mat mitk::GrabCutOpenCVImageFilter::GetMaskFromPointSets() { // initialize mask with values of propably background cv::Mat mask(m_InputImage.size().height, m_InputImage.size().width, CV_8UC1, cv::GC_PR_BGD); // get foreground and background points (guarded by mutex) m_PointSetsMutex.lock(); ModelPointsList pointsLists[2] = {ModelPointsList(m_ForegroundPoints), ModelPointsList(m_BackgroundPoints)}; m_PointSetsMutex.unlock(); // define values for foreground and background pixels unsigned int pixelValues[2] = {cv::GC_FGD, cv::GC_BGD}; for (unsigned int n = 0; n < 2; ++n) { for (auto it = pointsLists[n].begin(); it != pointsLists[n].end(); ++it) { // set pixels around current pixel to the same value (size of this // area is specified by ModelPointsDilationSize) for ( int i = -m_ModelPointsDilationSize; i <= m_ModelPointsDilationSize; ++i ) { for ( int j = -m_ModelPointsDilationSize; j <= m_ModelPointsDilationSize; ++j) { int x = it->GetElement(1) + i; int y = it->GetElement(0) + j; if ( x >= 0 && y >= 0 && x < mask.cols && y < mask.rows) { mask.at(x, y) = pixelValues[n]; } } } } } return mask; } cv::Rect mitk::GrabCutOpenCVImageFilter::GetBoundingRectFromMask(cv::Mat mask) { cv::Mat nonPropablyBackgroundMask, modelPoints; cv::compare(mask, cv::GC_PR_BGD, nonPropablyBackgroundMask, cv::CMP_NE); cv::findNonZero(nonPropablyBackgroundMask, modelPoints); if (modelPoints.empty()) { MITK_WARN("AbstractOpenCVImageFilter")("GrabCutOpenCVImageFilter") << "Cannot find any foreground points. Returning full image size as bounding rectangle."; return cv::Rect(0, 0, mask.rows, mask.cols); } // calculate bounding rect around the model points cv::Rect boundingRect = cv::boundingRect(modelPoints); // substract additional width to x and y value (and make sure that they aren't outside the image then) boundingRect.x = static_cast(boundingRect.x) > m_AdditionalWidth ? boundingRect.x - m_AdditionalWidth : 0; boundingRect.y = static_cast(boundingRect.y) > m_AdditionalWidth ? boundingRect.y - m_AdditionalWidth : 0; // add additional width to width of bounding rect (twice as x value was moved before) // and make sure that the bounding rect will stay inside the image borders) if ( static_cast(boundingRect.x + boundingRect.width) + 2 * m_AdditionalWidth < static_cast(mask.size().width) ) { boundingRect.width += 2 * m_AdditionalWidth; } else { boundingRect.width = mask.size().width - boundingRect.x - 1; } // add additional width to height of bounding rect (twice as y value was moved before) // and make sure that the bounding rect will stay inside the image borders) if ( static_cast(boundingRect.y + boundingRect.height) + 2 * m_AdditionalWidth < static_cast(mask.size().height) ) { boundingRect.height += 2 * m_AdditionalWidth; } else { boundingRect.height = mask.size().height - boundingRect.y - 1; } assert(boundingRect.x + boundingRect.width < mask.size().width); assert(boundingRect.y + boundingRect.height < mask.size().height); return boundingRect; } cv::Mat mitk::GrabCutOpenCVImageFilter::RunSegmentation(cv::Mat input, cv::Mat mask) { // test if foreground and background models are large enough for GrabCut cv::Mat compareFgResult, compareBgResult; cv::compare(mask, cv::GC_FGD, compareFgResult, cv::CMP_EQ); cv::compare(mask, cv::GC_PR_BGD, compareBgResult, cv::CMP_EQ); if ( cv::countNonZero(compareFgResult) < GMM_COMPONENTS_COUNT || cv::countNonZero(compareBgResult) < GMM_COMPONENTS_COUNT) { // return result mask with no pixels set to foreground return cv::Mat::zeros(mask.size(), mask.type()); } // do the actual grab cut segmentation (initialized with the mask) cv::Mat bgdModel, fgdModel; cv::grabCut(input, mask, cv::Rect(), bgdModel, fgdModel, 1, cv::GC_INIT_WITH_MASK); // set propably foreground pixels to white on result mask cv::Mat result; cv::compare(mask, cv::GC_PR_FGD, result, cv::CMP_EQ); // set foreground pixels to white on result mask cv::Mat foregroundMat; cv::compare(mask, cv::GC_FGD, foregroundMat, cv::CMP_EQ); foregroundMat.copyTo(result, foregroundMat); return result; // now the result mask can be returned } mitk::GrabCutOpenCVImageFilter::ModelPointsList mitk::GrabCutOpenCVImageFilter::ConvertMaskToModelPointsList(cv::Mat mask) { cv::Mat points; cv::findNonZero(mask, points); // push extracted points into a vector of itk indices ModelPointsList pointsVector; for ( size_t n = 0; n < points.total(); ++n) { itk::Index<2> index; index.SetElement(0, points.at(n).x); index.SetElement(1, points.at(n).y); pointsVector.push_back(index); } return pointsVector; } void mitk::GrabCutOpenCVImageFilter::SegmentationWorker() { std::mutex mutex; std::unique_lock lock(mutex); while (true) { m_WorkerBarrier.wait(lock, [this] { return !m_StopThread; }); m_ImageMutex.lock(); cv::Mat image = m_InputImage.clone(); int inputImageId = m_InputImageId; m_ImageMutex.unlock(); cv::Mat mask = this->GetMaskFromPointSets(); cv::Mat result; if (m_UseOnlyRegionAroundModelPoints) { result = cv::Mat(mask.rows, mask.cols, mask.type(), 0.0); m_BoundingBox = this->GetBoundingRectFromMask(mask); RunSegmentation(image(m_BoundingBox), mask(m_BoundingBox)).copyTo(result(m_BoundingBox)); } else { result = this->RunSegmentation(image, mask); } // save result to member attribute m_ResultMutex.lock(); m_ResultMask = result; m_ResultImageId = inputImageId; m_ResultMutex.unlock(); } } diff --git a/Modules/OpenCVVideoSupport/Testing/mitkBasicCombinationOpenCVImageFilterTest.cpp b/Modules/OpenCVVideoSupport/Testing/mitkBasicCombinationOpenCVImageFilterTest.cpp index 215ea78ac6..bd2ab53c9f 100644 --- a/Modules/OpenCVVideoSupport/Testing/mitkBasicCombinationOpenCVImageFilterTest.cpp +++ b/Modules/OpenCVVideoSupport/Testing/mitkBasicCombinationOpenCVImageFilterTest.cpp @@ -1,88 +1,89 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkBasicCombinationOpenCVImageFilter.h" #include "mitkConvertGrayscaleOpenCVImageFilter.h" #include "mitkCropOpenCVImageFilter.h" #include -#include +#include +#include #include static bool ImagesAreEqualInGray(const cv::Mat& img1, const cv::Mat& img2) { cv::Mat grayImg1; cv::Mat grayImg2; cv::cvtColor(img1, grayImg1, CV_RGB2GRAY, 1); cv::cvtColor(img2, grayImg2, CV_RGB2GRAY, 1); return cv::countNonZero(grayImg1 != grayImg2) == 0; } static void ConvertTestLoadedImage(std::string mitkImagePath) { cv::Mat image = cv::imread(mitkImagePath.c_str()); cv::Mat compareImg = image.clone(); mitk::BasicCombinationOpenCVImageFilter::Pointer combinationFilter = mitk::BasicCombinationOpenCVImageFilter::New(); MITK_TEST_CONDITION(combinationFilter->FilterImage(image), "Filtering with empty filter list is ok."); MITK_TEST_CONDITION(ImagesAreEqualInGray(image, compareImg), "Image must not be changed after filtering with empty filter list."); mitk::ConvertGrayscaleOpenCVImageFilter::Pointer grayscaleFilter = mitk::ConvertGrayscaleOpenCVImageFilter::New(); combinationFilter->PushFilter(grayscaleFilter.GetPointer()); MITK_TEST_CONDITION(combinationFilter->FilterImage(image), "Filtering with grayscale filter should be ok."); MITK_TEST_CONDITION(image.channels() == 1, "Image must not have more than one channel after grayscale conversion."); image.release(); image = compareImg.clone(); mitk::CropOpenCVImageFilter::Pointer cropFilter = mitk::CropOpenCVImageFilter::New(); combinationFilter->PushFilter(cropFilter.GetPointer()); MITK_TEST_CONDITION( ! combinationFilter->FilterImage(image), "Filter function must return false if an filter returns false."); MITK_TEST_CONDITION(combinationFilter->PopFilter() == cropFilter, "Last added filter is equal to returned filter."); image.release(); image = compareImg.clone(); MITK_TEST_CONDITION(combinationFilter->FilterImage(image), "Filter function should return true again after removing incorrect filter."); MITK_TEST_CONDITION(combinationFilter->RemoveFilter(grayscaleFilter.GetPointer()), "Filter must be found."); image.release(); image = compareImg.clone(); MITK_TEST_CONDITION(combinationFilter->FilterImage(image), "Filter function should still return true."); MITK_TEST_CONDITION(ImagesAreEqualInGray(image, compareImg), "Image must not be changed after all filters were removed."); } /**Documentation * test for the class "ConvertGrayscaleOpenCVImageFilter". */ int mitkBasicCombinationOpenCVImageFilterTest(int argc, char* argv[]) { MITK_TEST_BEGIN("BasicCombinationOpenCVImageFilter") MITK_TEST_CONDITION_REQUIRED(argc == 2, "Two parameters are needed for this test.") ConvertTestLoadedImage(argv[1]); MITK_TEST_END(); // always end with this! } diff --git a/Modules/OpenCVVideoSupport/Testing/mitkConvertGrayscaleOpenCVImageFilterTest.cpp b/Modules/OpenCVVideoSupport/Testing/mitkConvertGrayscaleOpenCVImageFilterTest.cpp index 734589e1d4..cb063aa307 100644 --- a/Modules/OpenCVVideoSupport/Testing/mitkConvertGrayscaleOpenCVImageFilterTest.cpp +++ b/Modules/OpenCVVideoSupport/Testing/mitkConvertGrayscaleOpenCVImageFilterTest.cpp @@ -1,53 +1,54 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkConvertGrayscaleOpenCVImageFilter.h" #include #include +#include #include static void ConvertTestLoadedImage(std::string mitkImagePath, std::string mitkGrayscaleImagePath) { cv::Mat image = cv::imread(mitkImagePath.c_str()); cv::Mat compareImg = cv::imread(mitkGrayscaleImagePath.c_str()); // directly convert the image for comparison cv::Mat comparisonImg; cv::cvtColor(compareImg, comparisonImg, CV_RGB2GRAY, 1); mitk::ConvertGrayscaleOpenCVImageFilter::Pointer grayscaleFilter = mitk::ConvertGrayscaleOpenCVImageFilter::New(); MITK_TEST_CONDITION(grayscaleFilter->FilterImage(image), "Filtering should return true for success."); MITK_TEST_CONDITION(image.channels() == 1, "Image must not have more than one channel after grayscale conversion."); MITK_TEST_CONDITION(cv::countNonZero(image != comparisonImg) == 0, "All pixel values must be the same between the two converted images."); MITK_TEST_CONDITION_REQUIRED(grayscaleFilter->FilterImage(image), "Image conversion should be no problem if image is a grayscale image already.") } /**Documentation * test for the class "ConvertGrayscaleOpenCVImageFilter". */ int mitkConvertGrayscaleOpenCVImageFilterTest(int argc, char* argv[]) { MITK_TEST_BEGIN("ConvertGrayscaleOpenCVImageFilter") MITK_TEST_CONDITION_REQUIRED(argc > 2, "At least three parameters needed for this test.") ConvertTestLoadedImage(argv[1], argv[2]); MITK_TEST_END(); // always end with this! } diff --git a/Modules/OpenCVVideoSupport/Testing/mitkCropOpenCVImageFilterTest.cpp b/Modules/OpenCVVideoSupport/Testing/mitkCropOpenCVImageFilterTest.cpp index 954b9c2791..d9f59c6bbd 100644 --- a/Modules/OpenCVVideoSupport/Testing/mitkCropOpenCVImageFilterTest.cpp +++ b/Modules/OpenCVVideoSupport/Testing/mitkCropOpenCVImageFilterTest.cpp @@ -1,97 +1,98 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkCropOpenCVImageFilter.h" #include #include +#include #include static bool ImagesAreEqualInGray(const cv::Mat& img1, const cv::Mat& img2) { cv::Mat grayImg1; cv::Mat grayImg2; cv::cvtColor(img1, grayImg1, CV_RGB2GRAY, 1); cv::cvtColor(img2, grayImg2, CV_RGB2GRAY, 1); return cv::countNonZero(grayImg1 != grayImg2) == 0; } static void CropTestLoadedImage(std::string mitkImagePath, std::string mitkCroppedImagePath) { cv::Mat image = cv::imread(mitkImagePath.c_str()); cv::Mat croppedImage = cv::imread(mitkCroppedImagePath.c_str()); MITK_INFO << mitkImagePath.c_str(); MITK_INFO << mitkCroppedImagePath.c_str(); mitk::CropOpenCVImageFilter::Pointer cropFilter = mitk::CropOpenCVImageFilter::New(); // try to crop without setting a region of interest cv::Mat testImage = image.clone(); MITK_TEST_CONDITION( ! cropFilter->FilterImage(testImage), "Filter function must return false if no region of interest is set."); MITK_TEST_CONDITION(ImagesAreEqualInGray(testImage, image), "Image should not be changed yet."); // set region of interst now and then try to crop again cv::Rect roi = cv::Rect(0,0, testImage.cols, testImage.rows); cropFilter->SetCropRegion(roi); MITK_TEST_CONDITION(cropFilter->FilterImage(testImage), "Filter function should return successfully."); MITK_TEST_CONDITION(ImagesAreEqualInGray(testImage, image), "Image should not be changed if cropping with a roi of the whole image."); // test if filter corrects negative roi position cv::Rect roiWrong = cv::Rect(-1,-1, 2, 2); roi = cv::Rect(0,0,2,2); cropFilter->SetCropRegion(roiWrong); MITK_TEST_CONDITION(cropFilter->FilterImage(testImage), "Filter function should return successfully."); MITK_TEST_CONDITION(ImagesAreEqualInGray(testImage, image(roi)), "Image should be equal to directly cropped image whith correct roi."); // test whith "normal" roi testImage = image.clone(); roi = cv::Rect( 150,100,100,100 ); cropFilter->SetCropRegion(roi); MITK_TEST_CONDITION(cropFilter->FilterImage(testImage), "Filter function should return successfully."); MITK_TEST_CONDITION(ImagesAreEqualInGray(testImage, croppedImage), "Image should be equal to cropped image (loaded from data directory)."); // test with not correctable roi roiWrong = cv::Rect( 5,5,-1,-1 ); MITK_TEST_FOR_EXCEPTION(mitk::Exception, cropFilter->SetCropRegion(roiWrong)); // test with rois where the top left corner is outside the image boundaries roiWrong = cv::Rect( testImage.cols,0,1,1 ); cropFilter->SetCropRegion(roiWrong); MITK_TEST_CONDITION(!cropFilter->FilterImage(testImage), "Filter function should return unsuccessfully if top left corner is outside image boundary (cols)."); roiWrong = cv::Rect( 0,testImage.rows,1,1 ); cropFilter->SetCropRegion(roiWrong); MITK_TEST_CONDITION(!cropFilter->FilterImage(testImage), "Filter function should return unsuccessfully if top left corner is outside image boundary (rows)."); roiWrong = cv::Rect( testImage.cols,testImage.rows,1,1 ); cropFilter->SetCropRegion(roiWrong); MITK_TEST_CONDITION(!cropFilter->FilterImage(testImage), "Filter function should return unsuccessfully if top left corner is outside image boundary (cols+rows)."); } /**Documentation * test for the class "CropOpenCVImageFilter". */ int mitkCropOpenCVImageFilterTest(int argc, char* argv[]) { MITK_TEST_BEGIN("CropOpenCVImageFilter") MITK_TEST_CONDITION_REQUIRED(argc > 2, "At least three parameters needed for this test."); CropTestLoadedImage(argv[1], argv[2]); MITK_TEST_END(); // always end with this! } diff --git a/Modules/OpenCVVideoSupport/Testing/mitkGrabCutOpenCVImageFilterTest.cpp b/Modules/OpenCVVideoSupport/Testing/mitkGrabCutOpenCVImageFilterTest.cpp index be50a4358c..4d9288e879 100644 --- a/Modules/OpenCVVideoSupport/Testing/mitkGrabCutOpenCVImageFilterTest.cpp +++ b/Modules/OpenCVVideoSupport/Testing/mitkGrabCutOpenCVImageFilterTest.cpp @@ -1,177 +1,178 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkGrabCutOpenCVImageFilter.h" #include #include "itkIndex.h" #include #include +#include #include #include "mitkOpenCVToMitkImageFilter.h" static void GrabCutTestLoadedImage(std::string imagePath, std::string maskPath, std::string resultMaskPath) { // load test images cv::Mat image = cv::imread(imagePath.c_str()); cv::Mat maskImage = cv::imread(maskPath.c_str()); cv::Mat resultMaskImage = cv::imread(resultMaskPath.c_str()); // make sure that the loaded mask is a gray scale image cv::Mat maskImageGray; cv::cvtColor(maskImage, maskImageGray, CV_RGB2GRAY, 1); // make sure that the loaded reference image is a gray scale image cv::Mat resultMaskImageGray; cv::cvtColor(resultMaskImage, resultMaskImageGray, CV_RGB2GRAY, 1); // extract foreground points from loaded mask image cv::Mat foregroundMask, foregroundPoints; cv::compare(maskImageGray, 250, foregroundMask, cv::CMP_GE); cv::findNonZero(foregroundMask, foregroundPoints); // push extracted forground points into a vector of itk indices std::vector > foregroundPointsVector; for ( size_t n = 0; n < foregroundPoints.total(); ++n) { itk::Index<2> index; index.SetElement(0, foregroundPoints.at(n).x); index.SetElement(1, foregroundPoints.at(n).y); foregroundPointsVector.push_back(index); } mitk::GrabCutOpenCVImageFilter::Pointer grabCutFilter = mitk::GrabCutOpenCVImageFilter::New(); int currentImageId = 0; // test filtering with image set but no model points set { MITK_TEST_CONDITION(grabCutFilter->FilterImage(image), "Filtering should return true for sucess.") cv::Mat resultMask = grabCutFilter->GetResultMask(); MITK_TEST_CONDITION(resultMask.empty(), "Result mask must be empty when no foreground points are set.") } // test filtering with very little model points set { std::vector > littleForegroundPointsSet(foregroundPointsVector.begin(), foregroundPointsVector.begin()+3); grabCutFilter->SetModelPoints(littleForegroundPointsSet); grabCutFilter->FilterImage(image, ++currentImageId); cv::Mat resultMask; // wait up to ten seconds for the segmentation to finish for (unsigned int n = 0; n < 100; ++n) { if ( grabCutFilter->GetResultImageId() == currentImageId ) { resultMask = grabCutFilter->GetResultMask(); break; } itksys::SystemTools::Delay(100); } MITK_TEST_CONDITION(!resultMask.empty(), "Result mask must not be empty when little (" << littleForegroundPointsSet.size() <<") foreground points are set."); } // test filtering with image and model points set { grabCutFilter->SetModelPoints(foregroundPointsVector); MITK_TEST_CONDITION(grabCutFilter->FilterImage(image, ++currentImageId), "Filtering should return true for sucess.") cv::Mat resultMask; // wait up to ten seconds for the segmentation to finish for (unsigned int n = 0; n < 100; ++n) { if ( grabCutFilter->GetResultImageId() == currentImageId ) { resultMask = grabCutFilter->GetResultMask(); break; } itksys::SystemTools::Delay(100); } MITK_TEST_CONDITION( ! resultMask.empty() && cv::countNonZero(resultMask != resultMaskImageGray) == 0, "Filtered image should match reference image.") // adding new image should still work MITK_TEST_CONDITION(grabCutFilter->FilterImage(image), "Adding new image should still work.") } // test filtering with using only region around model points // (but with really big additional width so that whole image should be used again) { grabCutFilter->SetUseOnlyRegionAroundModelPoints(image.cols); grabCutFilter->FilterImage(image, ++currentImageId); cv::Mat resultMask; // wait up to ten seconds for the segmentation to finish for (unsigned int n = 0; n < 100; ++n) { if (grabCutFilter->GetResultImageId() == currentImageId) { resultMask = grabCutFilter->GetResultMask(); break; } itksys::SystemTools::Delay(100); } MITK_TEST_CONDITION( ! resultMask.empty() && cv::countNonZero(resultMask != resultMaskImageGray) == 0, "Filtered image with really big region used should match reference image again.") } // test filtering with using only region around model points { grabCutFilter->SetUseOnlyRegionAroundModelPoints(0); grabCutFilter->FilterImage(image, ++currentImageId); cv::Mat resultMask; // wait up to ten seconds for the segmentation to finish for (unsigned int n = 0; n < 100; ++n) { if (grabCutFilter->GetResultImageId() == currentImageId) { resultMask = grabCutFilter->GetResultMask(); break; } itksys::SystemTools::Delay(100); } cv::Mat nonPropablyBackgroundMask, modelPoints; cv::compare(maskImageGray, 250, nonPropablyBackgroundMask, cv::CMP_GE); cv::findNonZero(nonPropablyBackgroundMask, modelPoints); cv::Rect boundingRect = cv::boundingRect(modelPoints); cv::Mat compareMask(resultMask.rows, resultMask.cols, resultMask.type(), 0.0); resultMaskImageGray(boundingRect).copyTo(compareMask(boundingRect)); MITK_TEST_CONDITION( ! resultMask.empty() && cv::countNonZero(resultMask != compareMask) == 0, "Filtered image with region just around the model points used should match reference image again.") } } int mitkGrabCutOpenCVImageFilterTest(int argc, char* argv[]) { MITK_TEST_BEGIN("GrabCutOpenCVImageFilter") MITK_TEST_CONDITION_REQUIRED(argc == 4, "Test needs four command line parameters.") GrabCutTestLoadedImage(argv[1], argv[2], argv[3]); MITK_TEST_END() // always end with this! } diff --git a/Modules/OpenCVVideoSupport/Testing/mitkOpenCVMitkConversionTest.cpp b/Modules/OpenCVVideoSupport/Testing/mitkOpenCVMitkConversionTest.cpp index a5c5b0a068..0ed282d3a3 100644 --- a/Modules/OpenCVVideoSupport/Testing/mitkOpenCVMitkConversionTest.cpp +++ b/Modules/OpenCVVideoSupport/Testing/mitkOpenCVMitkConversionTest.cpp @@ -1,247 +1,246 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // mitk includes #include "mitkImageToOpenCVImageFilter.h" #include "mitkOpenCVToMitkImageFilter.h" #include #include #include #include #include "mitkImageReadAccessor.h" #include "mitkImageSliceSelector.h" // itk includes #include #include -#include // define test pixel indexes and intensities and other values typedef itk::RGBPixel< unsigned char > TestUCRGBPixelType; cv::Size testImageSize; cv::Point pos1; cv::Point pos2; cv::Point pos3; cv::Vec3b color1; cv::Vec3b color2; cv::Vec3b color3; uchar greyValue1; uchar greyValue2; uchar greyValue3; /*! Documentation * Test for image conversion of OpenCV images and mitk::Images. It tests the classes * OpenCVToMitkImageFilter and ImageToOpenCVImageFilter */ // Some declarations template void ComparePixels( itk::Image,VImageDimension>* image ); void ReadImageDataAndConvertForthAndBack(std::string imageFileName); void ConvertCVMatForthAndBack(mitk::Image::Pointer inputForCVMat, std::string imageFileName); // Begin the test for mitkImage to OpenCV image conversion and back. int mitkOpenCVMitkConversionTest(int argc, char* argv[]) { MITK_TEST_BEGIN("ImageToOpenCVImageFilter") // the first part of this test checks the conversion of a cv::Mat style OpenCV image. // we build an cv::Mat image MITK_INFO << "setting test values"; testImageSize = cv::Size(11,11); pos1 = cv::Point(0,0); pos2 = cv::Point(5,5); pos3 = cv::Point(10,10); color1 = cv::Vec3b(50,0,0); color2 = cv::Vec3b(0,128,0); color3 = cv::Vec3b(0,0,255); greyValue1 = 0; greyValue2 = 128; greyValue3 = 255; MITK_INFO << "generating test OpenCV image (RGB)"; cv::Mat testRGBImage = cv::Mat::zeros( testImageSize, CV_8UC3 ); // generate some test intensity values testRGBImage.at(pos1)= color1; testRGBImage.at(pos2)= color2; testRGBImage.at(pos3)= color3; // convert it to a mitk::Image MITK_INFO << "converting OpenCV test image to mitk image and comparing scalar rgb values"; mitk::OpenCVToMitkImageFilter::Pointer openCvToMitkFilter = mitk::OpenCVToMitkImageFilter::New(); openCvToMitkFilter->SetOpenCVMat( testRGBImage ); openCvToMitkFilter->Update(); mitk::Image::Pointer mitkImage = openCvToMitkFilter->GetOutput(); AccessFixedTypeByItk(mitkImage.GetPointer(), ComparePixels, (itk::RGBPixel), // rgb image (2) ); // convert it back to OpenCV image MITK_INFO << "converting mitk image to OpenCV image and comparing scalar rgb values"; mitk::ImageToOpenCVImageFilter::Pointer mitkToOpenCv = mitk::ImageToOpenCVImageFilter::New(); mitkToOpenCv->SetImage( mitkImage ); cv::Mat openCvImage = mitkToOpenCv->GetOpenCVMat(); // and test equality of the sentinel pixel cv::Vec3b convertedColor1 = openCvImage.at(pos1); cv::Vec3b convertedColor2 = openCvImage.at(pos2); cv::Vec3b convertedColor3 = openCvImage.at(pos3); MITK_TEST_CONDITION( color1 == convertedColor1, "Testing if initially created color values " << static_cast( color1[0] ) << ", " << static_cast( color1[1] ) << ", " << static_cast( color1[2] ) << " matches the color values " << static_cast( convertedColor1[0] ) << ", " << static_cast( convertedColor1[1] ) << ", " << static_cast( convertedColor1[2] ) << " at the same position " << pos1.x << ", " << pos1.y << " in the back converted OpenCV image" ) MITK_TEST_CONDITION( color2 == convertedColor2, "Testing if initially created color values " << static_cast( color2[0] ) << ", " << static_cast( color2[1] ) << ", " << static_cast( color2[2] ) << " matches the color values " << static_cast( convertedColor2[0] ) << ", " << static_cast( convertedColor2[1] ) << ", " << static_cast( convertedColor2[2] ) << " at the same position " << pos2.x << ", " << pos2.y << " in the back converted OpenCV image" ) MITK_TEST_CONDITION( color3 == convertedColor3, "Testing if initially created color values " << static_cast( color3[0] ) << ", " << static_cast( color3[1] ) << ", " << static_cast( color3[2] ) << " matches the color values " << static_cast( convertedColor3[0] ) << ", " << static_cast( convertedColor3[1] ) << ", " << static_cast( convertedColor3[2] ) << " at the same position " << pos3.x << ", " << pos3.y << " in the back converted OpenCV image" ) // the second part of this test checks the conversion of mitk::Image to cv::Mat and back. for (int i = 1; i < argc; ++i) { ReadImageDataAndConvertForthAndBack(argv[i]); } MITK_TEST_END(); } template void ComparePixels( itk::Image,VImageDimension>* image ) { typedef itk::RGBPixel PixelType; typedef itk::Image ImageType; typename ImageType::IndexType pixelIndex; pixelIndex[0] = pos1.x; pixelIndex[1] = pos1.y; PixelType onePixel = image->GetPixel( pixelIndex ); MITK_TEST_CONDITION( color1[0] == onePixel.GetBlue(), "Testing if blue value (= " << static_cast(color1[0]) << ") at postion " << pos1.x << ", " << pos1.y << " in OpenCV image is " << "equals the blue value (= " << static_cast(onePixel.GetBlue()) << ")" << " in the generated mitk image"); pixelIndex[0] = pos2.x; pixelIndex[1] = pos2.y; onePixel = image->GetPixel( pixelIndex ); MITK_TEST_CONDITION( color2[1] == onePixel.GetGreen(), "Testing if green value (= " << static_cast(color2[1]) << ") at postion " << pos2.x << ", " << pos2.y << " in OpenCV image is " << "equals the green value (= " << static_cast(onePixel.GetGreen()) << ")" << " in the generated mitk image"); pixelIndex[0] = pos3.x; pixelIndex[1] = pos3.y; onePixel = image->GetPixel( pixelIndex ); MITK_TEST_CONDITION( color3[2] == onePixel.GetRed(), "Testing if red value (= " << static_cast(color3[2]) << ") at postion " << pos3.x << ", " << pos3.y << " in OpenCV image is " << "equals the red value (= " << static_cast(onePixel.GetRed()) << ")" << " in the generated mitk image"); } void ReadImageDataAndConvertForthAndBack(std::string imageFileName) { // first we load an mitk::Image from the data repository mitk::Image::Pointer mitkTestImage = mitk::IOUtil::Load(imageFileName); // some format checking mitk::Image::Pointer resultImg = nullptr; if( mitkTestImage->GetDimension() <= 3 ) { if( mitkTestImage->GetDimension() > 2 && mitkTestImage->GetDimension(2) == 1 ) { mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(mitkTestImage); sliceSelector->SetSliceNr(0); sliceSelector->Update(); resultImg = sliceSelector->GetOutput()->Clone(); } else if(mitkTestImage->GetDimension() < 3) { resultImg = mitkTestImage; } else { return; // 3D images are not supported, except with just one slice. } } else { return; // 4D images are not supported! } ConvertCVMatForthAndBack(resultImg, imageFileName); } void ConvertCVMatForthAndBack(mitk::Image::Pointer inputForCVMat, std::string) { // now we convert it to OpenCV Mat mitk::ImageToOpenCVImageFilter::Pointer toOCvConverter = mitk::ImageToOpenCVImageFilter::New(); toOCvConverter->SetImage(inputForCVMat); cv::Mat cvmatTestImage = toOCvConverter->GetOpenCVMat(); MITK_TEST_CONDITION_REQUIRED( !cvmatTestImage.empty(), "Conversion to cv::Mat successful!"); mitk::OpenCVToMitkImageFilter::Pointer toMitkConverter = mitk::OpenCVToMitkImageFilter::New(); toMitkConverter->SetOpenCVMat(cvmatTestImage); toMitkConverter->Update(); // initialize the image with the input image, since we want to test equality and OpenCV does not feature geometries and spacing mitk::Image::Pointer result = inputForCVMat->Clone(); mitk::ImageReadAccessor resultAcc(toMitkConverter->GetOutput(), toMitkConverter->GetOutput()->GetSliceData()); result->SetImportSlice(const_cast(resultAcc.GetData())); if( result->GetPixelType().GetNumberOfComponents() == 1 ) { MITK_ASSERT_EQUAL( result, inputForCVMat, "Testing equality of input and output image of cv::Mat conversion" ); } else if( result->GetPixelType().GetNumberOfComponents() == 3 ) { MITK_ASSERT_EQUAL( result, inputForCVMat, "Testing equality of input and output image of cv::Mat conversion" ); } else { MITK_WARN << "Unhandled number of components used to test equality, please enhance test!"; } // change OpenCV image to test if the filter gets updated cv::Mat changedcvmatTestImage = cvmatTestImage.clone(); if (result->GetPixelType().GetBitsPerComponent() == sizeof(char)*8) { changedcvmatTestImage.at(0,0) = cvmatTestImage.at(0,0) != 0 ? 0 : 1; } else if (result->GetPixelType().GetBitsPerComponent() == sizeof(float)*8) { changedcvmatTestImage.at(0,0) = cvmatTestImage.at(0,0) != 0 ? 0 : 1; } toMitkConverter->SetOpenCVMat(changedcvmatTestImage); toMitkConverter->Update(); MITK_TEST_NOT_EQUAL(toMitkConverter->GetOutput(), inputForCVMat, "Converted image must not be the same as before."); } diff --git a/Modules/OpenCVVideoSupport/UI/QmitkOpenCVVideoControls.cpp b/Modules/OpenCVVideoSupport/UI/QmitkOpenCVVideoControls.cpp index 41dc3fef68..7127bd0068 100644 --- a/Modules/OpenCVVideoSupport/UI/QmitkOpenCVVideoControls.cpp +++ b/Modules/OpenCVVideoSupport/UI/QmitkOpenCVVideoControls.cpp @@ -1,444 +1,446 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkOpenCVVideoControls.h" #include #include #include #include +#include + class QmitkOpenCVVideoControlsPrivate { public: QmitkOpenCVVideoControlsPrivate(QmitkOpenCVVideoControls* q, const std::string& id) : q(q) , m_Id(id) {} /// /// muellerm: persitence service implementation /// PERSISTENCE_GET_SERVICE_METHOD_MACRO QmitkOpenCVVideoControls* q; /// /// muellerm: a unique id for the prop list /// std::string m_Id; void ToPropertyList(); void FromPropertyList(); }; QmitkOpenCVVideoControls::QmitkOpenCVVideoControls(QmitkVideoBackground* _VideoBackground , QmitkRenderWindow* _RenderWindow , QWidget * parent, Qt::WindowFlags f) : QWidget(parent, f) , m_VideoBackground(nullptr) , m_RenderWindow(nullptr) , m_VideoSource(nullptr) , m_Controls(new Ui::QmitkOpenCVVideoControls) , m_SliderCurrentlyMoved(false) , d(new QmitkOpenCVVideoControlsPrivate(this, "QmitkOpenCVVideoControls")) { m_Controls->setupUi(this); m_Controls->FileChooser->SetFileMustExist(true); m_Controls->FileChooser->SetSelectDir(false); this->SetRenderWindow(_RenderWindow); this->SetVideoBackground(_VideoBackground); d->FromPropertyList(); mitk::IPersistenceService* persistenceService = d->GetPersistenceService(); if (persistenceService != nullptr) { persistenceService->AddPropertyListReplacedObserver(this); } else { MITK_WARN << "No Persistence Service available in constructor"; } } QmitkOpenCVVideoControls::~QmitkOpenCVVideoControls() { if (m_VideoSource != nullptr && m_VideoSource->IsCapturingEnabled()) { this->Stop(); // emulate stop } mitk::IPersistenceService* persistenceService = d->GetPersistenceService(); if (persistenceService != nullptr) { persistenceService->RemovePropertyListReplacedObserver(this); } else { MITK_WARN << "No Persistence Service available in destructor"; } d->ToPropertyList(); } void QmitkOpenCVVideoControls::on_VideoProgressSlider_valueChanged(int /*value*/) { //Fixes T23169 on_VideoProgressSlider_sliderReleased(); } void QmitkOpenCVVideoControls::on_UseGrabbingDeviceButton_clicked(bool /*checked=false*/) { m_Controls->GrabbingDevicePanel->setEnabled(true); m_Controls->VideoFilePanel->setEnabled(false); } void QmitkOpenCVVideoControls::on_UseVideoFileButton_clicked(bool /*checked=false*/) { m_Controls->GrabbingDevicePanel->setEnabled(false); m_Controls->VideoFilePanel->setEnabled(true); m_Controls->FileChooser->setEnabled(true); } void QmitkOpenCVVideoControls::on_VideoProgressSlider_sliderPressed() { m_SliderCurrentlyMoved = true; // temporary pause the video while sliding if (!m_VideoSource->GetCapturePaused()) m_VideoSource->PauseCapturing(); } void QmitkOpenCVVideoControls::on_VideoProgressSlider_sliderReleased() { double progressRatio = static_cast(m_Controls->VideoProgressSlider->value()) / static_cast(m_Controls->VideoProgressSlider->maximum()); m_VideoSource->SetVideoCaptureProperty(CV_CAP_PROP_POS_FRAMES, progressRatio*m_VideoSource->GetVideoCaptureProperty(CV_CAP_PROP_FRAME_COUNT)); // resume the video ( if it was not paused by the user) if (m_VideoSource->GetCapturePaused() && m_Controls->PlayButton->isChecked()) m_VideoSource->PauseCapturing(); m_SliderCurrentlyMoved = false; } void QmitkOpenCVVideoControls::on_RepeatVideoButton_clicked(bool checked) { MITK_INFO << "repeat video clicked"; m_VideoSource->SetRepeatVideo(checked); } void QmitkOpenCVVideoControls::on_PlayButton_clicked(bool checked) { MITK_INFO << "play button clicked"; if (checked) { this->Play(); } else { // show pause button this->IsPlaying(true); m_VideoSource->PauseCapturing(); } } void QmitkOpenCVVideoControls::on_StopButton_clicked(bool /*checked=false*/) { this->Stop(); } void QmitkOpenCVVideoControls::Play() { if (m_VideoSource->GetCapturePaused()) { this->IsPlaying(false); m_VideoSource->PauseCapturing(); } else { if (m_Controls->UseGrabbingDeviceButton->isChecked()) { m_VideoSource->SetVideoCameraInput(m_Controls->GrabbingDeviceNumber->text().toInt(), false); m_Controls->VideoFileControls->setEnabled(false); } else { m_VideoSource->SetVideoFileInput(m_Controls->FileChooser->GetFile().c_str(), m_Controls->RepeatVideoButton->isChecked(), false); m_VideoSource->SetRepeatVideo(m_Controls->RepeatVideoButton->isChecked()); m_Controls->VideoProgressSlider->setValue(0); } m_VideoSource->StartCapturing(); if (!m_VideoSource->IsCapturingEnabled()) { MITK_ERROR << "Video could not be initialized!"; m_Controls->PlayButton->setChecked(false); } else { int hertz = m_Controls->UpdateRate->text().toInt(); int updateTime = itk::Math::Round(1000.0 / hertz); // resets the whole background m_VideoBackground->SetTimerDelay(updateTime); m_VideoBackground->AddRenderWindow(m_RenderWindow->GetVtkRenderWindow()); this->connect(m_VideoBackground, SIGNAL(NewFrameAvailable(mitk::VideoSource*)) , this, SLOT(NewFrameAvailable(mitk::VideoSource*))); this->connect(m_VideoBackground, SIGNAL(EndOfVideoSourceReached(mitk::VideoSource*)) , this, SLOT(EndOfVideoSourceReached(mitk::VideoSource*))); m_VideoBackground->Enable(); this->m_Controls->StopButton->setEnabled(true); // show video file controls if (m_Controls->UseVideoFileButton->isChecked()) { m_Controls->VideoFileControls->setEnabled(true); m_Controls->RepeatVideoButton->setEnabled(true); m_Controls->VideoProgressSlider->setEnabled(true); } // show pause button this->IsPlaying(false); // disable other controls m_Controls->GrabbingDevicePanel->setEnabled(false); m_Controls->VideoFilePanel->setEnabled(false); m_Controls->UseGrabbingDeviceButton->setEnabled(false); m_Controls->UseVideoFileButton->setEnabled(false); m_Controls->UpdateRatePanel->setEnabled(false); } } } void QmitkOpenCVVideoControls::Stop() { // disable video file controls, stop button and show play button again m_Controls->UseGrabbingDeviceButton->setEnabled(true); m_Controls->UseVideoFileButton->setEnabled(true); if (m_Controls->UseGrabbingDeviceButton->isChecked()) on_UseGrabbingDeviceButton_clicked(true); else on_UseVideoFileButton_clicked(true); m_Controls->UpdateRatePanel->setEnabled(true); m_Controls->VideoProgressSlider->setValue(0); m_Controls->VideoFileControls->setEnabled(false); this->m_Controls->StopButton->setEnabled(false); this->IsPlaying(true); if (m_VideoBackground) { m_VideoBackground->Disable(); if (m_RenderWindow) m_VideoBackground->RemoveRenderWindow(m_RenderWindow->GetVtkRenderWindow()); this->disconnect(m_VideoBackground, SIGNAL(NewFrameAvailable(mitk::VideoSource*)) , this, SLOT(NewFrameAvailable(mitk::VideoSource*))); } if (m_VideoSource != nullptr) m_VideoSource->StopCapturing(); } void QmitkOpenCVVideoControls::Reset() { this->Stop(); } void QmitkOpenCVVideoControls::IsPlaying(bool paused) { if (paused) { m_Controls->PlayButton->setText("Play"); m_Controls->PlayButton->setIcon(QIcon(":/OpenCVVideoSupportUI/media-playback-start.png")); m_Controls->PlayButton->setChecked(false); } else { m_Controls->PlayButton->setText("Pause"); m_Controls->PlayButton->setIcon(QIcon(":/OpenCVVideoSupportUI/media-playback-pause.png")); m_Controls->PlayButton->setChecked(true); } } void QmitkOpenCVVideoControls::NewFrameAvailable(mitk::VideoSource* /*videoSource*/) { emit NewOpenCVFrameAvailable(m_VideoSource->GetCurrentFrame()); if (!m_SliderCurrentlyMoved) { m_Controls->VideoProgressSlider->setValue(itk::Math::Round(m_VideoSource->GetVideoCaptureProperty(CV_CAP_PROP_POS_FRAMES) *(1 / m_VideoSource->GetVideoCaptureProperty(CV_CAP_PROP_FRAME_COUNT) *m_Controls->VideoProgressSlider->maximum()))); } } void QmitkOpenCVVideoControls::EndOfVideoSourceReached(mitk::VideoSource* /*videoSource*/) { if (m_Controls->RepeatVideoButton->isChecked()) { this->Reset(); this->Play(); } else { this->Stop(); } } void QmitkOpenCVVideoControls::SetRenderWindow(QmitkRenderWindow* _RenderWindow) { if (m_RenderWindow == _RenderWindow) return; // In Reset() m_MultiWidget is used, set it to 0 now for avoiding errors if (_RenderWindow == nullptr) m_RenderWindow = nullptr; this->Reset(); m_RenderWindow = _RenderWindow; if (m_RenderWindow == nullptr) { this->setEnabled(false); } else { this->setEnabled(true); } } QmitkRenderWindow* QmitkOpenCVVideoControls::GetRenderWindow() const { return m_RenderWindow; } void QmitkOpenCVVideoControls::SetVideoBackground(QmitkVideoBackground* _VideoBackground) { if (m_VideoBackground == _VideoBackground) return; if (m_VideoBackground != nullptr) this->disconnect(m_VideoBackground, SIGNAL(destroyed(QObject*)) , this, SLOT(QObjectDestroyed(QObject*))); this->Reset(); m_VideoBackground = _VideoBackground; if (m_VideoBackground == nullptr) { m_VideoSource = nullptr; MITK_WARN << "m_MultiWidget is 0"; this->setEnabled(false); } else { this->setEnabled(true); m_VideoSource = dynamic_cast(m_VideoBackground->GetVideoSource()); // preset form entries if (m_VideoSource != nullptr) { if (!m_VideoSource->GetVideoFileName().empty()) { m_Controls->FileChooser->SetFile(m_VideoSource->GetVideoFileName()); on_UseGrabbingDeviceButton_clicked(false); } else if (m_VideoSource->GetGrabbingDeviceNumber() >= 0) m_Controls->GrabbingDeviceNumber->setValue(m_VideoSource->GetGrabbingDeviceNumber()); m_Controls->UpdateRate->setValue(m_VideoBackground->GetTimerDelay()); this->connect(m_VideoBackground, SIGNAL(destroyed(QObject*)) , this, SLOT(QObjectDestroyed(QObject*))); } else { MITK_WARN << "m_VideoSource is 0"; this->setEnabled(false); } } } QmitkVideoBackground* QmitkOpenCVVideoControls::GetVideoBackground() const { return m_VideoBackground; } void QmitkOpenCVVideoControls::QObjectDestroyed(QObject * obj /*= 0 */) { if (m_VideoBackground == obj) { m_VideoSource = nullptr; this->SetVideoBackground(nullptr); } } void QmitkOpenCVVideoControlsPrivate::ToPropertyList() { mitk::IPersistenceService* persistenceService = this->GetPersistenceService(); if (persistenceService != nullptr) { mitk::PropertyList::Pointer propList = persistenceService->GetPropertyList(m_Id); propList->Set("deviceType", q->m_Controls->UseGrabbingDeviceButton->isChecked() ? 0 : 1); propList->Set("grabbingDeviceNumber", q->m_Controls->GrabbingDeviceNumber->value()); propList->Set("updateRate", q->m_Controls->UpdateRate->value()); propList->Set("repeatVideo", q->m_Controls->RepeatVideoButton->isChecked()); } else { MITK_WARN << "Persistence Service not available."; } } void QmitkOpenCVVideoControlsPrivate::FromPropertyList() { mitk::IPersistenceService* persistenceService = this->GetPersistenceService(); if (persistenceService != nullptr) { mitk::PropertyList::Pointer propList = persistenceService->GetPropertyList(m_Id); bool repeatVideo = false; propList->Get("repeatVideo", repeatVideo); q->m_Controls->RepeatVideoButton->setChecked(repeatVideo); int updateRate = 25; propList->Get("updateRate", updateRate); q->m_Controls->UpdateRate->setValue(updateRate); int grabbingDeviceNumber = 0; propList->Get("grabbingDeviceNumber", grabbingDeviceNumber); q->m_Controls->GrabbingDeviceNumber->setValue(grabbingDeviceNumber); int deviceType = 0; propList->Get("deviceType", deviceType); if (deviceType == 0) { q->m_Controls->UseGrabbingDeviceButton->setChecked(true); } else { q->m_Controls->UseVideoFileButton->setChecked(true); } } else { MITK_WARN << "Persistence Service not available."; } } void QmitkOpenCVVideoControls::AfterPropertyListReplaced(const std::string& id, mitk::PropertyList* /*propertyList*/) { if (id == d->m_Id) d->FromPropertyList(); } diff --git a/Modules/OpenCVVideoSupport/UI/QmitkOpenCVVideoControls.h b/Modules/OpenCVVideoSupport/UI/QmitkOpenCVVideoControls.h index ed5ea696f2..d70660f536 100644 --- a/Modules/OpenCVVideoSupport/UI/QmitkOpenCVVideoControls.h +++ b/Modules/OpenCVVideoSupport/UI/QmitkOpenCVVideoControls.h @@ -1,116 +1,117 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkOpenCVVideoControls_h #define QmitkOpenCVVideoControls_h #include #include #include -#include "opencv2/core.hpp" +#include +#include class QmitkRenderWindow; class QmitkVideoBackground; namespace mitk { class VideoSource; class OpenCVVideoSource; } class QmitkOpenCVVideoControlsPrivate; /// /// \brief Offers widgets to play/pause/stop a video on a certain render window with /// the use of an !initialized! QmitkVideoBackground. The QmitkVideoBackground should /// contain an OpenCVVideoSource is then owned by this widget (and deleted) /// class MITKOPENCVVIDEOSUPPORTUI_EXPORT QmitkOpenCVVideoControls : public QWidget, public mitk::PropertyListReplacedObserver { Q_OBJECT public: /// /// Construct the widget with the given render window and the given preset values /// QmitkOpenCVVideoControls(QmitkVideoBackground* _VideoBackground, QmitkRenderWindow* _RenderWindow , QWidget* parent = nullptr, Qt::WindowFlags f = nullptr); /// /// call reset if video playback is enabled here /// ~QmitkOpenCVVideoControls() override; /// /// sets the render window for this video player /// void SetRenderWindow(QmitkRenderWindow* _RenderWindow); /// /// returns the current render window /// QmitkRenderWindow* GetRenderWindow() const; /// /// sets the qmitkvideobackground for this /// void SetVideoBackground(QmitkVideoBackground* _VideoBackground); /// /// returns the current QmitkVideoBackground /// QmitkVideoBackground* GetVideoBackground() const; /// /// calls FromPropertyList /// void AfterPropertyListReplaced(const std::string& id, mitk::PropertyList* propertyList) override; signals: /// /// When playback is started this informs when a new frame was grabbed /// void NewOpenCVFrameAvailable(const IplImage*); protected slots: void on_UseGrabbingDeviceButton_clicked(bool checked = false); void on_UseVideoFileButton_clicked(bool checked = false); void on_VideoProgressSlider_sliderPressed(); void on_VideoProgressSlider_sliderReleased(); void on_VideoProgressSlider_valueChanged(int value); void on_RepeatVideoButton_clicked(bool checked = false); void on_PlayButton_clicked(bool checked = false); void on_StopButton_clicked(bool checked = false); void Play(); void Stop(); void Reset(); void IsPlaying(bool paused); void QObjectDestroyed(QObject * obj = nullptr); void NewFrameAvailable(mitk::VideoSource* videoSource); void EndOfVideoSourceReached(mitk::VideoSource* videoSource); protected: QmitkVideoBackground* m_VideoBackground; QmitkRenderWindow* m_RenderWindow; mitk::OpenCVVideoSource* m_VideoSource; Ui::QmitkOpenCVVideoControls* m_Controls; bool m_SliderCurrentlyMoved; private: friend class QmitkOpenCVVideoControlsPrivate; QScopedPointer d; }; #endif diff --git a/Modules/OpenCVVideoSupport/mitkMovieGeneratorOpenCV.cpp b/Modules/OpenCVVideoSupport/mitkMovieGeneratorOpenCV.cpp index 44a9a8dfa2..a150980125 100644 --- a/Modules/OpenCVVideoSupport/mitkMovieGeneratorOpenCV.cpp +++ b/Modules/OpenCVVideoSupport/mitkMovieGeneratorOpenCV.cpp @@ -1,129 +1,130 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkMovieGeneratorOpenCV.h" //#include #include "mitkGL.h" #include +#include mitk::MovieGeneratorOpenCV::MovieGeneratorOpenCV() { m_initialized = false; m_aviWriter = nullptr; m_dwRate = 20; m_FourCCCodec = nullptr; m_RemoveColouredFrame = true; } void mitk::MovieGeneratorOpenCV::SetFileName( const char *fileName ) { m_sFile = fileName; } void mitk::MovieGeneratorOpenCV::SetFrameRate(unsigned int rate) { m_dwRate = static_cast(rate); } void mitk::MovieGeneratorOpenCV::SetRemoveColouredFrame(bool RemoveColouredFrame) { m_RemoveColouredFrame = RemoveColouredFrame; } bool mitk::MovieGeneratorOpenCV::InitGenerator() { m_width = m_renderer->GetRenderWindow()->GetSize()[0]; // changed from glGetIntegerv( GL_VIEWPORT, viewport ); m_height = m_renderer->GetRenderWindow()->GetSize()[1]; // due to sometimes strange dimensions if(m_RemoveColouredFrame) { m_width -= 10; //remove colored boarders around renderwindows m_height -= 10; } m_width -= m_width % 4; // some video codecs have prerequisites to the image dimensions m_height -= m_height % 4; m_currentFrame = cvCreateImage(cvSize(m_width,m_height),8,3); // creating image with widget size, 8 bit per pixel and 3 channel r,g,b m_currentFrame->origin = 1; // avoid building a video with bottom up /* m_sFile = Name of the output video file. CV_FOURCC = 4-character code of codec used to compress the frames. For example, CV_FOURCC('P','I','M','1') is MPEG-1 codec, CV_FOURCC('M','J','P','G') is motion-jpeg codec etc. CV_FOURCC('P','I','M','1') = MPEG-1 codec CV_FOURCC('M','J','P','G') = motion-jpeg codec (does not work well) CV_FOURCC('M', 'P', '4', '2') = MPEG-4.2 codec CV_FOURCC('D', 'I', 'V', '3') = MPEG-4.3 codec CV_FOURCC('D', 'I', 'V', 'X') = MPEG-4 codec CV_FOURCC('U', '2', '6', '3') = H263 codec CV_FOURCC('I', '2', '6', '3') = H263I codec CV_FOURCC('F', 'L', 'V', '1') = FLV1 codec Under Win32 it is possible to pass -1 in order to choose compression method and additional compression parameters from dialog. m_dwRate = Framerate of the created video stream. frame_size Size of video frames. InitGenerator 1 = If it is not zero, the encoder will expect and encode color frames, otherwise it will work with grayscale frames (the flag is currently supported on Windows only).*/ if(m_FourCCCodec != nullptr) { #ifdef WIN32 m_aviWriter = cvCreateVideoWriter(m_sFile.c_str(),CV_FOURCC(m_FourCCCodec[0],m_FourCCCodec[1],m_FourCCCodec[2], m_FourCCCodec[3]),m_dwRate,cvSize(m_width,m_height),1); //initializing video writer #else m_aviWriter = cvCreateVideoWriter(m_sFile.c_str(),CV_FOURCC(m_FourCCCodec[0],m_FourCCCodec[1],m_FourCCCodec[2], m_FourCCCodec[3]),m_dwRate,cvSize(m_width,m_height)); //initializing video writer #endif } else { #ifdef WIN32 m_aviWriter = cvCreateVideoWriter(m_sFile.c_str(),-1,m_dwRate,cvSize(m_width,m_height),1); //initializing video writer #else m_aviWriter = cvCreateVideoWriter(m_sFile.c_str(),CV_FOURCC('X','V','I','D'),m_dwRate,cvSize(m_width,m_height)); //initializing video writer #endif } if(!m_aviWriter) { std::cout << "errors initializing video writer...correct video file path? on linux: ffmpeg must be included in OpenCV."; return false; } return true; } bool mitk::MovieGeneratorOpenCV::AddFrame( void *data ) { //cvSetImageData(m_currentFrame,data,m_width*3); memcpy(m_currentFrame->imageData,data,m_width*m_height*3); cvWriteFrame(m_aviWriter,m_currentFrame); return true; } bool mitk::MovieGeneratorOpenCV::TerminateGenerator() { if (m_aviWriter) { cvReleaseVideoWriter(&m_aviWriter); } return true; } diff --git a/Modules/OpenCVVideoSupport/mitkMovieGeneratorOpenCV.h b/Modules/OpenCVVideoSupport/mitkMovieGeneratorOpenCV.h index f677abe127..9ff24df620 100755 --- a/Modules/OpenCVVideoSupport/mitkMovieGeneratorOpenCV.h +++ b/Modules/OpenCVVideoSupport/mitkMovieGeneratorOpenCV.h @@ -1,85 +1,85 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MovieGeneratorOpenCV_H_HEADER_INCLUDED #define MovieGeneratorOpenCV_H_HEADER_INCLUDED #include "mitkMovieGenerator.h" #include #include #include // OpenCV includes -#include "cv.h" -#include "highgui.h" +#include +#include namespace mitk { class MITKOPENCVVIDEOSUPPORT_EXPORT MovieGeneratorOpenCV : public MovieGenerator { public: mitkClassMacro(MovieGeneratorOpenCV, MovieGenerator); itkFactorylessNewMacro(Self); itkCloneMacro(Self); void SetFileName( const char *fileName ) override; void SetFourCCCodec(char* codec) { m_FourCCCodec = codec; } void SetFrameRate(unsigned int rate) override; /// /// if true the movie will be cutted by a 10 pixel margin /// in order to remove the standard mitk coloured borders /// default: true /// void SetRemoveColouredFrame(bool); protected: MovieGeneratorOpenCV(); //! called directly before the first frame is added bool InitGenerator() override; //! used to add a frame bool AddFrame( void *data ) override; //! called after the last frame is added bool TerminateGenerator() override; //! name of output file std::string m_sFile; //! frame rate int m_dwRate; private: CvVideoWriter* m_aviWriter; IplImage * m_currentFrame; char * m_FourCCCodec; bool m_RemoveColouredFrame; }; } // namespace mitk #endif /* MovieGeneratorOpenCV_H_HEADER_INCLUDED */ diff --git a/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.h b/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.h index c92612cadd..e0ecd28678 100644 --- a/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.h +++ b/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.h @@ -1,96 +1,97 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkOpenCVToMitkImageFilter_h #define mitkOpenCVToMitkImageFilter_h // mitk includes #include #include #include // itk includes #include #include // OpenCV includes #include +#include #include namespace mitk { /// /// \brief Filter for creating MITK RGB Images from an OpenCV image /// class MITKOPENCVVIDEOSUPPORT_EXPORT OpenCVToMitkImageFilter : public ImageSource { public: typedef itk::RGBPixel< unsigned char > UCRGBPixelType; typedef itk::RGBPixel< unsigned short > USRGBPixelType; typedef itk::RGBPixel< float > FloatRGBPixelType; typedef itk::RGBPixel< double > DoubleRGBPixelType; /// /// the static function for the conversion /// template static Image::Pointer ConvertCVMatToMitkImage(const cv::Mat input); mitkClassMacro(OpenCVToMitkImageFilter, ImageSource); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /// /// sets an iplimage as input /// void SetOpenCVImage(const IplImage* image); //itkGetMacro(OpenCVImage, const IplImage*); /// /// sets an opencv mat as input (will be used if OpenCVImage Ipl image is 0) /// void SetOpenCVMat(const cv::Mat& image); itkGetMacro(OpenCVMat, cv::Mat); OutputImageType* GetOutput(void); //##Documentation //## @brief Convenient method to insert an openCV image as a slice at a //## certain time step into a 3D or 4D mitk::Image. //## //## @param openCVImage - the image that is inserted into the mitk Image //## @param mitkImage - pointer to the mitkImage, which is changed by this method! //## @param timeStep - the time step, at which the openCVImage is inserted //## //## @attention The parameter mitkImage will be changed! void InsertOpenCVImageAsMitkTimeSlice(const cv::Mat openCVImage, Image::Pointer mitkImage, int timeStep); protected: OpenCVToMitkImageFilter(); // purposely hidden ~OpenCVToMitkImageFilter() override; void GenerateData() override; protected: Image::Pointer m_Image; cv::Mat m_OpenCVMat; std::mutex m_ImageMutex; std::mutex m_OpenCVMatMutex; }; } // namespace mitk #endif // mitkOpenCVToMitkImageFilter_h diff --git a/Modules/OpenCVVideoSupport/mitkOpenCVVideoSource.cpp b/Modules/OpenCVVideoSupport/mitkOpenCVVideoSource.cpp index 1fb0d2a342..b63d9da6cf 100644 --- a/Modules/OpenCVVideoSupport/mitkOpenCVVideoSource.cpp +++ b/Modules/OpenCVVideoSupport/mitkOpenCVVideoSource.cpp @@ -1,424 +1,426 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkOpenCVVideoSource.h" #include #include +#include + mitk::OpenCVVideoSource::OpenCVVideoSource() : m_VideoCapture(nullptr), m_CurrentImage(nullptr), m_CurrentVideoTexture(nullptr), m_PauseImage(nullptr), m_GrabbingDeviceNumber(-1), m_RepeatVideo(false), m_UseCVCAMLib(false), m_UndistortImage(false), m_FlipXAxisEnabled(false), m_FlipYAxisEnabled(false) { } mitk::OpenCVVideoSource::~OpenCVVideoSource() { this->Reset(); } void mitk::OpenCVVideoSource::SetVideoFileInput(const char * filename, bool repeatVideo, bool /*useCVCAMLib*/) { this->Reset(); m_VideoFileName = filename; m_VideoCapture = cvCaptureFromFile(filename); if(!m_VideoCapture) MITK_WARN << "Error in initializing video file input!"; m_RepeatVideo = repeatVideo; //m_CurrentImage = cvCreateImage(cvSize(m_CaptureWidth,m_CaptureHeight),8,3); this->Modified(); } void mitk::OpenCVVideoSource::SetVideoCameraInput(int cameraindex, bool /*useCVCAMLib*/) { this->Reset(); m_GrabbingDeviceNumber = cameraindex; m_VideoCapture = cvCaptureFromCAM(m_GrabbingDeviceNumber); if(!m_VideoCapture) MITK_ERROR << "Error in initializing CVHighGUI video camera!"<< std::endl; this->Modified(); } double mitk::OpenCVVideoSource::GetVideoCaptureProperty(int property_id) { return cvGetCaptureProperty(m_VideoCapture, property_id); } int mitk::OpenCVVideoSource::SetVideoCaptureProperty(int property_id, double value) { return cvSetCaptureProperty(m_VideoCapture, property_id, value); } //method extended for "static video feature" if enabled unsigned char* mitk::OpenCVVideoSource::GetVideoTexture() { // Fetch Frame and return pointer to opengl texture FetchFrame(); if (m_FlipXAxisEnabled || m_FlipYAxisEnabled) { //rotate the image to get a static video m_CurrentImage = this->FlipImage(m_CurrentImage); } //transfer the image to a texture this->UpdateVideoTexture(); return this->m_CurrentVideoTexture; this->Modified(); } cv::Mat mitk::OpenCVVideoSource::GetImage() { if(m_CurrentImage) { cv::Mat copy = cv::cvarrToMat( m_CurrentImage, false ); return copy.clone(); } return cv::Mat(); } const IplImage * mitk::OpenCVVideoSource::GetCurrentFrame() { return m_CurrentImage; } void mitk::OpenCVVideoSource::GetCurrentFrameAsOpenCVImage(IplImage * image) { // get last captured frame for processing the image data if(m_CurrentImage) { if(image) { image->origin = m_CurrentImage->origin; memcpy(image->imageData,m_CurrentImage->imageData,m_CurrentImage->width*m_CurrentImage->height*m_CurrentImage->nChannels); } } } void mitk::OpenCVVideoSource::FetchFrame() { // main procedure for updating video data if(m_CapturingInProcess) { if(m_VideoCapture) // we use highgui { if(!m_CapturePaused) { // release old image here m_CurrentImage = cvQueryFrame(m_VideoCapture); ++m_FrameCount; } if(m_CurrentImage == nullptr) // do we need to repeat the video if it is from video file? { double framePos = this->GetVideoCaptureProperty(CV_CAP_PROP_POS_AVI_RATIO); MITK_DEBUG << "End of video file found. framePos: " << framePos; if(m_RepeatVideo && framePos >= 0.99) { MITK_DEBUG << "Restarting video file playback."; this->SetVideoCaptureProperty(CV_CAP_PROP_POS_AVI_RATIO, 0); m_FrameCount = 0; m_CurrentImage = cvQueryFrame(m_VideoCapture); } else { std::ostringstream s; s << "End of video file " << m_VideoFileName; std::logic_error err( s.str() ); throw err; } } else { // only undistort if not paused if(m_UndistortImage && m_UndistortCameraImage.IsNotNull()) m_UndistortCameraImage->UndistortImageFast(m_CurrentImage, nullptr); } if(m_CaptureWidth == 0 || m_CaptureHeight == 0) { MITK_DEBUG << "Trying to set m_CaptureWidth & m_CaptureHeight."; m_CaptureWidth = m_CurrentImage->width; m_CaptureHeight = m_CurrentImage->height; MITK_INFO << "frame width: " << m_CaptureWidth << ", height: " << m_CaptureHeight; m_CurrentImage->origin = 0; } } } } void mitk::OpenCVVideoSource::UpdateVideoTexture() { //write the grabbed frame into an opengl compatible array, that means flip it and swap channel order if(!m_CurrentImage) return; if(m_CurrentVideoTexture == nullptr) m_CurrentVideoTexture = new unsigned char[m_CaptureWidth*m_CaptureHeight*3]; int width = m_CurrentImage->width; int height = m_CurrentImage->height; int widthStep = m_CurrentImage->widthStep; int nChannels = m_CurrentImage->nChannels; unsigned char* tex = m_CurrentVideoTexture; char* data = m_CurrentImage->imageData; char* currentData = m_CurrentImage->imageData; int hIndex=0; int wIndex=0; int iout,jout; for(int i=0;i= width) { wIndex=0; hIndex++; } // vertically flip the image iout = -hIndex+height-1; jout = wIndex; currentData = data + iout*widthStep; tex[i+2] = currentData[jout*nChannels + 0]; // B tex[i+1] = currentData[jout*nChannels + 1]; // G tex[i] = currentData[jout*nChannels + 2]; // R } } void mitk::OpenCVVideoSource::StartCapturing() { if(m_VideoCapture != nullptr) m_CapturingInProcess = true; else m_CapturingInProcess = false; } void mitk::OpenCVVideoSource::StopCapturing() { m_CapturePaused = false; m_CapturingInProcess = false; } bool mitk::OpenCVVideoSource::OnlineImageUndistortionEnabled() const { return m_UndistortCameraImage; } void mitk::OpenCVVideoSource::PauseCapturing() { m_CapturePaused = !m_CapturePaused; if(m_CapturePaused) { m_PauseImage = cvCloneImage(m_CurrentImage); // undistort this pause image if necessary if(m_UndistortImage) m_UndistortCameraImage->UndistortImageFast(m_PauseImage, nullptr); m_CurrentImage = m_PauseImage; } else { cvReleaseImage( &m_PauseImage ); // release old pause image if necessary m_CurrentImage = nullptr; m_PauseImage = nullptr; } } void mitk::OpenCVVideoSource::EnableOnlineImageUndistortion(mitk::Point3D focal, mitk::Point3D principal, mitk::Point4D distortion) { // Initialize Undistortion m_UndistortImage = true; float kc[4]; kc[0] = distortion[0]; kc[1] = distortion[1]; kc[2] = distortion[2]; kc[3] = distortion[3]; if(m_CaptureWidth == 0 || m_CaptureHeight == 0) FetchFrame(); m_UndistortCameraImage = mitk::UndistortCameraImage::New(); m_UndistortCameraImage->SetUndistortImageFastInfo(focal[0], focal[1], principal[0], principal[1], kc, (float)m_CaptureWidth, (float)m_CaptureHeight); } void mitk::OpenCVVideoSource::DisableOnlineImageUndistortion() { m_UndistortImage = false; } // functions for compatibility with ITK segmentation only void mitk::OpenCVVideoSource::GetCurrentFrameAsItkHSVPixelImage(HSVPixelImageType::Pointer &Image) { FetchFrame(); // Prepare iteration HSVConstIteratorType itImage( Image, Image->GetLargestPossibleRegion()); itImage.GoToBegin(); HSVPixelType pixel; int rowsize = 3 * m_CaptureWidth; char* bufferend; char* picture; picture = this->m_CurrentImage->imageData; bufferend = this->m_CurrentImage->imageData + 3*(m_CaptureHeight*m_CaptureWidth); float r,g,b,h,s,v; try { // we have to flip the image for(char* datapointer = bufferend - rowsize;datapointer >= picture; datapointer -= rowsize) { for(char* current = datapointer; current < datapointer + rowsize; current++) { b = *current; current++; g = *current; current++; r = *current; RGBtoHSV(r,g,b,h,s,v); pixel[0] = h; pixel[1] = s; pixel[2] = v; itImage.Set(pixel); ++itImage; } } } catch( ... ) { std::cout << "Exception raised mitkOpenCVVideoSource: get hsv itk image conversion error." << std::endl; } } void mitk::OpenCVVideoSource::RGBtoHSV(float r, float g, float b, float &h, float &s, float &v) { if(r > 1.0) r = r/255; if(b > 1.0) b = b/255; if(g > 1.0) g = g/255; float mn=r,mx=r; int maxVal=0; if (g > mx){ mx=g;maxVal=1;} if (b > mx){ mx=b;maxVal=2;} if (g < mn) mn=g; if (b < mn) mn=b; float delta = mx - mn; v = mx; if( mx != 0 ) s = delta / mx; else { s = 0; h = 0; return; } if (s==0.0f) { h=-1; return; } else { switch (maxVal) { case 0:{h = ( g - b ) / delta;break;} // yel < h < mag case 1:{h = 2 + ( b - r ) / delta;break;} // cyan < h < yel case 2:{h = 4 + ( r - g ) / delta;break;} // mag < h < cyan } } h *= 60; if( h < 0 ) h += 360; } /* * Rotate input image according to rotation angle around the viewing direction. * Angle is supposed to be calculated in QmitkARRotationComponet in the update() method. */ IplImage* mitk::OpenCVVideoSource::FlipImage(IplImage* input) { if(input == nullptr) { //warn the user and quit std::cout<<"openCVVideoSource: Current video image is null! "<< std::endl; return input; } if(m_FlipXAxisEnabled && !m_FlipYAxisEnabled) { cvFlip(input,nullptr,0); } if(!m_FlipXAxisEnabled && m_FlipYAxisEnabled) { cvFlip(input,nullptr,1); } if(m_FlipXAxisEnabled && m_FlipYAxisEnabled) { cvFlip(input,nullptr,-1); } return input; } void mitk::OpenCVVideoSource::Reset() { // set capturing to false this->StopCapturing(); this->m_FrameCount = 0; if(m_VideoCapture) cvReleaseCapture(&m_VideoCapture); m_VideoCapture = nullptr; m_CurrentImage = nullptr; m_CaptureWidth = 0; m_CaptureHeight = 0; delete m_CurrentVideoTexture; m_CurrentVideoTexture = nullptr; if(m_PauseImage) cvReleaseImage(&m_PauseImage); m_PauseImage = nullptr; m_CapturePaused = false; m_VideoFileName.clear(); m_GrabbingDeviceNumber = -1; // do not touch repeat video //m_RepeatVideo = false; m_UseCVCAMLib = false; // do not touch undistort settings // bool m_UndistortImage; } void mitk::OpenCVVideoSource::SetEnableXAxisFlip(bool enable) { this->m_FlipXAxisEnabled = enable; this->Modified(); } void mitk::OpenCVVideoSource::SetEnableYAxisFlip(bool enable) { this->m_FlipXAxisEnabled = enable; this->Modified(); } diff --git a/Modules/OpenCVVideoSupport/mitkUndistortCameraImage.cpp b/Modules/OpenCVVideoSupport/mitkUndistortCameraImage.cpp index 1d78e43af2..40844de3fc 100644 --- a/Modules/OpenCVVideoSupport/mitkUndistortCameraImage.cpp +++ b/Modules/OpenCVVideoSupport/mitkUndistortCameraImage.cpp @@ -1,223 +1,231 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ /// The list of internal camera parameters: /// * Focal length: The focal length in pixels is stored in m_fcX and m_fcY. /// * Principal point: The principal point coordinates are stored in the m_ccX and m_ccY. /// * Skew coefficient: The skew coefficient defining the angle between the x and y pixel axes is stored in the scalar alpha_c = 0. /// * Distortions: The image distortion coefficients (radial and tangential distortions) are stored in the 4x1 vector. #include "mitkUndistortCameraImage.h" - +#include +#include mitk::UndistortCameraImage::UndistortCameraImage() { m_tempImage = nullptr; } mitk::UndistortCameraImage::~UndistortCameraImage() { if(m_tempImage != nullptr) cvReleaseImage(&m_tempImage); } /// undistort one pixel coordinate using floating point accuracy... mitk::Point2D mitk::UndistortCameraImage::UndistortPixel(const mitk::Point2D& src) { float r_2 = 0; // radial distance squared const mitk::Point2D old_src = src; // copy of the original distorted point // distortion coefficients float k1 = m_distortionMatrixData[0]; float k2 = m_distortionMatrixData[1]; float p1 = m_distortionMatrixData[2]; float p2 = m_distortionMatrixData[3]; // Shift points to principal point and use focal length mitk::Point2D dstd; dstd[0] = (src[0] - m_ccX) / m_fcX; dstd[1] = (src[1] - m_ccY) / m_fcY; mitk::Point2D desPnt = dstd; // Compensate lens distortion float x = dstd[0]; float y = dstd[1]; for (int iter = 0; iter < 5; iter++) { r_2 = x*x + y*y; const float k_radial = 1 + k1 * r_2 + k2 * r_2 * r_2; const float delta_x = 2 * p1*x*y + p2 * (r_2 + 2*x*x); const float delta_y = 2 * p2*x*y + p1 * (r_2 + 2*y*y); x = (desPnt[0] - delta_x) / k_radial; y = (desPnt[1] - delta_y) / k_radial; } dstd[0] = x; dstd[1] = y; dstd[0] *= m_fcX; dstd[1] *= m_fcY; dstd[0] += m_ccX; dstd[1] += m_ccY; // ready // const mitk::Point2D dst = dstd; // do a sanity check to make sure this ideal point translates properly to the distorted point // this does the reverse of the above. It maps ideal undistorted to distorted image coordinates x = dstd[0] - m_ccX; y = dstd[1] - m_ccY; x /= m_fcX; y /= m_fcY; r_2 = x*x + y*y; float distx = x + x*(k1*r_2 + k2*r_2*r_2) + (2*p1*x*y + p2*(r_2 + 2*x*x)); float disty = y + y*(k1*r_2 + k2*r_2*r_2) + (2*p2*x*y + p1*(r_2 + 2*y*y)); distx *= m_fcX; disty *= m_fcY; distx += m_ccX; disty += m_ccY; // this should never be more than .2 pixels... const float diffx = old_src[0] - distx; const float diffy = old_src[1] - disty; if (fabs(diffx) > .1 || fabs(diffy) > .1) { std::cout << "undistort sanity check error: diffx =" << diffx << " , diffy = " << diffy; } return dstd; } void mitk::UndistortCameraImage::UndistortImage(IplImage *src, IplImage *dst) { // init intrinsic camera matrix [fx 0 cx; 0 fy cy; 0 0 1]. m_intrinsicMatrixData[0] = (double)m_fcX; m_intrinsicMatrixData[1] = 0.0; m_intrinsicMatrixData[2] = (double)m_ccX; m_intrinsicMatrixData[3] = 0.0; m_intrinsicMatrixData[4] = (double)m_fcY; m_intrinsicMatrixData[5] = (double)m_ccY; m_intrinsicMatrixData[6] = 0.0; m_intrinsicMatrixData[7] = 0.0; m_intrinsicMatrixData[8] = 1.0; m_intrinsicMatrix = cvMat(3, 3, CV_32FC1, m_intrinsicMatrixData); // init distortion matrix m_distortionMatrix = cvMat(1, 4, CV_32F, m_distortionMatrixData); // undistort - cvUndistort2(src,dst, &m_intrinsicMatrix, &m_distortionMatrix); + auto srcMat = cv::cvarrToMat(src); + auto dstMat = cv::cvarrToMat(dst); + auto intrinsicMat = cv::cvarrToMat(&m_intrinsicMatrix); + auto distortionMat = cv::cvarrToMat(&m_distortionMatrix); + cv::undistort(srcMat, dstMat, intrinsicMat, distortionMat); } // FAST METHODS FOR UNDISTORTION IN REALTIME // void mitk::UndistortCameraImage::UndistortImageFast(IplImage * src, IplImage* dst) { if(!src) return; /*if(dst == nullptr) dst = src; if(src->nChannels == 3) { IplImage *r = cvCreateImage(cvGetSize(src),src->depth,1);//subpixel IplImage *g = cvCreateImage(cvGetSize(src),src->depth,1);//subpixel IplImage *b = cvCreateImage(cvGetSize(src),src->depth,1);//subpixel cvSplit(src, r,g,b, nullptr); cvRemap( r, r, m_mapX, m_mapY ); // Undistort image cvRemap( g, g, m_mapX, m_mapY ); // Undistort image cvRemap( b, b, m_mapX, m_mapY ); // Undistort image cvMerge(r,g,b, nullptr, dst); } else { cvRemap(src, dst, m_mapX, m_mapY); }*/ /*if(m_tempImage == nullptr) m_tempImage = cvCreateImage(cvSize(src->width,src->height),src->depth,src->nChannels);*/ /*if(dst == nullptr) dst = cvCreateImage(cvSize(src->width,src->height),src->depth,src->nChannels);*/ if(!dst) { m_tempImage = cvCloneImage( src ); cvRemap(m_tempImage, src, m_mapX, m_mapY, CV_INTER_CUBIC); cvReleaseImage( &m_tempImage ); m_tempImage = nullptr; /*memcpy( src->imageData, m_tempImage->imageData, m_tempImage->imageSize ); cvReleaseImage( &m_tempImage );*/ } else { cvRemap(src, dst, m_mapX, m_mapY, CV_INTER_CUBIC); } /*m_tempImage->origin = src->origin; if(dst == nullptr) memcpy( src->imageData, m_tempImage->imageData, m_tempImage->imageSize ); else memcpy( dst->imageData, m_tempImage->imageData, m_tempImage->imageSize ); //cvUnDistort(m_srcImg, m_dstImg, m_undistMap,m_interpolationMode); //cvUndistort2(m_srcImg, m_dstImg, &m_intrinsicMatrix,&m_distortionMatrixDataCoefficients);*/ } void mitk::UndistortCameraImage::SetUndistortImageFastInfo(float in_dF1, float in_dF2, float in_dPrincipalX, float in_dPrincipalY, float in_Dist[4], float ImageSizeX, float ImageSizeY) { //create new matrix m_DistortionCoeffs = cvCreateMat(4, 1, CV_64FC1); m_CameraMatrix = cvCreateMat(3, 3, CV_64FC1); //set the camera matrix [fx 0 cx; 0 fy cy; 0 0 1]. cvSetReal2D(m_CameraMatrix, 0, 0, in_dF1); cvSetReal2D(m_CameraMatrix, 0, 1, 0.0); cvSetReal2D(m_CameraMatrix, 0, 2, in_dPrincipalX); cvSetReal2D(m_CameraMatrix, 1, 0, 0.0); cvSetReal2D(m_CameraMatrix, 1, 1, in_dF2); cvSetReal2D(m_CameraMatrix, 1, 2, in_dPrincipalY); cvSetReal2D(m_CameraMatrix, 2, 0, 0.0); cvSetReal2D(m_CameraMatrix, 2, 1, 0.0); cvSetReal2D(m_CameraMatrix, 2, 2, 1.0); //set distortions coefficients cvSetReal1D(m_DistortionCoeffs, 0, in_Dist[0]); cvSetReal1D(m_DistortionCoeffs, 1, in_Dist[1]); cvSetReal1D(m_DistortionCoeffs, 2, in_Dist[2]); cvSetReal1D(m_DistortionCoeffs, 3, in_Dist[3]); m_mapX = cvCreateMat(ImageSizeY, ImageSizeX, CV_32FC1); m_mapY = cvCreateMat(ImageSizeY, ImageSizeX, CV_32FC1); - //cv::initUndistortRectifyMap(m_CameraMatrix, m_DistortionCoeffs, m_mapX, m_mapY); - cvInitUndistortMap(m_CameraMatrix, m_DistortionCoeffs, m_mapX, m_mapY); + auto cameraMat = cv::cvarrToMat(m_CameraMatrix); + auto distortionCoeffs = cv::cvarrToMat(m_DistortionCoeffs); + auto mapX = cv::cvarrToMat(m_mapX); + auto mapY = cv::cvarrToMat(m_mapY); + cv::initUndistortRectifyMap(cameraMat, distortionCoeffs, cv::Mat(), cameraMat, mapX.size(), mapX.type(), mapX, mapY); } diff --git a/Modules/OpenCVVideoSupport/mitkUndistortCameraImage.h b/Modules/OpenCVVideoSupport/mitkUndistortCameraImage.h index 1f9509b827..cd678889e3 100644 --- a/Modules/OpenCVVideoSupport/mitkUndistortCameraImage.h +++ b/Modules/OpenCVVideoSupport/mitkUndistortCameraImage.h @@ -1,125 +1,125 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __mitkUndistortCameraImage_h #define __mitkUndistortCameraImage_h #include "mitkCommon.h" #include #include "itkObject.h" #include "mitkPoint.h" -#include "opencv2/core.hpp" -#include "opencv2/imgproc.hpp" +#include +#include /*! \brief UndistortCameraImage This class is used to undistort camera images. Before any undistortion the class has to be initialized using the functions: SetFocalLength(),SetPrinzipalPoint() and SetCameraDistortion(). After this you can either use UndistortPixel() to undistort a single pixel's coordinates or UndistortImage() to undistort an OpenCV image. A faster version of UndistortImage() is UndistortImageFast(), however, it has to be initialized once with SetUndistortImageFastInfo() instead of the Set... methods before use. \ingroup Functionalities */ namespace mitk { class MITKOPENCVVIDEOSUPPORT_EXPORT UndistortCameraImage : public itk::Object { public: mitkClassMacroItkParent(UndistortCameraImage,itk::Object); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /// Initialization /// /* * Set the camera's intrinsic focal length */ void SetFocalLength(float fc_x, float fc_y) { m_fcX = fc_x; m_fcY = fc_y; } /* * Set the camera's intrinsic principal point */ void SetPrincipalPoint(float cc_x, float cc_y) { m_ccX = cc_x; m_ccY = cc_y; } /* * Set the camera's intrinsic distortion parameters */ void SetCameraDistortion(float kc1, float kc2, float kc3, float kc4) { m_distortionMatrixData[0] = kc1; m_distortionMatrixData[1] = kc2; m_distortionMatrixData[2] = kc3; m_distortionMatrixData[3] = kc4; } /* * Pre-Calculates matrices for the later use of UndistortImageFast() */ void InitRemapUndistortion(int sizeX, int sizeY); /// USAGE /// /* * Undistort a single pixel, returns undistorted pixel */ mitk::Point2D UndistortPixel(const mitk::Point2D& src); /* * Complete undistortion of an OpenCV image, including all calculations */ void UndistortImage(IplImage* src, IplImage* dst); /* * Complete undistortion of an OpenCV image, using pre-calculated matrices from SetUndistortImageFastInfo() * The use of only a source parameter will cause the source to be overwritten. * NOTE: Using the Fast undistortion methods does not require a initialization via the Set... methods. */ void UndistortImageFast( IplImage * src, IplImage* dst = nullptr ); void SetUndistortImageFastInfo(float in_dF1, float in_dF2, float in_dPrincipalX, float in_dPrincipalY, float in_Dist[4], float ImageSizeX, float ImageSizeY); UndistortCameraImage(); ~UndistortCameraImage() override; protected: // principal point and focal length parameters float m_ccX, m_ccY, m_fcX, m_fcY; // undistortion parameters float m_distortionMatrixData[4]; // intrinsic camera parameters float m_intrinsicMatrixData[9]; // precalculated matrices for fast image undistortion with UndistortImageFast() CvMat * m_mapX, * m_mapY; // intrinsic and undistortion camera matrices CvMat m_intrinsicMatrix, m_distortionMatrix; // temp image IplImage * m_tempImage; CvMat *m_DistortionCoeffs; CvMat *m_CameraMatrix; }; } #endif diff --git a/Modules/OpenIGTLink/mitkIGTLClient.cpp b/Modules/OpenIGTLink/mitkIGTLClient.cpp index 649c6a808d..ea192cbbec 100644 --- a/Modules/OpenIGTLink/mitkIGTLClient.cpp +++ b/Modules/OpenIGTLink/mitkIGTLClient.cpp @@ -1,117 +1,117 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkIGTLClient.h" //#include "mitkIGTTimeStamp.h" //#include "mitkIGTHardwareException.h" #include "igtlTrackingDataMessage.h" #include #include #include -#include +#include mitk::IGTLClient::IGTLClient(bool ReadFully) : IGTLDevice(ReadFully) { } mitk::IGTLClient::~IGTLClient() { } bool mitk::IGTLClient::OpenConnection() { if (this->GetState() != Setup) { mitkThrowException(mitk::Exception) << "Can only try to open the connection if in setup mode. State was " << this->GetState(); return false; } std::string hostname = this->GetHostname(); int portNumber = this->GetPortNumber(); if (portNumber == -1 || hostname.size() <= 0) { //port number or hostname was not correct MITK_WARN << "Port number or hostname was not correct"; return false; } //create a new client socket m_Socket = igtl::ClientSocket::New(); //try to connect to the igtl server int response = dynamic_cast(m_Socket.GetPointer())-> ConnectToServer(hostname.c_str(), portNumber); //check the response if (response != 0) { MITK_ERROR << "The client could not connect to " << hostname << " port: " << portNumber; return false; } // everything is initialized and connected so the communication can be started this->SetState(Ready); //inform observers about this new client this->InvokeEvent(NewClientConnectionEvent()); return true; } void mitk::IGTLClient::Receive() { //MITK_INFO << "Trying to receive message"; //try to receive a message, if the socket is not present anymore stop the //communication unsigned int status = this->ReceivePrivate(this->m_Socket); if (status == IGTL_STATUS_NOT_PRESENT) { this->StopCommunicationWithSocket(this->m_Socket); //inform observers about loosing the connection to this socket this->InvokeEvent(LostConnectionEvent()); MITK_WARN("IGTLClient") << "Lost connection to server socket."; } } void mitk::IGTLClient::Send() { mitk::IGTLMessage::Pointer mitkMessage; //get the latest message from the queue mitkMessage = this->m_MessageQueue->PullSendMessage(); // there is no message => return if (mitkMessage.IsNull()) return; if (!this->SendMessagePrivate(mitkMessage, this->m_Socket)) { MITK_WARN("IGTLDevice") << "Could not send the message."; } } void mitk::IGTLClient::StopCommunicationWithSocket(igtl::Socket* /*socket*/) { m_StopCommunicationMutex.lock(); m_StopCommunication = true; m_StopCommunicationMutex.unlock(); } unsigned int mitk::IGTLClient::GetNumberOfConnections() { return this->m_Socket->GetConnected(); } diff --git a/Modules/OpenIGTLink/mitkIGTLDevice.cpp b/Modules/OpenIGTLink/mitkIGTLDevice.cpp index 8d531d085d..a9a5640b36 100644 --- a/Modules/OpenIGTLink/mitkIGTLDevice.cpp +++ b/Modules/OpenIGTLink/mitkIGTLDevice.cpp @@ -1,496 +1,496 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkIGTLDevice.h" //#include "mitkIGTException.h" //#include "mitkIGTTimeStamp.h" #include #include #include #include #include #include -#include +#include //remove later #include namespace mitk { itkEventMacroDefinition(MessageSentEvent, itk::AnyEvent); itkEventMacroDefinition(MessageReceivedEvent, itk::AnyEvent); itkEventMacroDefinition(CommandReceivedEvent, itk::AnyEvent); itkEventMacroDefinition(NewClientConnectionEvent, itk::AnyEvent); itkEventMacroDefinition(LostConnectionEvent, itk::AnyEvent); } //TODO: Which timeout is acceptable and also needed to transmit image data? Is there a maximum data limit? static const int SOCKET_SEND_RECEIVE_TIMEOUT_MSEC = 100; mitk::IGTLDevice::IGTLDevice(bool ReadFully) : // m_Data(mitk::DeviceDataUnspecified), m_State(mitk::IGTLDevice::Setup), m_Name("Unspecified Device"), m_StopCommunication(false), m_Hostname("127.0.0.1"), m_PortNumber(-1), m_LogMessages(false) { m_ReadFully = ReadFully; // execution rights are owned by the application thread at the beginning m_SendingFinishedMutex.lock(); m_ReceivingFinishedMutex.lock(); m_ConnectingFinishedMutex.lock(); // m_Data = mitk::DeviceDataUnspecified; // m_LatestMessage = igtl::MessageBase::New(); m_MessageFactory = mitk::IGTLMessageFactory::New(); m_MessageQueue = mitk::IGTLMessageQueue::New(); } mitk::IGTLDevice::~IGTLDevice() { /* stop communication and disconnect from igtl device */ if (GetState() == Running) { this->StopCommunication(); this->CloseConnection(); } else if (GetState() == Ready) { this->CloseConnection(); } /* cleanup tracking thread */ if (m_SendThread.joinable()) m_SendThread.detach(); if (m_ReceiveThread.joinable()) m_ReceiveThread.detach(); if (m_ConnectThread.joinable()) m_ConnectThread.detach(); } mitk::IGTLDevice::IGTLDeviceState mitk::IGTLDevice::GetState() const { std::lock_guard lock(m_StateMutex); return m_State; } void mitk::IGTLDevice::SetState(IGTLDeviceState state) { itkDebugMacro("setting m_State to " << state); m_StateMutex.lock(); // MutexLockHolder lock(*m_StateMutex); // lock and unlock the mutex if (m_State == state) { m_StateMutex.unlock(); return; } m_State = state; m_StateMutex.unlock(); this->Modified(); } bool mitk::IGTLDevice::TestConnection() { return true; } unsigned int mitk::IGTLDevice::ReceivePrivate(igtl::Socket* socket) { // Create a message buffer to receive header igtl::MessageHeader::Pointer headerMsg; headerMsg = igtl::MessageHeader::New(); // Initialize receive buffer headerMsg->InitPack(); // Receive generic header from the socket - int r = - socket->Receive(headerMsg->GetPackPointer(), headerMsg->GetPackSize(), 0); + bool timeout = false; + auto r = socket->Receive(headerMsg->GetPackPointer(), headerMsg->GetPackSize(), timeout, 0); //MITK_INFO << "Server received r = " << r; //MITK_INFO << "Received r = " << r; - if (r == 0) //connection error + if (timeout == true) //timeout + { + // a timeout was received, this is no error state, thus, do nothing + return IGTL_STATUS_TIME_OUT; + } + else if (r == 0) //connection error { // an error was received, therefore the communication with this socket // must be stoppedy return IGTL_STATUS_NOT_PRESENT; } - else if (r == -1) //timeout - { - // a timeout was received, this is no error state, thus, do nothing - return IGTL_STATUS_TIME_OUT; - } else if (r == headerMsg->GetPackSize()) { // Deserialize the header and check the CRC // ERROR HERE: This probably means the header data is corrupted... int crcCheck = headerMsg->Unpack(1); if (crcCheck & igtl::MessageHeader::UNPACK_HEADER) { // Allocate a time stamp igtl::TimeStamp::Pointer ts; ts = igtl::TimeStamp::New(); // Get time stamp igtlUint32 sec; igtlUint32 nanosec; headerMsg->GetTimeStamp(ts); ts->GetTimeStamp(&sec, &nanosec); // std::cerr << "Time stamp: " // << sec << "." // << nanosec << std::endl; // std::cerr << "Dev type and name: " << headerMsg->GetDeviceType() << " " // << headerMsg->GetDeviceName() << std::endl; // headerMsg->Print(std::cout); //check the type of the received message //if it is a GET_, STP_ or RTS_ command push it into the command queue //otherwise continue reading the whole message from the socket const char* curDevType = headerMsg->GetDeviceType(); if (std::strstr(curDevType, "GET_") != nullptr || std::strstr(curDevType, "STP_") != nullptr || std::strstr(curDevType, "RTS_") != nullptr) { this->m_MessageQueue->PushCommandMessage(headerMsg); this->InvokeEvent(CommandReceivedEvent()); return IGTL_STATUS_OK; } //Create a message according to the header message igtl::MessageBase::Pointer curMessage; curMessage = m_MessageFactory->CreateInstance(headerMsg); //check if the curMessage is created properly, if not the message type is //not supported and the message has to be skipped if (curMessage.IsNull()) { socket->Skip(headerMsg->GetBodySizeToRead(), 0); // MITK_ERROR("IGTLDevice") << "The received type is not supported. Please " // "add it to the message factory."; return IGTL_STATUS_NOT_FOUND; } //insert the header to the message and allocate the pack curMessage->SetMessageHeader(headerMsg); curMessage->AllocatePack(); // Receive transform data from the socket int receiveCheck = 0; receiveCheck = socket->Receive(curMessage->GetPackBodyPointer(), curMessage->GetPackBodySize(), m_ReadFully); if (receiveCheck > 0) { int c = curMessage->Unpack(1); if (!(c & igtl::MessageHeader::UNPACK_BODY)) { return IGTL_STATUS_CHECKSUM_ERROR; } //check the type of the received message //if it is a command push it into the command queue //otherwise into the normal receive queue //STP_ commands are handled here because they implemented additional //member variables that are not stored in the header message if (std::strstr(curDevType, "STT_") != nullptr) { this->m_MessageQueue->PushCommandMessage(curMessage); this->InvokeEvent(CommandReceivedEvent()); } else { if(m_LogMessages) MITK_INFO << "Received Message: " << mitk::IGTLMessage::New(curMessage)->ToString(); this->m_MessageQueue->PushMessage(curMessage); this->InvokeEvent(MessageReceivedEvent()); } return IGTL_STATUS_OK; } else { MITK_WARN("IGTLDevice") << "Received a valid header but could not " << "read the whole message."; return IGTL_STATUS_UNKNOWN_ERROR; } } else { //CRC check failed MITK_WARN << "CRC Check failed"; return IGTL_STATUS_CHECKSUM_ERROR; } } else { //Message size information and actual data size don't match. //this state is not suppossed to be reached, return unknown error MITK_WARN << "IGTL status unknown"; return IGTL_STATUS_UNKNOWN_ERROR; } } void mitk::IGTLDevice::SendMessage(mitk::IGTLMessage::Pointer msg) { m_MessageQueue->PushSendMessage(msg); } unsigned int mitk::IGTLDevice::SendMessagePrivate(mitk::IGTLMessage::Pointer msg, igtl::Socket::Pointer socket) { //check the input message if (msg.IsNull()) { MITK_ERROR("IGTLDevice") << "Could not send message because message is not " "valid. Please check."; return false; } igtl::MessageBase* sendMessage = msg->GetMessage(); // Pack (serialize) and send sendMessage->Pack(); int sendSuccess = socket->Send(sendMessage->GetPackPointer(), sendMessage->GetPackSize()); if (sendSuccess) { if (m_LogMessages) { MITK_INFO << "Send IGTL message: " << msg->ToString(); } this->InvokeEvent(MessageSentEvent()); return IGTL_STATUS_OK; } else { return IGTL_STATUS_UNKNOWN_ERROR; } } void mitk::IGTLDevice::RunCommunication(void (IGTLDevice::*ComFunction)(void), std::mutex& mutex) { if (this->GetState() != Running) return; try { // keep lock until end of scope std::lock_guard communicationFinishedLockHolder(mutex); // Because m_StopCommunication is used by two threads, access has to be guarded // by a mutex. To minimize thread locking, a local copy is used here bool localStopCommunication; // update the local copy of m_StopCommunication this->m_StopCommunicationMutex.lock(); localStopCommunication = this->m_StopCommunication; this->m_StopCommunicationMutex.unlock(); while ((this->GetState() == Running) && (localStopCommunication == false)) { (this->*ComFunction)(); /* Update the local copy of m_StopCommunication */ this->m_StopCommunicationMutex.lock(); localStopCommunication = m_StopCommunication; this->m_StopCommunicationMutex.unlock(); // time to relax, this sets the maximum ever possible framerate to 1000 Hz itksys::SystemTools::Delay(1); } } catch (...) { mutex.unlock(); this->StopCommunication(); MITK_ERROR("IGTLDevice::RunCommunication") << "Error while communicating. Thread stopped."; //mitkThrowException(mitk::IGTException) << "Error while communicating. Thread stopped."; } // StopCommunication was called, thus the mode should be changed back to Ready now // that the tracking loop has ended. //this->SetState(Ready); //this is done elsewhere MITK_DEBUG("IGTLDevice::RunCommunication") << "Reached end of communication."; // returning from this function (and ThreadStartCommunication()) // this will end the thread return; } bool mitk::IGTLDevice::StartCommunication() { if (this->GetState() != Ready) return false; // go to mode Running this->SetState(Running); // set a timeout for the sending and receiving this->m_Socket->SetTimeout(SOCKET_SEND_RECEIVE_TIMEOUT_MSEC); // update the local copy of m_StopCommunication this->m_StopCommunicationMutex.lock(); this->m_StopCommunication = false; this->m_StopCommunicationMutex.unlock(); // transfer the execution rights to tracking thread m_SendingFinishedMutex.unlock(); m_ReceivingFinishedMutex.unlock(); m_ConnectingFinishedMutex.unlock(); // start new threads that execute the communication m_SendThread = std::thread(&IGTLDevice::ThreadStartSending, this); m_ReceiveThread = std::thread(&IGTLDevice::ThreadStartReceiving, this); m_ConnectThread = std::thread(&IGTLDevice::ThreadStartConnecting, this); // mitk::IGTTimeStamp::GetInstance()->Start(this); return true; } bool mitk::IGTLDevice::StopCommunication() { if (this->GetState() == Running) // Only if the object is in the correct state { // m_StopCommunication is used by two threads, so we have to ensure correct // thread handling m_StopCommunicationMutex.lock(); m_StopCommunication = true; m_StopCommunicationMutex.unlock(); // we have to wait here that the other thread recognizes the STOP-command // and executes it m_SendingFinishedMutex.lock(); m_ReceivingFinishedMutex.lock(); m_ConnectingFinishedMutex.lock(); // mitk::IGTTimeStamp::GetInstance()->Stop(this); // notify realtime clock // StopCommunication was called, thus the mode should be changed back // to Ready now that the tracking loop has ended. this->SetState(Ready); } return true; } bool mitk::IGTLDevice::CloseConnection() { if (this->GetState() == Setup) { return true; } else if (this->GetState() == Running) { this->StopCommunication(); } m_Socket->CloseSocket(); /* return to setup mode */ this->SetState(Setup); // this->InvokeEvent(mitk::LostConnectionEvent()); return true; } bool mitk::IGTLDevice::SendRTSMessage(const char* type) { //construct the device type for the return message, it starts with RTS_ and //continues with the requested type std::string returnType("RTS_"); returnType.append(type); //create a return message igtl::MessageBase::Pointer rtsMsg = this->m_MessageFactory->CreateInstance(returnType); //if retMsg is nullptr there is no return message defined and thus it is not //necessary to send one back if (rtsMsg.IsNotNull()) { this->SendMessage(mitk::IGTLMessage::New(rtsMsg)); return true; } else { return false; } } void mitk::IGTLDevice::Connect() { MITK_DEBUG << "mitk::IGTLDevice::Connect();"; } igtl::ImageMessage::Pointer mitk::IGTLDevice::GetNextImage2dMessage() { return this->m_MessageQueue->PullImage2dMessage(); } igtl::ImageMessage::Pointer mitk::IGTLDevice::GetNextImage3dMessage() { return this->m_MessageQueue->PullImage3dMessage(); } igtl::TransformMessage::Pointer mitk::IGTLDevice::GetNextTransformMessage() { return this->m_MessageQueue->PullTransformMessage(); } igtl::TrackingDataMessage::Pointer mitk::IGTLDevice::GetNextTrackingDataMessage() { igtl::TrackingDataMessage::Pointer msg = this->m_MessageQueue->PullTrackingMessage(); return msg; } igtl::StringMessage::Pointer mitk::IGTLDevice::GetNextStringMessage() { return this->m_MessageQueue->PullStringMessage(); } igtl::MessageBase::Pointer mitk::IGTLDevice::GetNextMiscMessage() { return this->m_MessageQueue->PullMiscMessage(); } igtl::MessageBase::Pointer mitk::IGTLDevice::GetNextCommand() { return m_MessageQueue->PullCommandMessage(); } void mitk::IGTLDevice::EnableNoBufferingMode(bool enable) { m_MessageQueue->EnableNoBufferingMode(enable); } void mitk::IGTLDevice::EnableNoBufferingMode( mitk::IGTLMessageQueue::Pointer queue, bool enable) { queue->EnableNoBufferingMode(enable); } void mitk::IGTLDevice::ThreadStartSending() { this->RunCommunication(&IGTLDevice::Send, m_SendingFinishedMutex); } void mitk::IGTLDevice::ThreadStartReceiving() { this->RunCommunication(&IGTLDevice::Receive, m_ReceivingFinishedMutex); } void mitk::IGTLDevice::ThreadStartConnecting() { this->RunCommunication(&IGTLDevice::Connect, m_ConnectingFinishedMutex); } diff --git a/Modules/OpenIGTLink/mitkIGTLDevice.h b/Modules/OpenIGTLink/mitkIGTLDevice.h index 684d5953f2..f204bb6dbb 100644 --- a/Modules/OpenIGTLink/mitkIGTLDevice.h +++ b/Modules/OpenIGTLink/mitkIGTLDevice.h @@ -1,408 +1,412 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKIGTLDEVICE_H #define MITKIGTLDEVICE_H #include #include #include "mitkCommon.h" //itk #include "itkObject.h" //igtl #include "igtlSocket.h" #include "igtlMessageBase.h" #include "igtlTransformMessage.h" //mitkIGTL #include "MitkOpenIGTLinkExports.h" #include "mitkIGTLMessageFactory.h" #include "mitkIGTLMessageQueue.h" #include "mitkIGTLMessage.h" +#if !defined(MITK_WINDOWS_NO_UNDEF) && defined(SendMessage) + #undef SendMessage +#endif + namespace mitk { /** * \brief Interface for all OpenIGTLink Devices * * Defines the methods that are common for all devices using OpenIGTLink. It * can open/close a connection, start/stop a communication and send/receive * messages. * * It uses message queues to store the incoming and outgoing mails. They are * configurable, you can set buffering on and off. * * The device is in one of three different states: Setup, Ready or Running. * Setup is the initial state. From this state on you can call * OpenConnection() and arrive in the Ready state. From the Ready state you * call StartCommunication() to arrive in the Running state. Now the device * is continuosly checking for new connections, receiving messages and * sending messages. This runs in a seperate thread. To stop the communication * call StopCommunication() (to arrive in Ready state) or CloseConnection() * (to arrive in the Setup state). * * \ingroup OpenIGTLink * */ class MITKOPENIGTLINK_EXPORT IGTLDevice : public itk::Object { public: mitkClassMacroItkParent(IGTLDevice, itk::Object); IGTLDevice(bool ReadFully); /** * \brief Type for state variable. * The IGTLDevice is always in one of these states. * */ enum IGTLDeviceState { Setup, Ready, Running }; /** * \brief Opens a connection to the device * * This may only be called if there is currently no connection to the * device. If OpenConnection() is successful, the object will change from * Setup state to Ready state. */ virtual bool OpenConnection() = 0; /** * \brief Closes the connection to the device * * This may only be called if there is currently a connection to the * device, but device is not running (e.g. object is in Ready state) */ virtual bool CloseConnection(); /** * \brief Stops the communication between the two devices * * This may only be called if the device is in Running state. */ virtual bool StopCommunication(); /** * \brief Starts the communication between the two devices * * This may only be called if the device is in Ready state. */ bool StartCommunication(); /** * \brief Continuously calls the given function * * This may only be called if the device is in Running state and only from * a seperate thread. * * \param ComFunction function pointer that specifies the method to be executed * \param mutex the mutex that corresponds to the function pointer */ void RunCommunication(void (IGTLDevice::*ComFunction)(void), std::mutex& mutex); /** * \brief Adds the given message to the sending queue * * This may only be called after the connection to the device has been * established with a call to OpenConnection(). Note that the message * is not send directly. This method just adds it to the send queue. * \param msg The message to be added to the sending queue */ void SendMessage(mitk::IGTLMessage::Pointer msg); /** * \brief Returns current object state (Setup, Ready or Running) */ IGTLDeviceState GetState() const; /** * \brief Returns the oldest message in the command queue * \return The oldest message from the command queue. */ igtl::MessageBase::Pointer GetNextCommand(); /** * \brief Returns the oldest message in the receive queue * \return The oldest message from the receive queue */ igtl::ImageMessage::Pointer GetNextImage2dMessage(); igtl::ImageMessage::Pointer GetNextImage3dMessage(); igtl::TransformMessage::Pointer GetNextTransformMessage(); igtl::TrackingDataMessage::Pointer GetNextTrackingDataMessage(); igtl::StringMessage::Pointer GetNextStringMessage(); igtl::MessageBase::Pointer GetNextMiscMessage(); /** * \brief Sets the port number of the device */ itkSetMacro(PortNumber, int); /** * \brief Returns the port number of the device */ itkGetMacro(PortNumber, int); /** * \brief Sets the ip/hostname of the device */ itkSetMacro(Hostname, std::string); /** * \brief Returns the ip/hostname of the device */ itkGetMacro(Hostname, std::string); /** * \brief Returns the name of this device */ itkGetConstMacro(Name, std::string); /** * \brief Sets the name of this device */ itkSetMacro(Name, std::string); /** * \brief Advises this IGTL Device to always block until the whole message is read. */ itkSetMacro(ReadFully, bool); /** * \brief Returns a const reference to the receive queue */ itkGetConstMacro(MessageQueue, mitk::IGTLMessageQueue::Pointer); /** * \brief Returns the message factory */ itkGetMacro(MessageFactory, mitk::IGTLMessageFactory::Pointer); /** * \brief start method for the sending thread. */ void ThreadStartSending(); /** * \brief start method for the receiving thread. */ void ThreadStartReceiving(); /** * \brief start method for the connection thread. */ void ThreadStartConnecting(); /** * \brief TestConnection() tries to connect to a IGTL device on the current * ip and port * * \todo Implement this method. Send a status message and check the answer. * * TestConnection() tries to connect to a IGTL server on the current * ip and port and returns which device it has found. * \return It returns the type of the device that answers. Throws an * exception * if no device is available on that ip/port. * @throw mitk::Exception Throws an exception if there are errors * while connecting to the device. */ virtual bool TestConnection(); /** * \brief Send RTS message of given type */ bool SendRTSMessage(const char* type); /** * \brief Sets the buffering mode of the given queue */ void EnableNoBufferingMode(mitk::IGTLMessageQueue::Pointer queue, bool enable = true); void EnableNoBufferingMode(bool enable = true); /** * \brief Returns the number of connections of this device */ virtual unsigned int GetNumberOfConnections() = 0; itkGetMacro(LogMessages, bool); itkSetMacro(LogMessages, bool); protected: /** * \brief Sends a message. * * This may only be called after the connection to the device has been * established with a call to OpenConnection(). This method uses the given * socket to send the given MessageReceivedEvent * * \param msg the message to be sent * \param socket the socket used to communicate with the other device * * \retval IGTL_STATUS_OK the message was sent * \retval IGTL_STATUS_UNKONWN_ERROR the message was not sent because an * unknown error occurred */ unsigned int SendMessagePrivate(mitk::IGTLMessage::Pointer msg, igtl::Socket::Pointer socket); /** * \brief Call this method to receive a message. * * The message will be saved in the receive queue. */ virtual void Receive() = 0; /** * \brief Call this method to receive a message from the given device. * * The message will be saved in the receive queue. * * \param device the socket that connects this device with the other one. * * \retval IGTL_STATUS_OK a message or a command was received * \retval IGTL_STATUS_NOT_PRESENT the socket is not connected anymore * \retval IGTL_STATUS_TIME_OUT the socket timed out * \retval IGTL_STATUS_CHECKSUM_ERROR the checksum of the received msg was * incorrect * \retval IGTL_STATUS_UNKNOWN_ERROR an unknown error occurred */ unsigned int ReceivePrivate(igtl::Socket* device); /** * \brief Call this method to send a message. The message will be read from * the queue. */ virtual void Send() = 0; /** * \brief Call this method to check for other devices that want to connect * to this one. * * In case of a client this method is doing nothing. In case of a server it * is checking for other devices and if there is one it establishes a * connection. */ virtual void Connect(); /** * \brief Stops the communication with the given socket * */ virtual void StopCommunicationWithSocket(igtl::Socket* socket) = 0; /** * \brief change object state */ void SetState(IGTLDeviceState state); IGTLDevice(); ~IGTLDevice() override; /** current object state (Setup, Ready or Running) */ IGTLDeviceState m_State; /** the name of this device */ std::string m_Name; /** signal used to stop the thread*/ bool m_StopCommunication; /** mutex to control access to m_StopCommunication */ std::mutex m_StopCommunicationMutex; /** mutex used to make sure that the send thread is just started once */ std::mutex m_SendingFinishedMutex; /** mutex used to make sure that the receive thread is just started once */ std::mutex m_ReceivingFinishedMutex; /** mutex used to make sure that the connect thread is just started once */ std::mutex m_ConnectingFinishedMutex; /** mutex to control access to m_State */ mutable std::mutex m_StateMutex; /** the hostname or ip of the device */ std::string m_Hostname; /** the port number of the device */ int m_PortNumber; /** the socket used to communicate with other IGTL devices */ igtl::Socket::Pointer m_Socket; /** The message receive queue */ mitk::IGTLMessageQueue::Pointer m_MessageQueue; /** A message factory that provides the New() method for all msg types */ mitk::IGTLMessageFactory::Pointer m_MessageFactory; bool m_LogMessages; private: /** Sending thread */ std::thread m_SendThread; /** Receiving thread */ std::thread m_ReceiveThread; /** Connecting thread */ std::thread m_ConnectThread; /** Always try to read the full message. */ bool m_ReadFully; }; /** * \brief connect to this Event to get notified when a message was successfully sent * * \note This event is invoked in the communication thread, therefore do not use it to make * changes in the GUI!!! Use the QT signal slot system to decouple this call from the com thread * */ itkEventMacroDeclaration(MessageSentEvent, itk::AnyEvent); /** * \brief connect to this Event to get notified when a message was received * * \note Check if you can invoke this events like this or if you have to make * it thread-safe. They are not invoked in the main thread!!! * */ itkEventMacroDeclaration(MessageReceivedEvent, itk::AnyEvent); /** * \brief connect to this Event to get notified when a command was received * * \note Check if you can invoke this events like this or if you have to make * it thread-safe. They are not invoked in the main thread!!! * */ itkEventMacroDeclaration(CommandReceivedEvent, itk::AnyEvent); /** * \brief connect to this Event to get notified when another igtl device * connects with this device. * * \note Check if you can invoke this events like this or if you have to make * it thread-safe. They are not invoked in the main thread!!! * */ itkEventMacroDeclaration(NewClientConnectionEvent, itk::AnyEvent); /** * \brief connect to this Event to get notified when this device looses the * connection to a socket. * * \note Check if you can invoke this events like this or if you have to make * it thread-safe. They are not invoked in the main thread!!! * */ itkEventMacroDeclaration(LostConnectionEvent, itk::AnyEvent); } // namespace mitk #endif /* MITKIGTLDEVICE_H */ diff --git a/Modules/OpenIGTLink/mitkIGTLDummyMessage.h b/Modules/OpenIGTLink/mitkIGTLDummyMessage.h index 9ebd122a2b..13f7ee48e3 100644 --- a/Modules/OpenIGTLink/mitkIGTLDummyMessage.h +++ b/Modules/OpenIGTLink/mitkIGTLDummyMessage.h @@ -1,67 +1,62 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKIGTLDUMMYMESSAGE_H #define MITKIGTLDUMMYMESSAGE_H #include "MitkOpenIGTLinkExports.h" #include "igtlObject.h" #include "igtlStringMessage.h" #include "mitkIGTLMessageCloneHandler.h" namespace mitk { /** * \class IGTLDummyMessage * \brief This class is a dummy message to show how to implement a new message type */ class MITKOPENIGTLINK_EXPORT IGTLDummyMessage : public igtl::StringMessage { public: - typedef IGTLDummyMessage Self; - typedef StringMessage Superclass; - typedef igtl::SmartPointer Pointer; - typedef igtl::SmartPointer ConstPointer; - igtlTypeMacro(mitk::IGTLDummyMessage, igtl::StringMessage); igtlNewMacro(mitk::IGTLDummyMessage); public: /** * Set dummy string */ void SetDummyString( const std::string& dummyString ); /** * Get dummy string */ std::string GetDummyString(); /** * Returns a clone of itself */ igtl::MessageBase::Pointer Clone() override ; protected: IGTLDummyMessage(); ~IGTLDummyMessage() override; std::string m_dummyString; }; mitkIGTMessageCloneClassMacro(IGTLDummyMessage, DummyMsgCloneHandler); } // namespace mitk #endif diff --git a/Modules/OpenIGTLink/mitkIGTLServer.cpp b/Modules/OpenIGTLink/mitkIGTLServer.cpp index e18c9ab5be..f68ad4fa30 100644 --- a/Modules/OpenIGTLink/mitkIGTLServer.cpp +++ b/Modules/OpenIGTLink/mitkIGTLServer.cpp @@ -1,207 +1,207 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkIGTLServer.h" #include #include #include #include #include -#include +#include mitk::IGTLServer::IGTLServer(bool ReadFully) : IGTLDevice(ReadFully) { } mitk::IGTLServer::~IGTLServer() { } bool mitk::IGTLServer::OpenConnection() { if (this->GetState() != Setup) { mitkThrowException(mitk::Exception) << "Can only try to create a server if in setup mode"; return false; } int portNumber = this->GetPortNumber(); if (portNumber == -1) { //port number was not correct return false; } //create a new server socket m_Socket = igtl::ServerSocket::New(); //try to create the igtl server int response = dynamic_cast(m_Socket.GetPointer())-> CreateServer(portNumber); //check the response if (response != 0) { mitkThrowException(mitk::Exception) << "The server could not be created. Port: " << portNumber; return false; } // everything is initialized and connected so the communication can be started this->SetState(Ready); return true; } bool mitk::IGTLServer::CloseConnection() { //remove all registered clients m_SentListMutex.lock(); m_ReceiveListMutex.lock(); SocketListType allRegisteredSockets(m_RegisteredClients); m_SentListMutex.unlock(); m_ReceiveListMutex.unlock(); this->StopCommunicationWithSocket(allRegisteredSockets); return mitk::IGTLDevice::CloseConnection(); } void mitk::IGTLServer::Connect() { igtl::Socket::Pointer socket; //check if another igtl device wants to connect to this socket socket = ((igtl::ServerSocket*)(this->m_Socket.GetPointer()))->WaitForConnection(1); //if there is a new connection the socket is not null if (socket.IsNotNull()) { //add the new client socket to the list of registered clients m_SentListMutex.lock(); m_ReceiveListMutex.lock(); this->m_RegisteredClients.push_back(socket); m_SentListMutex.unlock(); m_ReceiveListMutex.unlock(); //inform observers about this new client this->InvokeEvent(NewClientConnectionEvent()); MITK_INFO("IGTLServer") << "Connected to a new client: " << socket; } } void mitk::IGTLServer::Receive() { unsigned int status = IGTL_STATUS_OK; SocketListType socketsToBeRemoved; //the server can be connected with several clients, therefore it has to check //all registered clients SocketListIteratorType it; m_ReceiveListMutex.lock(); auto it_end = this->m_RegisteredClients.end(); for (it = this->m_RegisteredClients.begin(); it != it_end; ++it) { //it is possible that ReceivePrivate detects that the current socket is //already disconnected. Therefore, it is necessary to remove this socket //from the registered clients list status = this->ReceivePrivate(*it); if (status == IGTL_STATUS_NOT_PRESENT) { //remember this socket for later, it is not a good idea to remove it //from the list directly because we iterate over the list at this point socketsToBeRemoved.push_back(*it); MITK_WARN("IGTLServer") << "Lost connection to a client socket. "; } else if (status != 1) { MITK_DEBUG("IGTLServer") << "IGTL Message with status: " << status; } } m_ReceiveListMutex.unlock(); if (socketsToBeRemoved.size() > 0) { //remove the sockets that are not connected anymore this->StopCommunicationWithSocket(socketsToBeRemoved); //inform observers about loosing the connection to these sockets this->InvokeEvent(LostConnectionEvent()); } } void mitk::IGTLServer::Send() { //get the latest message from the queue mitk::IGTLMessage::Pointer curMessage = this->m_MessageQueue->PullSendMessage(); // there is no message => return if (curMessage.IsNull()) return; //the server can be connected with several clients, therefore it has to check //all registered clients //sending a message to all registered clients might not be the best solution, //it could be better to store the client together with the requested type. Then //the data would be send to the appropriate client and to noone else. //(I know it is no excuse but PLUS is doing exactly the same, they broadcast //everything) m_SentListMutex.lock(); SocketListIteratorType it; auto it_end = this->m_RegisteredClients.end(); for (it = this->m_RegisteredClients.begin(); it != it_end; ++it) { //maybe there should be a check here if the current socket is still active this->SendMessagePrivate(curMessage, *it); MITK_DEBUG("IGTLServer") << "Sent IGTL Message"; } m_SentListMutex.unlock(); } void mitk::IGTLServer::StopCommunicationWithSocket( SocketListType& toBeRemovedSockets) { for (auto i = toBeRemovedSockets.begin(); i != toBeRemovedSockets.end(); i++) this->StopCommunicationWithSocket(*i); } void mitk::IGTLServer::StopCommunicationWithSocket(igtl::Socket* client) { m_SentListMutex.lock(); m_ReceiveListMutex.lock(); auto i = m_RegisteredClients.begin(); auto end = m_RegisteredClients.end(); while (i != end) { if ((*i) == client) { // //close the socket (*i)->CloseSocket(); //and remove it from the list i = this->m_RegisteredClients.erase(i); MITK_INFO("IGTLServer") << "Removed client socket from server client list."; break; } else { ++i; } } m_SentListMutex.unlock(); m_ReceiveListMutex.unlock(); } unsigned int mitk::IGTLServer::GetNumberOfConnections() { return this->m_RegisteredClients.size(); } diff --git a/Modules/OpenIGTLink/mitkIGTLStatus.h b/Modules/OpenIGTLink/mitkIGTLStatus.h new file mode 100644 index 0000000000..1257cad374 --- /dev/null +++ b/Modules/OpenIGTLink/mitkIGTLStatus.h @@ -0,0 +1,39 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef __IGTL_STATUS_H // Same include guard as OpenIGTLink's igtl_status.h file +#define __IGTL_STATUS_H + +// Internal status codes from OpenIGTLink's igtl_status.h file + +#define IGTL_STATUS_INVALID 0 +#define IGTL_STATUS_OK 1 +#define IGTL_STATUS_UNKNOWN_ERROR 2 +#define IGTL_STATUS_PANICK_MODE 3 +#define IGTL_STATUS_NOT_FOUND 4 +#define IGTL_STATUS_ACCESS_DENIED 5 +#define IGTL_STATUS_BUSY 6 +#define IGTL_STATUS_TIME_OUT 7 +#define IGTL_STATUS_OVERFLOW 8 +#define IGTL_STATUS_CHECKSUM_ERROR 9 +#define IGTL_STATUS_CONFIG_ERROR 10 +#define IGTL_STATUS_RESOURCE_ERROR 11 +#define IGTL_STATUS_ILLEGAL_INSTRUCTION 12 +#define IGTL_STATUS_NOT_READY 13 +#define IGTL_STATUS_MANUAL_MODE 14 +#define IGTL_STATUS_DISABLED 15 +#define IGTL_STATUS_NOT_PRESENT 16 +#define IGTL_STATUS_UNKNOWN_VERSION 17 +#define IGTL_STATUS_HARDWARE_FAILURE 18 +#define IGTL_STATUS_SHUT_DOWN 19 + +#endif diff --git a/Modules/Python/CMakeLists.txt b/Modules/Python/CMakeLists.txt index 14a48964fa..161bddb4c7 100644 --- a/Modules/Python/CMakeLists.txt +++ b/Modules/Python/CMakeLists.txt @@ -1,13 +1,8 @@ if(MITK_USE_Python3) - set(OpenCV_DEP ) - if(MITK_USE_OpenCV) - set(OpenCV_DEP OpenCV) - endif() - mitk_create_module( DEPENDS MitkCore PACKAGE_DEPENDS PUBLIC Python3|Python ) add_subdirectory(autoload/PythonService) endif() diff --git a/Modules/QtPython/CMakeLists.txt b/Modules/QtPython/CMakeLists.txt index d497d472ef..433b7ae4f4 100644 --- a/Modules/QtPython/CMakeLists.txt +++ b/Modules/QtPython/CMakeLists.txt @@ -1,18 +1,13 @@ if(MITK_USE_Python3) mitkFunctionCheckCompilerFlags("/wd4273" CMAKE_CXX_FLAGS) - set(OpenCV_DEP ) - if(MITK_USE_OpenCV) - set(OpenCV_DEP OpenCV) - endif() - mitk_create_module( DEPENDS MitkCore MitkQtWidgets MitkPython PACKAGE_DEPENDS PUBLIC Qt5|Widgets CTK|CTKScriptingPythonCore+CTKScriptingPythonWidgets ) if(BUILD_TESTING) add_subdirectory(Testing) endif() endif() diff --git a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp index db34ff9a9b..1c1f4f2138 100644 --- a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp +++ b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp @@ -1,284 +1,284 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkMxNMultiWidget.h" #include "QmitkRenderWindowWidget.h" // mitk core #include #include #include // qt #include QmitkMxNMultiWidget::QmitkMxNMultiWidget(QWidget* parent, Qt::WindowFlags f/* = 0*/, const QString& multiWidgetName/* = "mxnmulti"*/) : QmitkAbstractMultiWidget(parent, f, multiWidgetName) , m_TimeNavigationController(nullptr) , m_CrosshairVisibility(false) { m_TimeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); } QmitkMxNMultiWidget::~QmitkMxNMultiWidget() { auto allRenderWindows = this->GetRenderWindows(); for (auto& renderWindow : allRenderWindows) { m_TimeNavigationController->Disconnect(renderWindow->GetSliceNavigationController()); } } void QmitkMxNMultiWidget::InitializeMultiWidget() { SetLayout(1, 1); SetDisplayActionEventHandler(std::make_unique()); auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } } void QmitkMxNMultiWidget::Synchronize(bool synchronized) { if (synchronized) { SetDisplayActionEventHandler(std::make_unique()); } else { SetDisplayActionEventHandler(std::make_unique()); } auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const QString& widgetName) const { if ("axial" == widgetName || "sagittal" == widgetName || "coronal" == widgetName || "3d" == widgetName) { return GetActiveRenderWindowWidget()->GetRenderWindow(); } return QmitkAbstractMultiWidget::GetRenderWindow(widgetName); } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const mitk::BaseRenderer::ViewDirection& /*viewDirection*/) const { // currently no mapping between view directions and render windows // simply return the currently active render window return GetActiveRenderWindowWidget()->GetRenderWindow(); } void QmitkMxNMultiWidget::SetActiveRenderWindowWidget(RenderWindowWidgetPointer activeRenderWindowWidget) { auto currentActiveRenderWindowWidget = GetActiveRenderWindowWidget(); if (currentActiveRenderWindowWidget == activeRenderWindowWidget) { return; } // reset the decoration color of the previously active render window widget if (nullptr != currentActiveRenderWindowWidget) { auto decorationColor = currentActiveRenderWindowWidget->GetDecorationColor(); QColor hexColor(decorationColor[0] * 255, decorationColor[1] * 255, decorationColor[2] * 255); currentActiveRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid " + hexColor.name(QColor::HexRgb) + "; }"); } // set the new decoration color of the currently active render window widget if (nullptr != activeRenderWindowWidget) { activeRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid #FF6464; }"); } QmitkAbstractMultiWidget::SetActiveRenderWindowWidget(activeRenderWindowWidget); } void QmitkMxNMultiWidget::SetSelectedPosition(const mitk::Point3D& newPosition, const QString& widgetName) { RenderWindowWidgetPointer renderWindowWidget; if (widgetName.isNull()) { renderWindowWidget = GetActiveRenderWindowWidget(); } else { renderWindowWidget = GetRenderWindowWidget(widgetName); } if (nullptr != renderWindowWidget) { renderWindowWidget->GetSliceNavigationController()->SelectSliceByPoint(newPosition); renderWindowWidget->RequestUpdate(); return; } MITK_ERROR << "Position can not be set for an unknown render window widget."; } const mitk::Point3D QmitkMxNMultiWidget::GetSelectedPosition(const QString& /*widgetName*/) const { // see T26208 return mitk::Point3D(); } void QmitkMxNMultiWidget::SetCrosshairVisibility(bool activate) { auto renderWindowWidgets = GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { renderWindowWidget.second->ActivateCrosshair(activate); } m_CrosshairVisibility = activate; } void QmitkMxNMultiWidget::ResetCrosshair() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(dataStorage); SetWidgetPlaneMode(mitk::InteractionSchemeSwitcher::MITKStandard); } void QmitkMxNMultiWidget::SetWidgetPlaneMode(int userMode) { MITK_DEBUG << "Changing crosshair mode to " << userMode; switch (userMode) { case 0: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKStandard); break; case 1: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationUncoupled); break; case 2: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationCoupled); break; case 3: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKSwivel); break; } } mitk::SliceNavigationController* QmitkMxNMultiWidget::GetTimeNavigationController() { return m_TimeNavigationController; } ////////////////////////////////////////////////////////////////////////// // PUBLIC SLOTS // MOUSE EVENTS ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::wheelEvent(QWheelEvent* e) { emit WheelMoved(e); } void QmitkMxNMultiWidget::mousePressEvent(QMouseEvent*) { // nothing here, but necessary for mouse interactions (.xml-configuration files) } void QmitkMxNMultiWidget::moveEvent(QMoveEvent* e) { QWidget::moveEvent(e); // it is necessary to readjust the position of the overlays as the MultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } void QmitkMxNMultiWidget::RemoveRenderWindowWidget() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); auto iterator = renderWindowWidgets.find(this->GetNameFromIndex(this->GetNumberOfRenderWindowWidgets() - 1)); if (iterator == renderWindowWidgets.end()) { return; } // disconnect each signal of this render window widget RenderWindowWidgetPointer renderWindowWidgetToRemove = iterator->second; m_TimeNavigationController->Disconnect(renderWindowWidgetToRemove->GetSliceNavigationController()); QmitkAbstractMultiWidget::RemoveRenderWindowWidget(); } ////////////////////////////////////////////////////////////////////////// // PRIVATE ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::SetLayoutImpl() { int requiredRenderWindowWidgets = GetRowCount() * GetColumnCount(); int existingRenderWindowWidgets = GetRenderWindowWidgets().size(); int difference = requiredRenderWindowWidgets - existingRenderWindowWidgets; while (0 < difference) { // more render window widgets needed CreateRenderWindowWidget(); --difference; } while (0 > difference) { // less render window widgets needed RemoveRenderWindowWidget(); ++difference; } auto firstRenderWindowWidget = GetFirstRenderWindowWidget(); if (nullptr != firstRenderWindowWidget) { SetActiveRenderWindowWidget(firstRenderWindowWidget); } GetMultiWidgetLayoutManager()->SetLayoutDesign(QmitkMultiWidgetLayoutManager::LayoutDesign::DEFAULT); } void QmitkMxNMultiWidget::CreateRenderWindowWidget() { // create the render window widget and connect signal / slot QString renderWindowWidgetName = GetNameFromIndex(GetNumberOfRenderWindowWidgets()); RenderWindowWidgetPointer renderWindowWidget = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); renderWindowWidget->SetCornerAnnotationText(renderWindowWidgetName.toStdString()); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget); auto renderWindow = renderWindowWidget->GetRenderWindow(); auto layoutManager = GetMultiWidgetLayoutManager(); connect(renderWindow, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(renderWindow, &QmitkRenderWindow::ResetView, this, &QmitkMxNMultiWidget::ResetCrosshair); connect(renderWindow, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkMxNMultiWidget::SetCrosshairVisibility); connect(renderWindow, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkMxNMultiWidget::SetWidgetPlaneMode); // connect time navigation controller to react on geometry time events with the render window's slice naviation controller - m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow->GetSliceNavigationController(), false); + m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow->GetSliceNavigationController()); // reverse connection between the render window's slice navigation controller and the time navigation controller - renderWindow->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); + renderWindow->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); } diff --git a/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp b/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp index 0f67cfc350..3194fec820 100644 --- a/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp +++ b/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp @@ -1,808 +1,808 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #define SMW_INFO MITK_INFO("widget.stdmulti") #include "QmitkStdMultiWidget.h" #include "QmitkRenderWindowWidget.h" // mitk core #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // qt #include #include #include // vtk #include // c++ #include QmitkStdMultiWidget::QmitkStdMultiWidget(QWidget *parent, Qt::WindowFlags f/* = 0*/, const QString &name/* = "stdmulti"*/) : QmitkAbstractMultiWidget(parent, f, name) , m_TimeNavigationController(nullptr) , m_PendingCrosshairPositionEvent(false) { m_TimeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); } QmitkStdMultiWidget::~QmitkStdMultiWidget() { auto allRenderWindows = this->GetRenderWindows(); for (auto& renderWindow : allRenderWindows) { m_TimeNavigationController->Disconnect(renderWindow->GetSliceNavigationController()); } } void QmitkStdMultiWidget::InitializeMultiWidget() { // yellow is default color for widget4 m_DecorationColorWidget4[0] = 1.0f; m_DecorationColorWidget4[1] = 1.0f; m_DecorationColorWidget4[2] = 0.0f; SetLayout(2, 2); // transfer colors in WorldGeometry-Nodes of the associated Renderer mitk::IntProperty::Pointer layer; // of widget 1 m_PlaneNode1 = mitk::BaseRenderer::GetInstance(GetRenderWindow1()->renderWindow())->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode1->SetColor(GetDecorationColor(0)); layer = mitk::IntProperty::New(1000); m_PlaneNode1->SetProperty("layer", layer); // of widget 2 m_PlaneNode2 = mitk::BaseRenderer::GetInstance(GetRenderWindow2()->renderWindow())->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode2->SetColor(GetDecorationColor(1)); layer = mitk::IntProperty::New(1000); m_PlaneNode2->SetProperty("layer", layer); // of widget 3 m_PlaneNode3 = mitk::BaseRenderer::GetInstance(GetRenderWindow3()->renderWindow())->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode3->SetColor(GetDecorationColor(2)); layer = mitk::IntProperty::New(1000); m_PlaneNode3->SetProperty("layer", layer); // the parent node m_ParentNodeForGeometryPlanes = mitk::BaseRenderer::GetInstance(GetRenderWindow4()->renderWindow())->GetCurrentWorldPlaneGeometryNode(); layer = mitk::IntProperty::New(1000); m_ParentNodeForGeometryPlanes->SetProperty("layer", layer); AddDisplayPlaneSubTree(); SetDisplayActionEventHandler(std::make_unique()); auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow(const QString& widgetName) const { if ("axial" == widgetName) { return GetRenderWindow1(); } if ("sagittal" == widgetName) { return GetRenderWindow2(); } if ("coronal" == widgetName) { return GetRenderWindow3(); } if ("3d" == widgetName) { return GetRenderWindow4(); } return QmitkAbstractMultiWidget::GetRenderWindow(widgetName); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow(const mitk::BaseRenderer::ViewDirection& viewDirection) const { return GetRenderWindow(static_cast(viewDirection)); } void QmitkStdMultiWidget::SetSelectedPosition(const mitk::Point3D& newPosition, const QString& /*widgetName*/) { GetRenderWindow1()->GetSliceNavigationController()->SelectSliceByPoint(newPosition); GetRenderWindow2()->GetSliceNavigationController()->SelectSliceByPoint(newPosition); GetRenderWindow3()->GetSliceNavigationController()->SelectSliceByPoint(newPosition); RequestUpdateAll(); } const mitk::Point3D QmitkStdMultiWidget::GetSelectedPosition(const QString& /*widgetName*/) const { const mitk::PlaneGeometry* plane1 = GetRenderWindow1()->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry* plane2 = GetRenderWindow2()->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry* plane3 = GetRenderWindow3()->GetSliceNavigationController()->GetCurrentPlaneGeometry(); mitk::Line3D line; if ((plane1 != nullptr) && (plane2 != nullptr) && (plane1->IntersectionLine(plane2, line))) { mitk::Point3D point; if ((plane3 != nullptr) && (plane3->IntersectionPoint(line, point))) { return point; } } return mitk::Point3D(); } void QmitkStdMultiWidget::SetCrosshairVisibility(bool visible) { if (m_PlaneNode1.IsNotNull()) { m_PlaneNode1->SetVisibility(visible); } if (m_PlaneNode2.IsNotNull()) { m_PlaneNode2->SetVisibility(visible); } if (m_PlaneNode3.IsNotNull()) { m_PlaneNode3->SetVisibility(visible); } emit NotifyCrosshairVisibilityChanged(visible); RequestUpdateAll(); } bool QmitkStdMultiWidget::GetCrosshairVisibility() const { bool crosshairVisibility = true; if (m_PlaneNode1.IsNotNull()) { bool visibilityProperty = false; m_PlaneNode1->GetVisibility(visibilityProperty, nullptr); crosshairVisibility &= visibilityProperty; } if (m_PlaneNode2.IsNotNull()) { bool visibilityProperty = false; crosshairVisibility &= m_PlaneNode2->GetVisibility(visibilityProperty, nullptr); crosshairVisibility &= visibilityProperty; } if (m_PlaneNode3.IsNotNull()) { bool visibilityProperty = false; crosshairVisibility &= m_PlaneNode3->GetVisibility(visibilityProperty, nullptr); crosshairVisibility &= visibilityProperty; } return crosshairVisibility; } void QmitkStdMultiWidget::ResetCrosshair() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(dataStorage); SetWidgetPlaneMode(mitk::InteractionSchemeSwitcher::MITKStandard); } void QmitkStdMultiWidget::SetWidgetPlaneMode(int userMode) { MITK_DEBUG << "Changing crosshair mode to " << userMode; switch (userMode) { case 0: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKStandard); break; case 1: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationUncoupled); break; case 2: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationCoupled); break; case 3: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKSwivel); break; } emit NotifyCrosshairRotationModeChanged(userMode); } mitk::SliceNavigationController* QmitkStdMultiWidget::GetTimeNavigationController() { return m_TimeNavigationController; } void QmitkStdMultiWidget::AddPlanesToDataStorage() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_ParentNodeForGeometryPlanes.IsNotNull()) { dataStorage->Add(m_ParentNodeForGeometryPlanes); dataStorage->Add(m_PlaneNode1, m_ParentNodeForGeometryPlanes); dataStorage->Add(m_PlaneNode2, m_ParentNodeForGeometryPlanes); dataStorage->Add(m_PlaneNode3, m_ParentNodeForGeometryPlanes); } } void QmitkStdMultiWidget::RemovePlanesFromDataStorage() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_ParentNodeForGeometryPlanes.IsNotNull()) { dataStorage->Remove(m_PlaneNode1); dataStorage->Remove(m_PlaneNode2); dataStorage->Remove(m_PlaneNode3); dataStorage->Remove(m_ParentNodeForGeometryPlanes); } } void QmitkStdMultiWidget::HandleCrosshairPositionEvent() { if (!m_PendingCrosshairPositionEvent) { m_PendingCrosshairPositionEvent = true; QTimer::singleShot(0, this, SLOT(HandleCrosshairPositionEventDelayed())); } } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow(unsigned int number) const { switch (number) { case 0: return GetRenderWindow1(); case 1: return GetRenderWindow2(); case 2: return GetRenderWindow3(); case 3: return GetRenderWindow4(); default: MITK_ERROR << "Requested unknown render window"; break; } return nullptr; } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow1() const { return QmitkAbstractMultiWidget::GetRenderWindow(GetNameFromIndex(0, 0)); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow2() const { return QmitkAbstractMultiWidget::GetRenderWindow(GetNameFromIndex(0, 1)); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow3() const { return QmitkAbstractMultiWidget::GetRenderWindow(GetNameFromIndex(1, 0)); } QmitkRenderWindow* QmitkStdMultiWidget::GetRenderWindow4() const { return QmitkAbstractMultiWidget::GetRenderWindow(GetNameFromIndex(1, 1)); } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane1() const { return m_PlaneNode1; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane2() const { return m_PlaneNode2; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane3() const { return m_PlaneNode3; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane(unsigned number) const { switch (number) { case 1: return m_PlaneNode1; case 2: return m_PlaneNode2; case 3: return m_PlaneNode3; default: MITK_ERROR << "Requested unknown render window"; break; } return nullptr; } void QmitkStdMultiWidget::SetDecorationColor(unsigned int widgetNumber, mitk::Color color) { switch (widgetNumber) { case 0: if (m_PlaneNode1.IsNotNull()) { m_PlaneNode1->SetColor(color); } break; case 1: if (m_PlaneNode2.IsNotNull()) { m_PlaneNode2->SetColor(color); } break; case 2: if (m_PlaneNode3.IsNotNull()) { m_PlaneNode3->SetColor(color); } break; case 3: m_DecorationColorWidget4 = color; break; default: MITK_ERROR << "Decoration color for unknown widget!"; break; } } mitk::Color QmitkStdMultiWidget::GetDecorationColor(unsigned int widgetNumber) { // The implementation looks a bit messy here, but it avoids // synchronization of the color of the geometry nodes and an // internal member here. // Default colors were chosen for decent visibility. // Feel free to change your preferences in the workbench. float tmp[3] = { 0.0f, 0.0f, 0.0f }; switch (widgetNumber) { case 0: { if (m_PlaneNode1.IsNotNull()) { if (m_PlaneNode1->GetColor(tmp)) { return dynamic_cast(m_PlaneNode1->GetProperty("color"))->GetColor(); } } float red[3] = { 0.753f, 0.0f, 0.0f }; // This is #C00000 in hex return mitk::Color(red); } case 1: { if (m_PlaneNode2.IsNotNull()) { if (m_PlaneNode2->GetColor(tmp)) { return dynamic_cast(m_PlaneNode2->GetProperty("color"))->GetColor(); } } float green[3] = { 0.0f, 0.69f, 0.0f }; // This is #00B000 in hex return mitk::Color(green); } case 2: { if (m_PlaneNode3.IsNotNull()) { if (m_PlaneNode3->GetColor(tmp)) { return dynamic_cast(m_PlaneNode3->GetProperty("color"))->GetColor(); } } float blue[3] = { 0.0, 0.502f, 1.0f }; // This is #0080FF in hex return mitk::Color(blue); } case 3: { return m_DecorationColorWidget4; } default: MITK_ERROR << "Decoration color for unknown widget!"; float black[3] = { 0.0f, 0.0f, 0.0f }; return mitk::Color(black); } } void QmitkStdMultiWidget::mousePressEvent(QMouseEvent*) { // nothing here, but necessary for mouse interactions (.xml-configuration files) } void QmitkStdMultiWidget::moveEvent(QMoveEvent* e) { QWidget::moveEvent(e); // it is necessary to readjust the position of the Annotation as the StdMultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } void QmitkStdMultiWidget::wheelEvent(QWheelEvent* e) { emit WheelMoved(e); } void QmitkStdMultiWidget::HandleCrosshairPositionEventDelayed() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } m_PendingCrosshairPositionEvent = false; // find image with highest layer mitk::TNodePredicateDataType::Pointer isImageData = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = dataStorage->GetSubset(isImageData).GetPointer(); mitk::Point3D crosshairPos = GetSelectedPosition(""); mitk::BaseRenderer* baseRenderer = GetRenderWindow1()->GetSliceNavigationController()->GetRenderer(); auto globalCurrentTimePoint = baseRenderer->GetTime(); mitk::DataNode::Pointer node = mitk::FindTopmostVisibleNode(nodes, crosshairPos, globalCurrentTimePoint, baseRenderer); mitk::DataNode::Pointer topSourceNode; mitk::Image::Pointer image; bool isBinary = false; int component = 0; if (node.IsNotNull()) { node->GetBoolProperty("binary", isBinary); if (isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = dataStorage->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, crosshairPos, globalCurrentTimePoint, baseRenderer); } if (topSourceNode.IsNotNull()) { image = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } std::string statusText; std::stringstream stream; itk::Index<3> p; unsigned int timestep = baseRenderer->GetTimeStep(); if (image.IsNotNull() && (image->GetTimeSteps() > timestep)) { image->GetGeometry()->WorldToIndex(crosshairPos, p); stream.precision(2); stream << "Position: <" << std::fixed << crosshairPos[0] << ", " << std::fixed << crosshairPos[1] << ", " << std::fixed << crosshairPos[2] << "> mm"; stream << "; Index: <" << p[0] << ", " << p[1] << ", " << p[2] << "> "; mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5(mitk::FastSinglePixelAccess, image->GetChannelDescriptor().GetPixelType(), image, image->GetVolumeData(image->GetTimeGeometry()->TimePointToTimeStep(globalCurrentTimePoint)), p, pixelValue, component); if (fabs(pixelValue) > 1000000 || fabs(pixelValue) < 0.01) { stream << "; Time: " << globalCurrentTimePoint << " ms; Pixelvalue: " << std::scientific << pixelValue << " "; } else { stream << "; Time: " << globalCurrentTimePoint << " ms; Pixelvalue: " << pixelValue << " "; } } else { stream << "No image information at this position!"; } statusText = stream.str(); mitk::StatusBar::GetInstance()->DisplayGreyValueText(statusText.c_str()); } void QmitkStdMultiWidget::Fit() { vtkSmartPointer vtkrenderer; vtkrenderer = mitk::BaseRenderer::GetInstance(GetRenderWindow1()->renderWindow())->GetVtkRenderer(); if (nullptr != vtkrenderer) { vtkrenderer->ResetCamera(); } vtkrenderer = mitk::BaseRenderer::GetInstance(GetRenderWindow2()->renderWindow())->GetVtkRenderer(); if (nullptr != vtkrenderer) { vtkrenderer->ResetCamera(); } vtkrenderer = mitk::BaseRenderer::GetInstance(GetRenderWindow3()->renderWindow())->GetVtkRenderer(); if (nullptr != vtkrenderer) { vtkrenderer->ResetCamera(); } vtkrenderer = mitk::BaseRenderer::GetInstance(GetRenderWindow4()->renderWindow())->GetVtkRenderer(); if (nullptr != vtkrenderer) { vtkrenderer->ResetCamera(); } mitk::BaseRenderer::GetInstance(GetRenderWindow1()->renderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(GetRenderWindow2()->renderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(GetRenderWindow3()->renderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(GetRenderWindow4()->renderWindow())->GetCameraController()->Fit(); int w = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); vtkObject::SetGlobalWarningDisplay(w); } void QmitkStdMultiWidget::AddDisplayPlaneSubTree() { // add the displayed planes of the multiwidget to a node to which the subtree // @a planesSubTree points ... mitk::PlaneGeometryDataMapper2D::Pointer mapper; // ... of widget 1 mitk::BaseRenderer* renderer1 = mitk::BaseRenderer::GetInstance(GetRenderWindow1()->renderWindow()); m_PlaneNode1 = renderer1->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode1->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode1->SetProperty("name", mitk::StringProperty::New(std::string(renderer1->GetName()) + ".plane")); m_PlaneNode1->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode1->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode1->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 2 mitk::BaseRenderer* renderer2 = mitk::BaseRenderer::GetInstance(GetRenderWindow2()->renderWindow()); m_PlaneNode2 = renderer2->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode2->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode2->SetProperty("name", mitk::StringProperty::New(std::string(renderer2->GetName()) + ".plane")); m_PlaneNode2->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode2->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode2->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 3 mitk::BaseRenderer *renderer3 = mitk::BaseRenderer::GetInstance(GetRenderWindow3()->renderWindow()); m_PlaneNode3 = renderer3->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode3->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode3->SetProperty("name", mitk::StringProperty::New(std::string(renderer3->GetName()) + ".plane")); m_PlaneNode3->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode3->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode3->SetMapper(mitk::BaseRenderer::Standard2D, mapper); m_ParentNodeForGeometryPlanes = mitk::DataNode::New(); m_ParentNodeForGeometryPlanes->SetProperty("name", mitk::StringProperty::New("Widgets")); m_ParentNodeForGeometryPlanes->SetProperty("helper object", mitk::BoolProperty::New(true)); } void QmitkStdMultiWidget::EnsureDisplayContainsPoint(mitk::BaseRenderer *renderer, const mitk::Point3D &p) { mitk::Point2D pointOnDisplay; renderer->WorldToDisplay(p, pointOnDisplay); if (pointOnDisplay[0] < renderer->GetVtkRenderer()->GetOrigin()[0] || pointOnDisplay[1] < renderer->GetVtkRenderer()->GetOrigin()[1] || pointOnDisplay[0] > renderer->GetVtkRenderer()->GetOrigin()[0] + renderer->GetViewportSize()[0] || pointOnDisplay[1] > renderer->GetVtkRenderer()->GetOrigin()[1] + renderer->GetViewportSize()[1]) { mitk::Point2D pointOnPlane; renderer->GetCurrentWorldPlaneGeometry()->Map(p, pointOnPlane); renderer->GetCameraController()->MoveCameraToPoint(pointOnPlane); } } void QmitkStdMultiWidget::SetWidgetPlaneVisibility(const char *widgetName, bool visible, mitk::BaseRenderer *renderer) { auto dataStorage = GetDataStorage(); if (nullptr != dataStorage) { mitk::DataNode* dataNode = dataStorage->GetNamedNode(widgetName); if (dataNode != nullptr) { dataNode->SetVisibility(visible, renderer); } } } void QmitkStdMultiWidget::SetWidgetPlanesVisibility(bool visible, mitk::BaseRenderer *renderer) { if (m_PlaneNode1.IsNotNull()) { m_PlaneNode1->SetVisibility(visible, renderer); } if (m_PlaneNode2.IsNotNull()) { m_PlaneNode2->SetVisibility(visible, renderer); } if (m_PlaneNode3.IsNotNull()) { m_PlaneNode3->SetVisibility(visible, renderer); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } ////////////////////////////////////////////////////////////////////////// // PRIVATE ////////////////////////////////////////////////////////////////////////// void QmitkStdMultiWidget::SetLayoutImpl() { CreateRenderWindowWidgets(); GetMultiWidgetLayoutManager()->SetLayoutDesign(QmitkMultiWidgetLayoutManager::LayoutDesign::DEFAULT); // Initialize views as axial, sagittal, coronal to all data objects in DataStorage auto geo = GetDataStorage()->ComputeBoundingGeometry3D(GetDataStorage()->GetAll()); mitk::RenderingManager::GetInstance()->InitializeViews(geo); } void QmitkStdMultiWidget::CreateRenderWindowWidgets() { // create axial render window (widget) QString renderWindowWidgetName = GetNameFromIndex(0, 0); RenderWindowWidgetPointer renderWindowWidget1 = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); auto renderWindow1 = renderWindowWidget1->GetRenderWindow(); renderWindow1->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); renderWindowWidget1->SetDecorationColor(GetDecorationColor(0)); renderWindowWidget1->SetCornerAnnotationText("Axial"); renderWindowWidget1->GetRenderWindow()->SetLayoutIndex(ViewDirection::AXIAL); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget1); // create sagittal render window (widget) renderWindowWidgetName = GetNameFromIndex(0, 1); RenderWindowWidgetPointer renderWindowWidget2 = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); auto renderWindow2 = renderWindowWidget2->GetRenderWindow(); renderWindow2->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Sagittal); renderWindowWidget2->SetDecorationColor(GetDecorationColor(1)); renderWindowWidget2->setStyleSheet("border: 0px"); renderWindowWidget2->SetCornerAnnotationText("Sagittal"); renderWindowWidget2->GetRenderWindow()->SetLayoutIndex(ViewDirection::SAGITTAL); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget2); // create coronal render window (widget) renderWindowWidgetName = GetNameFromIndex(1, 0); RenderWindowWidgetPointer renderWindowWidget3 = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); auto renderWindow3 = renderWindowWidget3->GetRenderWindow(); renderWindow3->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Coronal); renderWindowWidget3->SetDecorationColor(GetDecorationColor(2)); renderWindowWidget3->SetCornerAnnotationText("Coronal"); renderWindowWidget3->GetRenderWindow()->SetLayoutIndex(ViewDirection::CORONAL); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget3); // create 3D render window (widget) renderWindowWidgetName = GetNameFromIndex(1, 1); RenderWindowWidgetPointer renderWindowWidget4 = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); auto renderWindow4 = renderWindowWidget4->GetRenderWindow(); renderWindow4->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Original); renderWindowWidget4->SetDecorationColor(GetDecorationColor(3)); renderWindowWidget4->SetCornerAnnotationText("3D"); renderWindowWidget4->GetRenderWindow()->SetLayoutIndex(ViewDirection::THREE_D); mitk::BaseRenderer::GetInstance(renderWindowWidget4->GetRenderWindow()->renderWindow())->SetMapperID(mitk::BaseRenderer::Standard3D); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget4); SetActiveRenderWindowWidget(renderWindowWidget1); // connect to the "time navigation controller": send time via sliceNavigationControllers - m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow1->GetSliceNavigationController(), false); - m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow2->GetSliceNavigationController(), false); - m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow3->GetSliceNavigationController(), false); - m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow4->GetSliceNavigationController(), false); + m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow1->GetSliceNavigationController()); + m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow2->GetSliceNavigationController()); + m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow3->GetSliceNavigationController()); + m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow4->GetSliceNavigationController()); renderWindow1->GetSliceNavigationController()->ConnectGeometrySendEvent( mitk::BaseRenderer::GetInstance(renderWindow4->renderWindow())); // reverse connection between sliceNavigationControllers and timeNavigationController - renderWindow1->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); - renderWindow2->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); - renderWindow3->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); - //renderWindow4->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); + renderWindow1->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); + renderWindow2->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); + renderWindow3->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); + //renderWindow4->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); auto layoutManager = GetMultiWidgetLayoutManager(); connect(renderWindow1, &QmitkRenderWindow::ResetView, this, &QmitkStdMultiWidget::ResetCrosshair); connect(renderWindow1, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkStdMultiWidget::SetCrosshairVisibility); connect(renderWindow1, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkStdMultiWidget::SetWidgetPlaneMode); connect(renderWindow1, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(this, &QmitkStdMultiWidget::NotifyCrosshairVisibilityChanged, renderWindow1, &QmitkRenderWindow::UpdateCrosshairVisibility); connect(this, &QmitkStdMultiWidget::NotifyCrosshairRotationModeChanged, renderWindow1, &QmitkRenderWindow::UpdateCrosshairRotationMode); connect(renderWindow2, &QmitkRenderWindow::ResetView, this, &QmitkStdMultiWidget::ResetCrosshair); connect(renderWindow2, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkStdMultiWidget::SetCrosshairVisibility); connect(renderWindow2, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkStdMultiWidget::SetWidgetPlaneMode); connect(renderWindow2, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(this, &QmitkStdMultiWidget::NotifyCrosshairVisibilityChanged, renderWindow2, &QmitkRenderWindow::UpdateCrosshairVisibility); connect(this, &QmitkStdMultiWidget::NotifyCrosshairRotationModeChanged, renderWindow2, &QmitkRenderWindow::UpdateCrosshairRotationMode); connect(renderWindow3, &QmitkRenderWindow::ResetView, this, &QmitkStdMultiWidget::ResetCrosshair); connect(renderWindow3, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkStdMultiWidget::SetCrosshairVisibility); connect(renderWindow3, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkStdMultiWidget::SetWidgetPlaneMode); connect(renderWindow3, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(this, &QmitkStdMultiWidget::NotifyCrosshairVisibilityChanged, renderWindow3, &QmitkRenderWindow::UpdateCrosshairVisibility); connect(this, &QmitkStdMultiWidget::NotifyCrosshairRotationModeChanged, renderWindow3, &QmitkRenderWindow::UpdateCrosshairRotationMode); connect(renderWindow4, &QmitkRenderWindow::ResetView, this, &QmitkStdMultiWidget::ResetCrosshair); connect(renderWindow4, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkStdMultiWidget::SetCrosshairVisibility); connect(renderWindow4, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkStdMultiWidget::SetWidgetPlaneMode); connect(renderWindow4, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(this, &QmitkStdMultiWidget::NotifyCrosshairVisibilityChanged, renderWindow4, &QmitkRenderWindow::UpdateCrosshairVisibility); connect(this, &QmitkStdMultiWidget::NotifyCrosshairRotationModeChanged, renderWindow4, &QmitkRenderWindow::UpdateCrosshairRotationMode); } diff --git a/Modules/QtWidgetsExt/include/QmitkHistogram.h b/Modules/QtWidgetsExt/include/QmitkHistogram.h index 6a0138af17..8becdc7e9d 100644 --- a/Modules/QtWidgetsExt/include/QmitkHistogram.h +++ b/Modules/QtWidgetsExt/include/QmitkHistogram.h @@ -1,57 +1,58 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QMITKHISTOGRAM_H #define QMITKHISTOGRAM_H #include #include #include #include +#include /** \brief Used to create a histogram that can be shown in a Qwt Plot. See QmitkHistogramWidget for an example of its usage. */ class QmitkHistogram : public QwtPlotItem { public: explicit QmitkHistogram(const QString &title = QString::null); explicit QmitkHistogram(const QwtText &title); ~QmitkHistogram() override; void setData(const QwtIntervalSeriesData &data); const QwtIntervalSeriesData &data() const; void setColor(const QColor &); QColor color() const; QRectF boundingRect() const override; void draw(QPainter *, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &) const override; void setBaseline(double reference); double baseline() const; protected: virtual void drawBar(QPainter *, Qt::Orientation o, const QRect &) const; private: void init(); class HistogramData; HistogramData *m_Data; }; #endif diff --git a/Modules/QtWidgetsExt/include/QmitkPlotWidget.h b/Modules/QtWidgetsExt/include/QmitkPlotWidget.h index d8849416c1..caf0e86ab5 100644 --- a/Modules/QtWidgetsExt/include/QmitkPlotWidget.h +++ b/Modules/QtWidgetsExt/include/QmitkPlotWidget.h @@ -1,300 +1,301 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _QmitkPlotWidget_H_ #define _QmitkPlotWidget_H_ #include "MitkQtWidgetsExtExports.h" #include "mitkCommon.h" #include #include #include #include #include #include +#include #include #include /** * Provides a convenient interface for plotting curves using qwt. * Designed for qwt version 5.2.1. * Can be used with a QmitkPlotDialog, which provides a "Close" button. * @see QmitkPlotDialog * * To plot data do the following: * 1. Create two QmitkPlotWidget::DataVector Objects and fill them * with corresponding x/y values. DataVectors are simple stl-vectors * of type std::vector. Please note that the xValues * vector and the yValues vector MUST have the same size. * 2. Instantiate the widget for example like that: * QmitkPlotWidget* widget = new QmitkPlotWidget( this, "widget" ); * widget->SetAxisTitle( QwtPlot::xBottom, "My x asis [mm]" ); * widget->SetAxisTitle( QwtPlot::yLeft, "My y axis [mm]" ); * int curveId = widget->InsertCurve( "My sophisticated data" ); * widget->SetCurveData( curveId, xValues, yValues ); * widget->SetCurvePen( curveId, QPen( red ) ); * widget->SetCurveTitle( curveId, "My curve description" ); * widget->Replot(); * 3. You can modify the behavior of the plot by directly referencing * the QwtPlot instance using the method GetPlot(). * @see QwtPlot * @deprecatedSince{2018_04} Use QmitkChartWidget instead */ class MITKQTWIDGETSEXT_EXPORT QmitkPlotWidget : public QWidget { private: Q_OBJECT public: /** * represents the data type used for scalar values stored * in data arrays. This type is provided by qwt and may not * be changed. */ typedef double ScalarType; /** * This type may be used to store a set of scalar values * representing either x or y coordinates of the data * points that should be rendered. */ typedef std::vector DataVector; /** * convenience type used to store pairs representing x/y coordinates * that should be rendered as a curve by the plot widget */ typedef std::vector> XYDataVector; /** * Standard qt constructor */ QmitkPlotWidget(QWidget *parent = nullptr, const char *title = nullptr, const char *name = nullptr, Qt::WindowFlags f = nullptr); /** * Virtual destructor */ ~QmitkPlotWidget() override; /** * Returns the instance of the plot-widget. This may be used * to modify any detail of the appearance of the plot. */ QwtPlot *GetPlot(); /** * Set the title using (formatted) QwtText object */ void SetPlotTitle(const QwtText &qwt_title); /** * Set plain text title, using default formatting */ void SetPlotTitle(const char *title); /** * Inserts a new curve into the plot-window. * @param title the name of the curve * @param color * @returns the id of the curve. Use this id to * refer to the curve, if you want to modify or add data. */ unsigned int InsertCurve(const char *title, QColor color = QColor(Qt::black)); /** * Sets the title of the given axis. For the set of available axes * @see QwtPlot::Axis. * @param axis the axis for which the description should be set. * @param title the name of the axis. */ void SetAxisTitle(int axis, const char *title); /** * Sets the data for a previously added curve. Data is provided as two vectors of double. * The first vector represents the x coordinates, the second vector represents the y coordinates. * @param curveId the id of the curve for which data should be added. * @param xValues the x coordinates of the points that define the curve * @param yValues the y coordinates of the points that define the curve * @returns whether data was added successfully or not */ bool SetCurveData(unsigned int curveId, const DataVector &xValues, const DataVector &yValues); /** * @brief Sets the data with errors for a previously added curve. * * @param curveId the id of the curve for which data should be added. * @param xValues the x coordinates of the points that define the curve * @param yValues the y coordinates of the points that define the curve * @param yLowerError the magnitude (>0) of the error in the lesser direction of y * @param yUpperError the magnitude (>0) of the error in the larger direction of y * @returns whether data was added successfully or not */ bool SetCurveData(unsigned int curveId, const DataVector &xValues, const DataVector &yValues, const DataVector &yLowerError, const DataVector &yUpperError); /** * @brief Sets the data with errors for a previously added curve. * * @param curveId the id of the curve for which data should be added. * @param xValues the x coordinates of the points that define the curve * @param yValues the y coordinates of the points that define the curve * @param xLowerError the magnitude (>0) of the error in the lesser direction of x * @param xUpperError the magnitude (>0) of the error in the larger direction of x * @param yLowerError the magnitude (>0) of the error in the lesser direction of y * @param yUpperError the magnitude (>0) of the error in the larger direction of y * @returns whether data was added successfully or not */ bool SetCurveData(unsigned int curveId, const DataVector &xValues, const DataVector &yValues, const DataVector &xLowerError, const DataVector &xUpperError, const DataVector &yLowerError, const DataVector &yUpperError); /** * Sets the data for a previously added curve. Data is provided as a vectors of pairs. * The pairs represent x/y coordinates of the points that define the curve. * @param curveId the id of the curve for which data should be added. * @param data the coordinates of the points that define the curve * @returns whether data was added successfully or not */ bool SetCurveData(unsigned int curveId, const XYDataVector &data); /** * Defines how a curve should be drawn. For drawing a curve, a QPen is used. * @param curveId the id of the curve for which appearance should be changed * @param pen a QPen (@see QPen) defining the line style */ void SetCurvePen(unsigned int curveId, const QPen &pen); /** * Assign a brush, which defines the fill pattern of shapes drawn by a QPainter. * In case of brush.style() != QBrush::NoBrush and * style() != QwtPlotCurve::Sticks * the area between the curve and the baseline will be filled. * In case !brush.color().isValid() the area will be filled by pen.color(). * The fill algorithm simply connects the first and the last curve point to the * baseline. So the curve data has to be sorted (ascending or descending). * @param curveId the id of the curve for which appearance should be changed * @param brush a QBrush (@see QBrush) defining the line style */ void SetCurveBrush(unsigned int curveId, const QBrush &brush); /** * Sets the style how the line is drawn for the curve; like, plain line, * or with the data points marked with a symbol; * @param curveId * @param style A QwtPlotCurve::CurveStyle */ void SetCurveStyle(unsigned int curveId, const QwtPlotCurve::CurveStyle style); /** * Sets the style data points are drawn for the curve; like, a line, * or dots; * @param curveId * @param symbol A QwtSymbol */ void SetCurveSymbol(unsigned int curveId, QwtSymbol *symbol); void SetCurveAntialiasingOn(unsigned int curveId); void SetCurveAntialiasingOff(unsigned int curveId); /** * Sets the title of the given curve. The title will be shown in the legend of * the QwtPlot. * @param curveId the id of the curve for which the title should be set * @param title the description of the curve that will be shown in the legend. */ void SetCurveTitle(unsigned int curveId, const char *title); /** * Defines how a curves errors should be drawn. For drawing a QPen is used. * @param curveId the id of the curve for which error appearance should be changed * @param pen a QPen (@see QPen) defining the line style */ void SetErrorPen(unsigned int curveId, const QPen &pen); /** * Defines the style of errors, symbols or as a curve. * @param curveId the id of the curve for which error appearance should be changed * @param drawSmybols true - draw symbols, false - draw curve */ void SetErrorStyleSymbols(unsigned int curveId, bool drawSmybols); /** * Sets the legend of the plot * */ void SetLegend(QwtLegend *legend, QwtPlot::LegendPosition pos = QwtPlot::RightLegend, double ratio = -1); /** * Set a curve's legend attribute * @param curveId the id of the curve * @param attribute the legend attribute to be set */ void SetLegendAttribute(unsigned int curveId, const QwtPlotCurve::LegendAttribute &attribute); /** * Triggers a replot of the curve. Replot should be called once after * setting new data. */ void Replot(); /** * Resets the plot into an empty state */ void Clear(); protected: /** * Converts the given values into a raw double* array. * A new array is allocated via new and must be deleted[] by the caller. */ double *ConvertToRawArray(const DataVector &values); /** * Converts the given values into a raw double* array. * A new array is allocated via new and must be deleted[] by the caller. * @param values the x/y values to convert to an array * @param component defines if the x values (0) or the y values(1) should * be converted. Other values than 0 and 1 will not be accepted. */ double *ConvertToRawArray(const XYDataVector &values, unsigned int component); /** * Adds an error interval curve. * * All errors should be absolutes. The magnitude will be used. * * @param curveId Which curve should the error curve be added to * @param lessError Error in the negative direction (value - lessError) * @param moreError Error in the positive direction (value + lessError) * @param isXError Should the error bars be drawn horizontally */ bool AddErrorIntervalCurve(unsigned int curveId, const DataVector &lessError, const DataVector &moreError, bool isXError); QwtPlot *m_Plot; std::vector> m_PlotCurveVector; }; #endif diff --git a/Modules/RT/CMakeLists.txt b/Modules/RT/CMakeLists.txt index 753686d460..50956dfe8b 100644 --- a/Modules/RT/CMakeLists.txt +++ b/Modules/RT/CMakeLists.txt @@ -1,10 +1,7 @@ -if(NOT DEFINED DCMTK_dcmrt_LIBRARY OR DCMTK_dcmrt_LIBRARY) - mitk_create_module( - DEPENDS MitkSceneSerializationBase MitkDICOM - PACKAGE_DEPENDS PUBLIC DCMTK - ) - add_subdirectory(autoload/DICOMRTIO) - add_subdirectory(test) -else() - message("MITK RT Support disabled because the DCMTK dcmrt library not found") -endif() +mitk_create_module( + DEPENDS MitkSceneSerializationBase MitkDICOM + PACKAGE_DEPENDS PRIVATE DCMTK|dcmrt +) + +add_subdirectory(autoload/DICOMRTIO) +add_subdirectory(test) diff --git a/Modules/RT/autoload/DICOMRTIO/CMakeLists.txt b/Modules/RT/autoload/DICOMRTIO/CMakeLists.txt index 54d3ff5335..fe902e05a0 100644 --- a/Modules/RT/autoload/DICOMRTIO/CMakeLists.txt +++ b/Modules/RT/autoload/DICOMRTIO/CMakeLists.txt @@ -1,8 +1,8 @@ MITK_CREATE_MODULE(DICOMRTIO INCLUDE_DIRS PRIVATE src/IO DEPENDS PUBLIC MitkRT MitkContourModel - PACKAGE_DEPENDS PUBLIC DCMTK + PACKAGE_DEPENDS PRIVATE DCMTK|dcmrt AUTOLOAD_WITH MitkCore ) diff --git a/Modules/RTUI/CMakeLists.txt b/Modules/RTUI/CMakeLists.txt index eb039647d2..148b6682b9 100644 --- a/Modules/RTUI/CMakeLists.txt +++ b/Modules/RTUI/CMakeLists.txt @@ -1,5 +1,7 @@ MITK_CREATE_MODULE( INCLUDE_DIRS Qmitk Helper DEPENDS MitkRT - PACKAGE_DEPENDS PUBLIC CTK Qt5|Core + PACKAGE_DEPENDS + PUBLIC Qt5|Widgets + PRIVATE CTK|CTKWidgets ) diff --git a/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp b/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp index 6144ccae55..c28b3e54ce 100644 --- a/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp +++ b/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp @@ -1,463 +1,463 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSceneReaderV1.h" #include "Poco/Path.h" #include "mitkBaseRenderer.h" #include "mitkIOUtil.h" #include "mitkProgressBar.h" #include "mitkPropertyListDeserializer.h" #include "mitkSerializerMacros.h" #include #include #include -#include +#include #include MITK_REGISTER_SERIALIZER(SceneReaderV1) namespace { typedef std::pair> NodesAndParentsPair; bool NodeSortByLayerIsLessThan(const NodesAndParentsPair &left, const NodesAndParentsPair &right) { if (left.first.IsNotNull() && right.first.IsNotNull()) { int leftLayer; int rightLayer; if (left.first->GetIntProperty("layer", leftLayer) && right.first->GetIntProperty("layer", rightLayer)) { return leftLayer < rightLayer; } else { // fall back to name sort return left.first->GetName() < right.first->GetName(); } } // in all other cases, fall back to stupid pointer comparison // this is not reasonable but at least answers the sorting // question clearly return left.first.GetPointer() < right.first.GetPointer(); } // This is a workaround until we are able to save time-related information in an // actual file format of surfaces. void ApplyProportionalTimeGeometryProperties(mitk::BaseData* data) { auto* geometry = dynamic_cast(data->GetTimeGeometry()); if (nullptr == geometry) return; auto properties = data->GetPropertyList(); float value = 0.0f; if (properties->GetFloatProperty("ProportionalTimeGeometry.FirstTimePoint", value)) { if (value == -std::numeric_limits::infinity()) value = std::numeric_limits::lowest(); geometry->SetFirstTimePoint(value); } if (properties->GetFloatProperty("ProportionalTimeGeometry.StepDuration", value)) geometry->SetStepDuration(value); } - mitk::PropertyList::Pointer DeserializeProperties(const tinyxml2::XMLElement *propertiesElement, const fs::path& basePath) + mitk::PropertyList::Pointer DeserializeProperties(const tinyxml2::XMLElement *propertiesElement, const std::filesystem::path& basePath) { if (propertiesElement == nullptr) return nullptr; - fs::path path(propertiesElement->Attribute("file")); + std::filesystem::path path(propertiesElement->Attribute("file")); if (path.empty()) return nullptr; if (!basePath.empty()) path = basePath / path; auto deserializer = mitk::PropertyListDeserializer::New(); deserializer->SetFilename(path.string()); deserializer->Deserialize(); return deserializer->GetOutput(); } } bool mitk::SceneReaderV1::LoadScene(tinyxml2::XMLDocument &document, const std::string &workingDirectory, DataStorage *storage) { assert(storage); bool error(false); // TODO prepare to detect errors (such as cycles) from wrongly written or edited xml files // Get number of elements to initialze progress bar // 1. if there is a element, // - construct a name for the appropriate serializer // - try to instantiate this serializer via itk object factory // - if serializer could be created, use it to read the file into a BaseData object // - if successful, call the new node's SetData(..) // create a node for the tag "data" and test if node was created typedef std::vector DataNodeVector; DataNodeVector DataNodes; unsigned int listSize = 0; for (auto *element = document.FirstChildElement("node"); element != nullptr; element = element->NextSiblingElement("node")) { ++listSize; } ProgressBar::GetInstance()->AddStepsToDo(listSize * 2); // Deserialize base data properties before reading the actual data to be // able to provide them as read-only meta data to the data reader. std::map baseDataPropertyLists; for (auto *nodeElement = document.FirstChildElement("node"); nodeElement != nullptr; nodeElement = nodeElement->NextSiblingElement("node")) { const auto *uid = nodeElement->Attribute("UID"); if (uid == nullptr) continue; auto *dataElement = nodeElement->FirstChildElement("data"); if (dataElement != nullptr) { auto properties = DeserializeProperties(dataElement->FirstChildElement("properties"), workingDirectory); if (properties.IsNotNull()) baseDataPropertyLists[uid] = properties; } } for (auto *element = document.FirstChildElement("node"); element != nullptr; element = element->NextSiblingElement("node")) { mitk::PropertyList* properties = nullptr; const auto *uid = element->Attribute("UID"); if (uid != nullptr) { auto iter = baseDataPropertyLists.find(uid); if (iter != baseDataPropertyLists.end()) properties = iter->second; } const auto *dataElement = element->FirstChildElement("data"); auto dataNode = this->LoadBaseDataFromDataTag(dataElement, properties, workingDirectory, error); if (dataNode.IsNull()) continue; auto* baseData = dataNode->GetData(); if (baseData != nullptr && properties != nullptr) { baseData->SetPropertyList(properties); ApplyProportionalTimeGeometryProperties(baseData); } DataNodes.push_back(dataNode); ProgressBar::GetInstance()->Progress(); } // iterate all nodes // first level nodes should be elements auto nit = DataNodes.begin(); for (auto *element = document.FirstChildElement("node"); element != nullptr || nit != DataNodes.end(); element = element->NextSiblingElement("node"), ++nit) { mitk::DataNode::Pointer node = *nit; // 1. check child nodes const char *uida = element->Attribute("UID"); std::string uid(""); if (uida) { uid = uida; m_NodeForID[uid] = node.GetPointer(); m_IDForNode[node.GetPointer()] = uid; } else { MITK_ERROR << "No UID found for current node. Node will have no parents."; error = true; } // 2. if there are nodes, // - instantiate the appropriate PropertyListDeSerializer // - use them to construct PropertyList objects // - add these properties to the node (if necessary, use renderwindow name) bool success = DecorateNodeWithProperties(node, element, workingDirectory); if (!success) { MITK_ERROR << "Could not load properties for node."; error = true; } // remember node for later adding to DataStorage m_OrderedNodePairs.push_back(std::make_pair(node, std::list())); // 3. if there are elements, remember parent objects for (auto *source = element->FirstChildElement("source"); source != nullptr; source = source->NextSiblingElement("source")) { const char *sourceUID = source->Attribute("UID"); if (sourceUID) { m_OrderedNodePairs.back().second.push_back(std::string(sourceUID)); } } ProgressBar::GetInstance()->Progress(); } // end for all // sort our nodes by their "layer" property // (to be inserted in that order) m_OrderedNodePairs.sort(&NodeSortByLayerIsLessThan); // remove all unknown parent UIDs for (auto nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end(); ++nodesIter) { for (auto parentsIter = nodesIter->second.begin(); parentsIter != nodesIter->second.end();) { if (m_NodeForID.find(*parentsIter) == m_NodeForID.end()) { parentsIter = nodesIter->second.erase(parentsIter); MITK_WARN << "Found a DataNode with unknown parents. Will add it to DataStorage without any parent objects."; error = true; } else { ++parentsIter; } } } // repeat the following loop ... // ... for all created nodes unsigned int lastMapSize(0); while (lastMapSize != m_OrderedNodePairs .size()) // this is to prevent infinite loops; each iteration must at least add one node to DataStorage { lastMapSize = m_OrderedNodePairs.size(); // iterate (layer) ordered nodes backwards // we insert the highest layers first for (auto nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end(); ++nodesIter) { bool addThisNode(true); // if any parent node is not yet in DataStorage, skip node for now and check later for (auto parentsIter = nodesIter->second.begin(); parentsIter != nodesIter->second.end(); ++parentsIter) { if (!storage->Exists(m_NodeForID[*parentsIter])) { addThisNode = false; break; } } if (addThisNode) { DataStorage::SetOfObjects::Pointer parents = DataStorage::SetOfObjects::New(); for (auto parentsIter = nodesIter->second.begin(); parentsIter != nodesIter->second.end(); ++parentsIter) { parents->push_back(m_NodeForID[*parentsIter]); } // if all parents are found in datastorage (or are unknown), add node to DataStorage storage->Add(nodesIter->first, parents); // remove this node from m_OrderedNodePairs m_OrderedNodePairs.erase(nodesIter); // break this for loop because iterators are probably invalid break; } } } // All nodes that are still in m_OrderedNodePairs at this point are not part of a proper directed graph structure. // We'll add such nodes without any parent information. for (auto nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end(); ++nodesIter) { storage->Add(nodesIter->first); MITK_WARN << "Encountered node that is not part of a directed graph structure. Will be added to DataStorage " "without parents."; error = true; } return !error; } mitk::DataNode::Pointer mitk::SceneReaderV1::LoadBaseDataFromDataTag(const tinyxml2::XMLElement *dataElement, const PropertyList *properties, const std::string &workingDirectory, bool &error) { DataNode::Pointer node; if (dataElement) { const char *filename = dataElement->Attribute("file"); if (filename && strlen(filename) != 0) { try { auto baseData = IOUtil::Load(workingDirectory + Poco::Path::separator() + filename, properties); node = DataNode::New(); node->SetData(baseData); } catch (std::exception &e) { MITK_ERROR << "Error during attempt to read '" << filename << "'. Exception says: " << e.what(); error = true; } if (node.IsNull()) { MITK_ERROR << "Error during attempt to read '" << filename << "'. Factory returned nullptr object."; error = true; } } const char* dataUID = dataElement->Attribute("UID"); if (!error && dataUID != nullptr) { UIDManipulator manip(node->GetData()); manip.SetUID(dataUID); } } // in case there was no element we create a new empty node (for appending a propertylist later) if (node.IsNull()) { node = DataNode::New(); } return node; } void mitk::SceneReaderV1::ClearNodePropertyListWithExceptions(DataNode &node, PropertyList &propertyList) { // Basically call propertyList.Clear(), but implement exceptions (see bug 19354) BaseData *data = node.GetData(); PropertyList::Pointer propertiesToKeep = PropertyList::New(); if (dynamic_cast(data)) { /* Older scene files (before changes of bug 17547) could contain a RenderingMode property with value "LevelWindow_Color". Since bug 17547 this value has been removed and replaced by the default value LookupTable_LevelWindow_Color. This new default value does only result in "black-to-white" CT images (or others) if there is a corresponding lookup table. Such a lookup table is provided as a default value by the Image mapper. Since that value was never present in older scene files, we do well in not removing the new default value here. Otherwise the mapper would fall back to another default which is all the colors of the rainbow :-( */ BaseProperty::Pointer lutProperty = propertyList.GetProperty("LookupTable"); propertiesToKeep->SetProperty("LookupTable", lutProperty); /* Older scene files (before changes of T14807) may contain multi-component images without the "Image.Displayed Component" property. As the treatment as multi-component image and the corresponding visualization options hinges on that property we should not delete it, if it was added by the mapper. This is a fix for the issue reported in T19919. */ BaseProperty::Pointer compProperty = propertyList.GetProperty("Image.Displayed Component"); if (compProperty.IsNotNull()) { propertiesToKeep->SetProperty("Image.Displayed Component", compProperty); } } propertyList.Clear(); propertyList.ConcatenatePropertyList(propertiesToKeep); } bool mitk::SceneReaderV1::DecorateNodeWithProperties(DataNode *node, const tinyxml2::XMLElement *nodeElement, const std::string &workingDirectory) { assert(node); assert(nodeElement); bool error(false); for (auto *properties = nodeElement->FirstChildElement("properties"); properties != nullptr; properties = properties->NextSiblingElement("properties")) { const char *propertiesfilea(properties->Attribute("file")); std::string propertiesfile(propertiesfilea ? propertiesfilea : ""); const char *renderwindowa(properties->Attribute("renderwindow")); std::string renderwindow(renderwindowa ? renderwindowa : ""); PropertyList::Pointer propertyList = node->GetPropertyList(renderwindow); // DataNode implementation always returns a propertylist ClearNodePropertyListWithExceptions(*node, *propertyList); // use deserializer to construct new properties PropertyListDeserializer::Pointer deserializer = PropertyListDeserializer::New(); deserializer->SetFilename(workingDirectory + Poco::Path::separator() + propertiesfile); bool success = deserializer->Deserialize(); error |= !success; PropertyList::Pointer readProperties = deserializer->GetOutput(); if (readProperties.IsNotNull()) { propertyList->ConcatenatePropertyList(readProperties, true); // true = replace } else { MITK_ERROR << "Property list reader did not return a property list. This is an implementation error. Please tell " "your developer."; error = true; } } return !error; } diff --git a/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.cpp b/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.cpp deleted file mode 100644 index 77eae790c0..0000000000 --- a/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.cpp +++ /dev/null @@ -1,332 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkCalculateGrayValueStatisticsTool.h" - -#include "mitkCalculateGrayValueStatisticsTool.xpm" - -#include "mitkImageAccessByItk.h" -#include "mitkImageCast.h" -#include "mitkImageTimeSelector.h" -#include "mitkProgressBar.h" -#include "mitkStatusBar.h" -#include "mitkToolManager.h" - -#include - -#include - -namespace mitk -{ - MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, CalculateGrayValueStatisticsTool, "Statistics tool"); -} - -mitk::CalculateGrayValueStatisticsTool::CalculateGrayValueStatisticsTool() -{ -} - -mitk::CalculateGrayValueStatisticsTool::~CalculateGrayValueStatisticsTool() -{ -} - -const char **mitk::CalculateGrayValueStatisticsTool::GetXPM() const -{ - return mitkCalculateGrayValueStatisticsTool_xpm; -} - -const char *mitk::CalculateGrayValueStatisticsTool::GetName() const -{ - return "Statistics"; -} - -std::string mitk::CalculateGrayValueStatisticsTool::GetErrorMessage() -{ - return "No statistics generated for these segmentations:"; -} - -void mitk::CalculateGrayValueStatisticsTool::StartProcessingAllData() -{ - // clear/prepare report - m_CompleteReport.str(""); -} - -bool mitk::CalculateGrayValueStatisticsTool::ProcessOneWorkingData(DataNode *node) -{ - if (node) - { - Image::Pointer image = dynamic_cast(node->GetData()); - if (image.IsNull()) - return false; - - DataNode *referencenode = this->GetToolManager()->GetReferenceData(0); - if (!referencenode) - return false; - - try - { - ProgressBar::GetInstance()->AddStepsToDo(1); - - // add to report - std::string nodename("structure"); - node->GetName(nodename); - - std::string message = "Calculating statistics for "; - message += nodename; - - StatusBar::GetInstance()->DisplayText(message.c_str()); - - Image::Pointer refImage = dynamic_cast(referencenode->GetData()); - Image::Pointer image = dynamic_cast(node->GetData()); - - m_CompleteReport << "======== Gray value analysis of " << nodename << " ========\n"; - - if (image.IsNotNull() && refImage.IsNotNull()) - { - for (unsigned int timeStep = 0; timeStep < image->GetTimeSteps(); ++timeStep) - { - ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); - timeSelector->SetInput(refImage); - timeSelector->SetTimeNr(timeStep); - timeSelector->UpdateLargestPossibleRegion(); - Image::Pointer refImage3D = timeSelector->GetOutput(); - - ImageTimeSelector::Pointer timeSelector2 = ImageTimeSelector::New(); - timeSelector2->SetInput(image); - timeSelector2->SetTimeNr(timeStep); - timeSelector2->UpdateLargestPossibleRegion(); - Image::Pointer image3D = timeSelector2->GetOutput(); - - if (image3D.IsNotNull() && refImage3D.IsNotNull()) - { - m_CompleteReport << "=== " << nodename << ", time step " << timeStep << " ===\n"; - AccessFixedDimensionByItk_2(refImage3D, ITKHistogramming, 3, image3D, m_CompleteReport); - } - } - } - - m_CompleteReport << "======== End of analysis for " << nodename << " ===========\n\n\n"; - - ProgressBar::GetInstance()->Progress(); - } - catch (...) - { - return false; - } - } - - return true; -} - -void mitk::CalculateGrayValueStatisticsTool::FinishProcessingAllData() -{ - SegmentationsProcessingTool::FinishProcessingAllData(); - - // show/send results - StatisticsCompleted.Send(); - // MITK_INFO << m_CompleteReport.str() << std::endl; -} - -#define ROUND_P(x) ((int)((x) + 0.5)) - -template -void mitk::CalculateGrayValueStatisticsTool::CalculateMinMax(itk::Image *referenceImage, - Image *segmentation, - TPixel &minimum, - TPixel &maximum) -{ - typedef itk::Image ImageType; - typedef itk::Image SegmentationType; - - typename SegmentationType::Pointer segmentationItk; - CastToItkImage(segmentation, segmentationItk); - - typename SegmentationType::RegionType segmentationRegion = segmentationItk->GetLargestPossibleRegion(); - - segmentationRegion.Crop(referenceImage->GetLargestPossibleRegion()); - - itk::ImageRegionConstIteratorWithIndex segmentationIterator(segmentationItk, segmentationRegion); - itk::ImageRegionConstIteratorWithIndex referenceIterator(referenceImage, segmentationRegion); - - segmentationIterator.GoToBegin(); - referenceIterator.GoToBegin(); - - minimum = std::numeric_limits::max(); - maximum = std::numeric_limits::min(); - - while (!segmentationIterator.IsAtEnd()) - { - itk::Point pt; - segmentationItk->TransformIndexToPhysicalPoint(segmentationIterator.GetIndex(), pt); - - typename ImageType::IndexType ind; - itk::ContinuousIndex contInd; - if (referenceImage->TransformPhysicalPointToContinuousIndex(pt, contInd)) - { - for (unsigned int i = 0; i < 3; ++i) - ind[i] = ROUND_P(contInd[i]); - - referenceIterator.SetIndex(ind); - - if (segmentationIterator.Get() > 0) - { - if (referenceIterator.Get() < minimum) - minimum = referenceIterator.Get(); - if (referenceIterator.Get() > maximum) - maximum = referenceIterator.Get(); - } - } - - ++segmentationIterator; - } -} - -template -void mitk::CalculateGrayValueStatisticsTool::ITKHistogramming(itk::Image *referenceImage, - Image *segmentation, - std::stringstream &report) -{ - typedef itk::Image ImageType; - typedef itk::Image SegmentationType; - - typename SegmentationType::Pointer segmentationItk; - CastToItkImage(segmentation, segmentationItk); - - // generate histogram - typename SegmentationType::RegionType segmentationRegion = segmentationItk->GetLargestPossibleRegion(); - - segmentationRegion.Crop(referenceImage->GetLargestPossibleRegion()); - - itk::ImageRegionConstIteratorWithIndex segmentationIterator(segmentationItk, segmentationRegion); - itk::ImageRegionConstIteratorWithIndex referenceIterator(referenceImage, segmentationRegion); - - segmentationIterator.GoToBegin(); - referenceIterator.GoToBegin(); - - m_ITKHistogram = HistogramType::New(); - - TPixel minimum = std::numeric_limits::max(); - TPixel maximum = std::numeric_limits::min(); - - CalculateMinMax(referenceImage, segmentation, minimum, maximum); - - // initialize the histogram to the range of the cropped region - HistogramType::SizeType size; -#if defined(ITK_USE_REVIEW_STATISTICS) - typedef typename HistogramType::SizeType::ValueType HSizeValueType; -#else - typedef typename HistogramType::SizeType::SizeValueType HSizeValueType; -#endif - size.Fill(static_cast(maximum - minimum + 1)); - HistogramType::MeasurementVectorType lowerBound; - HistogramType::MeasurementVectorType upperBound; - lowerBound[0] = minimum; - upperBound[0] = maximum; - m_ITKHistogram->Initialize(size, lowerBound, upperBound); - - double mean(0.0); - double sd(0.0); - double voxelCount(0.0); - - // iterate through the cropped region add the values to the histogram - while (!segmentationIterator.IsAtEnd()) - { - itk::Point pt; - segmentationItk->TransformIndexToPhysicalPoint(segmentationIterator.GetIndex(), pt); - - typename ImageType::IndexType ind; - itk::ContinuousIndex contInd; - if (referenceImage->TransformPhysicalPointToContinuousIndex(pt, contInd)) - { - for (unsigned int i = 0; i < 3; ++i) - ind[i] = ROUND_P(contInd[i]); - - referenceIterator.SetIndex(ind); - - if (segmentationIterator.Get() > 0) - { - HistogramType::MeasurementVectorType currentMeasurementVector; - - currentMeasurementVector[0] = static_cast(referenceIterator.Get()); - m_ITKHistogram->IncreaseFrequencyOfMeasurement(currentMeasurementVector, 1); - - mean = (mean * (static_cast(voxelCount) / - static_cast(voxelCount + 1))) // 3 points: old center * 2/3 + currentPoint * 1/3; - + static_cast(referenceIterator.Get()) / static_cast(voxelCount + 1); - - voxelCount += 1.0; - } - } - - ++segmentationIterator; - } - - // second pass for SD - segmentationIterator.GoToBegin(); - referenceIterator.GoToBegin(); - while (!segmentationIterator.IsAtEnd()) - { - itk::Point pt; - segmentationItk->TransformIndexToPhysicalPoint(segmentationIterator.GetIndex(), pt); - - typename ImageType::IndexType ind; - itk::ContinuousIndex contInd; - if (referenceImage->TransformPhysicalPointToContinuousIndex(pt, contInd)) - { - for (unsigned int i = 0; i < 3; ++i) - ind[i] = ROUND_P(contInd[i]); - - referenceIterator.SetIndex(ind); - - if (segmentationIterator.Get() > 0) - { - sd += ((static_cast(referenceIterator.Get()) - mean) * - (static_cast(referenceIterator.Get()) - mean)); - } - } - - ++segmentationIterator; - } - - sd /= static_cast(voxelCount - 1); - sd = sqrt(sd); - - // generate quantiles - TPixel histogramQuantileValues[5]; - histogramQuantileValues[0] = m_ITKHistogram->Quantile(0, 0.05); - histogramQuantileValues[1] = m_ITKHistogram->Quantile(0, 0.25); - histogramQuantileValues[2] = m_ITKHistogram->Quantile(0, 0.50); - histogramQuantileValues[3] = m_ITKHistogram->Quantile(0, 0.75); - histogramQuantileValues[4] = m_ITKHistogram->Quantile(0, 0.95); - - // report histogram values - std::locale C("C"); - std::locale originalLocale = report.getloc(); - report.imbue(C); - - report << " Minimum:" << minimum << "\n 5% quantile: " << histogramQuantileValues[0] - << "\n 25% quantile: " << histogramQuantileValues[1] << "\n 50% quantile: " << histogramQuantileValues[2] - << "\n 75% quantile: " << histogramQuantileValues[3] << "\n 95% quantile: " << histogramQuantileValues[4] - << "\n Maximum: " << maximum << "\n Mean: " << mean << "\n SD: " << sd << "\n"; - - report.imbue(originalLocale); -} - -std::string mitk::CalculateGrayValueStatisticsTool::GetReport() const -{ - return m_CompleteReport.str(); -} - -mitk::CalculateGrayValueStatisticsTool::HistogramType::ConstPointer - mitk::CalculateGrayValueStatisticsTool::GetHistogram() -{ - return m_ITKHistogram.GetPointer(); -} diff --git a/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.h b/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.h deleted file mode 100644 index 69bd8c4204..0000000000 --- a/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.h +++ /dev/null @@ -1,96 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#ifndef mitkCalculateGrayValueStatisticsTool_h_Included -#define mitkCalculateGrayValueStatisticsTool_h_Included - -#include "mitkCommon.h" -#include "mitkSegmentationsProcessingTool.h" -#include - -#ifndef __itkHistogram_h -#include -#endif - -#include -#include - -namespace mitk -{ - /** - \brief Calculates some gray value statistics for segmentations. - - \ingroup ToolManagerEtAl - \sa mitk::Tool - \sa QmitkInteractiveSegmentation - - Last contributor: $Author$ - */ - class MITKSEGMENTATION_EXPORT CalculateGrayValueStatisticsTool : public SegmentationsProcessingTool - { - public: - Message<> StatisticsCompleted; - - mitkClassMacro(CalculateGrayValueStatisticsTool, SegmentationsProcessingTool); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); - - const char **GetXPM() const override; - const char *GetName() const override; - - virtual std::string GetReport() const; - // - // Insight/Code/Review/Algorithms version of Histogram takes - // only one template parameter, and the 'release' version - // takes 2, but the default value for the second, 1, is what - // was specified here. - typedef itk::Statistics::Histogram HistogramType; - HistogramType::Pointer m_ITKHistogram; - - HistogramType::ConstPointer GetHistogram(); - - typedef HistogramType::MeasurementType HistogramMeasurementType; - - protected: - CalculateGrayValueStatisticsTool(); // purposely hidden - ~CalculateGrayValueStatisticsTool() override; - - void StartProcessingAllData() override; - bool ProcessOneWorkingData(DataNode *node) override; - void FinishProcessingAllData() override; - - std::string GetErrorMessage() override; - - /** - Calculates the minimum and maximum of the pixelvalues. They have to be known to initialize the histogram. - */ - template - void CalculateMinMax(itk::Image *referenceImage, - Image *segmentation, - TPixel &minimum, - TPixel &maximum); - - /** - - initializes and fills the histogram - - calculates mean, sd and quantiles - */ - template - void ITKHistogramming(itk::Image *referenceImage, - Image *segmentation, - std::stringstream &report); - - std::stringstream m_CompleteReport; - }; - -} // namespace - -#endif diff --git a/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.xpm b/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.xpm deleted file mode 100644 index 571c5c2801..0000000000 --- a/Modules/Segmentation/Interactions/mitkCalculateGrayValueStatisticsTool.xpm +++ /dev/null @@ -1,20 +0,0 @@ -/* XPM */ -static const char * mitkCalculateGrayValueStatisticsTool_xpm[] = { -"18 12 5 1", -" c None", -". c #2E3436", -"+ c #EEEEEC", -"@ c #D3D7CF", -"# c #555753", -"..................", -".++++++++++++++++.", -".+@@@@@@@@@@@@@@+.", -".+@@#@@@@@@@@@@@+.", -".+@@##@#@@@@@@@@+.", -".+@###@#@@@@@@@@+.", -".+@#####@#@@@@@@+.", -".+######@#@@@#@@+.", -".+##########@#@@+.", -".+##############+.", -".++++++++++++++++.", -".................."}; diff --git a/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.cpp b/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.cpp deleted file mode 100644 index f5b53f6c3a..0000000000 --- a/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkCalculateVolumetryTool.h" - -#include "mitkCalculateVolumetryTool.xpm" - -#include "mitkProperties.h" -#include "mitkToolManager.h" -#include "mitkVolumeCalculator.h" - -namespace mitk -{ - MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, CalculateVolumetryTool, "Volumetry tool"); -} - -mitk::CalculateVolumetryTool::CalculateVolumetryTool() -{ -} - -mitk::CalculateVolumetryTool::~CalculateVolumetryTool() -{ -} - -const char **mitk::CalculateVolumetryTool::GetXPM() const -{ - return mitkCalculateVolumetryTool_xpm; -} - -const char *mitk::CalculateVolumetryTool::GetName() const -{ - return "Volumetry"; -} - -std::string mitk::CalculateVolumetryTool::GetErrorMessage() -{ - return "Volume could not be calculated for these nodes:"; -} - -bool mitk::CalculateVolumetryTool::ProcessOneWorkingData(DataNode *node) -{ - if (node) - { - Image::Pointer image = dynamic_cast(node->GetData()); - if (image.IsNull()) - return false; - - if (image->GetDimension() == 4) - { - Tool::ErrorMessage("Volumetry only valid for timestep 0! Bug #1280"); - } - - VolumeCalculator::Pointer volumetryFilter = VolumeCalculator::New(); - volumetryFilter->SetImage(image); - volumetryFilter->SetThreshold(1); // comparison is >= - try - { - volumetryFilter->ComputeVolume(); - - float volumeInTimeStep0 = volumetryFilter->GetVolume(); - - node->SetProperty("volume", FloatProperty::New(volumeInTimeStep0)); - } - catch (...) - { - return false; - } - } - - return true; -} - -void mitk::CalculateVolumetryTool::FinishProcessingAllData() -{ - Superclass::FinishProcessingAllData(); - this->GetToolManager()->NodePropertiesChanged(); -} diff --git a/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.h b/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.h deleted file mode 100644 index 51d9253d2a..0000000000 --- a/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.h +++ /dev/null @@ -1,53 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#ifndef mitkCalculateVolumetryTool_h_Included -#define mitkCalculateVolumetryTool_h_Included - -#include "mitkCommon.h" -#include "mitkSegmentationsProcessingTool.h" -#include - -namespace mitk -{ - /** - \brief Calculates the segmented volumes for binary images. - - \ingroup ToolManagerEtAl - \sa mitk::Tool - \sa QmitkInteractiveSegmentation - - Last contributor: $Author$ - */ - class MITKSEGMENTATION_EXPORT CalculateVolumetryTool : public SegmentationsProcessingTool - { - public: - mitkClassMacro(CalculateVolumetryTool, SegmentationsProcessingTool); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); - - const char **GetXPM() const override; - const char *GetName() const override; - - protected: - bool ProcessOneWorkingData(DataNode *node) override; - std::string GetErrorMessage() override; - - void FinishProcessingAllData() override; - - CalculateVolumetryTool(); // purposely hidden - ~CalculateVolumetryTool() override; - }; - -} // namespace - -#endif diff --git a/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.xpm b/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.xpm deleted file mode 100644 index 54ffb9c3e5..0000000000 --- a/Modules/Segmentation/Interactions/mitkCalculateVolumetryTool.xpm +++ /dev/null @@ -1,37 +0,0 @@ -/* XPM */ -static const char * mitkCalculateVolumetryTool_xpm[] = { -"19 16 18 1", -" c None", -". c #2E3436", -"+ c #EEEEEC", -"@ c #555753", -"# c #595B57", -"$ c #70726E", -"% c #656763", -"& c #565854", -"* c #646662", -"= c #BEBEBC", -"- c #ADAEAB", -"; c #CDCECB", -"> c #ACADAA", -", c #CECFCC", -"' c #666864", -") c #E5E5E3", -"! c #EBEBE9", -"~ c #EDEDEB", -"...................", -".+++++++++++++++++.", -".+++++++++++++++++.", -".++++++++++++++@@+.", -".++++++++++++++@@+.", -".++++++++++++++@@+.", -".+@@@@#$%&*=+++@@+.", -".+@@-;@@>,@'+++@@+.", -".+@@)!@@)~@@+++@@+.", -".+@@++@@++@@+++@@+.", -".+@@++@@++@@+++@@+.", -".+@@++@@++@@+++@@+.", -".+@@++@@++@@+++@@+.", -".+++++++++++++++++.", -".+++++++++++++++++.", -"..................."}; diff --git a/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp b/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp index e608aaec13..1482b9ddcd 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp +++ b/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp @@ -1,164 +1,242 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkContourModelInteractor.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include #include mitk::ContourModelInteractor::ContourModelInteractor() { } void mitk::ContourModelInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("checkisOverPoint", OnCheckPointClick); CONNECT_CONDITION("mouseMove", IsHovering); - CONNECT_FUNCTION("movePoints", OnMovePoint); + CONNECT_FUNCTION("movePoint", OnMovePoint); CONNECT_FUNCTION("deletePoint", OnDeletePoint); + CONNECT_FUNCTION("addPoint", OnAddPoint); CONNECT_FUNCTION("finish", OnFinishEditing); } mitk::ContourModelInteractor::~ContourModelInteractor() { } +void mitk::ContourModelInteractor::SetRestrictedArea(mitk::ContourModel* restrictedArea) +{ + m_RestrictedArea = restrictedArea; +} + bool mitk::ContourModelInteractor::OnCheckPointClick(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return false; const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); auto *contour = dynamic_cast(this->GetDataNode()->GetData()); + if (contour == nullptr) + { + MITK_ERROR << "Invalid Contour"; + return false; + } contour->Deselect(); mitk::Point3D click = positionEvent->GetPositionInWorld(); - if (contour->SelectVertexAt(click, 1.5, timeStep)) + bool isVertexSelected = contour->SelectControlVertexAt(click, ContourModelInteractor::eps, timeStep); + + if (isVertexSelected) + { + auto foundVertex = contour->GetSelectedVertex(); + const auto restrictedVs = m_RestrictedArea->GetVertexList(timeStep); + for (auto restrictedV : restrictedVs) + { + if (restrictedV->Coordinates == foundVertex->Coordinates) + { + isVertexSelected = false; + contour->Deselect(); + break; + } + } + } + + if (isVertexSelected) { - contour->SetSelectedVertexAsControlPoint(); mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow()); - m_lastMousePosition = click; + } + + return isVertexSelected; +} + +void mitk::ContourModelInteractor::OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent) +{ + const auto* positionEvent = dynamic_cast(interactionEvent); + if (!positionEvent) + return; + + const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); - auto *contourGeometry = dynamic_cast(contour->GetGeometry(timeStep)); + auto* contour = dynamic_cast(this->GetDataNode()->GetData()); - if (contourGeometry->IsInside(click)) + Point3D currentPosition = positionEvent->GetPositionInWorld(); + + ContourElement::VertexSizeType segmentStart; + ContourElement::VertexSizeType segmentEnd; + Point3D closestContourPoint; + if (contour->GetLineSegmentForPoint(currentPosition, ContourModelInteractor::eps, timeStep, segmentStart, segmentEnd, closestContourPoint, true)) + { + const auto vertexList = contour->GetVertexList(timeStep); + //check if the segment is NOT within restricted control points. + auto controlStartIt = vertexList.begin(); + auto controlEndIt = vertexList.begin(); + for (auto searchIt = vertexList.begin() + segmentStart; searchIt != vertexList.begin(); searchIt--) { - m_lastMousePosition = click; - return true; + if ((*searchIt)->IsControlPoint) + { + controlStartIt = searchIt; + break; + } + } + for (auto searchIt = vertexList.begin() + segmentEnd; searchIt != vertexList.end(); searchIt++) + { + if ((*searchIt)->IsControlPoint) + { + controlEndIt = searchIt; + break; + } + } + + const auto restrictedVs = m_RestrictedArea->GetVertexList(timeStep); + bool startIsRestricted = false; + bool stopIsRestricted = false; + + for (auto restrictedV : restrictedVs) + { + if (restrictedV->Coordinates == (*controlStartIt)->Coordinates) + { + startIsRestricted = true; + } + if (restrictedV->Coordinates == (*controlEndIt)->Coordinates) + { + stopIsRestricted = true; + } + } + + if (!(startIsRestricted && stopIsRestricted)) + { + //add the point + contour->InsertVertexAtIndex(closestContourPoint, segmentEnd, true, timeStep); + contour->SelectVertexAt(segmentEnd); + mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } - else - return false; - } - else - { - return false; } - return true; } void mitk::ContourModelInteractor::OnDeletePoint(StateMachineAction *, InteractionEvent *) { auto *contour = dynamic_cast(this->GetDataNode()->GetData()); contour->RemoveVertex(contour->GetSelectedVertex()); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } bool mitk::ContourModelInteractor::IsHovering(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return false; const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); auto *contour = dynamic_cast(this->GetDataNode()->GetData()); - mitk::Point3D currentPosition = positionEvent->GetPositionInWorld(); + Point3D currentPosition = positionEvent->GetPositionInWorld(); bool isHover = false; this->GetDataNode()->GetBoolProperty("contour.hovering", isHover, positionEvent->GetSender()); - if (contour->IsNearContour(currentPosition, 1.5, timeStep)) + if (contour->IsNearContour(currentPosition, ContourModelInteractor::eps, timeStep)) { if (isHover == false) { this->GetDataNode()->SetBoolProperty("contour.hovering", true); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } + //check if it would be valid to add a point. } else { if (isHover == true) { this->GetDataNode()->SetBoolProperty("contour.hovering", false); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } } + this->m_lastMousePosition = currentPosition; + return false; } void mitk::ContourModelInteractor::OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; auto *contour = dynamic_cast(this->GetDataNode()->GetData()); mitk::Vector3D translation; mitk::Point3D currentPosition = positionEvent->GetPositionInWorld(); translation[0] = currentPosition[0] - this->m_lastMousePosition[0]; translation[1] = currentPosition[1] - this->m_lastMousePosition[1]; translation[2] = currentPosition[2] - this->m_lastMousePosition[2]; contour->ShiftSelectedVertex(translation); this->m_lastMousePosition = positionEvent->GetPositionInWorld(); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::ContourModelInteractor::OnMoveContour(StateMachineAction *, InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); auto *contour = dynamic_cast(this->GetDataNode()->GetData()); mitk::Vector3D translation; mitk::Point3D currentPosition = positionEvent->GetPositionInWorld(); translation[0] = currentPosition[0] - this->m_lastMousePosition[0]; translation[1] = currentPosition[1] - this->m_lastMousePosition[1]; translation[2] = currentPosition[2] - this->m_lastMousePosition[2]; contour->ShiftContour(translation, timeStep); this->m_lastMousePosition = positionEvent->GetPositionInWorld(); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } -void mitk::ContourModelInteractor::OnFinishEditing(StateMachineAction *, InteractionEvent *interactionEvent) +void mitk::ContourModelInteractor::OnFinishEditing(StateMachineAction *, InteractionEvent *) { - auto *contour = dynamic_cast(this->GetDataNode()->GetData()); - contour->Deselect(); - mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow()); } diff --git a/Modules/Segmentation/Interactions/mitkContourModelInteractor.h b/Modules/Segmentation/Interactions/mitkContourModelInteractor.h index 0ea308590f..6c2e779f20 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelInteractor.h +++ b/Modules/Segmentation/Interactions/mitkContourModelInteractor.h @@ -1,63 +1,68 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkContourModelInteractor_h_Included #define mitkContourModelInteractor_h_Included #include "mitkCommon.h" #include "mitkDataInteractor.h" #include #include #include namespace mitk { /** \brief \sa Interactor \ingroup Interaction \ingroup ToolManagerEtAl */ class MITKSEGMENTATION_EXPORT ContourModelInteractor : public DataInteractor { public: mitkClassMacro(ContourModelInteractor, DataInteractor); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * Here actions strings from the loaded state machine pattern are mapped to functions of * the DataInteractor. These functions are called when an action from the state machine pattern is executed. */ void ConnectActionsAndFunctions() override; + void SetRestrictedArea(mitk::ContourModel* restrictedArea); + protected: ContourModelInteractor(); ~ContourModelInteractor() override; virtual bool OnCheckPointClick(const InteractionEvent *interactionEvent); virtual bool IsHovering(const InteractionEvent *interactionEvent); + virtual void OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent); virtual void OnDeletePoint(StateMachineAction *, InteractionEvent *interactionEvent); virtual void OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent); virtual void OnMoveContour(StateMachineAction *, InteractionEvent *interactionEvent); virtual void OnFinishEditing(StateMachineAction *, InteractionEvent *interactionEvent); + const float eps = 3.0; mitk::Point3D m_lastMousePosition; + mitk::ContourModel::Pointer m_RestrictedArea; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp index 27e02d9986..1f25e5d026 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp +++ b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp @@ -1,426 +1,333 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkContourModelLiveWireInteractor.h" #include "mitkInteractionPositionEvent.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include #include "mitkIOUtil.h" mitk::ContourModelLiveWireInteractor::ContourModelLiveWireInteractor() : ContourModelInteractor() { m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New(); m_LiveWireFilter->SetUseCostFunction(true); m_NextActiveVertexDown.Fill(0); m_NextActiveVertexUp.Fill(0); } mitk::ContourModelLiveWireInteractor::~ContourModelLiveWireInteractor() { } void mitk::ContourModelLiveWireInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("checkisOverPoint", OnCheckPointClick); CONNECT_CONDITION("mouseMove", IsHovering); CONNECT_FUNCTION("movePoint", OnMovePoint); CONNECT_FUNCTION("deletePoint", OnDeletePoint); + CONNECT_FUNCTION("addPoint", OnAddPoint) CONNECT_FUNCTION("finish", OnFinishEditing); } bool mitk::ContourModelLiveWireInteractor::OnCheckPointClick(const InteractionEvent *interactionEvent) { - const auto *positionEvent = dynamic_cast(interactionEvent); - - if (!positionEvent) - return false; - - const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); - - auto *contour = dynamic_cast(this->GetDataNode()->GetData()); - if (contour == nullptr) - { - MITK_ERROR << "Invalid Contour"; - return false; - } - - contour->Deselect(); - - // Check distance to any vertex. - // Transition YES if click close to a vertex - mitk::Point3D click = positionEvent->GetPositionInWorld(); - - bool isVertexSelected = false; - // Check, if clicked position is close to control vertex and if so, select closest control vertex. - isVertexSelected = contour->SelectControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep); - - // If the position is not close to control vertex. but hovering the contour line, we check, if it is close to non-control vertex. - // The closest vertex will be set as a control vertex. - if (isVertexSelected == false) - isVertexSelected = contour->SelectVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep); - - // If the position is not close to control or non-control vertex. but hovering the contour line, we create a vertex at the position. - if (isVertexSelected == false) - { - bool isHover = false; - if (this->GetDataNode()->GetBoolProperty("contour.hovering", isHover, positionEvent->GetSender()) == false) - { - MITK_WARN << "Unknown property contour.hovering"; - } - if (isHover) - { - contour->AddVertex(click, timeStep); - isVertexSelected = contour->SelectVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep); - } - } + auto isVertexSelected = Superclass::OnCheckPointClick(interactionEvent); if (isVertexSelected) { - contour->SetSelectedVertexAsControlPoint(true); + auto* contour = dynamic_cast(this->GetDataNode()->GetData()); + const auto* positionEvent = + dynamic_cast(interactionEvent); + mitk::Point3D click = positionEvent->GetPositionInWorld(); + const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); + auto controlVertices = contour->GetControlVertices(timeStep); - const mitk::ContourModel::VertexType *nextPoint = contour->GetNextControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep); - const mitk::ContourModel::VertexType *previousPoint = contour->GetPreviousControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep); + const mitk::ContourModel::VertexType* nextPoint = contour->GetNextControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep); + const mitk::ContourModel::VertexType* previousPoint = contour->GetPreviousControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep); this->SplitContourFromSelectedVertex(contour, nextPoint, previousPoint, timeStep); m_NextActiveVertexUp = nextPoint->Coordinates; m_NextActiveVertexDown = previousPoint->Coordinates; // clear previous void positions this->m_LiveWireFilter->ClearRepulsivePoints(); + // all points in lower and upper part should be marked as repulsive points to not be changed this->SetRepulsivePoints(previousPoint, m_ContourLeft, timeStep); this->SetRepulsivePoints(nextPoint, m_ContourRight, timeStep); // clear container with void points between neighboring control points m_ContourBeingModified.clear(); - - // finally, return true to pass this condition - return true; - } - else - { - // do not pass condition - return false; - } - - mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); - return true; -} - -void mitk::ContourModelLiveWireInteractor::SetEditingContourModelNode(mitk::DataNode *_arg) -{ - if (this->m_EditingContourNode != _arg) - { - this->m_EditingContourNode = _arg; } + return isVertexSelected; } void mitk::ContourModelLiveWireInteractor::SetWorkingImage(mitk::Image *_arg) { if (this->m_WorkingSlice != _arg) { this->m_WorkingSlice = _arg; this->m_LiveWireFilter->SetInput(this->m_WorkingSlice); } } +void mitk::ContourModelLiveWireInteractor::OnAddPoint(StateMachineAction* sm, InteractionEvent* interactionEvent) +{ + Superclass::OnAddPoint(sm, interactionEvent); +} + void mitk::ContourModelLiveWireInteractor::OnDeletePoint(StateMachineAction *, InteractionEvent *interactionEvent) { const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); auto *contour = dynamic_cast(this->GetDataNode()->GetData()); if (contour == nullptr) { MITK_ERROR << "Invalid Contour!"; return; } if (contour->GetSelectedVertex()) { mitk::ContourModel::Pointer newContour = mitk::ContourModel::New(); newContour->Expand(contour->GetTimeSteps()); newContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone()); newContour->Concatenate(m_ContourLeft, timeStep); // recompute contour between neighbored two active control points this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown); this->m_LiveWireFilter->SetEndPoint(this->m_NextActiveVertexUp); this->m_LiveWireFilter->Update(); mitk::ContourModel *liveWireContour = this->m_LiveWireFilter->GetOutput(); assert(liveWireContour); if (liveWireContour->IsEmpty(timeStep)) return; liveWireContour->RemoveVertexAt(0, timeStep); liveWireContour->RemoveVertexAt(liveWireContour->GetNumberOfVertices(timeStep) - 1, timeStep); // insert new live wire computed points newContour->Concatenate(liveWireContour, timeStep); // insert right side of original contour newContour->Concatenate(this->m_ContourRight, timeStep); newContour->SetClosed(contour->IsClosed(timeStep), timeStep); // instead of leaving a single point, delete all points if (newContour->GetNumberOfVertices(timeStep) <= 2) { newContour->Clear(timeStep); } this->GetDataNode()->SetData(newContour); mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow()); } } void mitk::ContourModelLiveWireInteractor::OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); mitk::Point3D currentPosition = positionEvent->GetPositionInWorld(); auto *contour = dynamic_cast(this->GetDataNode()->GetData()); if (contour == nullptr) { MITK_ERROR << "invalid contour"; return; } + std::cout << currentPosition << std::endl; + mitk::ContourModel::Pointer editingContour = mitk::ContourModel::New(); editingContour->Expand(contour->GetTimeSteps()); editingContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone()); // recompute left live wire, i.e. the contour between previous active vertex and selected vertex this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown); this->m_LiveWireFilter->SetEndPoint(currentPosition); // remove void positions between previous active vertex and next active vertex. if (!m_ContourBeingModified.empty()) { std::vector>::const_iterator iter = m_ContourBeingModified.begin(); for (; iter != m_ContourBeingModified.end(); iter++) { this->m_LiveWireFilter->RemoveRepulsivePoint((*iter)); } } // update to get the left livewire. Remember that the points in the rest of the contour are already // set as void positions in the filter this->m_LiveWireFilter->Update(); mitk::ContourModel::Pointer leftLiveWire = this->m_LiveWireFilter->GetOutput(); assert(leftLiveWire); if (!leftLiveWire->IsEmpty(timeStep)) leftLiveWire->RemoveVertexAt(0, timeStep); editingContour->Concatenate(leftLiveWire, timeStep); // the new index of the selected vertex unsigned int selectedVertexIndex = this->m_ContourLeft->GetNumberOfVertices(timeStep) + leftLiveWire->GetNumberOfVertices(timeStep) - 1; // at this point the container has to be empty m_ContourBeingModified.clear(); // add points from left live wire contour auto iter = leftLiveWire->IteratorBegin(timeStep); for (; iter != leftLiveWire->IteratorEnd(timeStep); iter++) { itk::Index<2> idx; this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint(idx); // add indices m_ContourBeingModified.push_back(idx); } // recompute right live wire, i.e. the contour between selected vertex and next active vertex this->m_LiveWireFilter->SetStartPoint(currentPosition); this->m_LiveWireFilter->SetEndPoint(m_NextActiveVertexUp); // update filter with all contour points set as void but the right live wire portion to be calculated now this->m_LiveWireFilter->Update(); mitk::ContourModel::Pointer rightLiveWire = this->m_LiveWireFilter->GetOutput(); assert(rightLiveWire); - // reject strange paths - if (abs(rightLiveWire->GetNumberOfVertices(timeStep) - leftLiveWire->GetNumberOfVertices(timeStep)) > 50) - { - return; - } - if (!leftLiveWire->IsEmpty(timeStep)) leftLiveWire->SetControlVertexAt(leftLiveWire->GetNumberOfVertices() - 1, timeStep); if (!rightLiveWire->IsEmpty(timeStep)) rightLiveWire->RemoveVertexAt(0, timeStep); editingContour->Concatenate(rightLiveWire, timeStep); - m_EditingContourNode->SetData(editingContour); - mitk::ContourModel::Pointer newContour = mitk::ContourModel::New(); newContour->Expand(contour->GetTimeSteps()); newContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone()); // concatenate left original contour newContour->Concatenate(this->m_ContourLeft, timeStep); newContour->Concatenate(editingContour, timeStep, true); // set last inserted vertex as selected newContour->SelectVertexAt(selectedVertexIndex, timeStep); // set as control point newContour->SetSelectedVertexAsControlPoint(true); // concatenate right original contour newContour->Concatenate(this->m_ContourRight, timeStep); newContour->SetClosed(contour->IsClosed(timeStep), timeStep); this->GetDataNode()->SetData(newContour); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } -bool mitk::ContourModelLiveWireInteractor::IsHovering(const InteractionEvent *interactionEvent) -{ - const auto *positionEvent = dynamic_cast(interactionEvent); - if (!positionEvent) - return false; - - const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); - - auto *contour = dynamic_cast(this->GetDataNode()->GetData()); - - mitk::Point3D currentPosition = positionEvent->GetPositionInWorld(); - - bool isHover = false; - this->GetDataNode()->GetBoolProperty("contour.hovering", isHover, positionEvent->GetSender()); - if (contour->IsNearContour(currentPosition, mitk::ContourModelLiveWireInteractor::eps, timeStep)) - { - if (isHover == false) - { - this->GetDataNode()->SetBoolProperty("contour.hovering", true); - mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); - } - return true; - } - else - { - if (isHover == true) - { - this->GetDataNode()->SetBoolProperty("contour.hovering", false); - mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); - } - } - return false; -} void mitk::ContourModelLiveWireInteractor::SplitContourFromSelectedVertex(mitk::ContourModel *srcContour, const mitk::ContourModel::VertexType *nextPoint, const mitk::ContourModel::VertexType *previousPoint, int timeStep) { m_ContourLeft = mitk::ContourModel::New(); m_ContourRight = mitk::ContourModel::New(); auto it = srcContour->IteratorBegin(); // part between nextPoint and end of Countour bool upperPart = false; // part between start of countour and previousPoint bool lowerPart = true; // edge cases when point right before first control vertex is selected or first control vertex is selected if (nextPoint == (*it) || srcContour->GetSelectedVertex() == (*it)) { upperPart = true; lowerPart = false; m_ContourLeft->AddVertex(previousPoint->Coordinates, previousPoint->IsControlPoint, timeStep); } // if first control vertex is selected, move to next point before adding vertices to m_ContourRight // otherwise, second line appears when moving the vertex if (srcContour->GetSelectedVertex() == (*it)) { while (*it != nextPoint) { it++; } } for (; it != srcContour->IteratorEnd(timeStep); it++) { // everything in lower part should be added to m_CountoutLeft if (lowerPart) { m_ContourLeft->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timeStep); } // start of "restricted area" where no vertex should be added to m_CountoutLeft or m_CountoutRight if (*it == previousPoint) { lowerPart = false; upperPart = false; } // start of upperPart if (*it == nextPoint) { upperPart = true; } // everything in upper part should be added to m_CountoutRight if (upperPart) { m_ContourRight->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timeStep); } } } void mitk::ContourModelLiveWireInteractor::SetRepulsivePoints(const mitk::ContourModel::VertexType *pointToExclude, mitk::ContourModel *contour, int timeStep) { auto it = contour->IteratorBegin(); for (; it != contour->IteratorEnd(timeStep); it++) { if (*it != pointToExclude) { itk::Index<2> idx; this->m_WorkingSlice->GetGeometry()->WorldToIndex((*it)->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint(idx); } } } -void mitk::ContourModelLiveWireInteractor::OnFinishEditing(StateMachineAction *, InteractionEvent *interactionEvent) +void mitk::ContourModelLiveWireInteractor::OnFinishEditing(StateMachineAction *, InteractionEvent *) { - const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); - - auto *editingContour = dynamic_cast(this->m_EditingContourNode->GetData()); - - editingContour->Clear(timeStep); - mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow()); } diff --git a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.h b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.h index 6352775048..e9ba614f3b 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.h +++ b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.h @@ -1,95 +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 mitkContourModelLiveWireInteractor_h_Included #define mitkContourModelLiveWireInteractor_h_Included #include "mitkCommon.h" #include "mitkContourModelInteractor.h" #include #include namespace mitk { /** \brief \sa Interactor \sa ContourModelInteractor \ingroup Interaction \warning Make sure the working image is properly set, otherwise the algorithm for computing livewire contour segments will not work! */ class MITKSEGMENTATION_EXPORT ContourModelLiveWireInteractor : public ContourModelInteractor { public: mitkClassMacro(ContourModelLiveWireInteractor, ContourModelInteractor); itkFactorylessNewMacro(Self); itkCloneMacro(Self); - virtual void SetEditingContourModelNode(mitk::DataNode *_arg); - virtual void SetWorkingImage(mitk::Image *_arg); void ConnectActionsAndFunctions() override; protected: ContourModelLiveWireInteractor(); ~ContourModelLiveWireInteractor() override; /// \brief Select/ add and select vertex to modify contour and prepare for modification of contour. bool OnCheckPointClick(const InteractionEvent *interactionEvent) override; - /// \brief Check if mouse is hovering over contour - bool IsHovering(const InteractionEvent *interactionEvent) override; /// \brief Update contour when point is moved. void OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent) override; + /// \brief Add a new control point. + void OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent) override; /// \brief Delete selected vertex and recompute contour. void OnDeletePoint(StateMachineAction *, InteractionEvent *interactionEvent) override; /// \brief Finish modification of contour. void OnFinishEditing(StateMachineAction *, InteractionEvent *interactionEvent) override; /// \brief Split contour into a part before the selected vertex and after the selected vertex void SplitContourFromSelectedVertex(mitk::ContourModel *srcContour, const mitk::ContourModel::VertexType *nextPoint, const mitk::ContourModel::VertexType *previousPoint, int timestep); /// \brief Set repulsive points which should not be changed during editing of the contour. void SetRepulsivePoints(const mitk::ContourModel::VertexType *nextPoint, mitk::ContourModel *contour, int timestep); - const float eps = 3.0; mitk::ImageLiveWireContourModelFilter::Pointer m_LiveWireFilter; mitk::Image::Pointer m_WorkingSlice; mitk::Point3D m_NextActiveVertexDown; mitk::Point3D m_NextActiveVertexUp; mitk::ContourModel::VertexIterator m_NextActiveVertexDownIter; mitk::ContourModel::VertexIterator m_NextActiveVertexUpIter; std::vector> m_ContourBeingModified; - mitk::DataNode::Pointer m_EditingContourNode; mitk::ContourModel::Pointer m_ContourLeft; mitk::ContourModel::Pointer m_ContourRight; }; } // namespace mitk #endif // mitkContourModelLiveWireInteractor_h_Included diff --git a/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.cpp b/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.cpp deleted file mode 100644 index 0e5bcfbe90..0000000000 --- a/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkCreateSurfaceTool.h" - -#include "mitkCreateSurfaceTool.xpm" - -#include "mitkProgressBar.h" -#include "mitkShowSegmentationAsSurface.h" -#include "mitkStatusBar.h" -#include "mitkToolManager.h" - -#include "itkCommand.h" - -namespace mitk -{ - MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, CreateSurfaceTool, "Surface creation tool"); -} - -mitk::CreateSurfaceTool::CreateSurfaceTool() -{ -} - -mitk::CreateSurfaceTool::~CreateSurfaceTool() -{ -} - -const char **mitk::CreateSurfaceTool::GetXPM() const -{ - return mitkCreateSurfaceTool_xpm; -} - -const char *mitk::CreateSurfaceTool::GetName() const -{ - return "Surface"; -} - -std::string mitk::CreateSurfaceTool::GetErrorMessage() -{ - return "No surfaces created for these segmentations:"; -} - -bool mitk::CreateSurfaceTool::ProcessOneWorkingData(DataNode *node) -{ - if (node) - { - Image::Pointer image = dynamic_cast(node->GetData()); - if (image.IsNull()) - return false; - - try - { - mitk::ShowSegmentationAsSurface::Pointer surfaceFilter = mitk::ShowSegmentationAsSurface::New(); - - // attach observer to get notified about result - itk::SimpleMemberCommand::Pointer goodCommand = - itk::SimpleMemberCommand::New(); - goodCommand->SetCallbackFunction(this, &CreateSurfaceTool::OnSurfaceCalculationDone); - surfaceFilter->AddObserver(mitk::ResultAvailable(), goodCommand); - itk::SimpleMemberCommand::Pointer badCommand = - itk::SimpleMemberCommand::New(); - badCommand->SetCallbackFunction(this, &CreateSurfaceTool::OnSurfaceCalculationDone); - surfaceFilter->AddObserver(mitk::ProcessingError(), badCommand); - - DataNode::Pointer nodepointer = node; - surfaceFilter->SetPointerParameter("Input", image); - surfaceFilter->SetPointerParameter("Group node", nodepointer); - surfaceFilter->SetParameter("Show result", true); - surfaceFilter->SetParameter("Sync visibility", false); - surfaceFilter->SetDataStorage(*this->GetToolManager()->GetDataStorage()); - - ProgressBar::GetInstance()->AddStepsToDo(1); - StatusBar::GetInstance()->DisplayText("Surface creation started in background..."); - surfaceFilter->StartAlgorithm(); - } - catch (...) - { - return false; - } - } - - return true; -} - -void mitk::CreateSurfaceTool::OnSurfaceCalculationDone() -{ - ProgressBar::GetInstance()->Progress(); - this->GetToolManager()->NewNodesGenerated(); -} diff --git a/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.h b/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.h deleted file mode 100644 index ad6f1518af..0000000000 --- a/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.h +++ /dev/null @@ -1,52 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#ifndef mitkCreateSurfaceTool_h_Included -#define mitkCreateSurfaceTool_h_Included - -#include "mitkCommon.h" -#include "mitkSegmentationsProcessingTool.h" -#include - -namespace mitk -{ - /** - \brief Creates surface models from segmentations. - \ingroup ToolManagerEtAl - \sa mitk::Tool - \sa QmitkInteractiveSegmentation - - Last contributor: $Author$ - */ - class MITKSEGMENTATION_EXPORT CreateSurfaceTool : public SegmentationsProcessingTool - { - public: - mitkClassMacro(CreateSurfaceTool, SegmentationsProcessingTool); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); - - const char **GetXPM() const override; - const char *GetName() const override; - - void OnSurfaceCalculationDone(); - - protected: - bool ProcessOneWorkingData(DataNode *node) override; - std::string GetErrorMessage() override; - - CreateSurfaceTool(); // purposely hidden - ~CreateSurfaceTool() override; - }; - -} // namespace - -#endif diff --git a/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.xpm b/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.xpm deleted file mode 100644 index 176d646b0f..0000000000 --- a/Modules/Segmentation/Interactions/mitkCreateSurfaceTool.xpm +++ /dev/null @@ -1,109 +0,0 @@ -/* XPM */ -static const char * mitkCreateSurfaceTool_xpm[] = { -"18 18 88 1", -" c None", -". c #202020", -"+ c #232323", -"@ c #393939", -"# c #454545", -"$ c #E2E2E2", -"% c #F2F2F2", -"& c #F1F1F1", -"* c #A5A5A5", -"= c #515151", -"- c #4E4E4E", -"; c #C6C6C6", -"> c #FDFDFD", -", c #FCFCFC", -"' c #FBFBFB", -") c #F9F9F9", -"! c #F0F0F0", -"~ c #868686", -"{ c #4B4B4B", -"] c #FAFAFA", -"^ c #F6F6F6", -"/ c #E8E8E8", -"( c #E1E1E1", -"_ c #DEDEDE", -": c #C9C9C9", -"< c #6F6F6F", -"[ c #000000", -"} c #353535", -"| c #BFBFBF", -"1 c #EFEFEF", -"2 c #E5E5E5", -"3 c #D6D6D6", -"4 c #D8D8D8", -"5 c #DBDBDB", -"6 c #B4B4B4", -"7 c #7C7C7C", -"8 c #474747", -"9 c #EAEAEA", -"0 c #E7E7E7", -"a c #E4E4E4", -"b c #CECECE", -"c c #B6B6B6", -"d c #C5C5C5", -"e c #B3B3B3", -"f c #A3A3A3", -"g c #1C1C1C", -"h c #D3D3D3", -"i c #F3F3F3", -"j c #DFDFDF", -"k c #B1B1B1", -"l c #C2C2C2", -"m c #898989", -"n c #212121", -"o c #E9E9E9", -"p c #F7F7F7", -"q c #D1D1D1", -"r c #BABABA", -"s c #E0E0E0", -"t c #8F8F8F", -"u c #E6E6E6", -"v c #EBEBEB", -"w c #BCBCBC", -"x c #979797", -"y c #F8F8F8", -"z c #5E5E5E", -"A c #464646", -"B c #D7D7D7", -"C c #BBBBBB", -"D c #D2D2D2", -"E c #CACACA", -"F c #040404", -"G c #7E7E7E", -"H c #DDDDDD", -"I c #B8B8B8", -"J c #2C2C2C", -"K c #8C8C8C", -"L c #CDCDCD", -"M c #C3C3C3", -"N c #F4F4F4", -"O c #D9D9D9", -"P c #878787", -"Q c #ECECEC", -"R c #C4C4C4", -"S c #8E8E8E", -"T c #686868", -"U c #070707", -"V c #030303", -"W c #0D0D0D", -" ", -" .++. ", -" @#$%&*= ", -" -;%>,')!~ ", -" {;]'%^/(_:<[ ", -" }|]^&12(34567[ ", -" 8^,9&0abc|def[ ", -" gh>%ij/;kkkklbm[ ", -" no'p1jqkkkkkrst[ ", -" .u)]v5wkkkkklux[ ", -" gx^^ybkkkkkk2az ", -" A19vBCkkkkD9EF ", -" GsEHbkkIBv5J ", -" KLw6eMaNOg ", -" [P*EBQjRJ ", -" [[mS*TU ", -" [VW ", -" "}; diff --git a/Modules/Segmentation/Interactions/mitkEditableContourTool.cpp b/Modules/Segmentation/Interactions/mitkEditableContourTool.cpp new file mode 100644 index 0000000000..f25e261373 --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkEditableContourTool.cpp @@ -0,0 +1,471 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkEditableContourTool.h" +#include + + +mitk::EditableContourTool::EditableContourTool() : FeedbackContourTool("EditableContourTool"), m_AutoConfirm(true), m_AddMode(true) +{} + +mitk::EditableContourTool::~EditableContourTool() +{ + this->ReleaseHelperObjects(); + this->ReleaseInteractors(); +} + + +void mitk::EditableContourTool::ConnectActionsAndFunctions() +{ + CONNECT_FUNCTION("InitObject", OnInitContour); + CONNECT_FUNCTION("AddPoint", OnAddPoint); + CONNECT_FUNCTION("CtrlAddPoint", OnAddPoint); + CONNECT_FUNCTION("Drawing", OnDrawing); + CONNECT_FUNCTION("EndDrawing", OnEndDrawing); + CONNECT_FUNCTION("FinishContour", OnFinish); + CONNECT_FUNCTION("CtrlMovePoint", OnMouseMoved); +} + +void mitk::EditableContourTool::Activated() +{ + Superclass::Activated(); + this->ResetToStartState(); + this->EnableContourInteraction(true); +} + +void mitk::EditableContourTool::Deactivated() +{ + this->ClearSegmentation(); + Superclass::Deactivated(); +} + +void mitk::EditableContourTool::ConfirmSegmentation(bool resetStatMachine) +{ + auto referenceImage = this->GetReferenceData(); + auto workingImage = this->GetWorkingData(); + + if (nullptr != referenceImage && nullptr != workingImage) + { + std::vector sliceInfos; + + const auto currentTimePoint = + mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); + TimeStepType workingImageTimeStep = workingImage->GetTimeGeometry()->TimePointToTimeStep(currentTimePoint); + + auto contour = this->GetContour(); + if (nullptr == contour || contour->IsEmpty()) + return; + + auto workingSlice = + this->GetAffectedImageSliceAs2DImage(m_PlaneGeometry, workingImage, workingImageTimeStep)->Clone(); + sliceInfos.emplace_back(workingSlice, m_PlaneGeometry, workingImageTimeStep); + + auto projectedContour = ContourModelUtils::ProjectContourTo2DSlice(workingSlice, contour); + int activePixelValue = ContourModelUtils::GetActivePixelValue(workingImage); + if (!m_AddMode) + { + activePixelValue = 0; + } + + ContourModelUtils::FillContourInSlice(projectedContour, workingSlice, workingImage, activePixelValue); + + this->WriteBackSegmentationResults(sliceInfos); + } + + this->ReleaseHelperObjects(); + this->ReleaseInteractors(); + if (resetStatMachine) this->ResetToStartState(); +} + +void mitk::EditableContourTool::ClearSegmentation() +{ + this->ReleaseHelperObjects(); + this->ReleaseInteractors(); + this->ResetToStartState(); +} + +bool mitk::EditableContourTool::IsEditingContour() const +{ + return (nullptr != GetContour()) && !this->IsDrawingContour(); +}; + + +bool mitk::EditableContourTool::IsDrawingContour() const +{ + return m_PreviewContour.IsNotNull(); +}; + +bool mitk::EditableContourTool::IsPositionEventInsideImageRegion(mitk::InteractionPositionEvent *positionEvent, + mitk::BaseData *data) +{ + bool isPositionEventInsideImageRegion = + nullptr != data && data->GetGeometry()->IsInside(positionEvent->GetPositionInWorld()); + + if (!isPositionEventInsideImageRegion) + MITK_WARN("EditableContourTool") << "PositionEvent is outside ImageRegion!"; + + return isPositionEventInsideImageRegion; +} + +mitk::Point3D mitk::EditableContourTool::PrepareInitContour(const Point3D& clickedPoint) +{ //default implementation does nothing + return clickedPoint; +} + +void mitk::EditableContourTool::OnInitContour(StateMachineAction *, InteractionEvent *interactionEvent) +{ + auto positionEvent = dynamic_cast(interactionEvent); + if (nullptr == positionEvent) + return; + + auto workingDataNode = this->GetWorkingDataNode(); + + if (nullptr != this->GetContour()) + { + this->ConfirmSegmentation(false); + } + + if (!IsPositionEventInsideImageRegion(positionEvent, workingDataNode->GetData())) + { + this->ResetToStartState(); + return; + } + + m_LastEventSender = positionEvent->GetSender(); + m_LastEventSlice = m_LastEventSender->GetSlice(); + + auto contour = this->CreateNewContour(); + m_ContourNode = mitk::DataNode::New(); + m_ContourNode->SetData(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_PreviewContour = this->CreateNewContour(); + m_PreviewContourNode = mitk::DataNode::New(); + m_PreviewContourNode->SetData(m_PreviewContour); + m_PreviewContourNode->SetName("active preview node"); + m_PreviewContourNode->SetProperty("layer", IntProperty::New(101)); + m_PreviewContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); + m_PreviewContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); + m_PreviewContourNode->AddProperty("contour.color", ColorProperty::New(0.1f, 1.0f, 0.1f), nullptr, true); + m_PreviewContourNode->AddProperty("contour.width", mitk::FloatProperty::New(4.0f), nullptr, true); + + + m_ClosureContour = this->CreateNewContour(); + m_ClosureContourNode = mitk::DataNode::New(); + m_ClosureContourNode->SetData(m_ClosureContour); + m_ClosureContourNode->SetName("active closure node"); + m_ClosureContourNode->SetProperty("layer", IntProperty::New(101)); + m_ClosureContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); + m_ClosureContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); + m_ClosureContourNode->AddProperty("contour.color", ColorProperty::New(0.0f, 1.0f, 0.1f), nullptr, true); + m_ClosureContourNode->AddProperty("contour.width", mitk::FloatProperty::New(2.0f), nullptr, true); + + m_CurrentRestrictedArea = this->CreateNewContour(); + + auto dataStorage = this->GetToolManager()->GetDataStorage(); + dataStorage->Add(m_ContourNode, workingDataNode); + dataStorage->Add(m_PreviewContourNode, workingDataNode); + dataStorage->Add(m_ClosureContourNode, workingDataNode); + + m_ReferenceDataSlice = this->GetAffectedReferenceSlice(positionEvent); + + auto origin = m_ReferenceDataSlice->GetSlicedGeometry()->GetOrigin(); + m_ReferenceDataSlice->GetSlicedGeometry()->WorldToIndex(origin, origin); + m_ReferenceDataSlice->GetSlicedGeometry()->IndexToWorld(origin, origin); + m_ReferenceDataSlice->GetSlicedGeometry()->SetOrigin(origin); + + // Remember PlaneGeometry to determine if events were triggered in the same plane + m_PlaneGeometry = interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry(); + + // Map click to pixel coordinates + auto click = positionEvent->GetPositionInWorld(); + + click = this->PrepareInitContour(click); + + this->InitializePreviewContour(click); + // Set initial start point + contour->AddVertex(click, true); + m_PreviewContour->AddVertex(click, false); + m_ClosureContour->AddVertex(click); + + mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); +} + +void mitk::EditableContourTool::FinalizePreviewContour(const Point3D& /*clickedPoint*/) +{ // Remove duplicate first vertex, it's already contained in m_ContourNode + m_PreviewContour->RemoveVertexAt(0); + + m_PreviewContour->SetControlVertexAt(m_PreviewContour->GetNumberOfVertices() - 1); +} + +void mitk::EditableContourTool::InitializePreviewContour(const Point3D& clickedPoint) +{ //default implementation only clears the preview and sets the start point + m_PreviewContour = this->CreateNewContour(); + m_PreviewContour->AddVertex(clickedPoint); + m_PreviewContourNode->SetData(m_PreviewContour); +} + +void mitk::EditableContourTool::UpdatePreviewContour(const Point3D& clickedPoint) +{ //default implementation draws just a simple line to position + if (m_PreviewContour->GetNumberOfVertices() > 2) + { + auto contour = this->GetContour(); + this->InitializePreviewContour(contour->GetVertexAt(contour->GetNumberOfVertices() - 1)->Coordinates); + } + + if (m_PreviewContour->GetNumberOfVertices() == 2) + { + m_PreviewContour->RemoveVertexAt(m_PreviewContour->GetNumberOfVertices()-1); + } + + m_PreviewContour->AddVertex(clickedPoint); +} + +void mitk::EditableContourTool::OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent) +{ + 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; + } + + this->FinalizePreviewContour(positionEvent->GetPositionInWorld()); + + // Merge contours + auto contour = this->GetContour(); + contour->Concatenate(m_PreviewContour); + + this->InitializePreviewContour(positionEvent->GetPositionInWorld()); + + mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); +} + +void mitk::EditableContourTool::OnDrawing(StateMachineAction*, InteractionEvent* interactionEvent) +{ + auto* positionEvent = dynamic_cast(interactionEvent); + if (!positionEvent) + return; + + m_PreviewContourNode->SetVisibility(false); + + auto contour = this->GetContour(); + contour->AddVertex(positionEvent->GetPositionInWorld(), false); + UpdateClosureContour(positionEvent->GetPositionInWorld()); + + assert(positionEvent->GetSender()->GetRenderWindow()); + mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); +} + +void mitk::EditableContourTool::OnEndDrawing(StateMachineAction*, InteractionEvent* interactionEvent) +{ + auto* positionEvent = dynamic_cast(interactionEvent); + if (!positionEvent) + return; + + auto contour = this->GetContour(); + auto controlVs = contour->GetControlVertices(0); + if (!controlVs.empty()) + { //add the last control point (after that the draw part start) + m_CurrentRestrictedArea->AddVertex(controlVs.back()->Coordinates); + } + m_PreviewContourNode->SetVisibility(true); + contour->SetControlVertexAt(contour->GetNumberOfVertices() - 1); + + //add the just created/set last control point (with it the draw part ends) + m_CurrentRestrictedArea->AddVertex(contour->GetVertexAt(contour->GetNumberOfVertices() - 1)->Coordinates); + + this->InitializePreviewContour(positionEvent->GetPositionInWorld()); + + mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); +} + +void mitk::EditableContourTool::OnMouseMoved(StateMachineAction*, InteractionEvent* interactionEvent) +{ + 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; + } + + this->UpdatePreviewContour(positionEvent->GetPositionInWorld()); + this->UpdateClosureContour(positionEvent->GetPositionInWorld()); + + RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); +} + +void mitk::EditableContourTool::OnFinish(StateMachineAction *, InteractionEvent *interactionEvent) +{ + 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; + } + + this->FinalizePreviewContour(positionEvent->GetPositionInWorld()); + + this->FinishTool(); + + // Merge contours + auto contour = this->GetContour(); + contour->Concatenate(m_PreviewContour); + + auto numberOfTimesteps = static_cast(contour->GetTimeSteps()); + + for (int i = 0; i <= numberOfTimesteps; ++i) + contour->Close(i); + + this->ReleaseHelperObjects(false); + + if (m_AutoConfirm) + { + this->ConfirmSegmentation(); + } +} + +void mitk::EditableContourTool::ReleaseHelperObjects(bool includeWorkingContour) +{ + this->RemoveHelperObjectsFromDataStorage(includeWorkingContour); + + if (includeWorkingContour) + { + m_ContourNode = nullptr; + m_CurrentRestrictedArea = nullptr; + } + + m_PreviewContourNode = nullptr; + m_PreviewContour = nullptr; + + m_ClosureContourNode = nullptr; + m_ClosureContour = nullptr; +} + +void mitk::EditableContourTool::RemoveHelperObjectsFromDataStorage(bool includeWorkingContour) +{ + auto dataStorage = this->GetToolManager()->GetDataStorage(); + + if (nullptr == dataStorage) + return; + + if (includeWorkingContour) + { + if (m_ContourNode.IsNotNull()) + dataStorage->Remove(m_ContourNode); + } + + if (m_PreviewContourNode.IsNotNull()) + dataStorage->Remove(m_PreviewContourNode); + + if (m_ClosureContourNode.IsNotNull()) + dataStorage->Remove(m_ClosureContourNode); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +mitk::ContourModel::Pointer mitk::EditableContourTool::CreateNewContour() const +{ + auto workingData = this->GetWorkingData(); + if (nullptr == workingData) + { + this->InteractiveSegmentationBugMessage( + "Cannot create new contour. No valid working data is set. Application is in invalid state."); + mitkThrow() << "Cannot create new contour. No valid working data is set. Application is in invalid state."; + } + + auto contour = ContourModel::New(); + + // generate a time geometry that is always visible as the working contour should always be. + auto contourTimeGeometry = ProportionalTimeGeometry::New(); + contourTimeGeometry->SetStepDuration(std::numeric_limits::max()); + contourTimeGeometry->SetTimeStepGeometry(contour->GetTimeGeometry()->GetGeometryForTimeStep(0)->Clone(), 0); + contour->SetTimeGeometry(contourTimeGeometry); + + return contour; +} + +void mitk::EditableContourTool::UpdateClosureContour(mitk::Point3D endpoint) +{ + if (m_ClosureContour->GetNumberOfVertices() > 2) + { + m_ClosureContour = this->CreateNewContour(); + m_ClosureContourNode->SetData(m_ClosureContour); + } + + if (m_ClosureContour->GetNumberOfVertices() == 0) + { + auto contour = this->GetContour(); + m_ClosureContour->AddVertex(contour->GetVertexAt(0)->Coordinates); + m_ClosureContour->Update(); + } + + if (m_ClosureContour->GetNumberOfVertices() == 2) + { + m_ClosureContour->RemoveVertexAt(0); + } + + m_ClosureContour->AddVertexAtFront(endpoint); +} + +void mitk::EditableContourTool::EnableContourInteraction(bool on) +{ + if (m_ContourInteractor.IsNotNull()) + { + m_ContourInteractor->EnableInteraction(on); + } +} + +void mitk::EditableContourTool::ReleaseInteractors() +{ + this->EnableContourInteraction(false); + m_ContourInteractor = nullptr; +} + +mitk::ContourModel* mitk::EditableContourTool::GetContour() +{ + if (this->m_ContourNode.IsNotNull()) + { + return dynamic_cast(this->m_ContourNode->GetData()); + } + return nullptr; +}; + +const mitk::ContourModel* mitk::EditableContourTool::GetContour() const +{ + if (this->m_ContourNode.IsNotNull()) + { + return dynamic_cast(this->m_ContourNode->GetData()); + } + return nullptr; +}; \ No newline at end of file diff --git a/Modules/Segmentation/Interactions/mitkEditableContourTool.h b/Modules/Segmentation/Interactions/mitkEditableContourTool.h new file mode 100644 index 0000000000..8be7c24ac9 --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkEditableContourTool.h @@ -0,0 +1,127 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkEditbaleContourTool_h_Included +#define mitkEditbaleContourTool_h_Included + +#include + +namespace mitk +{ + /** + * Base class for lasso like tools that allow to draw closed contours with multiple ancor points. + * The segments between the ancor points may be freehand contour segments or computed segments + * (e.g. straight lines or live wire). Derive from the class to implement the computation of the non-freehand + * segments. + * @sa LassoTool LivewWireTool2D + */ + class MITKSEGMENTATION_EXPORT EditableContourTool : public FeedbackContourTool + { + public: + mitkClassMacro(EditableContourTool, FeedbackContourTool); + + /// \brief Convert current contour to binary segmentations. + virtual void ConfirmSegmentation(bool resetStatMachine = true); + + /// \brief Delete all current contours. + virtual void ClearSegmentation(); + + itkBooleanMacro(AutoConfirm); + itkSetMacro(AutoConfirm, bool); + itkGetMacro(AutoConfirm, bool); + + itkBooleanMacro(AddMode); + itkSetMacro(AddMode, bool); + itkGetMacro(AddMode, bool); + + /* Indicated if a contour is drawn, but not confirmed yet. This the tool is in interactor mode + to allow users to edit the contour. This state can be reached if AutoConfirm is false, after the finalizing double + click before the contour is confirmed.*/ + bool IsEditingContour() const; + /* Indicate if a contour is currently drawn by the user (state between the initializing double click and + the finalizing double click).*/ + bool IsDrawingContour() const; + + protected: + EditableContourTool(); + ~EditableContourTool() override; + + void ConnectActionsAndFunctions() override; + + void Activated() override; + void Deactivated() override; + + virtual Point3D PrepareInitContour(const Point3D& clickedPoint); + virtual void FinalizePreviewContour(const Point3D& clickedPoint); + virtual void InitializePreviewContour(const Point3D& clickedPoint); + virtual void UpdatePreviewContour(const Point3D& clickedPoint); + + /// \brief Initialize tool. + virtual void OnInitContour(StateMachineAction *, InteractionEvent *interactionEvent); + + /// \brief Add a control point and finish current segment. + virtual void OnAddPoint(StateMachineAction *, InteractionEvent *interactionEvent); + + /// \brief Draw a contour according to the mouse movement when mouse button is pressed and mouse is moved. + virtual void OnDrawing(StateMachineAction *, InteractionEvent *interactionEvent); + + virtual void OnEndDrawing(StateMachineAction *, InteractionEvent *interactionEvent); + + /// \brief Computation of the preview contour. + virtual void OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent); + + /// \brief Finish EditableContour tool. + virtual void OnFinish(StateMachineAction *, InteractionEvent *interactionEvent); + + /// \brief Finish contour interaction. + virtual void FinishTool() = 0; + + void EnableContourInteraction(bool on); + + void ReleaseInteractors(); + + virtual void ReleaseHelperObjects(bool includeWorkingContour = true); + + virtual void RemoveHelperObjectsFromDataStorage(bool includeWorkingContour = true); + + ContourModel::Pointer CreateNewContour() const; + + virtual void UpdateClosureContour(mitk::Point3D endpoint); + + bool IsPositionEventInsideImageRegion(InteractionPositionEvent *positionEvent, BaseData *data); + + mitk::ContourModel* GetContour(); + const mitk::ContourModel* GetContour() const; + mitk::DataNode::Pointer m_ContourNode; + + mitk::ContourModel::Pointer m_PreviewContour; + mitk::DataNode::Pointer m_PreviewContourNode; + + mitk::ContourModel::Pointer m_ClosureContour; + mitk::DataNode::Pointer m_ClosureContourNode; + + mitk::ContourModel::Pointer m_CurrentRestrictedArea; + + /** Slice of the reference data the tool is currently actively working on to + define contours.*/ + mitk::Image::Pointer m_ReferenceDataSlice; + + PlaneGeometry::ConstPointer m_PlaneGeometry; + + mitk::DataInteractor::Pointer m_ContourInteractor; + + bool m_AutoConfirm; + bool m_AddMode; + }; +} + +#endif diff --git a/Modules/Segmentation/Interactions/mitkLassoTool.cpp b/Modules/Segmentation/Interactions/mitkLassoTool.cpp new file mode 100644 index 0000000000..84adb3ee05 --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkLassoTool.cpp @@ -0,0 +1,70 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +//#include +#include +#include + +#include +#include + +#include + +namespace mitk +{ + MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, LassoTool, "Lasso tool"); +} + +mitk::LassoTool::LassoTool() : EditableContourTool() +{ +} + +mitk::LassoTool::~LassoTool() +{} + +void mitk::LassoTool::ConnectActionsAndFunctions() +{ + mitk::EditableContourTool::ConnectActionsAndFunctions(); + CONNECT_FUNCTION("MovePoint", OnMouseMoved); +} + +const char **mitk::LassoTool::GetXPM() const +{ + return nullptr; +} + +us::ModuleResource mitk::LassoTool::GetIconResource() const +{ + return us::GetModuleContext()->GetModule()->GetResource("NewAdd_48x48.png"); +} + +us::ModuleResource mitk::LassoTool::GetCursorIconResource() const +{ + return us::GetModuleContext()->GetModule()->GetResource("NewAdd_Cursor_32x32.png"); +} + +const char *mitk::LassoTool::GetName() const +{ + return "Lasso"; +} + +void mitk::LassoTool::FinishTool() +{ + auto contourInteractor = mitk::ContourModelInteractor::New(); + contourInteractor->SetDataNode(m_ContourNode); + contourInteractor->LoadStateMachine("ContourModelModificationInteractor.xml", us::GetModuleContext()->GetModule()); + contourInteractor->SetEventConfig("ContourModelModificationConfig.xml", us::GetModuleContext()->GetModule()); + contourInteractor->SetRestrictedArea(this->m_CurrentRestrictedArea); + + this->m_ContourInteractor = contourInteractor; + m_ContourNode->SetDataInteractor(m_ContourInteractor.GetPointer()); +} diff --git a/Modules/Segmentation/Interactions/mitkLassoTool.h b/Modules/Segmentation/Interactions/mitkLassoTool.h new file mode 100644 index 0000000000..d86b1d3e92 --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkLassoTool.h @@ -0,0 +1,63 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkLassoTool_h +#define mitkLassoTool_h + +#include "mitkEditableContourTool.h" +#include "mitkContourTool.h" +#include "mitkContourModelInteractor.h" + +namespace mitk +{ + /** + \brief A 2D segmentation tool to draw polygon structures. + + The contour between the last point and the current mouse position is + computed by forming a straight line. + + The tool always assumes that unconfirmed contours are always defined for the + current time point. So the time step in which the contours will be stored as + segmentations will be determined when the contours got confirmed. Then they + will be transfered to the slices of the currently selected time step. + + \sa SegTool2D + + \ingroup Interaction + \ingroup ToolManagerEtAl + + \warning Only to be instantiated by mitk::ToolManager. + */ + class MITKSEGMENTATION_EXPORT LassoTool : public EditableContourTool + { + public: + mitkClassMacro(LassoTool, SegTool2D); + itkFactorylessNewMacro(Self); + + us::ModuleResource GetCursorIconResource() const override; + us::ModuleResource GetIconResource() const override; + const char *GetName() const override; + const char **GetXPM() const override; + + protected: + LassoTool(); + ~LassoTool() override; + + void ConnectActionsAndFunctions() override; + void FinishTool() override; + + private: + mitk::ContourModelInteractor::Pointer m_ContourInteractor; + }; +} + +#endif diff --git a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp index e39f44b64e..861f2bd557 100644 --- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp @@ -1,740 +1,261 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All 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_SnapClosureContour(false), m_CreateAndUseDynamicCosts(false) + : EditableContourTool(), m_CreateAndUseDynamicCosts(false) { } mitk::LiveWireTool2D::~LiveWireTool2D() { - this->ClearSegmentation(); -} - -void mitk::LiveWireTool2D::RemoveHelperObjects() -{ - auto dataStorage = this->GetToolManager()->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_ClosureContourNode.IsNotNull()) - dataStorage->Remove(m_ClosureContourNode); - - 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_ClosureContourNode = nullptr; - m_ClosureContour = nullptr; - - m_ContourNode = nullptr; - m_Contour = nullptr; -} - -void mitk::LiveWireTool2D::ReleaseInteractors() -{ - this->EnableContourLiveWireInteraction(false); - m_LiveWireInteractors.clear(); } void mitk::LiveWireTool2D::ConnectActionsAndFunctions() { - CONNECT_FUNCTION("InitObject", OnInitLiveWire); - CONNECT_FUNCTION("AddPoint", OnAddPoint); - CONNECT_FUNCTION("CtrlAddPoint", OnAddPoint); + mitk::EditableContourTool::ConnectActionsAndFunctions(); 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::UpdateLiveWireContour() { - if (m_Contour.IsNotNull()) + auto contour = this->GetContour(); + if (nullptr != contour) { - auto timeGeometry = m_Contour->GetTimeGeometry()->Clone(); - m_LiveWireContour = this->m_LiveWireFilter->GetOutput(); - m_LiveWireContour->SetTimeGeometry(timeGeometry); // needed because the results of the filter are always from 0 ms - // to 1 ms and the filter also resets its outputs. - m_LiveWireContourNode->SetData(this->m_LiveWireContour); - - if (m_SnapClosureContour) - { - m_ClosureContour = this->m_LiveWireFilterClosure->GetOutput(); - } - - m_ClosureContour->SetTimeGeometry(timeGeometry); // needed because the results of the filter are always from 0 ms - // to 1 ms and the filter also resets its outputs. - m_ClosureContourNode->SetData(this->m_ClosureContour); + auto timeGeometry = contour->GetTimeGeometry()->Clone(); + m_PreviewContour = this->m_LiveWireFilter->GetOutput(); + + // needed because the results of the filter are always from 0 ms + // to 1 ms and the filter also resets its outputs. + // generate a time geometry that is always visible as the working contour should always be. + auto contourTimeGeometry = ProportionalTimeGeometry::New(); + contourTimeGeometry->SetStepDuration(std::numeric_limits::max()); + contourTimeGeometry->SetTimeStepGeometry(contour->GetTimeGeometry()->GetGeometryForTimeStep(0)->Clone(), 0); + m_PreviewContour->SetTimeGeometry(contourTimeGeometry); + m_PreviewContourNode->SetData(this->m_PreviewContour); } } void mitk::LiveWireTool2D::OnTimePointChanged() { auto reference = this->GetReferenceData(); - if (nullptr == reference || m_PlaneGeometry.IsNull() || m_LiveWireFilter.IsNull() || m_LiveWireContourNode.IsNull()) + if (nullptr == reference || m_PlaneGeometry.IsNull() || m_LiveWireFilter.IsNull() || m_PreviewContourNode.IsNull()) return; auto timeStep = reference->GetTimeGeometry()->TimePointToTimeStep(this->GetLastTimePointTriggered()); m_ReferenceDataSlice = GetAffectedImageSliceAs2DImageByTimePoint(m_PlaneGeometry, reference, timeStep); m_LiveWireFilter->SetInput(m_ReferenceDataSlice); m_LiveWireFilter->Update(); - m_LiveWireFilterClosure->SetInput(m_ReferenceDataSlice); - - m_LiveWireFilterClosure->Update(); - this->UpdateLiveWireContour(); RenderingManager::GetInstance()->RequestUpdateAll(); }; -void mitk::LiveWireTool2D::EnableContourLiveWireInteraction(bool on) -{ - for (const auto &interactor : m_LiveWireInteractors) - interactor->EnableInteraction(on); -} - -void mitk::LiveWireTool2D::ConfirmSegmentation() -{ - auto referenceImage = this->GetReferenceData(); - auto workingImage = this->GetWorkingData(); - - if (nullptr != referenceImage && nullptr != workingImage) - { - std::vector sliceInfos; - sliceInfos.reserve(m_WorkingContours.size()); - - const auto currentTimePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); - TimeStepType workingImageTimeStep = workingImage->GetTimeGeometry()->TimePointToTimeStep(currentTimePoint); - - for (const auto &workingContour : m_WorkingContours) - { - auto contour = dynamic_cast(workingContour.first->GetData()); - - if (nullptr == contour || contour->IsEmpty()) - continue; - - auto sameSlicePredicate = [&workingContour, workingImageTimeStep](const SliceInformation& si) { return workingContour.second->IsOnPlane(si.plane) && workingImageTimeStep == si.timestep; }; - - auto finding = std::find_if(sliceInfos.begin(), sliceInfos.end(), sameSlicePredicate); - if (finding == sliceInfos.end()) - { - auto workingSlice = this->GetAffectedImageSliceAs2DImage(workingContour.second, workingImage, workingImageTimeStep)->Clone(); - sliceInfos.emplace_back(workingSlice, workingContour.second, workingImageTimeStep); - finding = std::prev(sliceInfos.end()); - } - - //cast const away is OK in this case, because these are all slices created and manipulated - //localy in this function call. And we want to keep the high constness of SliceInformation for - //public interfaces. - auto workingSlice = const_cast(finding->slice.GetPointer()); - - auto projectedContour = ContourModelUtils::ProjectContourTo2DSlice(workingSlice, contour); - int activePixelValue = ContourModelUtils::GetActivePixelValue(workingImage); - - ContourModelUtils::FillContourInSlice( - projectedContour, workingSlice, workingImage, activePixelValue); - } - - this->WriteBackSegmentationResults(sliceInfos); - } - - this->ClearSegmentation(); -} - -void mitk::LiveWireTool2D::ClearSegmentation() -{ - this->ReleaseHelperObjects(); - this->ReleaseInteractors(); - this->ResetToStartState(); -} - -void mitk::LiveWireTool2D::SetSnapClosureContour(bool snap) -{ - m_SnapClosureContour = snap; - if (!m_LiveWireContour || !m_Contour) - { - return; - } - - if (m_LiveWireContour->GetNumberOfVertices() > 0) - { - UpdateClosureContour(m_LiveWireContour->GetVertexAt(m_LiveWireContour->GetNumberOfVertices() - 1)->Coordinates); - } - else if (m_Contour->GetNumberOfVertices() > 0) - { - UpdateClosureContour(m_Contour->GetVertexAt(m_Contour->GetNumberOfVertices() - 1)->Coordinates); - } - - this->UpdateLiveWireContour(); -} - -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; -} - -mitk::ContourModel::Pointer mitk::LiveWireTool2D::CreateNewContour() const -{ - auto workingData = this->GetWorkingData(); - if (nullptr == workingData) - { - this->InteractiveSegmentationBugMessage("Cannot create new contour. No valid working data is set. Application is in invalid state."); - mitkThrow() << "Cannot create new contour. No valid working data is set. Application is in invalid state."; - } - - auto contour = ContourModel::New(); - - //generate a time geometry that is always visible as the working contour should always be. - auto contourTimeGeometry = ProportionalTimeGeometry::New(); - contourTimeGeometry->SetStepDuration(std::numeric_limits::max()); - contourTimeGeometry->SetTimeStepGeometry(contour->GetTimeGeometry()->GetGeometryForTimeStep(0)->Clone(), 0); - contour->SetTimeGeometry(contourTimeGeometry); - - return contour; -} - -void mitk::LiveWireTool2D::OnInitLiveWire(StateMachineAction *, InteractionEvent *interactionEvent) +mitk::Point3D mitk::LiveWireTool2D::PrepareInitContour(const mitk::Point3D& clickedPoint) { - auto positionEvent = dynamic_cast(interactionEvent); - - if (nullptr == positionEvent) - return; - - auto workingDataNode = this->GetWorkingDataNode(); - - if (!IsPositionEventInsideImageRegion(positionEvent, workingDataNode->GetData())) - { - this->ResetToStartState(); - return; - } - - m_LastEventSender = positionEvent->GetSender(); - m_LastEventSlice = m_LastEventSender->GetSlice(); - - m_Contour = this->CreateNewContour(); - 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 = this->CreateNewContour(); - 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_ClosureContour = this->CreateNewContour(); - m_ClosureContourNode = mitk::DataNode::New(); - m_ClosureContourNode->SetData(m_ClosureContour); - m_ClosureContourNode->SetName("active closure node"); - m_ClosureContourNode->SetProperty("layer", IntProperty::New(101)); - m_ClosureContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); - m_ClosureContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); - m_ClosureContourNode->AddProperty("contour.color", ColorProperty::New(0.0f, 1.0f, 0.1f), nullptr, true); - m_ClosureContourNode->AddProperty("contour.width", mitk::FloatProperty::New(2.0f), nullptr, true); - - m_EditingContour = this->CreateNewContour(); - 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 = this->GetToolManager()->GetDataStorage(); - dataStorage->Add(m_ContourNode, workingDataNode); - dataStorage->Add(m_LiveWireContourNode, workingDataNode); - dataStorage->Add(m_ClosureContourNode, workingDataNode); - dataStorage->Add(m_EditingContourNode, workingDataNode); - // Set current slice as input for ImageToLiveWireContourFilter - m_ReferenceDataSlice = this->GetAffectedReferenceSlice(positionEvent); - - auto origin = m_ReferenceDataSlice->GetSlicedGeometry()->GetOrigin(); - m_ReferenceDataSlice->GetSlicedGeometry()->WorldToIndex(origin, origin); - m_ReferenceDataSlice->GetSlicedGeometry()->IndexToWorld(origin, origin); - m_ReferenceDataSlice->GetSlicedGeometry()->SetOrigin(origin); - m_LiveWireFilter = ImageLiveWireContourModelFilter::New(); m_LiveWireFilter->SetUseCostFunction(true); m_LiveWireFilter->SetInput(m_ReferenceDataSlice); - m_LiveWireFilterClosure = ImageLiveWireContourModelFilter::New(); - m_LiveWireFilterClosure->SetUseCostFunction(true); - m_LiveWireFilterClosure->SetInput(m_ReferenceDataSlice); - - // Map click to pixel coordinates - auto click = positionEvent->GetPositionInWorld(); itk::Index<3> idx; - m_ReferenceDataSlice->GetGeometry()->WorldToIndex(click, idx); + m_ReferenceDataSlice->GetGeometry()->WorldToIndex(clickedPoint, idx); // Get the pixel with the highest gradient in a 7x7 region itk::Index<3> indexWithHighestGradient; AccessFixedDimensionByItk_2(m_ReferenceDataSlice, FindHighestGradientMagnitudeByITK, 2, idx, indexWithHighestGradient); - click[0] = indexWithHighestGradient[0]; - click[1] = indexWithHighestGradient[1]; - click[2] = indexWithHighestGradient[2]; - m_ReferenceDataSlice->GetGeometry()->IndexToWorld(click, click); - - // Set initial start point - m_Contour->AddVertex(click, true); - m_LiveWireFilter->SetStartPoint(click); - //m_LiveWireFilterClosure->SetStartPoint(click); - m_LiveWireFilterClosure->SetEndPoint(click); - if (!m_SnapClosureContour) - { - m_ClosureContour->AddVertex(click); - } - - // Remember PlaneGeometry to determine if events were triggered in the same plane - m_PlaneGeometry = interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry(); + Point3D adaptedClick; + m_ReferenceDataSlice->GetGeometry()->IndexToWorld(indexWithHighestGradient, adaptedClick); m_CreateAndUseDynamicCosts = true; - mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); + return adaptedClick; } -void mitk::LiveWireTool2D::OnAddPoint(StateMachineAction *, InteractionEvent *interactionEvent) +void mitk::LiveWireTool2D::FinalizePreviewContour(const Point3D& clickedPoint) { - // 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; - } - // Add repulsive points to avoid getting the same path again - std::for_each(m_LiveWireContour->IteratorBegin(), m_LiveWireContour->IteratorEnd(), [this](ContourElement::VertexType *vertex) { + std::for_each(m_PreviewContour->IteratorBegin(), m_PreviewContour->IteratorEnd(), [this](ContourElement::VertexType* vertex) { ImageLiveWireContourModelFilter::InternalImageType::IndexType idx; this->m_ReferenceDataSlice->GetGeometry()->WorldToIndex(vertex->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint(idx); - this->m_LiveWireFilterClosure->AddRepulsivePoint(idx); }); - // Remove duplicate first vertex, it's already contained in m_Contour - m_LiveWireContour->RemoveVertexAt(0); - - // Set last point as control point - m_LiveWireContour->SetControlVertexAt(m_LiveWireContour->GetNumberOfVertices() - 1); - - // Merge contours - m_Contour->Concatenate(m_LiveWireContour); + EditableContourTool::FinalizePreviewContour(clickedPoint); +} - // Clear the LiveWire contour and reset the corresponding DataNode - m_LiveWireContour->Clear(); +void mitk::LiveWireTool2D::InitializePreviewContour(const Point3D& clickedPoint) +{ + m_PreviewContour->Clear(); // Set new start point - m_LiveWireFilter->SetStartPoint(positionEvent->GetPositionInWorld()); - m_LiveWireFilterClosure->SetStartPoint(positionEvent->GetPositionInWorld()); + m_LiveWireFilter->SetStartPoint(clickedPoint); if (m_CreateAndUseDynamicCosts) { + auto contour = this->GetContour(); // Use dynamic cost map for next update - m_LiveWireFilter->CreateDynamicCostMap(m_Contour); + m_LiveWireFilter->CreateDynamicCostMap(contour); m_LiveWireFilter->SetUseDynamicCostMap(true); - - m_LiveWireFilterClosure->CreateDynamicCostMap(m_Contour); - m_LiveWireFilterClosure->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; - - if (m_PlaneGeometry.IsNotNull()) - { - // Check if the point is in the correct slice - if (m_PlaneGeometry->DistanceFromPlane(positionEvent->GetPositionInWorld()) > mitk::sqrteps) - return; - } - - m_LiveWireFilter->SetEndPoint(positionEvent->GetPositionInWorld()); +void mitk::LiveWireTool2D::UpdatePreviewContour(const Point3D& clickedPoint) +{ // Compute LiveWire segment from last control point to current mouse position + m_LiveWireFilter->SetEndPoint(clickedPoint); m_LiveWireFilter->Update(); - UpdateClosureContour(positionEvent->GetPositionInWorld()); - this->UpdateLiveWireContour(); - - RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); -} - -void mitk::LiveWireTool2D::UpdateClosureContour(mitk::Point3D endpoint) -{ - if (m_SnapClosureContour) - { - m_LiveWireFilterClosure->SetStartPoint(endpoint); - m_LiveWireFilterClosure->Update(); - } - else - { - if (m_ClosureContour->GetNumberOfVertices() > 2) - { - m_ClosureContour = this->CreateNewContour(); - } - - if (m_ClosureContour->GetNumberOfVertices() == 0) - { - m_ClosureContour->AddVertex(m_Contour->GetVertexAt(0)->Coordinates); - m_ClosureContour->Update(); - } - - if (m_ClosureContour->GetNumberOfVertices() == 2) - { - m_ClosureContour->RemoveVertexAt(0); - } - - m_ClosureContour->AddVertexAtFront(endpoint); - } } void mitk::LiveWireTool2D::OnMouseMoveNoDynamicCosts(StateMachineAction *, InteractionEvent *interactionEvent) { m_LiveWireFilter->SetUseDynamicCostMap(false); - m_LiveWireFilterClosure->SetUseDynamicCostMap(false); this->OnMouseMoved(nullptr, interactionEvent); m_LiveWireFilter->SetUseDynamicCostMap(true); - m_LiveWireFilterClosure->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; - - mitk::Point3D click = positionEvent->GetPositionInWorld(); - mitk::Point3D first = this->m_Contour->GetVertexAt(0)->Coordinates; - - return first.EuclideanDistanceTo(click) < 4.5; -} - -void mitk::LiveWireTool2D::OnFinish(StateMachineAction *, InteractionEvent *interactionEvent) -{ - // Finish LiveWire tool interaction - - m_Contour->Concatenate(m_ClosureContour); - 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; - } - - //m_Contour->AddVertex(m_Contour->GetVertexAt(0)->Coordinates, false); - // Remove last control point added by double click, if double click was performed on first point - //if (OnCheckPoint(interactionEvent)) - // m_Contour->RemoveVertexAt(m_Contour->GetNumberOfVertices() - 1); - - // remove green connection between mouse position and start point - m_ClosureContour->Clear(); - - // 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); - m_LiveWireFilterClosure->SetUseDynamicCostMap(false); - - this->FinishTool(); } void mitk::LiveWireTool2D::FinishTool() { - auto numberOfTimesteps = static_cast(m_Contour->GetTimeSteps()); - - for (int i = 0; i <= numberOfTimesteps; ++i) - m_Contour->Close(i); - - this->GetToolManager()->GetDataStorage()->Remove(m_LiveWireContourNode); - - m_LiveWireContourNode = nullptr; - m_LiveWireContour = nullptr; + m_LiveWireFilter->SetUseDynamicCostMap(false); 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_ReferenceDataSlice); - m_ContourInteractor->SetEditingContourModelNode(this->m_EditingContourNode); + m_ContourInteractor->SetRestrictedArea(this->m_CurrentRestrictedArea); m_ContourNode->SetDataInteractor(m_ContourInteractor.GetPointer()); - - this->m_LiveWireInteractors.push_back(m_ContourInteractor); -} - -void mitk::LiveWireTool2D::OnLastSegmentDelete(StateMachineAction *, InteractionEvent *interactionEvent) -{ - // If last point of current contour will be removed go to start state and remove nodes - if (m_Contour->GetNumberOfVertices() <= 1) - { - auto dataStorage = this->GetToolManager()->GetDataStorage(); - - dataStorage->Remove(m_LiveWireContourNode); - dataStorage->Remove(m_ContourNode); - dataStorage->Remove(m_EditingContourNode); - - m_LiveWireContour = this->CreateNewContour(); - m_LiveWireContourNode->SetData(m_LiveWireContour); - - m_Contour = this->CreateNewContour(); - m_ContourNode->SetData(m_Contour); - - this->ResetToStartState(); - } - else // Remove last segment from contour and reset LiveWire contour - { - m_LiveWireContour = this->CreateNewContour(); - m_LiveWireContourNode->SetData(m_LiveWireContour); - - auto newContour = this->CreateNewContour(); - - 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); - //m_LiveWireFilterClosure->SetStartPoint((*newLast)->Coordinates); - - auto it = m_Contour->IteratorBegin(); - - // Fll new Contour - while (it <= newLast) - { - newContour->AddVertex((*it)->Coordinates, (*it)->IsControlPoint); - ++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/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h index 755e9eedc2..a598e8c3bb 100644 --- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h +++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h @@ -1,148 +1,90 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkLiveWireTool2D_h #define mitkLiveWireTool2D_h -#include +#include #include namespace mitk { /** \brief A 2D segmentation tool based on a LiveWire approach. The contour between the last point and the current mouse position is computed by searching the shortest path according to specific features of the image. The contour thus tends to snap to the boundary of objects. The tool always assumes that unconfirmed contours are always defined for the current time point. So the time step in which the contours will be stored as segmentations will be determined when the contours got confirmed. Then they will be transfered to the slices of the currently selected time step. Changing the time point/time step while tool is active will updated the working slice the live wire filter. So the behavior of the active live wire contour is always WYSIWYG (What you see is what you get). \sa SegTool2D \sa ImageLiveWireContourModelFilter \ingroup Interaction \ingroup ToolManagerEtAl \warning Only to be instantiated by mitk::ToolManager. */ - class MITKSEGMENTATION_EXPORT LiveWireTool2D : public SegTool2D + class MITKSEGMENTATION_EXPORT LiveWireTool2D : public EditableContourTool { public: - mitkClassMacro(LiveWireTool2D, SegTool2D); + mitkClassMacro(LiveWireTool2D, EditableContourTool); itkFactorylessNewMacro(Self); us::ModuleResource GetCursorIconResource() const override; us::ModuleResource GetIconResource() const override; const char *GetName() const override; const char **GetXPM() const override; - /// \brief Convert all current contours to binary segmentations. - void ConfirmSegmentation(); - - /// \brief Delete all current contours. - void ClearSegmentation(); - - void SetSnapClosureContour(bool snap); - protected: LiveWireTool2D(); ~LiveWireTool2D() override; void ConnectActionsAndFunctions() override; - void Activated() override; - void Deactivated() override; + void UpdateLiveWireContour(); void OnTimePointChanged() override; - private: - /// \brief Initialize tool. - void OnInitLiveWire(StateMachineAction *, InteractionEvent *interactionEvent); - - /// \brief Add a control point and finish current segment. - void OnAddPoint(StateMachineAction *, InteractionEvent *interactionEvent); + mitk::Point3D PrepareInitContour(const mitk::Point3D& clickedPoint) override; + virtual void FinalizePreviewContour(const Point3D& clickedPoint) override; + virtual void InitializePreviewContour(const Point3D& clickedPoint) override; + virtual void UpdatePreviewContour(const Point3D& clickedPoint) override; - /// \brief Actual LiveWire computation. - void OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent); - - /// \brief Check double click on first control point to finish the LiveWire tool. - bool OnCheckPoint(const InteractionEvent *interactionEvent); - - /// \brief Finish LiveWire tool. - void OnFinish(StateMachineAction *, InteractionEvent *interactionEvent); - - /// \brief Close the contour. - void OnLastSegmentDelete(StateMachineAction *, InteractionEvent *interactionEvent); + private: /// \brief Don't use dynamic cost map for LiveWire calculation. void OnMouseMoveNoDynamicCosts(StateMachineAction *, InteractionEvent *interactionEvent); /// \brief Finish contour interaction. - void FinishTool(); - - void EnableContourLiveWireInteraction(bool on); - - bool IsPositionEventInsideImageRegion(InteractionPositionEvent *positionEvent, BaseData *data); - - void ReleaseInteractors(); - - void ReleaseHelperObjects(); - - void RemoveHelperObjects(); + void FinishTool() override; template void FindHighestGradientMagnitudeByITK(itk::Image *inputImage, itk::Index<3> &index, itk::Index<3> &returnIndex); - ContourModel::Pointer CreateNewContour() const; - - void UpdateClosureContour(mitk::Point3D endpoint); - - mitk::ContourModel::Pointer m_Contour; - mitk::DataNode::Pointer m_ContourNode; - - mitk::ContourModel::Pointer m_LiveWireContour; - mitk::DataNode::Pointer m_LiveWireContourNode; - - mitk::ContourModel::Pointer m_ClosureContour; - mitk::DataNode::Pointer m_ClosureContourNode; - bool m_SnapClosureContour; - - mitk::ContourModel::Pointer m_EditingContour; - mitk::DataNode::Pointer m_EditingContourNode; mitk::ContourModelLiveWireInteractor::Pointer m_ContourInteractor; - /** Slice of the reference data the tool is currently actively working on to - define contours.*/ - mitk::Image::Pointer m_ReferenceDataSlice; - mitk::ImageLiveWireContourModelFilter::Pointer m_LiveWireFilter; - mitk::ImageLiveWireContourModelFilter::Pointer m_LiveWireFilterClosure; bool m_CreateAndUseDynamicCosts; - - std::vector> m_WorkingContours; - std::vector> m_EditingContours; - std::vector m_LiveWireInteractors; - - PlaneGeometry::ConstPointer m_PlaneGeometry; }; } #endif diff --git a/Modules/Segmentation/Interactions/mitkPixelManipulationTool.cpp b/Modules/Segmentation/Interactions/mitkPixelManipulationTool.cpp deleted file mode 100644 index 1507bbcc3a..0000000000 --- a/Modules/Segmentation/Interactions/mitkPixelManipulationTool.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ -#include "mitkPixelManipulationTool.h" - -#include "mitkBoundingObjectToSegmentationFilter.h" -#include "mitkImage.h" -#include "mitkImageAccessByItk.h" -#include "mitkImageCast.h" -#include "mitkProperties.h" -#include "mitkToolManager.h" -#include - -#include "mitkPixelManipulationTool.xpm" - -namespace mitk -{ - MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, PixelManipulationTool, "Pixel manipulation tool"); -} - -mitk::PixelManipulationTool::PixelManipulationTool() : Tool("dummy"), m_Value(0), m_FixedValue(false) -{ -} - -mitk::PixelManipulationTool::~PixelManipulationTool() -{ -} - -void mitk::PixelManipulationTool::Activated() -{ - Superclass::Activated(); - - this->GetToolManager()->RoiDataChanged += - mitk::MessageDelegate(this, &mitk::PixelManipulationTool::OnRoiDataChanged); - m_OriginalImageNode = this->GetToolManager()->GetReferenceData(0); - - if (m_OriginalImageNode.IsNotNull()) - { - mitk::Image::Pointer image = dynamic_cast(m_OriginalImageNode->GetData()); - if (image.IsNotNull()) - { - // mitk::ScalarType scalar = image->GetScalarValueMax(); - } - } - else - this->GetToolManager()->ActivateTool(-1); -} - -void mitk::PixelManipulationTool::Deactivated() -{ - this->GetToolManager()->RoiDataChanged -= - mitk::MessageDelegate(this, &mitk::PixelManipulationTool::OnRoiDataChanged); - - Superclass::Deactivated(); -} - -const char *mitk::PixelManipulationTool::GetName() const -{ - return "pixelmanipulation"; -} - -const char **mitk::PixelManipulationTool::GetXPM() const -{ - return mitkPixelManipulationTool_xpm; -} - -void mitk::PixelManipulationTool::OnRoiDataChanged() -{ -} - -void mitk::PixelManipulationTool::CalculateImage() -{ - if (m_OriginalImageNode.IsNotNull()) - { - mitk::Image::Pointer image = dynamic_cast(m_OriginalImageNode->GetData()); - mitk::DataNode *maskNode = this->GetToolManager()->GetRoiData(0); - mitk::Image::Pointer roi = mitk::Image::New(); - - if (maskNode) - { - auto *boundingObject = dynamic_cast(maskNode->GetData()); - - if (boundingObject) - { - mitk::BoundingObjectToSegmentationFilter::Pointer filter = mitk::BoundingObjectToSegmentationFilter::New(); - filter->SetBoundingObject(boundingObject); - filter->SetInput(image); - filter->Update(); - roi = filter->GetOutput(); - } - else - roi = dynamic_cast(maskNode->GetData()); - - mitk::Image::Pointer newImage = mitk::Image::New(); - newImage->Initialize(image); - - if (image) - { - AccessByItk_3(image, ITKPixelManipulation, roi, newImage, m_Value); - this->AddImageToDataStorage(newImage); - } - } - } -} - -template -void mitk::PixelManipulationTool::ITKPixelManipulation(itk::Image *originalImage, - Image *maskImage, - Image *newImage, - int newValue) -{ - typedef itk::Image itkImageType; - typedef itk::Image itkMaskType; - typename itkImageType::Pointer itkImage; - typename itkMaskType::Pointer itkMask; - CastToItkImage(newImage, itkImage); - CastToItkImage(maskImage, itkMask); - - typedef itk::ImageRegionConstIterator InputIteratorType; - typedef itk::ImageRegionIterator OutputIteratorType; - typedef itk::ImageRegionConstIterator MaskIteratorType; - - MaskIteratorType maskIterator(itkMask, itkMask->GetLargestPossibleRegion()); - InputIteratorType inputIterator(originalImage, originalImage->GetLargestPossibleRegion()); - OutputIteratorType outputIterator(itkImage, itkImage->GetLargestPossibleRegion()); - - inputIterator.GoToBegin(); - outputIterator.GoToBegin(); - maskIterator.GoToBegin(); - - while (!outputIterator.IsAtEnd()) - { - if (maskIterator.Get()) - { - if (m_FixedValue) - outputIterator.Set(newValue); - else - outputIterator.Set(inputIterator.Get() + newValue); - } - else - outputIterator.Set(inputIterator.Get()); - - ++inputIterator; - ++outputIterator; - ++maskIterator; - } -} - -void mitk::PixelManipulationTool::AddImageToDataStorage(mitk::Image::Pointer image) -{ - if (image.IsNotNull()) - { - mitk::DataNode::Pointer node = mitk::DataNode::New(); - std::string name = m_OriginalImageNode->GetName(); - name.append("_modified"); - node->SetName(name); - node->SetProperty("binary", mitk::BoolProperty::New(false)); - node->SetData(image); - - if (nullptr != this->GetToolManager()) - this->GetToolManager()->GetDataStorage()->Add(node, m_OriginalImageNode); - } -} - -void mitk::PixelManipulationTool::SetValue(int value) -{ - m_Value = value; -} - -int mitk::PixelManipulationTool::GetValue() -{ - return m_Value; -} - -void mitk::PixelManipulationTool::SetFixedValue(int value) -{ - m_FixedValue = value; -} - -int mitk::PixelManipulationTool::GetFixedValue() -{ - return m_FixedValue; -} diff --git a/Modules/Segmentation/Interactions/mitkPixelManipulationTool.h b/Modules/Segmentation/Interactions/mitkPixelManipulationTool.h deleted file mode 100644 index 9e55cea83b..0000000000 --- a/Modules/Segmentation/Interactions/mitkPixelManipulationTool.h +++ /dev/null @@ -1,63 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ -#ifndef MITKPIXELMANIPULATIONTOOL_H -#define MITKPIXELMANIPULATIONTOOL_H - -#include "mitkTool.h" -#include - -#include "itkImage.h" - -namespace mitk -{ - class Image; - - class MITKSEGMENTATION_EXPORT PixelManipulationTool : public Tool - { - public: - mitkClassMacro(PixelManipulationTool, Tool); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); - - void SetValue(int value); - int GetValue(); - - void SetFixedValue(int value); - int GetFixedValue(); - - const char *GetName() const override; - const char **GetXPM() const override; - - void Activated() override; - void Deactivated() override; - - virtual void CalculateImage(); - - protected: - PixelManipulationTool(); - ~PixelManipulationTool() override; - - virtual void OnRoiDataChanged(); - void AddImageToDataStorage(itk::SmartPointer image); - - template - void ITKPixelManipulation(itk::Image *originalImage, - Image *maskImage, - Image *newImage, - int newValue); - - mitk::DataNode::Pointer m_OriginalImageNode; - int m_Value; - bool m_FixedValue; - }; // clas -} // namespace -#endif diff --git a/Modules/Segmentation/Interactions/mitkPixelManipulationTool.xpm b/Modules/Segmentation/Interactions/mitkPixelManipulationTool.xpm deleted file mode 100644 index daafedc1be..0000000000 --- a/Modules/Segmentation/Interactions/mitkPixelManipulationTool.xpm +++ /dev/null @@ -1,31 +0,0 @@ -/* XPM */ -static const char * mitkPixelManipulationTool_xpm[] = { -"20 20 8 1", -" c None", -". c #2E3436", -"+ c #EEEEEC", -"@ c #EFEFEF", -"# c #F7F3F7", -"$ c #F7F7F7", -"% c #FFFBFF", -"& c #FFFFFF", -". . . . . . . . . . ", -" .", -". . . . . ", -" .. .. .", -". ... ... ", -" .", -". ........ ", -" .++++++. .", -". .+@@@##. ", -" .+@###$. .", -". .+##$$%. ", -" .+#$$%%. .", -". .+$$%%&. ", -" ........ .", -". ", -" ... ... .", -". .. .. ", -" . . . . .", -". ", -" . . . . . . . . . ."}; \ No newline at end of file diff --git a/Modules/Segmentation/Resources/Interactions/ContourModelModificationConfig.xml b/Modules/Segmentation/Resources/Interactions/ContourModelModificationConfig.xml index da40ab89b3..fd6317e057 100644 --- a/Modules/Segmentation/Resources/Interactions/ContourModelModificationConfig.xml +++ b/Modules/Segmentation/Resources/Interactions/ContourModelModificationConfig.xml @@ -1,15 +1,22 @@ + + + + + + + diff --git a/Modules/Segmentation/Resources/Interactions/ContourModelModificationInteractor.xml b/Modules/Segmentation/Resources/Interactions/ContourModelModificationInteractor.xml index 32a7574791..4963143426 100644 --- a/Modules/Segmentation/Resources/Interactions/ContourModelModificationInteractor.xml +++ b/Modules/Segmentation/Resources/Interactions/ContourModelModificationInteractor.xml @@ -1,22 +1,28 @@ + + + + + + diff --git a/Modules/Segmentation/Resources/Interactions/LiveWireTool.xml b/Modules/Segmentation/Resources/Interactions/EditableContourTool.xml similarity index 55% rename from Modules/Segmentation/Resources/Interactions/LiveWireTool.xml rename to Modules/Segmentation/Resources/Interactions/EditableContourTool.xml index 27d02bc15d..c9ba8da33f 100644 --- a/Modules/Segmentation/Resources/Interactions/LiveWireTool.xml +++ b/Modules/Segmentation/Resources/Interactions/EditableContourTool.xml @@ -1,30 +1,42 @@ - + - + - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/Modules/Segmentation/Resources/NewAdd_48x48.png b/Modules/Segmentation/Resources/NewAdd_48x48.png new file mode 100644 index 0000000000..3336c07b3c Binary files /dev/null and b/Modules/Segmentation/Resources/NewAdd_48x48.png differ diff --git a/Modules/Segmentation/Resources/NewAdd_Cursor_32x32.png b/Modules/Segmentation/Resources/NewAdd_Cursor_32x32.png new file mode 100644 index 0000000000..a22b98f416 Binary files /dev/null and b/Modules/Segmentation/Resources/NewAdd_Cursor_32x32.png differ diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake index c35539f7fe..93b40ede21 100644 --- a/Modules/Segmentation/files.cmake +++ b/Modules/Segmentation/files.cmake @@ -1,108 +1,108 @@ set(CPP_FILES Algorithms/mitkCalculateSegmentationVolume.cpp Algorithms/mitkContourModelSetToImageFilter.cpp Algorithms/mitkContourSetToPointSetFilter.cpp Algorithms/mitkContourUtils.cpp Algorithms/mitkCorrectorAlgorithm.cpp Algorithms/mitkDiffImageApplier.cpp Algorithms/mitkDiffSliceOperation.cpp Algorithms/mitkDiffSliceOperationApplier.cpp Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp Algorithms/mitkImageLiveWireContourModelFilter.cpp Algorithms/mitkImageToContourFilter.cpp #Algorithms/mitkImageToContourModelFilter.cpp Algorithms/mitkImageToLiveWireContourFilter.cpp Algorithms/mitkManualSegmentationToSurfaceFilter.cpp Algorithms/mitkOtsuSegmentationFilter.cpp Algorithms/mitkSegmentationHelper.cpp Algorithms/mitkSegmentationObjectFactory.cpp Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp Algorithms/mitkShowSegmentationAsSurface.cpp Algorithms/mitkVtkImageOverwrite.cpp Controllers/mitkSegmentationInterpolationController.cpp Controllers/mitkToolManager.cpp Controllers/mitkSegmentationModuleActivator.cpp Controllers/mitkToolManagerProvider.cpp DataManagement/mitkContour.cpp DataManagement/mitkContourSet.cpp DataManagement/mitkExtrudedContour.cpp Interactions/mitkAddContourTool.cpp Interactions/mitkAutoCropTool.cpp Interactions/mitkSegWithPreviewTool.cpp Interactions/mitkBinaryThresholdBaseTool.cpp Interactions/mitkBinaryThresholdTool.cpp Interactions/mitkBinaryThresholdULTool.cpp - Interactions/mitkCalculateGrayValueStatisticsTool.cpp - Interactions/mitkCalculateVolumetryTool.cpp Interactions/mitkContourModelInteractor.cpp Interactions/mitkContourModelLiveWireInteractor.cpp + Interactions/mitkEditableContourTool.cpp Interactions/mitkLiveWireTool2D.cpp + Interactions/mitkLassoTool.cpp Interactions/mitkContourTool.cpp - Interactions/mitkCreateSurfaceTool.cpp Interactions/mitkDrawPaintbrushTool.cpp Interactions/mitkErasePaintbrushTool.cpp Interactions/mitkEraseRegionTool.cpp Interactions/mitkFeedbackContourTool.cpp Interactions/mitkFillRegionTool.cpp Interactions/mitkOtsuTool3D.cpp Interactions/mitkPaintbrushTool.cpp - Interactions/mitkPixelManipulationTool.cpp Interactions/mitkRegionGrowingTool.cpp Interactions/mitkSegmentationsProcessingTool.cpp Interactions/mitkSetRegionTool.cpp Interactions/mitkSegTool2D.cpp Interactions/mitkSubtractContourTool.cpp Interactions/mitkTool.cpp Interactions/mitkToolCommand.cpp Interactions/mitkPickingTool.cpp Interactions/mitknnUnetTool.cpp Interactions/mitkSegmentationInteractor.cpp #SO Interactions/mitkProcessExecutor.cpp Rendering/mitkContourMapper2D.cpp Rendering/mitkContourSetMapper2D.cpp Rendering/mitkContourSetVtkMapper3D.cpp Rendering/mitkContourVtkMapper3D.cpp SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp #Added from ML Controllers/mitkSliceBasedInterpolationController.cpp Algorithms/mitkSurfaceStampImageFilter.cpp ) set(RESOURCE_FILES Add_48x48.png Add_Cursor_32x32.png AI_48x48.png AI_Cursor_32x32.png Erase_48x48.png Erase_Cursor_32x32.png Fill_48x48.png Fill_Cursor_32x32.png LiveWire_48x48.png LiveWire_Cursor_32x32.png + NewAdd_48x48.png + NewAdd_Cursor_32x32.png Otsu_48x48.png Paint_48x48.png Paint_Cursor_32x32.png Pick_48x48.png RegionGrowing_48x48.png RegionGrowing_Cursor_32x32.png Subtract_48x48.png Subtract_Cursor_32x32.png Threshold_48x48.png TwoThresholds_48x48.png Wipe_48x48.png Wipe_Cursor_32x32.png Interactions/dummy.xml - Interactions/LiveWireTool.xml + Interactions/EditableContourTool.xml Interactions/PickingTool.xml Interactions/PressMoveRelease.xml Interactions/PressMoveReleaseAndPointSetting.xml Interactions/PressMoveReleaseWithCTRLInversion.xml Interactions/PressMoveReleaseWithCTRLInversionAllMouseMoves.xml Interactions/SegmentationToolsConfig.xml Interactions/ContourModelModificationConfig.xml Interactions/ContourModelModificationInteractor.xml ) diff --git a/Modules/SegmentationUI/Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.cpp deleted file mode 100644 index 81803dae4e..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "QmitkCalculateGrayValueStatisticsToolGUI.h" - -#include "QmitkCopyToClipBoardDialog.h" - -MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkCalculateGrayValueStatisticsToolGUI, "") - -QmitkCalculateGrayValueStatisticsToolGUI::QmitkCalculateGrayValueStatisticsToolGUI() : QmitkToolGUI() -{ - connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); -} - -QmitkCalculateGrayValueStatisticsToolGUI::~QmitkCalculateGrayValueStatisticsToolGUI() -{ - if (m_CalculateGrayValueStatisticsTool.IsNotNull()) - { - m_CalculateGrayValueStatisticsTool->StatisticsCompleted -= - mitk::MessageDelegate( - this, &QmitkCalculateGrayValueStatisticsToolGUI::OnCalculationsDone); - } -} - -void QmitkCalculateGrayValueStatisticsToolGUI::OnNewToolAssociated(mitk::Tool *tool) -{ - if (m_CalculateGrayValueStatisticsTool.IsNotNull()) - { - m_CalculateGrayValueStatisticsTool->StatisticsCompleted -= - mitk::MessageDelegate( - this, &QmitkCalculateGrayValueStatisticsToolGUI::OnCalculationsDone); - } - - m_CalculateGrayValueStatisticsTool = dynamic_cast(tool); - - if (m_CalculateGrayValueStatisticsTool.IsNotNull()) - { - m_CalculateGrayValueStatisticsTool->StatisticsCompleted += - mitk::MessageDelegate( - this, &QmitkCalculateGrayValueStatisticsToolGUI::OnCalculationsDone); - } -} - -void QmitkCalculateGrayValueStatisticsToolGUI::OnCalculationsDone() -{ - if (m_CalculateGrayValueStatisticsTool.IsNotNull()) - { - std::string report = m_CalculateGrayValueStatisticsTool->GetReport(); - - // one for linux users - std::cout << report << std::endl; - - // one for window users - QmitkCopyToClipBoardDialog *dialog = new QmitkCopyToClipBoardDialog(report.c_str(), nullptr); - dialog->show(); - } -} diff --git a/Modules/SegmentationUI/Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.h deleted file mode 100644 index f038f7a275..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.h +++ /dev/null @@ -1,58 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#ifndef QmitkCalculateGrayValueStatisticsToolGUI_h_Included -#define QmitkCalculateGrayValueStatisticsToolGUI_h_Included - -#include "QmitkToolGUI.h" -#include - -#include "mitkCalculateGrayValueStatisticsTool.h" - -/** - \ingroup org_mitk_gui_qt_interactivesegmentation_internal - \brief GUI for mitk::CalculateGrayValueStatisticsTool. - - Shows nothing. Only when the corresponding tool send a message that statistics are ready, this class pops up a window - showing the results. - - Last contributor: $Author$ -*/ -class MITKSEGMENTATIONUI_EXPORT QmitkCalculateGrayValueStatisticsToolGUI : public QmitkToolGUI -{ - Q_OBJECT - -public: - mitkClassMacro(QmitkCalculateGrayValueStatisticsToolGUI, QmitkToolGUI); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); - - /// Reacts to signals from mitk::CalculateGrayValueStatisticsTool - void OnCalculationsDone(); - -signals: - -public slots: - -protected slots: - - /// Connected to signal from QmitkToolGUI. We remember the current tool here - void OnNewToolAssociated(mitk::Tool *); - -protected: - QmitkCalculateGrayValueStatisticsToolGUI(); - ~QmitkCalculateGrayValueStatisticsToolGUI() override; - - mitk::CalculateGrayValueStatisticsTool::Pointer m_CalculateGrayValueStatisticsTool; -}; - -#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkEditableContourToolGUIBase.cpp b/Modules/SegmentationUI/Qmitk/QmitkEditableContourToolGUIBase.cpp new file mode 100644 index 0000000000..f0cb874239 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkEditableContourToolGUIBase.cpp @@ -0,0 +1,100 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "QmitkEditableContourToolGUIBase.h" + +QmitkEditableContourToolGUIBase::QmitkEditableContourToolGUIBase() : QmitkToolGUI() +{ + m_Controls.setupUi(this); + m_Controls.m_Information->hide(); + m_Controls.m_AutoCheck->setChecked(true); + m_Controls.m_ConfirmButton->hide(); + m_Controls.m_AddMode->setChecked(true); + m_Controls.m_SubstractMode->hide(); + m_Controls.m_AddMode->hide(); + + m_Controls.m_ClearButton->hide(); + + connect(m_Controls.m_ConfirmButton, SIGNAL(clicked()), this, SLOT(OnConfirmSegmentation())); + connect(m_Controls.m_ClearButton, SIGNAL(clicked()), this, SLOT(OnClearSegmentation())); + connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); + connect(m_Controls.m_InformationCheckBox, SIGNAL(toggled(bool)), this, SLOT(OnShowInformation(bool))); + connect(m_Controls.m_AutoCheck, SIGNAL(toggled(bool)), this, SLOT(OnAutoConfirm(bool))); + connect(m_Controls.m_AddMode, SIGNAL(toggled(bool)), this, SLOT(OnAddModeToogled(bool))); +} + +QmitkEditableContourToolGUIBase::~QmitkEditableContourToolGUIBase() +{ +} + +void QmitkEditableContourToolGUIBase::OnNewToolAssociated(mitk::Tool *tool) +{ + m_NewTool = dynamic_cast(tool); + if (m_NewTool.IsNull()) + { + mitkThrow() << "Tool is in an invalid state. QmitkEditableContourToolGUIBase needs tools based on EditableContourTool."; + } + + const auto autoConfirm = m_NewTool->GetAutoConfirm(); + m_Controls.m_AutoCheck->setChecked(autoConfirm); + const auto addMode = m_NewTool->GetAddMode(); + m_Controls.m_AddMode->setChecked(addMode); + this->OnAutoConfirm(autoConfirm); +} + +void QmitkEditableContourToolGUIBase::OnConfirmSegmentation() +{ + if (m_NewTool.IsNotNull()) + { + m_NewTool->ConfirmSegmentation(); + } +} + +void QmitkEditableContourToolGUIBase::OnClearSegmentation() +{ + if (m_NewTool.IsNotNull()) + m_NewTool->ClearSegmentation(); +} + +void QmitkEditableContourToolGUIBase::OnShowInformation(bool on) +{ + m_Controls.m_Information->setVisible(on); +} + +void QmitkEditableContourToolGUIBase::OnAutoConfirm(bool on) +{ + m_Controls.m_ConfirmButton->setVisible(!on); + m_Controls.m_ClearButton->setVisible(!on); + m_Controls.m_AddMode->setVisible(!on); + if (on) + { + m_Controls.m_AddMode->setChecked(true); + } + m_Controls.m_SubstractMode->setVisible(!on); + + if (m_NewTool.IsNotNull()) + { + if (on && m_NewTool->IsEditingContour()) + { + this->OnConfirmSegmentation(); + } + m_NewTool->SetAutoConfirm(on); + } +} + +void QmitkEditableContourToolGUIBase::OnAddModeToogled(bool on) +{ + if (m_NewTool.IsNotNull()) + { + m_NewTool->SetAddMode(on); + } +} diff --git a/Modules/SegmentationUI/Qmitk/QmitkEditableContourToolGUIBase.h b/Modules/SegmentationUI/Qmitk/QmitkEditableContourToolGUIBase.h new file mode 100644 index 0000000000..8343107799 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkEditableContourToolGUIBase.h @@ -0,0 +1,59 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef QmitkEditableContourToolGUIBase_h_Included +#define QmitkEditableContourToolGUIBase_h_Included + +#include "QmitkToolGUI.h" +#include "mitkEditableContourTool.h" +#include "ui_QmitkEditableContourToolGUIControls.h" +#include + +class QmitkEditableContourToolGUIBaseControls; + +/** +\ingroup org_mitk_gui_qt_interactivesegmentation_internal +\brief GUI for mitk::EditableContourTool based classes. +\sa mitk::LassoTool +*/ +class MITKSEGMENTATIONUI_EXPORT QmitkEditableContourToolGUIBase : public QmitkToolGUI +{ + Q_OBJECT + +public: + mitkClassMacro(QmitkEditableContourToolGUIBase, QmitkToolGUI); + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); + +protected slots : + + void OnNewToolAssociated(mitk::Tool *); + + void OnConfirmSegmentation(); + + void OnClearSegmentation(); + + void OnAutoConfirm(bool on); + void OnAddModeToogled(bool on); + + void OnShowInformation(bool on); + +protected: + QmitkEditableContourToolGUIBase(); + ~QmitkEditableContourToolGUIBase() override; + + Ui::QmitkEditableContourToolGUIControls m_Controls; + + mitk::EditableContourTool::Pointer m_NewTool; +}; + +#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkEditableContourToolGUIControls.ui similarity index 63% rename from Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUIControls.ui rename to Modules/SegmentationUI/Qmitk/QmitkEditableContourToolGUIControls.ui index c093e3b61d..4cb2af9f25 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUIControls.ui +++ b/Modules/SegmentationUI/Qmitk/QmitkEditableContourToolGUIControls.ui @@ -1,131 +1,159 @@ - QmitkLiveWireTool2DGUIControls - + QmitkEditableContourToolGUIControls + 0 0 421 - 355 + 429 0 0 0 0 QmitkLiveWireTool2DGUIControls Confirm all previous contour. 0 0 0 0 + + + + Auto confirm contour + + + true + + + + + + + The contour will be added as segmentation for the active label. + + + Add mode + + + true + + + + + + + If confirmed the contour will be added as background, thus it will be substracted from the active label. + + + Substract mode + + + Confirm Segmentation Clear Segmentation Qt::Vertical QSizePolicy::Fixed 20 15 - - - - Snap closing contour - - - Show usage information 0 150 12 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">The tool is started by a double-click near the object of interest.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">You can fix preview segments by adding anchor points with a single left mouse button click. Pressing &quot;Delete&quot; will remove the last segment. To close a contour and finish current segmentation perform a double click on the first anchor point.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">You can fix preview segments by adding ancor points with a single left mouse button click. When you keep the mouse button pressed, you can manually draw contours (freehand contours) by moving the mouse. To close a contour and finish current segmentation perform a double click.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">If &quot;Auto confirm&quot; is active and the segmentation is finished, the preview will be directly added as segmentation. From there you are free to start a new contour (double click).</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">After finishing a segmentation the contour will be still editable. You can move, insert and delete its anchor points.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">If &quot;Auto confirm&quot; is not active, the contour will be still editable after finishing the segmentation. You can move, insert (CTRL+click on the contour segment) and delete (press DEL while ancor is selected) its ancor points. To confirm the segmentation, push the &quot;Confirm Segmentation&quot; button. Please be aware that freehand contour parts are treated differently: You cannot add an ancor point there, also you are not allowed to select/move/delete an ancor point that is at the beginning or end of a freehand contour part. </span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">Push the &quot;Confirm Segmentation&quot; button or deactivate the Live Wire Tool to fill all contours in the working image. Push the &quot;Clear Segmentation&quot; button to delete all unconfirmed contours.</span></p></body></html> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">Push the &quot;Clear Segmentation&quot; button to delete all unconfirmed contours.</span></p></body></html> diff --git a/Modules/Core/include/mitkFileSystem.h b/Modules/SegmentationUI/Qmitk/QmitkLassoToolGUI.cpp similarity index 57% rename from Modules/Core/include/mitkFileSystem.h rename to Modules/SegmentationUI/Qmitk/QmitkLassoToolGUI.cpp index b23d69df0a..5f468b83eb 100644 --- a/Modules/Core/include/mitkFileSystem.h +++ b/Modules/SegmentationUI/Qmitk/QmitkLassoToolGUI.cpp @@ -1,24 +1,24 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#ifndef mitkFileSystem_h -#define mitkFileSystem_h - -#if __has_include() - #include - namespace fs = std::filesystem; -#elif __has_include() - #include - namespace fs = std::experimental::filesystem; -#endif - -#endif +#include "QmitkLassoToolGUI.h" + + +MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkLassoToolGUI, "") + +QmitkLassoToolGUI::QmitkLassoToolGUI() : QmitkEditableContourToolGUIBase() +{ +} + +QmitkLassoToolGUI::~QmitkLassoToolGUI() +{ +} diff --git a/Modules/SegmentationUI/Qmitk/QmitkLassoToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkLassoToolGUI.h new file mode 100644 index 0000000000..71a92bf7d7 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkLassoToolGUI.h @@ -0,0 +1,38 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef QmitkLassoToolGUI_h_Included +#define QmitkLassoToolGUI_h_Included + +#include "QmitkEditableContourToolGUIBase.h" +#include + +/** +\ingroup org_mitk_gui_qt_interactivesegmentation_internal +\brief GUI for mitk::NewAddTool. +\sa mitk::LassoTool +*/ +class MITKSEGMENTATIONUI_EXPORT QmitkLassoToolGUI : public QmitkEditableContourToolGUIBase +{ + Q_OBJECT + +public: + mitkClassMacro(QmitkLassoToolGUI, QmitkEditableContourToolGUIBase); + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); + +protected: + QmitkLassoToolGUI(); + ~QmitkLassoToolGUI() override; +}; + +#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUI.cpp index 19887dc125..4466e7530a 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUI.cpp @@ -1,66 +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 "QmitkLiveWireTool2DGUI.h" -#include "mitkBaseRenderer.h" -#include "mitkStepper.h" -#include -#include -#include -#include -#include - MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkLiveWireTool2DGUI, "") -QmitkLiveWireTool2DGUI::QmitkLiveWireTool2DGUI() : QmitkToolGUI() +QmitkLiveWireTool2DGUI::QmitkLiveWireTool2DGUI() : QmitkEditableContourToolGUIBase() { - m_Controls.setupUi(this); - m_Controls.m_Information->hide(); - - connect(m_Controls.m_ConfirmButton, SIGNAL(clicked()), this, SLOT(OnConfirmSegmentation())); - connect(m_Controls.m_ClearButton, SIGNAL(clicked()), this, SLOT(OnClearSegmentation())); - connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); - connect(m_Controls.m_SnapClosureCheckBox, SIGNAL(toggled(bool)), this, SLOT(OnSnapClosureContour(bool))); - connect(m_Controls.m_InformationCheckBox, SIGNAL(toggled(bool)), this, SLOT(OnShowInformation(bool))); } QmitkLiveWireTool2DGUI::~QmitkLiveWireTool2DGUI() { } - -void QmitkLiveWireTool2DGUI::OnNewToolAssociated(mitk::Tool *tool) -{ - m_LiveWireTool = dynamic_cast(tool); -} - -void QmitkLiveWireTool2DGUI::OnConfirmSegmentation() -{ - if (m_LiveWireTool.IsNotNull()) - m_LiveWireTool->ConfirmSegmentation(); -} - -void QmitkLiveWireTool2DGUI::OnClearSegmentation() -{ - if (m_LiveWireTool.IsNotNull()) - m_LiveWireTool->ClearSegmentation(); -} - -void QmitkLiveWireTool2DGUI::OnSnapClosureContour(bool snap) -{ - m_LiveWireTool->SetSnapClosureContour(snap); -} - -void QmitkLiveWireTool2DGUI::OnShowInformation(bool on) -{ - m_Controls.m_Information->setVisible(on); -} diff --git a/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUI.h b/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUI.h index 74cb48476f..8d23694707 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitkLiveWireTool2DGUI.h @@ -1,66 +1,38 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkLiveWireTool2DGUI_h_Included #define QmitkLiveWireTool2DGUI_h_Included -#include "QmitkToolGUI.h" -#include "mitkLiveWireTool2D.h" -#include "ui_QmitkLiveWireTool2DGUIControls.h" +#include "QmitkEditableContourToolGUIBase.h" #include -class QSlider; -class QLabel; -class QFrame; -class QPushButton; -#include - -#include "QmitkStepperAdapter.h" - -class QmitkLiveWireTool2DGUIControls; - /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief GUI for mitk::LiveWireTool. \sa mitk::LiveWireTool2D */ -class MITKSEGMENTATIONUI_EXPORT QmitkLiveWireTool2DGUI : public QmitkToolGUI +class MITKSEGMENTATIONUI_EXPORT QmitkLiveWireTool2DGUI : public QmitkEditableContourToolGUIBase { Q_OBJECT public: mitkClassMacro(QmitkLiveWireTool2DGUI, QmitkToolGUI); itkFactorylessNewMacro(Self); itkCloneMacro(Self); - protected slots : - - void OnNewToolAssociated(mitk::Tool *); - - void OnConfirmSegmentation(); - - void OnClearSegmentation(); - - void OnSnapClosureContour(bool snap); - - void OnShowInformation(bool on); - protected: QmitkLiveWireTool2DGUI(); ~QmitkLiveWireTool2DGUI() override; - - Ui::QmitkLiveWireTool2DGUIControls m_Controls; - - mitk::LiveWireTool2D::Pointer m_LiveWireTool; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkPixelManipulationToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkPixelManipulationToolGUI.cpp deleted file mode 100644 index 7d2a831a20..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkPixelManipulationToolGUI.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ -#include "QmitkPixelManipulationToolGUI.h" - -#include -#include -#include - -MITK_TOOL_GUI_MACRO(, QmitkPixelManipulationToolGUI, ""); - -QmitkPixelManipulationToolGUI::QmitkPixelManipulationToolGUI() : QmitkToolGUI() -{ - QBoxLayout *mainLayout = new QVBoxLayout(this); - QRadioButton *radio1 = new QRadioButton("change the masked pixels by value: ", this); - QRadioButton *radio2 = new QRadioButton("set the masked pixels to value: ", this); - - radio1->setChecked(true); - - connect(radio1, SIGNAL(toggled(bool)), this, SLOT(SetFixedValueOff(bool))); - connect(radio2, SIGNAL(toggled(bool)), this, SLOT(SetFixedValueOn(bool))); - - mainLayout->addWidget(radio1); - mainLayout->addWidget(radio2); - - m_Slider = new QSlider(Qt::Horizontal, this); - m_Slider->setRange(-5000, 5000); - m_Slider->setValue(0); - - connect(m_Slider, SIGNAL(valueChanged(int)), this, SLOT(OnSliderValueChanged(int))); - - m_Spinner = new QSpinBox(this); - m_Spinner->setRange(-5000, 5000); - m_Spinner->setValue(0); - - connect(m_Spinner, SIGNAL(editingFinished()), this, SLOT(OnSpinBoxChanged())); - - QBoxLayout *layout1 = new QHBoxLayout(); - layout1->addWidget(m_Slider); - layout1->addWidget(m_Spinner); - - QPushButton *okButton = new QPushButton("Ok"); - - connect(okButton, SIGNAL(clicked()), this, SLOT(OnOkButtonClicked())); - - mainLayout->addLayout(layout1); - mainLayout->addWidget(okButton); - - connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); -} - -QmitkPixelManipulationToolGUI::~QmitkPixelManipulationToolGUI() -{ - if (m_PixelManipulationTool.IsNotNull()) - { - } -} - -void QmitkPixelManipulationToolGUI::OnNewToolAssociated(mitk::Tool *tool) -{ - if (m_PixelManipulationTool.IsNotNull()) - { - } - m_PixelManipulationTool = dynamic_cast(tool); - if (m_PixelManipulationTool.IsNotNull()) - { - } -} - -void QmitkPixelManipulationToolGUI::SetFixedValueOff(bool flag) -{ - if (flag) - { - if (m_PixelManipulationTool.IsNotNull()) - m_PixelManipulationTool->SetFixedValue(false); - } -} - -void QmitkPixelManipulationToolGUI::SetFixedValueOn(bool flag) -{ - if (flag) - { - if (m_PixelManipulationTool.IsNotNull()) - m_PixelManipulationTool->SetFixedValue(true); - } -} - -void QmitkPixelManipulationToolGUI::OnSpinBoxChanged() -{ - m_Slider->setValue(m_Spinner->value()); -} - -void QmitkPixelManipulationToolGUI::OnSliderValueChanged(int value) -{ - if (m_PixelManipulationTool.IsNotNull()) - { - m_PixelManipulationTool->SetValue(value); - m_Spinner->setValue(value); - } -} - -void QmitkPixelManipulationToolGUI::OnOkButtonClicked() -{ - if (m_PixelManipulationTool.IsNotNull()) - { - m_PixelManipulationTool->CalculateImage(); - } -} diff --git a/Modules/SegmentationUI/Qmitk/QmitkPixelManipulationToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkPixelManipulationToolGUI.h deleted file mode 100644 index e19705892a..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkPixelManipulationToolGUI.h +++ /dev/null @@ -1,47 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ -#ifndef QMITKPIXELMANIPULATIONTOOLGUI_H -#define QMITKPIXELMANIPULATIONTOOLGUI_H - -#include "QmitkDataStorageComboBox.h" -#include "QmitkToolGUI.h" -#include "mitkPixelManipulationTool.h" -#include -#include - -class QmitkPixelManipulationToolGUI : public QmitkToolGUI -{ - Q_OBJECT -public: - mitkClassMacro(QmitkPixelManipulationToolGUI, QmitkToolGUI); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); - - protected slots : - - void OnNewToolAssociated(mitk::Tool *); - void OnSliderValueChanged(int); - void OnSpinBoxChanged(); - void OnOkButtonClicked(); - void SetFixedValueOn(bool); - void SetFixedValueOff(bool); - -protected: - QmitkPixelManipulationToolGUI(); - ~QmitkPixelManipulationToolGUI() override; - - mitk::PixelManipulationTool::Pointer m_PixelManipulationTool; - QSlider *m_Slider; - QSpinBox *m_Spinner; - -}; // class -#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.cpp deleted file mode 100644 index e0f5f84b4d..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.cpp +++ /dev/null @@ -1,708 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "QmitkSliceBasedInterpolatorWidget.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -QmitkSliceBasedInterpolatorWidget::QmitkSliceBasedInterpolatorWidget(QWidget *parent, const char * /*name*/) - : QWidget(parent), - m_SliceInterpolatorController(mitk::SliceBasedInterpolationController::New()), - m_ToolManager(nullptr), - m_Activated(false), - m_DataStorage(nullptr), - m_LastSNC(nullptr), - m_LastSliceIndex(0) -{ - m_Controls.setupUi(this); - - m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); - - m_ToolManager->WorkingDataChanged += mitk::MessageDelegate( - this, &QmitkSliceBasedInterpolatorWidget::OnToolManagerWorkingDataModified); - - connect(m_Controls.m_btStart, SIGNAL(toggled(bool)), this, SLOT(OnToggleWidgetActivation(bool))); - connect(m_Controls.m_btApplyForCurrentSlice, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked())); - connect(m_Controls.m_btApplyForAllSlices, SIGNAL(clicked()), this, SLOT(OnAcceptAllInterpolationsClicked())); - - itk::ReceptorMemberCommand::Pointer command = - itk::ReceptorMemberCommand::New(); - command->SetCallbackFunction(this, &QmitkSliceBasedInterpolatorWidget::OnSliceInterpolationInfoChanged); - m_InterpolationInfoChangedObserverTag = m_SliceInterpolatorController->AddObserver(itk::ModifiedEvent(), command); - - // feedback node and its visualization properties - m_PreviewNode = mitk::DataNode::New(); - m_PreviewNode->SetName("3D tool preview"); - - m_PreviewNode->SetProperty("texture interpolation", mitk::BoolProperty::New(false)); - m_PreviewNode->SetProperty("layer", mitk::IntProperty::New(100)); - m_PreviewNode->SetProperty("binary", mitk::BoolProperty::New(true)); - m_PreviewNode->SetProperty("outline binary", mitk::BoolProperty::New(true)); - m_PreviewNode->SetProperty("outline binary shadow", mitk::BoolProperty::New(true)); - m_PreviewNode->SetProperty("helper object", mitk::BoolProperty::New(true)); - m_PreviewNode->SetOpacity(1.0); - m_PreviewNode->SetColor(0.0, 1.0, 0.0); - - m_Controls.m_btApplyForCurrentSlice->setEnabled(false); - m_Controls.m_btApplyForAllSlices->setEnabled(false); - - this->setEnabled(false); -} - -QmitkSliceBasedInterpolatorWidget::~QmitkSliceBasedInterpolatorWidget() -{ - m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate( - this, &QmitkSliceBasedInterpolatorWidget::OnToolManagerWorkingDataModified); - - foreach (mitk::SliceNavigationController *slicer, m_ControllerToSliceObserverTag.keys()) - { - slicer->RemoveObserver(m_ControllerToDeleteObserverTag.take(slicer)); - slicer->RemoveObserver(m_ControllerToTimeObserverTag.take(slicer)); - slicer->RemoveObserver(m_ControllerToSliceObserverTag.take(slicer)); - } - - m_ActionToSliceDimensionMap.clear(); - - // remove observer - m_SliceInterpolatorController->RemoveObserver(m_InterpolationInfoChangedObserverTag); -} - -const QmitkSliceBasedInterpolatorWidget::ActionToSliceDimensionMapType - QmitkSliceBasedInterpolatorWidget::CreateActionToSliceDimension() -{ - ActionToSliceDimensionMapType actionToSliceDimension; - foreach (mitk::SliceNavigationController *slicer, m_ControllerToDeleteObserverTag.keys()) - { - std::string name = slicer->GetRenderer()->GetName(); - if (name == "stdmulti.widget0") - name = "Axial (red window)"; - else if (name == "stdmulti.widget1") - name = "Sagittal (green window)"; - else if (name == "stdmulti.widget2") - name = "Coronal (blue window)"; - actionToSliceDimension[new QAction(QString::fromStdString(name), nullptr)] = slicer; - } - - return actionToSliceDimension; -} - -void QmitkSliceBasedInterpolatorWidget::SetDataStorage(mitk::DataStorage &storage) -{ - m_DataStorage = &storage; -} - -void QmitkSliceBasedInterpolatorWidget::SetSliceNavigationControllers( - const QList &controllers) -{ - Q_ASSERT(!controllers.empty()); - - // connect to the slice navigation controller. after each change, call the interpolator - foreach (mitk::SliceNavigationController *slicer, controllers) - { - // Has to be initialized - m_LastSNC = slicer; - - m_TimePoints.insert(slicer, slicer->GetSelectedTimePoint()); - - itk::MemberCommand::Pointer deleteCommand = - itk::MemberCommand::New(); - deleteCommand->SetCallbackFunction(this, &QmitkSliceBasedInterpolatorWidget::OnSliceNavigationControllerDeleted); - m_ControllerToDeleteObserverTag.insert(slicer, slicer->AddObserver(itk::DeleteEvent(), deleteCommand)); - - itk::MemberCommand::Pointer timeChangedCommand = - itk::MemberCommand::New(); - timeChangedCommand->SetCallbackFunction(this, &QmitkSliceBasedInterpolatorWidget::OnTimeChanged); - m_ControllerToTimeObserverTag.insert( - slicer, - slicer->AddObserver(mitk::SliceNavigationController::TimeGeometryEvent(nullptr, 0), timeChangedCommand)); - - itk::MemberCommand::Pointer sliceChangedCommand = - itk::MemberCommand::New(); - sliceChangedCommand->SetCallbackFunction(this, &QmitkSliceBasedInterpolatorWidget::OnSliceChanged); - m_ControllerToSliceObserverTag.insert( - slicer, slicer->AddObserver(mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0), sliceChangedCommand)); - } - - m_ActionToSliceDimensionMap = this->CreateActionToSliceDimension(); -} - -void QmitkSliceBasedInterpolatorWidget::OnToolManagerWorkingDataModified() -{ - mitk::DataNode *workingNode = this->m_ToolManager->GetWorkingData(0); - if (!workingNode) - { - this->setEnabled(false); - return; - } - - mitk::LabelSetImage *workingImage = dynamic_cast(workingNode->GetData()); - // TODO adapt tool manager so that this check is done there, e.g. convenience function - // Q_ASSERT(workingImage); - if (!workingImage) - { - this->setEnabled(false); - return; - } - - if (workingImage->GetDimension() > 4 || workingImage->GetDimension() < 3) - { - this->setEnabled(false); - return; - } - - m_WorkingImage = workingImage; - - this->setEnabled(true); -} - -void QmitkSliceBasedInterpolatorWidget::OnTimeChanged(itk::Object *sender, const itk::EventObject &e) -{ - // Check if we really have a GeometryTimeEvent - if (!dynamic_cast(&e)) - return; - - mitk::SliceNavigationController *slicer = dynamic_cast(sender); - Q_ASSERT(slicer); - - m_TimePoints[slicer] = slicer->GetSelectedTimePoint(); - - // TODO Macht das hier wirklich Sinn???? - if (m_LastSNC == slicer) - { - slicer->SendSlice(); // will trigger a new interpolation - } -} - -void QmitkSliceBasedInterpolatorWidget::OnSliceChanged(itk::Object *sender, const itk::EventObject &e) -{ - if (m_Activated && m_WorkingImage.IsNotNull()) - { - // Check whether we really have a GeometrySliceEvent - if (!dynamic_cast(&e)) - return; - - mitk::SliceNavigationController *slicer = dynamic_cast(sender); - if (slicer) - { - this->TranslateAndInterpolateChangedSlice(e, slicer); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - // slicer->GetRenderer()->RequestUpdate(); - } - } -} - -void QmitkSliceBasedInterpolatorWidget::TranslateAndInterpolateChangedSlice(const itk::EventObject &e, - mitk::SliceNavigationController *slicer) -{ - if (m_Activated && m_WorkingImage.IsNotNull()) - { - const mitk::SliceNavigationController::GeometrySliceEvent &geometrySliceEvent = - dynamic_cast(e); - mitk::TimeGeometry *timeGeometry = geometrySliceEvent.GetTimeGeometry(); - if (timeGeometry && m_TimePoints.contains(slicer) && timeGeometry->IsValidTimePoint(m_TimePoints[slicer])) - { - mitk::SlicedGeometry3D *slicedGeometry = - dynamic_cast(timeGeometry->GetGeometryForTimePoint(m_TimePoints[slicer]).GetPointer()); - if (slicedGeometry) - { - mitk::PlaneGeometry *plane = slicedGeometry->GetPlaneGeometry(geometrySliceEvent.GetPos()); - if (plane) - { - m_LastSNC = slicer; - this->Interpolate(plane, m_TimePoints[slicer], slicer); - } - } - } - } -} - -void QmitkSliceBasedInterpolatorWidget::Interpolate(mitk::PlaneGeometry *plane, - mitk::TimePointType timePoint, - mitk::SliceNavigationController *slicer) -{ - int clickedSliceDimension(-1); - int clickedSliceIndex(-1); - - if (!m_WorkingImage->GetTimeGeometry()->IsValidTimePoint(timePoint)) - { - MITK_WARN << "Cannot interpolate WorkingImage. Passed time point is not within the time bounds of WorkingImage. Time point: " << timePoint; - return; - } - const auto timeStep = m_WorkingImage->GetTimeGeometry()->TimePointToTimeStep(timePoint); - - // calculate real slice position, i.e. slice of the image - // see if timestep is needed here - mitk::SegTool2D::DetermineAffectedImageSlice(m_WorkingImage, plane, clickedSliceDimension, clickedSliceIndex); - - mitk::Image::Pointer interpolation = - m_SliceInterpolatorController->Interpolate(clickedSliceDimension, clickedSliceIndex, plane, timeStep); - - m_PreviewNode->SetData(interpolation); - - const mitk::Color &color = m_WorkingImage->GetActiveLabel()->GetColor(); - m_PreviewNode->SetColor(color); - - m_LastSNC = slicer; - m_LastSliceIndex = clickedSliceIndex; -} - -mitk::Image::Pointer QmitkSliceBasedInterpolatorWidget::GetWorkingSlice(const mitk::PlaneGeometry *planeGeometry) -{ - const auto timePoint = m_LastSNC->GetSelectedTimePoint(); - - if (!m_WorkingImage->GetTimeGeometry()->IsValidTimePoint(timePoint)) - { - MITK_WARN << "Cannot get slice of WorkingImage. Time point selected by SliceNavigationController is not within the time bounds of WorkingImage. Time point: " << timePoint; - return nullptr; - } - - // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk - // reslicer - vtkSmartPointer reslice = vtkSmartPointer::New(); - // set to false to extract a slice - reslice->SetOverwriteMode(false); - reslice->Modified(); - - // use ExtractSliceFilter with our specific vtkImageReslice for overwriting and extracting - mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); - extractor->SetInput(m_WorkingImage); - const auto timeStep = m_WorkingImage->GetTimeGeometry()->TimePointToTimeStep(timePoint); - extractor->SetTimeStep(timeStep); - extractor->SetWorldGeometry(planeGeometry); - extractor->SetVtkOutputRequest(false); - extractor->SetResliceTransformByGeometry(m_WorkingImage->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)); - - extractor->Modified(); - - try - { - extractor->Update(); - } - catch (itk::ExceptionObject &excep) - { - MITK_ERROR << "Exception caught: " << excep.GetDescription(); - return nullptr; - } - - mitk::Image::Pointer slice = extractor->GetOutput(); - - // specify the undo operation with the non edited slice - // MLI TODO added code starts here - mitk::SlicedGeometry3D *sliceGeometry = dynamic_cast(slice->GetGeometry()); - // m_undoOperation = new mitk::DiffSliceOperation(m_WorkingImage, extractor->GetVtkOutput(), slice->GetGeometry(), - // timeStep, const_cast(planeGeometry)); - // added code ends here - m_undoOperation = new mitk::DiffSliceOperation( - m_WorkingImage, extractor->GetOutput(), sliceGeometry, timeStep, const_cast(planeGeometry)); - - slice->DisconnectPipeline(); - - return slice; -} - -void QmitkSliceBasedInterpolatorWidget::OnToggleWidgetActivation(bool enabled) -{ - Q_ASSERT(m_ToolManager); - - mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); - if (!workingNode) - return; - - m_Controls.m_btApplyForCurrentSlice->setEnabled(enabled); - m_Controls.m_btApplyForAllSlices->setEnabled(enabled); - - if (enabled) - m_Controls.m_btStart->setText("Stop"); - else - m_Controls.m_btStart->setText("Start"); - - unsigned int numberOfExistingTools = m_ToolManager->GetTools().size(); - for (unsigned int i = 0; i < numberOfExistingTools; i++) - { - // mitk::SegTool2D* tool = dynamic_cast(m_ToolManager->GetToolById(i)); - // MLI TODO - // if (tool) tool->SetEnable2DInterpolation( enabled ); - } - - if (enabled) - { - if (!m_DataStorage->Exists(m_PreviewNode)) - { - m_DataStorage->Add(m_PreviewNode); - } - - m_SliceInterpolatorController->SetWorkingImage(m_WorkingImage); - this->UpdateVisibleSuggestion(); - } - else - { - if (m_DataStorage->Exists(m_PreviewNode)) - { - m_DataStorage->Remove(m_PreviewNode); - } - - mitk::UndoController::GetCurrentUndoModel()->Clear(); - } - - m_Activated = enabled; - - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); -} - -template -void QmitkSliceBasedInterpolatorWidget::WritePreviewOnWorkingImage(itk::Image *targetSlice, - const mitk::Image *sourceSlice, - int overwritevalue) -{ - typedef itk::Image ImageType; - - typename ImageType::Pointer sourceSliceITK; - mitk::CastToItkImage(sourceSlice, sourceSliceITK); - - // now the original slice and the ipSegmentation-painted slice are in the same format, and we can just copy all pixels - // that are non-zero - typedef itk::ImageRegionIterator OutputIteratorType; - typedef itk::ImageRegionConstIterator InputIteratorType; - - InputIteratorType inputIterator(sourceSliceITK, sourceSliceITK->GetLargestPossibleRegion()); - OutputIteratorType outputIterator(targetSlice, targetSlice->GetLargestPossibleRegion()); - - outputIterator.GoToBegin(); - inputIterator.GoToBegin(); - - int activePixelValue = m_WorkingImage->GetActiveLabel()->GetValue(); - - if (activePixelValue == 0) // if exterior is the active label - { - while (!outputIterator.IsAtEnd()) - { - if (inputIterator.Get() != 0) - { - outputIterator.Set(overwritevalue); - } - ++outputIterator; - ++inputIterator; - } - } - else if (overwritevalue != 0) // if we are not erasing - { - while (!outputIterator.IsAtEnd()) - { - int targetValue = static_cast(outputIterator.Get()); - if (inputIterator.Get() != 0) - { - if (!m_WorkingImage->GetLabel(targetValue)->GetLocked()) - outputIterator.Set(overwritevalue); - } - - ++outputIterator; - ++inputIterator; - } - } - else // if we are erasing - { - while (!outputIterator.IsAtEnd()) - { - const int targetValue = outputIterator.Get(); - if (inputIterator.Get() != 0) - { - if (targetValue == activePixelValue) - outputIterator.Set(overwritevalue); - } - - ++outputIterator; - ++inputIterator; - } - } -} - -void QmitkSliceBasedInterpolatorWidget::OnAcceptInterpolationClicked() -{ - if (m_WorkingImage.IsNotNull() && m_PreviewNode->GetData()) - { - const mitk::PlaneGeometry *planeGeometry = m_LastSNC->GetCurrentPlaneGeometry(); - if (!planeGeometry) - return; - - mitk::Image::Pointer sliceImage = this->GetWorkingSlice(planeGeometry); - if (sliceImage.IsNull()) - return; - - mitk::Image::Pointer previewSlice = dynamic_cast(m_PreviewNode->GetData()); - - AccessFixedDimensionByItk_2( - sliceImage, WritePreviewOnWorkingImage, 2, previewSlice, m_WorkingImage->GetActiveLabel()->GetValue()); - - // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk - // reslicer - vtkSmartPointer overwrite = vtkSmartPointer::New(); - overwrite->SetInputSlice(sliceImage->GetVtkImageData()); - // set overwrite mode to true to write back to the image volume - overwrite->SetOverwriteMode(true); - overwrite->Modified(); - - const auto timePoint = m_LastSNC->GetSelectedTimePoint(); - if (!m_WorkingImage->GetTimeGeometry()->IsValidTimePoint(timePoint)) - { - MITK_WARN << "Cannot accept interpolation. Time point selected by SliceNavigationController is not within the time bounds of WorkingImage. Time point: " << timePoint; - return; - } - - mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(overwrite); - extractor->SetInput(m_WorkingImage); - const auto timeStep = m_WorkingImage->GetTimeGeometry()->TimePointToTimeStep(timePoint); - extractor->SetTimeStep(timeStep); - extractor->SetWorldGeometry(planeGeometry); - extractor->SetVtkOutputRequest(false); - extractor->SetResliceTransformByGeometry(m_WorkingImage->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)); - - extractor->Modified(); - - try - { - extractor->Update(); - } - catch (itk::ExceptionObject &excep) - { - MITK_ERROR << "Exception caught: " << excep.GetDescription(); - return; - } - - // the image was modified within the pipeline, but not marked so - m_WorkingImage->Modified(); - - int clickedSliceDimension(-1); - int clickedSliceIndex(-1); - - mitk::SegTool2D::DetermineAffectedImageSlice( - m_WorkingImage, planeGeometry, clickedSliceDimension, clickedSliceIndex); - - m_SliceInterpolatorController->SetChangedSlice(sliceImage, clickedSliceDimension, clickedSliceIndex, timeStep); - - // specify the undo operation with the edited slice - // MLI TODO added code starts here - mitk::SlicedGeometry3D *sliceGeometry = dynamic_cast(sliceImage->GetGeometry()); - // m_undoOperation = new mitk::DiffSliceOperation(m_WorkingImage, extractor->GetVtkOutput(), slice->GetGeometry(), - // timeStep, const_cast(planeGeometry)); - // added code ends here - m_doOperation = new mitk::DiffSliceOperation(m_WorkingImage, - extractor->GetOutput(), - sliceGeometry, - timeStep, - const_cast(planeGeometry)); - - // create an operation event for the undo stack - mitk::OperationEvent *undoStackItem = new mitk::OperationEvent( - mitk::DiffSliceOperationApplier::GetInstance(), m_doOperation, m_undoOperation, "Slice Interpolation"); - - // add it to the undo controller - mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent(undoStackItem); - - // clear the pointers as the operation are stored in the undo controller and also deleted from there - m_undoOperation = nullptr; - m_doOperation = nullptr; - - m_PreviewNode->SetData(nullptr); - - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - } -} - -void QmitkSliceBasedInterpolatorWidget::AcceptAllInterpolations(mitk::SliceNavigationController *slicer) -{ - // Since we need to shift the plane it must be clone so that the original plane isn't altered - mitk::PlaneGeometry::Pointer reslicePlane = slicer->GetCurrentPlaneGeometry()->Clone(); - const auto timePoint = slicer->GetSelectedTimePoint(); - if (!m_WorkingImage->GetTimeGeometry()->IsValidTimePoint(timePoint)) - { - MITK_WARN << "Cannot accept all interpolations. Time point selected by SliceNavigationController is not within the time bounds of WorkingImage. Time point: " << timePoint; - - return; - } - const auto timeStep = m_WorkingImage->GetTimeGeometry()->TimePointToTimeStep(timePoint); - - int sliceDimension(-1); - int sliceIndex(-1); - - mitk::SegTool2D::DetermineAffectedImageSlice(m_WorkingImage, reslicePlane, sliceDimension, sliceIndex); - - unsigned int zslices = m_WorkingImage->GetDimension(sliceDimension); - - mitk::ProgressBar::GetInstance()->Reset(); - mitk::ProgressBar::GetInstance()->AddStepsToDo(zslices); - - mitk::Point3D origin = reslicePlane->GetOrigin(); - - for (unsigned int idx = 0; idx < zslices; ++idx) - { - // Transforming the current origin of the reslice plane - // so that it matches the one of the next slice - m_WorkingImage->GetSlicedGeometry()->WorldToIndex(origin, origin); - origin[sliceDimension] = idx; - m_WorkingImage->GetSlicedGeometry()->IndexToWorld(origin, origin); - reslicePlane->SetOrigin(origin); - - mitk::Image::Pointer interpolation = - m_SliceInterpolatorController->Interpolate(sliceDimension, idx, reslicePlane, timeStep); - - if (interpolation.IsNotNull()) - { - m_PreviewNode->SetData(interpolation); - - mitk::Image::Pointer sliceImage = this->GetWorkingSlice(reslicePlane); - if (sliceImage.IsNull()) - return; - - AccessFixedDimensionByItk_2( - sliceImage, WritePreviewOnWorkingImage, 2, interpolation, m_WorkingImage->GetActiveLabel()->GetValue()); - - // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk - // reslicer - vtkSmartPointer overwrite = vtkSmartPointer::New(); - overwrite->SetInputSlice(sliceImage->GetVtkImageData()); - // set overwrite mode to true to write back to the image volume - overwrite->SetOverwriteMode(true); - overwrite->Modified(); - - mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(overwrite); - extractor->SetInput(m_WorkingImage); - extractor->SetTimeStep(timeStep); - extractor->SetWorldGeometry(reslicePlane); - extractor->SetVtkOutputRequest(true); - extractor->SetResliceTransformByGeometry(m_WorkingImage->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)); - - extractor->Modified(); - - try - { - extractor->Update(); - } - catch (itk::ExceptionObject &excep) - { - MITK_ERROR << "Exception caught: " << excep.GetDescription(); - return; - } - - m_WorkingImage->Modified(); - - mitk::RenderingManager::GetInstance()->RequestUpdateAll(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); - } - - mitk::ProgressBar::GetInstance()->Progress(); - } - - m_SliceInterpolatorController->SetWorkingImage(m_WorkingImage); - - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); -} - -void QmitkSliceBasedInterpolatorWidget::OnAcceptAllInterpolationsClicked() -{ - QMenu orientationPopup(this); - std::map::const_iterator it; - for (it = m_ActionToSliceDimensionMap.begin(); it != m_ActionToSliceDimensionMap.end(); it++) - orientationPopup.addAction(it->first); - - connect(&orientationPopup, SIGNAL(triggered(QAction *)), this, SLOT(OnAcceptAllPopupActivated(QAction *))); - - orientationPopup.exec(QCursor::pos()); -} - -void QmitkSliceBasedInterpolatorWidget::OnAcceptAllPopupActivated(QAction *action) -{ - ActionToSliceDimensionMapType::const_iterator iter = m_ActionToSliceDimensionMap.find(action); - if (iter != m_ActionToSliceDimensionMap.end()) - { - mitk::SliceNavigationController *slicer = iter->second; - this->AcceptAllInterpolations(slicer); - } -} - -void QmitkSliceBasedInterpolatorWidget::UpdateVisibleSuggestion() -{ - if (m_Activated && m_LastSNC) - { - // determine which one is the current view, try to do an initial interpolation - mitk::BaseRenderer *renderer = m_LastSNC->GetRenderer(); - if (renderer && renderer->GetMapperID() == mitk::BaseRenderer::Standard2D) - { - const mitk::TimeGeometry *timeGeometry = - dynamic_cast(renderer->GetWorldTimeGeometry()); - if (timeGeometry) - { - mitk::SliceNavigationController::GeometrySliceEvent event(const_cast(timeGeometry), - renderer->GetSlice()); - this->TranslateAndInterpolateChangedSlice(event, m_LastSNC); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - } - } - } -} - -void QmitkSliceBasedInterpolatorWidget::OnSliceInterpolationInfoChanged(const itk::EventObject & /*e*/) -{ - // something (e.g. undo) changed the interpolation info, we should refresh our display - this->UpdateVisibleSuggestion(); -} - -void QmitkSliceBasedInterpolatorWidget::OnSliceNavigationControllerDeleted(const itk::Object *sender, - const itk::EventObject & /*e*/) -{ - // Don't know how to avoid const_cast here?! - mitk::SliceNavigationController *slicer = - dynamic_cast(const_cast(sender)); - if (slicer) - { - m_ControllerToTimeObserverTag.remove(slicer); - m_ControllerToSliceObserverTag.remove(slicer); - m_ControllerToDeleteObserverTag.remove(slicer); - } -} - -void QmitkSliceBasedInterpolatorWidget::WaitCursorOn() -{ - QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); -} - -void QmitkSliceBasedInterpolatorWidget::WaitCursorOff() -{ - this->RestoreOverrideCursor(); -} - -void QmitkSliceBasedInterpolatorWidget::RestoreOverrideCursor() -{ - QApplication::restoreOverrideCursor(); -} diff --git a/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.h b/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.h deleted file mode 100644 index 86c6cdab0a..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.h +++ /dev/null @@ -1,195 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#ifndef QmitkSliceBasedInterpolatorWidget_h_Included -#define QmitkSliceBasedInterpolatorWidget_h_Included - -#include "MitkSegmentationUIExports.h" -#include "mitkDataStorage.h" -#include "mitkSliceBasedInterpolationController.h" - -#include - -#include - -#include "ui_QmitkSliceBasedInterpolatorWidgetGUIControls.h" - -namespace mitk -{ - class PlaneGeometry; - class SliceNavigationController; - class LabelSetImage; - class ToolManager; - class DiffSliceOperation; -} - -/** - \brief GUI for slices interpolation. - - \ingroup ToolManagerEtAl - \ingroup Widgets - - \sa QmitkInteractiveSegmentation - \sa mitk::SegmentationInterpolation - - While mitk::SegmentationInterpolation does the bookkeeping of interpolation - (keeping track of which slices contain how much segmentation) and the algorithmic work, - QmitkSliceBasedInterpolatorWidget is responsible to watch the GUI, to notice, which slice is currently - visible. It triggers generation of interpolation suggestions and also triggers acception of - suggestions. - - \todo show/hide feedback on demand - - Last contributor: $Author: maleike $ -*/ - -class MITKSEGMENTATIONUI_EXPORT QmitkSliceBasedInterpolatorWidget : public QWidget -{ - Q_OBJECT - -public: - QmitkSliceBasedInterpolatorWidget(QWidget *parent = nullptr, const char *name = nullptr); - ~QmitkSliceBasedInterpolatorWidget() override; - - void SetDataStorage(mitk::DataStorage &storage); - - /** - Sets the slice navigation controllers for getting slice changed events from the views. - */ - void SetSliceNavigationControllers(const QList &controllers); - - void OnToolManagerWorkingDataModified(); - - void OnTimeChanged(itk::Object *sender, const itk::EventObject &); - - void OnSliceChanged(itk::Object *sender, const itk::EventObject &); - - void OnSliceNavigationControllerDeleted(const itk::Object *sender, const itk::EventObject &); - - /** - Just public because it is called by itk::Commands. You should not need to call this. - */ - void OnSliceInterpolationInfoChanged(const itk::EventObject &); - - Ui::QmitkSliceBasedInterpolatorWidgetGUIControls m_Controls; - -signals: - - void signalSliceBasedInterpolationEnabled(bool); - -public slots: - - /** - \brief Reaction to "Start/Stop" button click - */ - void OnToggleWidgetActivation(bool); - -protected slots: - - /** - \brief Reaction to "Accept Current Slice" button click. - */ - void OnAcceptInterpolationClicked(); - - /* - \brief Reaction to "Accept All Slices" button click. - Opens popup to ask about which orientation should be interpolated - */ - void OnAcceptAllInterpolationsClicked(); - - /* - \brief Called from popup menu of OnAcceptAllInterpolationsClicked() - Will trigger interpolation for all slices in given orientation - */ - void OnAcceptAllPopupActivated(QAction *action); - -protected: - typedef std::map ActionToSliceDimensionMapType; - - const ActionToSliceDimensionMapType CreateActionToSliceDimension(); - - ActionToSliceDimensionMapType m_ActionToSliceDimensionMap; - - void AcceptAllInterpolations(mitk::SliceNavigationController *slicer); - - void WaitCursorOn(); - - void WaitCursorOff(); - - void RestoreOverrideCursor(); - - /** - Gets the working slice based on the given plane geometry and last saved interaction - - \param planeGeometry a plane geometry - */ - mitk::Image::Pointer GetWorkingSlice(const mitk::PlaneGeometry *planeGeometry); - - /** - Retrieves the currently selected PlaneGeometry from a SlicedGeometry3D that is generated by a - SliceNavigationController - and calls Interpolate to further process this PlaneGeometry into an interpolation. - - \param e is a actually a mitk::SliceNavigationController::GeometrySliceEvent, sent by a SliceNavigationController - \param slicer the SliceNavigationController - */ - void TranslateAndInterpolateChangedSlice(const itk::EventObject &e, mitk::SliceNavigationController *slicer); - - /** - Given a PlaneGeometry, this method figures out which slice of the first working image (of the associated - ToolManager) - should be interpolated. The actual work is then done by our SegmentationInterpolation object. - */ - void Interpolate(mitk::PlaneGeometry *plane, mitk::TimePointType timePoint, mitk::SliceNavigationController *slicer); - - /** - Called internally to update the interpolation suggestion. Finds out about the focused render window and requests an - interpolation. - */ - void UpdateVisibleSuggestion(); - -private: - mitk::SliceBasedInterpolationController::Pointer m_SliceInterpolatorController; - - mitk::ToolManager *m_ToolManager; - - bool m_Activated; - - template - void WritePreviewOnWorkingImage(itk::Image *target, - const mitk::Image *source, - int overwritevalue); - - QHash m_ControllerToTimeObserverTag; - QHash m_ControllerToSliceObserverTag; - QHash m_ControllerToDeleteObserverTag; - - unsigned int m_InterpolationInfoChangedObserverTag; - - mitk::DiffSliceOperation *m_doOperation; - mitk::DiffSliceOperation *m_undoOperation; - - mitk::DataNode::Pointer m_PreviewNode; - mitk::Image::Pointer m_PreviewImage; - - mitk::LabelSetImage::Pointer m_WorkingImage; - - QHash m_TimePoints; - - mitk::DataStorage::Pointer m_DataStorage; - - mitk::SliceNavigationController *m_LastSNC; - - unsigned int m_LastSliceIndex; -}; - -#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidgetGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidgetGUIControls.ui deleted file mode 100644 index d8e8eeeca3..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidgetGUIControls.ui +++ /dev/null @@ -1,159 +0,0 @@ - - - QmitkSliceBasedInterpolatorWidgetGUIControls - - - - 0 - 0 - 265 - 94 - - - - - 0 - 0 - - - - - 0 - 0 - - - - Slice-Based Interpolation - - - - - - - QLayout::SetDefaultConstraint - - - 6 - - - - - QLayout::SetDefaultConstraint - - - - - true - - - - 0 - 0 - - - - Start/Stop the 3D interpolation utility - - - Start - - - - :/Qmitk/Start.png - :/Qmitk/Stop.png:/Qmitk/Start.png - - - - 32 - 32 - - - - true - - - false - - - Qt::ToolButtonTextUnderIcon - - - Qt::NoArrow - - - - - - - - 0 - 0 - - - - Accept Single - - - - :/Qmitk/AcceptCurrentInterpolation.png:/Qmitk/AcceptCurrentInterpolation.png - - - - 32 - 32 - - - - Qt::ToolButtonTextUnderIcon - - - - - - - - 0 - 0 - - - - Accept All - - - - :/Qmitk/AcceptAllInterpolations.png:/Qmitk/AcceptAllInterpolations.png - - - - 32 - 32 - - - - Qt::ToolButtonTextUnderIcon - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - diff --git a/Modules/SegmentationUI/files.cmake b/Modules/SegmentationUI/files.cmake index e1bf1397ec..aa38f2c30f 100644 --- a/Modules/SegmentationUI/files.cmake +++ b/Modules/SegmentationUI/files.cmake @@ -1,79 +1,76 @@ set( CPP_FILES Qmitk/QmitkSegWithPreviewToolGUIBase.cpp Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp Qmitk/QmitkBinaryThresholdToolGUIBase.cpp Qmitk/QmitkBinaryThresholdToolGUI.cpp Qmitk/QmitkBinaryThresholdULToolGUI.cpp -Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.cpp Qmitk/QmitkConfirmSegmentationDialog.cpp Qmitk/QmitkCopyToClipBoardDialog.cpp Qmitk/QmitkDrawPaintbrushToolGUI.cpp Qmitk/QmitkErasePaintbrushToolGUI.cpp +Qmitk/QmitkEditableContourToolGUIBase.cpp Qmitk/QmitkLiveWireTool2DGUI.cpp +Qmitk/QmitkLassoToolGUI.cpp Qmitk/QmitkOtsuTool3DGUI.cpp Qmitk/QmitkPaintbrushToolGUI.cpp Qmitk/QmitkPickingToolGUI.cpp -Qmitk/QmitkPixelManipulationToolGUI.cpp Qmitk/QmitkSlicesInterpolator.cpp Qmitk/QmitkToolGUI.cpp Qmitk/QmitkToolGUIArea.cpp Qmitk/QmitkToolSelectionBox.cpp Qmitk/QmitknnUNetFolderParser.cpp Qmitk/QmitknnUNetToolGUI.cpp Qmitk/QmitknnUNetWorker.cpp Qmitk/QmitknnUNetGPU.cpp Qmitk/QmitkSurfaceStampWidget.cpp Qmitk/QmitkMaskStampWidget.cpp -Qmitk/QmitkSliceBasedInterpolatorWidget.cpp Qmitk/QmitkStaticDynamicSegmentationDialog.cpp Qmitk/QmitkSurfaceBasedInterpolatorWidget.cpp Qmitk/QmitkSimpleLabelSetListWidget.cpp ) set(MOC_H_FILES Qmitk/QmitkSegWithPreviewToolGUIBase.h Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h Qmitk/QmitkBinaryThresholdToolGUIBase.h Qmitk/QmitkBinaryThresholdToolGUI.h Qmitk/QmitkBinaryThresholdULToolGUI.h -Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.h Qmitk/QmitkConfirmSegmentationDialog.h Qmitk/QmitkCopyToClipBoardDialog.h Qmitk/QmitkDrawPaintbrushToolGUI.h Qmitk/QmitkErasePaintbrushToolGUI.h +Qmitk/QmitkEditableContourToolGUIBase.h Qmitk/QmitkLiveWireTool2DGUI.h +Qmitk/QmitkLassoToolGUI.h Qmitk/QmitkOtsuTool3DGUI.h Qmitk/QmitkPaintbrushToolGUI.h Qmitk/QmitkPickingToolGUI.h -Qmitk/QmitkPixelManipulationToolGUI.h Qmitk/QmitkSlicesInterpolator.h Qmitk/QmitkToolGUI.h Qmitk/QmitkToolGUIArea.h Qmitk/QmitkToolSelectionBox.h Qmitk/QmitknnUNetFolderParser.h Qmitk/QmitknnUNetToolGUI.h Qmitk/QmitknnUNetGPU.h Qmitk/QmitknnUNetWorker.h Qmitk/QmitknnUNetEnsembleLayout.h Qmitk/QmitkSurfaceStampWidget.h Qmitk/QmitkMaskStampWidget.h -Qmitk/QmitkSliceBasedInterpolatorWidget.h Qmitk/QmitkStaticDynamicSegmentationDialog.h Qmitk/QmitkSurfaceBasedInterpolatorWidget.h Qmitk/QmitkSimpleLabelSetListWidget.h ) set(UI_FILES Qmitk/QmitkConfirmSegmentationDialog.ui Qmitk/QmitkOtsuToolWidgetControls.ui -Qmitk/QmitkLiveWireTool2DGUIControls.ui Qmitk/QmitkSurfaceStampWidgetGUIControls.ui Qmitk/QmitkMaskStampWidgetGUIControls.ui -Qmitk/QmitkSliceBasedInterpolatorWidgetGUIControls.ui Qmitk/QmitkSurfaceBasedInterpolatorWidgetGUIControls.ui Qmitk/QmitknnUNetToolGUIControls.ui +Qmitk/QmitkEditableContourToolGUIControls.ui ) set(QRC_FILES resources/SegmentationUI.qrc ) diff --git a/Modules/ToFHardware/mitkToFOpenCVImageGrabber.cpp b/Modules/ToFHardware/mitkToFOpenCVImageGrabber.cpp index fd8b4e0cb7..561a6386ff 100644 --- a/Modules/ToFHardware/mitkToFOpenCVImageGrabber.cpp +++ b/Modules/ToFHardware/mitkToFOpenCVImageGrabber.cpp @@ -1,176 +1,178 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkToFOpenCVImageGrabber.h" // mitk includes #include "mitkImageDataItem.h" #include #include "mitkImageReadAccessor.h" #include "vtkSmartPointer.h" #include "vtkColorTransferFunction.h" #include "vtkFloatArray.h" +#include + namespace mitk { ToFOpenCVImageGrabber::ToFOpenCVImageGrabber() { m_CurrentOpenCVIntensityImage = nullptr; m_CurrentOpenCVAmplitudeImage = nullptr; m_CurrentOpenCVDistanceImage = nullptr; m_ImageType = 0; m_ImageDepth = IPL_DEPTH_32F; m_ImageGrabber = nullptr; } ToFOpenCVImageGrabber::~ToFOpenCVImageGrabber() { } cv::Mat ToFOpenCVImageGrabber::GetImage() { m_ImageGrabber->Update(); unsigned int numOfPixel = m_ImageGrabber->GetCaptureWidth()*m_ImageGrabber->GetCaptureHeight(); // copy current mitk images unsigned int dimensions[4]; dimensions[0] = this->m_ImageGrabber->GetCaptureWidth(); dimensions[1] = this->m_ImageGrabber->GetCaptureHeight(); dimensions[2] = 1; dimensions[3] = 1; // create single component float pixel type mitk::PixelType FloatType = MakeScalarPixelType(); ImageReadAccessor imgGrabAcc0(m_ImageGrabber->GetOutput(0), m_ImageGrabber->GetOutput(0)->GetSliceData()); ImageReadAccessor imgGrabAcc1(m_ImageGrabber->GetOutput(1), m_ImageGrabber->GetOutput(1)->GetSliceData()); ImageReadAccessor imgGrabAcc2(m_ImageGrabber->GetOutput(2), m_ImageGrabber->GetOutput(2)->GetSliceData()); mitk::Image::Pointer currentMITKIntensityImage = mitk::Image::New(); currentMITKIntensityImage->Initialize(FloatType, 2, dimensions); currentMITKIntensityImage->SetSlice((float*) imgGrabAcc2.GetData(),0,0,0); mitk::Image::Pointer currentMITKAmplitudeImage = mitk::Image::New(); currentMITKAmplitudeImage->Initialize(FloatType, 2, dimensions); currentMITKAmplitudeImage->SetSlice((float*)imgGrabAcc1.GetData(),0,0,0); mitk::Image::Pointer currentMITKDistanceImage = mitk::Image::New(); currentMITKDistanceImage->Initialize(FloatType, 2, dimensions); currentMITKDistanceImage->SetSlice((float*)imgGrabAcc0.GetData(),0,0,0); // copy mitk images to OpenCV images if (m_ImageDepth==IPL_DEPTH_32F) { if (m_ImageType==1) { ImageReadAccessor currentAmplAcc(currentMITKAmplitudeImage, currentMITKAmplitudeImage->GetSliceData(0, 0, 0)); float* amplitudeFloatData = (float*) currentAmplAcc.GetData(); memcpy(m_CurrentOpenCVAmplitudeImage->imageData,(unsigned char*)amplitudeFloatData,numOfPixel*sizeof(float)); return cv::cvarrToMat(m_CurrentOpenCVAmplitudeImage, false); } else if (m_ImageType==2) { ImageReadAccessor currentIntenAcc(currentMITKIntensityImage, currentMITKIntensityImage->GetSliceData(0, 0, 0)); float* intensityFloatData = (float*) currentIntenAcc.GetData(); memcpy(m_CurrentOpenCVIntensityImage->imageData,(unsigned char*)intensityFloatData,numOfPixel*sizeof(float)); return cv::cvarrToMat(m_CurrentOpenCVIntensityImage, false); } else { ImageReadAccessor currentDistAcc(currentMITKDistanceImage, currentMITKDistanceImage->GetSliceData(0, 0, 0)); float* distanceFloatData = (float*) currentDistAcc.GetData(); memcpy(m_CurrentOpenCVDistanceImage->imageData,(unsigned char*)distanceFloatData,numOfPixel*sizeof(float)); return cv::cvarrToMat(m_CurrentOpenCVDistanceImage, false); } } else { if (m_ImageType==1) { this->MapScalars(currentMITKAmplitudeImage, m_CurrentOpenCVAmplitudeImage); return cv::cvarrToMat(m_CurrentOpenCVAmplitudeImage, false); } else if (m_ImageType==2) { this->MapScalars(currentMITKIntensityImage, m_CurrentOpenCVIntensityImage); return cv::cvarrToMat(m_CurrentOpenCVIntensityImage, false); } else { this->MapScalars(currentMITKDistanceImage, m_CurrentOpenCVDistanceImage); return cv::cvarrToMat(m_CurrentOpenCVDistanceImage, false); } } } void ToFOpenCVImageGrabber::SetImageType(unsigned int imageType) { m_ImageType = imageType; } void ToFOpenCVImageGrabber::SetImageDepth(unsigned int imageDepth) { m_ImageDepth = imageDepth; } void ToFOpenCVImageGrabber::SetToFImageGrabber(ToFImageGrabber::Pointer imageGrabber) { m_ImageGrabber = imageGrabber; } ToFImageGrabber::Pointer ToFOpenCVImageGrabber::GetToFImageGrabber() { return m_ImageGrabber; } void ToFOpenCVImageGrabber::StartCapturing() { if (m_ImageGrabber.IsNotNull()) { m_ImageGrabber->ConnectCamera(); //Initialize cv Images after the camera is conneceted and we know the resolution m_CurrentOpenCVIntensityImage = cvCreateImage(cvSize(m_ImageGrabber->GetCaptureWidth(), m_ImageGrabber->GetCaptureHeight()), m_ImageDepth, 1); m_CurrentOpenCVAmplitudeImage = cvCreateImage(cvSize(m_ImageGrabber->GetCaptureWidth(), m_ImageGrabber->GetCaptureHeight()), m_ImageDepth, 1); m_CurrentOpenCVDistanceImage = cvCreateImage(cvSize(m_ImageGrabber->GetCaptureWidth(), m_ImageGrabber->GetCaptureHeight()), m_ImageDepth, 1); m_ImageGrabber->StartCamera(); } } void ToFOpenCVImageGrabber::StopCapturing() { if (m_ImageGrabber.IsNotNull()) { m_ImageGrabber->StopCamera(); m_ImageGrabber->DisconnectCamera(); } } void ToFOpenCVImageGrabber::MapScalars( mitk::Image::Pointer mitkImage, IplImage* openCVImage) { unsigned int numOfPixel = m_ImageGrabber->GetCaptureWidth()*m_ImageGrabber->GetCaptureHeight(); ImageReadAccessor imgAcc(mitkImage, mitkImage->GetSliceData(0, 0, 0)); float* floatData = (float*)imgAcc.GetData(); vtkSmartPointer colorTransferFunction = vtkSmartPointer::New(); vtkSmartPointer floatArrayInt = vtkSmartPointer::New(); floatArrayInt->Initialize(); floatArrayInt->SetArray(floatData, numOfPixel, 0); mitk::ScalarType min = mitkImage->GetStatistics()->GetScalarValueMin(); mitk::ScalarType max = mitkImage->GetStatistics()->GetScalarValueMaxNoRecompute(); MITK_INFO<<"Minimum: "<RemoveAllPoints(); colorTransferFunction->AddRGBPoint(min, 0, 0, 0); colorTransferFunction->AddRGBPoint(max, 1, 1, 1); colorTransferFunction->SetColorSpaceToHSV(); colorTransferFunction->MapScalarsThroughTable(floatArrayInt, (unsigned char*)openCVImage->imageData, VTK_LUMINANCE); } } // end namespace mitk diff --git a/Modules/ToFHardware/mitkToFOpenCVImageGrabber.h b/Modules/ToFHardware/mitkToFOpenCVImageGrabber.h index 03a64cadd7..292ba8176b 100644 --- a/Modules/ToFHardware/mitkToFOpenCVImageGrabber.h +++ b/Modules/ToFHardware/mitkToFOpenCVImageGrabber.h @@ -1,102 +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 __mitkToFOpenCVImageGrabber_h #define __mitkToFOpenCVImageGrabber_h #include #include "mitkCommon.h" #include "mitkOpenCVImageSource.h" #include "mitkToFImageGrabber.h" #include "itkObject.h" #include "itkObjectFactory.h" +#include + namespace mitk { /** * @brief TofImageGrabber class providing OpenCV images * * * @ingroup ToFHardware */ class MITKTOFHARDWARE_EXPORT ToFOpenCVImageGrabber : public mitk::OpenCVImageSource { public: ToFOpenCVImageGrabber(); ~ToFOpenCVImageGrabber() override; mitkClassMacro( ToFOpenCVImageGrabber , OpenCVImageSource ); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /*! \brief Get current ToF image. Specify image you want to grab with SetImageType() */ cv::Mat GetImage() override; /*! \brief set type of image you want to grab. 0: Distance image (Default) 1: Amplitude image 2: Intensity image */ void SetImageType(unsigned int imageType); /*! \brief set the depth of the image. Some functions of OpenCV do not support IPL_DEPTH_32F. Warning: changing from default results in a mapping of the pixel value through a lookup table IPL_DEPTH_1U 1 IPL_DEPTH_8U 8 IPL_DEPTH_16U 16 IPL_DEPTH_32F 32 (Default) */ void SetImageDepth(unsigned int imageDepth); /*! \brief set the ImageGrabber used for fetching image data from the camera */ void SetToFImageGrabber(mitk::ToFImageGrabber::Pointer imageGrabber); /*! \brief get the ImageGrabber used for fetching image data from the camera */ mitk::ToFImageGrabber::Pointer GetToFImageGrabber(); void StartCapturing(); void StopCapturing(); protected: /*! \brief map scalars through lookup table \param mitkImage current MITK image \param openCVImage */ void MapScalars(mitk::Image::Pointer mitkImage, IplImage* openCVImage); mitk::ToFImageGrabber::Pointer m_ImageGrabber; ///< ImageGrabber used for fetching ToF image data from the camera unsigned int m_ImageType; ///< type of image currently supplied by this image source /*! \brief image depth currently used by this image source. Warning: Changing from default (IPL_DEPTH_32F) results in a mapping of the pixel value through a lookup table */ unsigned int m_ImageDepth; IplImage* m_CurrentOpenCVIntensityImage; ///< OpenCV image holding the current intensity data IplImage* m_CurrentOpenCVAmplitudeImage; ///< OpenCV image holding the current amplitude data IplImage* m_CurrentOpenCVDistanceImage; ///< OpenCV image holding the current distance data private: }; } //END mitk namespace #endif diff --git a/Modules/ToFProcessing/CMakeLists.txt b/Modules/ToFProcessing/CMakeLists.txt index 5e2e9cc973..47c6001b6a 100644 --- a/Modules/ToFProcessing/CMakeLists.txt +++ b/Modules/ToFProcessing/CMakeLists.txt @@ -1,12 +1,11 @@ MITK_CREATE_MODULE( DEPENDS MitkCameraCalibration - PACKAGE_DEPENDS OpenCV WARNINGS_NO_ERRORS ) if(BUILD_TESTING) add_subdirectory(Testing) endif(BUILD_TESTING) diff --git a/Modules/ToFProcessing/mitkToFCompositeFilter.cpp b/Modules/ToFProcessing/mitkToFCompositeFilter.cpp index e7306d8edc..f86fc47521 100644 --- a/Modules/ToFProcessing/mitkToFCompositeFilter.cpp +++ b/Modules/ToFProcessing/mitkToFCompositeFilter.cpp @@ -1,398 +1,398 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include "mitkImageReadAccessor.h" #include -#include "opencv2/imgproc.hpp" +#include mitk::ToFCompositeFilter::ToFCompositeFilter() : m_SegmentationMask(nullptr), m_ImageWidth(0), m_ImageHeight(0), m_ImageSize(0), m_IplDistanceImage(nullptr), m_IplOutputImage(nullptr), m_ItkInputImage(nullptr), m_ApplyTemporalMedianFilter(false), m_ApplyAverageFilter(false), m_ApplyMedianFilter(false), m_ApplyThresholdFilter(false), m_ApplyMaskSegmentation(false), m_ApplyBilateralFilter(false), m_DataBuffer(nullptr), m_DataBufferCurrentIndex(0), m_DataBufferMaxSize(0), m_TemporalMedianFilterNumOfFrames(10), m_ThresholdFilterMin(1), m_ThresholdFilterMax(7000), m_BilateralFilterDomainSigma(2), m_BilateralFilterRangeSigma(60), m_BilateralFilterKernelRadius(0) { } mitk::ToFCompositeFilter::~ToFCompositeFilter() { cvReleaseImage(&(this->m_IplDistanceImage)); cvReleaseImage(&(this->m_IplOutputImage)); if (m_DataBuffer!=nullptr) { delete [] m_DataBuffer; } } void mitk::ToFCompositeFilter::SetInput( const InputImageType* distanceImage ) { this->SetInput(0, distanceImage); } void mitk::ToFCompositeFilter::SetInput( unsigned int idx, const InputImageType* distanceImage ) { if ((distanceImage == nullptr) && (idx == this->GetNumberOfInputs() - 1)) // if the last input is set to nullptr, reduce the number of inputs by one { this->SetNumberOfIndexedInputs(this->GetNumberOfInputs() - 1); } else { if (idx==0) //create IPL image holding distance data { if (!distanceImage->IsEmpty()) { this->m_ImageWidth = distanceImage->GetDimension(0); this->m_ImageHeight = distanceImage->GetDimension(1); this->m_ImageSize = this->m_ImageWidth * this->m_ImageHeight * sizeof(float); if (this->m_IplDistanceImage != nullptr) { cvReleaseImage(&(this->m_IplDistanceImage)); } ImageReadAccessor distImgAcc(distanceImage, distanceImage->GetSliceData(0,0,0)); float* distanceFloatData = (float*) distImgAcc.GetData(); this->m_IplDistanceImage = cvCreateImage(cvSize(this->m_ImageWidth, this->m_ImageHeight), IPL_DEPTH_32F, 1); memcpy(this->m_IplDistanceImage->imageData, (void*)distanceFloatData, this->m_ImageSize); if (this->m_IplOutputImage != nullptr) { cvReleaseImage(&(this->m_IplOutputImage)); } this->m_IplOutputImage = cvCreateImage(cvSize(this->m_ImageWidth, this->m_ImageHeight), IPL_DEPTH_32F, 1); CreateItkImage(this->m_ItkInputImage); } } this->ProcessObject::SetNthInput(idx, const_cast(distanceImage)); // Process object is not const-correct so the const_cast is required here } this->CreateOutputsForAllInputs(); } mitk::Image* mitk::ToFCompositeFilter::GetInput() { return this->GetInput(0); } mitk::Image* mitk::ToFCompositeFilter::GetInput( unsigned int idx ) { if (this->GetNumberOfInputs() < 1) return nullptr; //TODO: geeignete exception werfen return static_cast< mitk::Image*>(this->ProcessObject::GetInput(idx)); } void mitk::ToFCompositeFilter::GenerateData() { // copy input 1...n to output 1...n for (unsigned int idx=0; idxGetNumberOfOutputs(); idx++) { mitk::Image::Pointer outputImage = this->GetOutput(idx); mitk::Image::Pointer inputImage = this->GetInput(idx); if (outputImage.IsNotNull()&&inputImage.IsNotNull()) { ImageReadAccessor inputAcc(inputImage, inputImage->GetSliceData()); outputImage->CopyInformation(inputImage); outputImage->Initialize(inputImage->GetPixelType(),inputImage->GetDimension(),inputImage->GetDimensions()); outputImage->SetSlice(inputAcc.GetData()); } } //mitk::Image::Pointer outputDistanceImage = this->GetOutput(); ImageReadAccessor outputAcc(this->GetOutput(), this->GetOutput()->GetSliceData(0, 0, 0) ); float* outputDistanceFloatData = (float*) outputAcc.GetData(); //mitk::Image::Pointer inputDistanceImage = this->GetInput(); ImageReadAccessor inputAcc(this->GetInput(), this->GetInput()->GetSliceData(0, 0, 0) ); // copy initial distance image to ipl image float* distanceFloatData = (float*)inputAcc.GetData(); memcpy(this->m_IplDistanceImage->imageData, (void*)distanceFloatData, this->m_ImageSize); if (m_ApplyThresholdFilter||m_ApplyMaskSegmentation) { ProcessSegmentation(this->m_IplDistanceImage); } if (this->m_ApplyTemporalMedianFilter||this->m_ApplyAverageFilter) { ProcessStreamedQuickSelectMedianImageFilter(this->m_IplDistanceImage); } if (this->m_ApplyMedianFilter) { ProcessCVMedianFilter(this->m_IplDistanceImage, this->m_IplOutputImage); memcpy( this->m_IplDistanceImage->imageData, this->m_IplOutputImage->imageData, this->m_ImageSize ); } if (this->m_ApplyBilateralFilter) { float* itkFloatData = this->m_ItkInputImage->GetBufferPointer(); memcpy(itkFloatData, this->m_IplDistanceImage->imageData, this->m_ImageSize ); ItkImageType2D::Pointer itkOutputImage = ProcessItkBilateralFilter(this->m_ItkInputImage); memcpy( this->m_IplDistanceImage->imageData, itkOutputImage->GetBufferPointer(), this->m_ImageSize ); //ProcessCVBilateralFilter(this->m_IplDistanceImage, this->m_OutputIplImage, domainSigma, rangeSigma, kernelRadius); //memcpy( distanceFloatData, this->m_OutputIplImage->imageData, distanceImageSize ); } memcpy( outputDistanceFloatData, this->m_IplDistanceImage->imageData, this->m_ImageSize ); } void mitk::ToFCompositeFilter::CreateOutputsForAllInputs() { this->SetNumberOfIndexedOutputs(this->GetNumberOfInputs()); // create outputs for all inputs for (unsigned int idx = 0; idx < this->GetNumberOfIndexedInputs(); ++idx) { if (this->GetOutput(idx) == nullptr) { DataObjectPointer newOutput = this->MakeOutput(idx); this->SetNthOutput(idx, newOutput); } } this->Modified(); } void mitk::ToFCompositeFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if (output->IsInitialized()) return; itkDebugMacro(<<"GenerateOutputInformation()"); output->Initialize(input->GetPixelType(), *input->GetTimeGeometry()); output->SetPropertyList(input->GetPropertyList()->Clone()); } void mitk::ToFCompositeFilter::ProcessSegmentation(IplImage* inputIplImage) { char* segmentationMask; if (m_SegmentationMask.IsNotNull()) { ImageReadAccessor segMaskAcc(m_SegmentationMask, m_SegmentationMask->GetSliceData(0,0,0)); segmentationMask = (char*)segMaskAcc.GetData(); } else { segmentationMask = nullptr; } float *f = (float*)inputIplImage->imageData; for(int i=0; im_ImageWidth*this->m_ImageHeight; i++) { if (this->m_ApplyThresholdFilter) { if (f[i]<=m_ThresholdFilterMin) { f[i] = 0.0; } else if (f[i]>=m_ThresholdFilterMax) { f[i] = 0.0; } } if (this->m_ApplyMaskSegmentation) { if (segmentationMask) { if (segmentationMask[i]==0) { f[i] = 0.0; } } } } } ItkImageType2D::Pointer mitk::ToFCompositeFilter::ProcessItkBilateralFilter(ItkImageType2D::Pointer inputItkImage) { ItkImageType2D::Pointer outputItkImage; BilateralFilterType::Pointer bilateralFilter = BilateralFilterType::New(); bilateralFilter->SetInput(inputItkImage); bilateralFilter->SetDomainSigma(m_BilateralFilterDomainSigma); bilateralFilter->SetRangeSigma(m_BilateralFilterRangeSigma); //bilateralFilter->SetRadius(m_BilateralFilterKernelRadius); outputItkImage = bilateralFilter->GetOutput(); outputItkImage->Update(); return outputItkImage; } void mitk::ToFCompositeFilter::ProcessCVBilateralFilter(IplImage* inputIplImage, IplImage* outputIplImage) { int diameter = m_BilateralFilterKernelRadius; double sigmaColor = m_BilateralFilterRangeSigma; double sigmaSpace = m_BilateralFilterDomainSigma; cvSmooth(inputIplImage, outputIplImage, CV_BILATERAL, diameter, 0, sigmaColor, sigmaSpace); } void mitk::ToFCompositeFilter::ProcessCVMedianFilter(IplImage* inputIplImage, IplImage* outputIplImage, int radius) { cvSmooth(inputIplImage, outputIplImage, CV_MEDIAN, radius, 0, 0, 0); } void mitk::ToFCompositeFilter::ProcessStreamedQuickSelectMedianImageFilter(IplImage* inputIplImage) { float* data = (float*)inputIplImage->imageData; int imageSize = inputIplImage->width * inputIplImage->height; float* tmpArray; if (this->m_TemporalMedianFilterNumOfFrames == 0) { return; } if (m_TemporalMedianFilterNumOfFrames != this->m_DataBufferMaxSize) // reset { //delete current buffer for( int i=0; im_DataBufferMaxSize; i++ ) { delete[] this->m_DataBuffer[i]; } if (this->m_DataBuffer != nullptr) { delete[] this->m_DataBuffer; } this->m_DataBufferMaxSize = m_TemporalMedianFilterNumOfFrames; // create new buffer with current size this->m_DataBuffer = new float*[this->m_DataBufferMaxSize]; for(int i=0; im_DataBufferMaxSize; i++) { this->m_DataBuffer[i] = nullptr; } this->m_DataBufferCurrentIndex = 0; } int currentBufferSize = this->m_DataBufferMaxSize; tmpArray = new float[this->m_DataBufferMaxSize]; // copy data to buffer if (this->m_DataBuffer[this->m_DataBufferCurrentIndex] == nullptr) { this->m_DataBuffer[this->m_DataBufferCurrentIndex] = new float[imageSize]; currentBufferSize = this->m_DataBufferCurrentIndex + 1; } for(int j=0; jm_DataBuffer[this->m_DataBufferCurrentIndex][j] = data[j]; } float tmpValue = 0.0f; for(int i=0; im_DataBuffer[j][i]; } data[i] = tmpValue/currentBufferSize; } else if (m_ApplyTemporalMedianFilter) { for(int j=0; jm_DataBuffer[j][i]; } data[i] = quick_select(tmpArray, currentBufferSize); } } this->m_DataBufferCurrentIndex = (this->m_DataBufferCurrentIndex + 1) % this->m_DataBufferMaxSize; delete[] tmpArray; } #define ELEM_SWAP(a,b) { float t=(a);(a)=(b);(b)=t; } float mitk::ToFCompositeFilter::quick_select(float arr[], int n) { int low = 0; int high = n-1; int median = (low + high)/2; int middle = 0; int ll = 0; int hh = 0; for (;;) { if (high <= low) /* One element only */ return arr[median] ; if (high == low + 1) { /* Two elements only */ if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]) ; return arr[median] ; } /* Find median of low, middle and high items; swap into position low */ middle = (low + high) / 2; if (arr[middle] > arr[high]) ELEM_SWAP(arr[middle], arr[high]) ; if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]) ; if (arr[middle] > arr[low]) ELEM_SWAP(arr[middle], arr[low]) ; /* Swap low item (now in position middle) into position (low+1) */ ELEM_SWAP(arr[middle], arr[low+1]) ; /* Nibble from each end towards middle, swapping items when stuck */ ll = low + 1; hh = high; for (;;) { do ll++; while (arr[low] > arr[ll]) ; do hh--; while (arr[hh] > arr[low]) ; if (hh < ll) break; ELEM_SWAP(arr[ll], arr[hh]) ; } /* Swap middle item (in position low) back into correct position */ ELEM_SWAP(arr[low], arr[hh]) ; /* Re-set active partition */ if (hh <= median) low = ll; if (hh >= median) high = hh - 1; } } #undef ELEM_SWAP void mitk::ToFCompositeFilter::SetTemporalMedianFilterParameter(int tmporalMedianFilterNumOfFrames) { this->m_TemporalMedianFilterNumOfFrames = tmporalMedianFilterNumOfFrames; } void mitk::ToFCompositeFilter::SetThresholdFilterParameter(int min, int max) { if (min > max) { min = max; } this->m_ThresholdFilterMin = min; this->m_ThresholdFilterMax = max; } void mitk::ToFCompositeFilter::SetBilateralFilterParameter(double domainSigma, double rangeSigma, int kernelRadius = 0) { this->m_BilateralFilterDomainSigma = domainSigma; this->m_BilateralFilterRangeSigma = rangeSigma; this->m_BilateralFilterKernelRadius = kernelRadius; } void mitk::ToFCompositeFilter::CreateItkImage(ItkImageType2D::Pointer &itkInputImage) { itkInputImage = ItkImageType2D::New(); ItkImageType2D::IndexType startIndex; startIndex[0] = 0; // first index on X startIndex[1] = 0; // first index on Y ItkImageType2D::SizeType size; size[0] = this->m_ImageWidth; // size along X size[1] = this->m_ImageHeight; // size along Y ItkImageType2D::RegionType region; region.SetSize( size ); region.SetIndex( startIndex ); itkInputImage->SetRegions( region ); itkInputImage->Allocate(); } diff --git a/Modules/ToFProcessing/mitkToFCompositeFilter.h b/Modules/ToFProcessing/mitkToFCompositeFilter.h index 81ce0f269b..eab524fa67 100644 --- a/Modules/ToFProcessing/mitkToFCompositeFilter.h +++ b/Modules/ToFProcessing/mitkToFCompositeFilter.h @@ -1,193 +1,193 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __mitkToFCompositeFilter_h #define __mitkToFCompositeFilter_h #include #include "mitkImageToImageFilter.h" #include #include -#include "opencv2/core.hpp" +#include typedef itk::Image ItkImageType2D; typedef itk::Image ItkImageType3D; typedef itk::BilateralImageFilter BilateralFilterType; namespace mitk { /** * @brief Applies a common filter-pipeline to the first input of this filter * * This class intends to allow quick preprocessing of (ToF) range data. Input 0 of this filter, holding the range image, * is processed using the following image processing filters: * - threshold filter * - mask segmentation * - temporal median filter * - spatial median filter * - bilateral filter * * @ingroup ToFProcessing */ class MITKTOFPROCESSING_EXPORT ToFCompositeFilter : public ImageToImageFilter { public: mitkClassMacro( ToFCompositeFilter , ImageToImageFilter ); itkFactorylessNewMacro(Self); itkCloneMacro(Self); itkSetMacro(SegmentationMask,mitk::Image::Pointer); itkSetMacro(ApplyTemporalMedianFilter,bool); itkGetConstMacro(ApplyTemporalMedianFilter,bool); itkSetMacro(ApplyAverageFilter,bool); itkGetConstMacro(ApplyAverageFilter,bool); itkSetMacro(ApplyMedianFilter,bool); itkGetConstMacro(ApplyMedianFilter,bool); itkSetMacro(ApplyThresholdFilter,bool); itkGetConstMacro(ApplyThresholdFilter,bool); itkSetMacro(ApplyMaskSegmentation,bool); itkGetConstMacro(ApplyMaskSegmentation,bool); itkSetMacro(ApplyBilateralFilter,bool); itkGetConstMacro(ApplyBilateralFilter,bool); using itk::ProcessObject::SetInput; /*! \brief sets the input of this filter \param distanceImage input is the distance image of e.g. a ToF camera */ void SetInput( const InputImageType* distanceImage) override; /*! \brief sets the input of this filter at idx \param idx number of the current input \param distanceImage input is the distance image of e.g. a ToF camera */ void SetInput(unsigned int idx, const InputImageType* distanceImage) override; /*! \brief returns the input of this filter */ Image* GetInput(); /*! \brief returns the input with id idx of this filter */ Image* GetInput(unsigned int idx); /*! \brief Sets the parameter of the temporal median filter \param tmporalMedianFilterNumOfFrames number of frames to be considered for calulating the temporal median */ void SetTemporalMedianFilterParameter(int tmporalMedianFilterNumOfFrames); /*! \brief Sets the parameters (lower, upper threshold) of the threshold filter \param min lower threshold of the threshold filter \param max upper threshold of the threshold filter */ void SetThresholdFilterParameter(int min, int max); /*! \brief Sets the parameters (domain sigma, range sigma, kernel radius) of the bilateral filter \param domainSigma Parameter controlling the smoothing effect of the bilateral filter. Default value: 2 \param rangeSigma Parameter controlling the edge preserving effect of the bilateral filter. Default value: 60 \param kernelRadius radius of the filter mask of the bilateral filter */ void SetBilateralFilterParameter(double domainSigma, double rangeSigma, int kernelRadius); protected: /*! \brief standard constructor */ ToFCompositeFilter(); /*! \brief standard destructor */ ~ToFCompositeFilter() override; void GenerateOutputInformation() override; /*! \brief method generating the output of this filter. Called in the updated process of the pipeline. This method generates the output of the ToFSurfaceSource: The generated surface of the 3d points */ void GenerateData() override; /** * \brief Create an output for each input * * This Method sets the number of outputs to the number of inputs * and creates missing outputs objects. * \warning any additional outputs that exist before the method is called are deleted */ void CreateOutputsForAllInputs(); /*! \brief Applies a mask and/or threshold segmentation to the input image. All pixels with values outside the mask, below the lower threshold (min) and above the upper threshold (max) are assigned the pixel value 0 */ void ProcessSegmentation(IplImage* inputIplImage); /*! \brief Applies the ITK bilateral filter to the input image */ ItkImageType2D::Pointer ProcessItkBilateralFilter(ItkImageType2D::Pointer inputItkImage); /*! \brief Applies the OpenCV bilateral filter to the input image. */ void ProcessCVBilateralFilter(IplImage* inputIplImage, IplImage* outputIplImage); /*! \brief Applies the OpenCV median filter to the input image. */ void ProcessCVMedianFilter(IplImage* inputIplImage, IplImage* outputIplImage, int radius = 3); /*! \brief Performs temporal median filter on an image given the number of frames to be considered */ void ProcessStreamedQuickSelectMedianImageFilter(IplImage* inputIplImage); /*! \brief Quickselect algorithm * This Quickselect routine is based on the algorithm described in * "Numerical recipes in C", Second Edition, * Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5 * This code by Nicolas Devillard - 1998. Public domain. */ float quick_select(float arr[], int n); /*! \brief Initialize and allocate a 2D ITK image of dimension m_ImageWidth*m_ImageHeight */ void CreateItkImage(ItkImageType2D::Pointer &itkInputImage); mitk::Image::Pointer m_SegmentationMask; ///< mask image used for segmenting the image int m_ImageWidth; ///< x-dimension of the image int m_ImageHeight; ///< y-dimension of the image int m_ImageSize; ///< size of the image in bytes IplImage* m_IplDistanceImage; ///< OpenCV-representation of the distance image IplImage* m_IplOutputImage; ///< OpenCV-representation of the output image ItkImageType2D::Pointer m_ItkInputImage; ///< ITK representation of the distance image bool m_ApplyTemporalMedianFilter; ///< Flag indicating if the temporal median filter is currently active for processing the distance image bool m_ApplyAverageFilter; ///< Flag indicating if the average filter is currently active for processing the distance image bool m_ApplyMedianFilter; ///< Flag indicating if the spatial median filter is currently active for processing the distance image bool m_ApplyThresholdFilter; ///< Flag indicating if the threshold filter is currently active for processing the distance image bool m_ApplyMaskSegmentation; ///< Flag indicating if a mask segmentation is performed bool m_ApplyBilateralFilter; ///< Flag indicating if the bilateral filter is currently active for processing the distance image float** m_DataBuffer; ///< Buffer used for calculating the pixel-wise median over the last n (m_TemporalMedianFilterNumOfFrames) number of frames int m_DataBufferCurrentIndex; ///< Current index in the buffer of the temporal median filter int m_DataBufferMaxSize; ///< Maximal size for the buffer of the temporal median filter (m_DataBuffer) int m_TemporalMedianFilterNumOfFrames; ///< Number of frames to be used in the calculation of the temporal median int m_ThresholdFilterMin; ///< Lower threshold of the threshold filter. Pixels with values below will be assigned value 0 when applying the threshold filter int m_ThresholdFilterMax; ///< Lower threshold of the threshold filter. Pixels with values above will be assigned value 0 when applying the threshold filter double m_BilateralFilterDomainSigma; ///< Parameter of the bilateral filter controlling the smoothing effect of the filter. Default value: 2 double m_BilateralFilterRangeSigma; ///< Parameter of the bilateral filter controlling the edge preserving effect of the filter. Default value: 60 int m_BilateralFilterKernelRadius; ///< Kernel radius of the bilateral filter mask }; } //END mitk namespace #endif diff --git a/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.h b/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.h index 1a65814578..ce5d418fce 100644 --- a/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.h +++ b/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.h @@ -1,190 +1,192 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __mitkToFDistanceImageToSurfaceFilter_h #define __mitkToFDistanceImageToSurfaceFilter_h #include #include #include #include #include #include "mitkCameraIntrinsics.h" #include #include #include +#include + namespace mitk { /** * @brief Converts a Time-of-Flight (ToF) distance image to a 3D surface using the pinhole camera model for coordinate computation. * The intrinsic parameters of the camera (FocalLength, PrincipalPoint, InterPixelDistance) are set via SetCameraIntrinsics(). The * measured distance for each pixel corresponds to the distance between the object point and the corresponding image point on the * image plane. * * The coordinate conversion follows the model of a common pinhole camera where the origin of the camera * coordinate system (world coordinates) is at the pinhole * \image html Modules/ToFProcessing/Documentation/PinholeCameraModel.png * The definition of the image plane and its coordinate systems (pixel and mm) is depicted in the following image * \image html Modules/ToFProcessing/Documentation/ImagePlane.png * * @ingroup SurfaceFilters * @ingroup ToFProcessing */ class MITKTOFPROCESSING_EXPORT ToFDistanceImageToSurfaceFilter : public SurfaceSource { public: mitkClassMacro( ToFDistanceImageToSurfaceFilter , SurfaceSource ); itkFactorylessNewMacro(Self); itkCloneMacro(Self); itkSetMacro(CameraIntrinsics, mitk::CameraIntrinsics::Pointer); itkGetMacro(CameraIntrinsics, mitk::CameraIntrinsics::Pointer); itkSetMacro(InterPixelDistance,ToFProcessingCommon::ToFPoint2D); itkGetMacro(InterPixelDistance,ToFProcessingCommon::ToFPoint2D); itkSetMacro(TextureIndex,int); /** * @brief SetTriangulationThreshold Sets a triangulation threshold in order * to remove unusually huge faces from the surface. If this value is set, * the filter will check whether the distance between two neighboring vertices * exceeds the triangulation threshold. If yes, there vertices will not be * triangulated (connected with lines). The vertices will still be added to * the surface, but only as single point (if they have no other neighbors). * @param triangulationThreshold The triangulationThreshold in mm. (not mm*mm!) * @note vtkMath::Distance2BetweenPoints returns the squared distance * between two points and hence we square m_TriangulationThreshold in * order to save run-time. */ void SetTriangulationThreshold( double triangulationThreshold ); itkGetMacro(TriangulationThreshold, double); itkSetMacro(VertexIdList, vtkSmartPointer); itkGetMacro(VertexIdList, vtkSmartPointer); itkSetMacro(GenerateTriangularMesh,bool); itkGetMacro(GenerateTriangularMesh,bool); /** * @brief The ReconstructionModeType enum: Defines the reconstruction mode, if using no interpixeldistances and focal lenghts in pixel units or interpixeldistances and focal length in mm. The Kinect option defines a special reconstruction mode for the kinect. */ enum ReconstructionModeType{ WithOutInterPixelDistance = 1, WithInterPixelDistance = 2, Kinect = 3}; itkSetEnumMacro(ReconstructionMode,ReconstructionModeType); itkGetEnumMacro(ReconstructionMode,ReconstructionModeType); /*! \brief Set scalar image used as texture of the surface. \param iplScalarImage OpenCV image for texturing */ void SetScalarImage(IplImage* iplScalarImage); /*! \brief Set scalar image used as texture of the surface. \return OpenCV image for texturing */ IplImage* GetScalarImage(); /*! \brief Set width of the scalar image used for texturing the surface \param width width (x-dimension) of the texture image */ void SetTextureImageWidth(int width); /*! \brief Set height of the scalar image used for texturing the surface \param height height (y-dimension) of the texture image */ void SetTextureImageHeight(int height); using itk::ProcessObject::SetInput; /*! \brief Sets the input of this filter \param distanceImage input is the distance image of e.g. a ToF camera */ virtual void SetInput( Image* distanceImage); /*! \brief Sets the input of this filter and the intrinsic parameters \param distanceImage input is the distance image of e.g. a ToF camera \param cameraIntrinsics */ virtual void SetInput( Image* distanceImage, mitk::CameraIntrinsics::Pointer cameraIntrinsics ); /*! \brief Sets the input of this filter at idx \param idx number of the current input \param distanceImage input is the distance image of e.g. a ToF camera */ virtual void SetInput(unsigned int idx, Image* distanceImage); /*! \brief Sets the input of this filter at idx and the intrinsic parameters \param idx number of the current input \param distanceImage input is the distance image of e.g. a ToF camera \param cameraIntrinsics This is the camera model which holds parameters like focal length, pixel size, etc. which are needed for the reconstruction of the surface. */ virtual void SetInput( unsigned int idx, Image* distanceImage, mitk::CameraIntrinsics::Pointer cameraIntrinsics ); /*! \brief Returns the input of this filter */ Image* GetInput(); /*! \brief Returns the input with id idx of this filter */ Image* GetInput(unsigned int idx); protected: /*! \brief Standard constructor */ ToFDistanceImageToSurfaceFilter(); /*! \brief Standard destructor */ ~ToFDistanceImageToSurfaceFilter() override; void GenerateOutputInformation() override; /*! \brief Method generating the output of this filter. Called in the updated process of the pipeline. This method generates the output of the ToFSurfaceSource: The generated surface of the 3d points */ void GenerateData() override; /** * \brief Create an output for each input * * This Method sets the number of outputs to the number of inputs * and creates missing outputs objects. * \warning any additional outputs that exist before the method is called are deleted */ void CreateOutputsForAllInputs(); IplImage* m_IplScalarImage; ///< Scalar image used for surface texturing mitk::CameraIntrinsics::Pointer m_CameraIntrinsics; ///< Specifies the intrinsic parameters int m_TextureImageWidth; ///< Width (x-dimension) of the texture image int m_TextureImageHeight; ///< Height (y-dimension) of the texture image ToFProcessingCommon::ToFPoint2D m_InterPixelDistance; ///< distance in mm between two adjacent pixels on the ToF camera chip int m_TextureIndex; ///< Index of the input used as texture image when no scalar image was set via SetIplScalarImage(). 0 = Distance, 1 = Amplitude, 2 = Intensity bool m_GenerateTriangularMesh; ReconstructionModeType m_ReconstructionMode; ///< The ReconstructionModeType enum: Defines the reconstruction mode, if using no interpixeldistances and focal lenghts in pixel units or interpixeldistances and focal length in mm. The Kinect option defines a special reconstruction mode for the kinect. vtkSmartPointer m_VertexIdList; ///< Make a vtkIdList to save the ID's of the polyData corresponding to the image pixel ID's. This can be accessed after generate data to obtain the mapping. double m_TriangulationThreshold; }; } //END mitk namespace #endif diff --git a/Modules/US/USFilters/mitkUSImageVideoSource.cpp b/Modules/US/USFilters/mitkUSImageVideoSource.cpp index 539158691c..f95ff55eed 100644 --- a/Modules/US/USFilters/mitkUSImageVideoSource.cpp +++ b/Modules/US/USFilters/mitkUSImageVideoSource.cpp @@ -1,226 +1,226 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // MITK HEADER #include "mitkUSImageVideoSource.h" #include "mitkImage.h" //Other #include -#include +#include mitk::USImageVideoSource::USImageVideoSource() : m_VideoCapture(new cv::VideoCapture()), m_IsVideoReady(false), m_IsGreyscale(false), m_IsCropped(false), m_ResolutionOverrideWidth(0), m_ResolutionOverrideHeight(0), m_ResolutionOverride(false), m_GrayscaleFilter(mitk::ConvertGrayscaleOpenCVImageFilter::New()), m_CropFilter(mitk::CropOpenCVImageFilter::New()) { } mitk::USImageVideoSource::~USImageVideoSource() { m_VideoCapture->release(); delete m_VideoCapture; } void mitk::USImageVideoSource::SetVideoFileInput(std::string path) { m_VideoCapture->open(path.c_str()); // check if we succeeded if(!m_VideoCapture->isOpened()) { m_IsVideoReady = false; } else { m_IsVideoReady = true; } // if Override is enabled, use it if (m_ResolutionOverride) { m_VideoCapture->set(CV_CAP_PROP_FRAME_WIDTH, this->m_ResolutionOverrideWidth); m_VideoCapture->set(CV_CAP_PROP_FRAME_HEIGHT, this->m_ResolutionOverrideHeight); } } void mitk::USImageVideoSource::SetCameraInput(int deviceID) { m_VideoCapture->open(deviceID); if(!m_VideoCapture->isOpened()) // check if we succeeded m_IsVideoReady = false; else m_IsVideoReady = true; // if Override is enabled, use it if (m_ResolutionOverride) { m_VideoCapture->set(CV_CAP_PROP_FRAME_WIDTH, this->m_ResolutionOverrideWidth); m_VideoCapture->set(CV_CAP_PROP_FRAME_HEIGHT, this->m_ResolutionOverrideHeight); } } void mitk::USImageVideoSource::ReleaseInput() { m_VideoCapture->release(); delete m_VideoCapture; m_VideoCapture = new cv::VideoCapture(); } void mitk::USImageVideoSource::SetColorOutput(bool isColor){ if ( ! isColor && ! m_IsGreyscale ) { this->PushFilter(m_GrayscaleFilter.GetPointer()); } else if ( isColor && m_IsGreyscale ) { this->RemoveFilter(m_GrayscaleFilter.GetPointer()); } m_IsGreyscale = !isColor; } int mitk::USImageVideoSource::GetImageHeight() { if (m_VideoCapture) { return m_VideoCapture->get(CV_CAP_PROP_FRAME_HEIGHT); } else { return 0; } } int mitk::USImageVideoSource::GetImageWidth() { if (m_VideoCapture) { return m_VideoCapture->get(CV_CAP_PROP_FRAME_WIDTH); } else { return 0; } } bool mitk::USImageVideoSource::GetIsReady() { if (!m_VideoCapture) { return false; } return m_VideoCapture->isOpened(); } void mitk::USImageVideoSource::SetRegionOfInterest(int topLeftX, int topLeftY, int bottomRightX, int bottomRightY) { m_CropFilter->SetCropRegion(topLeftX, topLeftY, bottomRightX, bottomRightY); if (! m_IsCropped && ! m_CropFilter->GetIsCropRegionEmpty()) { this->PushFilter(m_CropFilter.GetPointer()); m_IsCropped = true; } } void mitk::USImageVideoSource::SetRegionOfInterest(USImageRoi roi) { this->SetRegionOfInterest(roi.topLeftX, roi.topLeftY, roi.bottomRightX, roi.bottomRightY); } void mitk::USImageVideoSource::SetCropping(USImageCropping cropping) { int width = this->GetImageWidth(); int height = this->GetImageHeight(); this->SetRegionOfInterest(cropping.left, cropping.top, width - cropping.right, height - cropping.bottom); } mitk::USImageVideoSource::USImageCropping mitk::USImageVideoSource::GetCropping() { cv::Rect cropRect = m_CropFilter->GetCropRegion(); USImageCropping cropping; cropping.left = cropRect.x; cropping.top = cropRect.y; if ( cropRect.height == 0 ) { cropping.bottom = 0; } else { cropping.bottom = this->GetImageHeight() - (cropRect.y + cropRect.height); } if ( cropRect.width == 0 ) { cropping.right = 0; } else { cropping.right = this->GetImageWidth() - (cropRect.x + cropRect.width); } return cropping; } mitk::USImageVideoSource::USImageRoi mitk::USImageVideoSource::GetRegionOfInterest() { cv::Rect cropRect = m_CropFilter->GetCropRegion(); return USImageRoi(cropRect.x, cropRect.y, cropRect.x + cropRect.width, cropRect.y + cropRect.height); } void mitk::USImageVideoSource::RemoveRegionOfInterest() { this->RemoveFilter(m_CropFilter.GetPointer()); m_IsCropped = false; } void mitk::USImageVideoSource::GetNextRawImage(std::vector& image ) { // loop video if necessary //Commented out because setting and getting of these properties is not supported. Therefore on Linux //you'll always get some Highgui errors from OpenCV /*if (m_VideoCapture->get(CV_CAP_PROP_POS_FRAMES) == m_VideoCapture->get(CV_CAP_PROP_FRAME_COUNT)) { m_VideoCapture->set(CV_CAP_PROP_POS_FRAMES, 0); }*/ if (image.size() != 1) image.resize(1); // retrieve image *m_VideoCapture >> image[0]; // get a new frame from camera } void mitk::USImageVideoSource::GetNextRawImage(std::vector& image ) { if (image.size() != 1) image.resize(1); std::vector cv_img; this->GetNextRawImage(cv_img); // convert to MITK-Image IplImage ipl_img = cvIplImage(cv_img[0]); this->m_OpenCVToMitkFilter->SetOpenCVImage(&ipl_img); this->m_OpenCVToMitkFilter->Update(); // OpenCVToMitkImageFilter returns a standard mitk::image. We then transform it into an USImage image[0] = this->m_OpenCVToMitkFilter->GetOutput(); // clean up cv_img[0].release(); } void mitk::USImageVideoSource::OverrideResolution(int width, int height) { this->m_ResolutionOverrideHeight = height; this->m_ResolutionOverrideWidth = width; if (m_VideoCapture != nullptr) { m_VideoCapture->set(CV_CAP_PROP_FRAME_WIDTH, width); m_VideoCapture->set(CV_CAP_PROP_FRAME_HEIGHT, height); } } diff --git a/Plugins/org.blueberry.core.runtime/CMakeLists.txt b/Plugins/org.blueberry.core.runtime/CMakeLists.txt index fe06634e4c..1cfa45d44a 100644 --- a/Plugins/org.blueberry.core.runtime/CMakeLists.txt +++ b/Plugins/org.blueberry.core.runtime/CMakeLists.txt @@ -1,18 +1,18 @@ project(org_blueberry_core_runtime) mitk_create_plugin( EXPORT_DIRECTIVE org_blueberry_core_runtime_EXPORT EXPORTED_INCLUDE_SUFFIXES src src/application src/dynamichelpers src/registry MODULE_DEPENDS PUBLIC mbilog PACKAGE_DEPENDS PUBLIC Poco|Foundation+Util+XML PRIVATE Qt5|Gui+Xml ) -target_compile_definitions(${PLUGIN_TARGET} PUBLIC "$<$:POCO_NO_UNWINDOWS;WIN32_LEAN_AND_MEAN>") +target_compile_definitions(${PLUGIN_TARGET} PUBLIC "$<$:WIN32_LEAN_AND_MEAN>") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/berryConfig.h.in" "${CMAKE_CURRENT_BINARY_DIR}/berryConfig.h" @ONLY) diff --git a/Plugins/org.mitk.gui.qt.application/files.cmake b/Plugins/org.mitk.gui.qt.application/files.cmake index 4d661dfef3..701f4e7b55 100644 --- a/Plugins/org.mitk.gui.qt.application/files.cmake +++ b/Plugins/org.mitk.gui.qt.application/files.cmake @@ -1,86 +1,88 @@ set(SRC_CPP_FILES QmitkAbstractDataNodeAction.cpp QmitkCloseProjectAction.cpp QmitkDataNodeColorAction.cpp QmitkDataNodeColorMapAction.cpp QmitkDataNodeComponentAction.cpp QmitkDataNodeContextMenu.cpp QmitkDataNodeGlobalReinitAction.cpp QmitkDataNodeHideAllAction.cpp QmitkDataNodeOpacityAction.cpp QmitkDataNodeOpenInAction.cpp QmitkDataNodeReinitAction.cpp QmitkDataNodeRemoveAction.cpp + QmitkDataNodeResetGeometryAction.cpp QmitkDataNodeShowDetailsAction.cpp QmitkDataNodeShowSelectedNodesAction.cpp QmitkDataNodeSurfaceRepresentationAction.cpp QmitkDataNodeTextureInterpolationAction.cpp QmitkDataNodeToggleVisibilityAction.cpp QmitkDefaultDropTargetListener.cpp QmitkFileExitAction.cpp QmitkFileOpenAction.cpp QmitkFileSaveAction.cpp QmitkUndoAction.cpp QmitkRedoAction.cpp QmitkPreferencesDialog.cpp QmitkStatusBar.cpp ) set(INTERNAL_CPP_FILES org_mitk_gui_qt_application_Activator.cpp QmitkEditorsPreferencePage.cpp QmitkGeneralPreferencePage.cpp QmitkShowPreferencePageHandler.cpp ) set(MOC_H_FILES src/QmitkCloseProjectAction.h src/QmitkDataNodeColorAction.h src/QmitkDataNodeColorMapAction.h src/QmitkDataNodeComponentAction.h src/QmitkDataNodeGlobalReinitAction.h src/QmitkDataNodeContextMenu.h src/QmitkDataNodeHideAllAction.h src/QmitkDataNodeOpacityAction.h src/QmitkDataNodeOpenInAction.h src/QmitkDataNodeReinitAction.h src/QmitkDataNodeRemoveAction.h + src/QmitkDataNodeResetGeometryAction.h src/QmitkDataNodeShowDetailsAction.h src/QmitkDataNodeShowSelectedNodesAction.h src/QmitkDataNodeSurfaceRepresentationAction.h src/QmitkDataNodeTextureInterpolationAction.h src/QmitkDataNodeToggleVisibilityAction.h src/QmitkFileExitAction.h src/QmitkFileOpenAction.h src/QmitkFileSaveAction.h src/QmitkUndoAction.h src/QmitkRedoAction.h src/QmitkPreferencesDialog.h src/internal/org_mitk_gui_qt_application_Activator.h src/internal/QmitkEditorsPreferencePage.h src/internal/QmitkGeneralPreferencePage.h src/internal/QmitkShowPreferencePageHandler.h ) set(UI_FILES src/QmitkPreferencesDialog.ui ) set(CACHED_RESOURCE_FILES plugin.xml ) set(QRC_FILES resources/resources.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.application/src/QmitkDataNodeContextMenu.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeContextMenu.cpp index 9c3e7eea21..4afd52578c 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeContextMenu.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeContextMenu.cpp @@ -1,483 +1,488 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All 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 QmitkDataNodeContextMenu::QmitkDataNodeContextMenu(berry::IWorkbenchPartSite::Pointer workbenchPartSite, QWidget* parent) : QMenu(parent), m_Parent(parent), m_WorkbenchPartSite(workbenchPartSite) { this->InitNodeDescriptors(); this->InitDefaultActions(); this->InitExtensionPointActions(); } QmitkDataNodeContextMenu::~QmitkDataNodeContextMenu() { for (auto& descriptorActionPair : m_DescriptorActionList) descriptorActionPair.first->RemoveAction(descriptorActionPair.second); } void QmitkDataNodeContextMenu::SetDataStorage(mitk::DataStorage* dataStorage) { m_DataStorage = dataStorage; for (auto& descriptorActionPair : m_DescriptorActionList) { auto dataNodeAction = dynamic_cast(descriptorActionPair.second); if (nullptr != dataNodeAction) dataNodeAction->SetDataStorage(dataStorage); } } void QmitkDataNodeContextMenu::SetBaseRenderer(mitk::BaseRenderer* baseRenderer) { m_BaseRenderer = baseRenderer; for (auto& descriptorActionPair : m_DescriptorActionList) { auto dataNodeAction = dynamic_cast(descriptorActionPair.second); if (nullptr != dataNodeAction) dataNodeAction->SetBaseRenderer(baseRenderer); } } void QmitkDataNodeContextMenu::SetSurfaceDecimation(bool surfaceDecimation) { m_SurfaceDecimation = surfaceDecimation; } void QmitkDataNodeContextMenu::SetSelectedNodes(const QList& selectedNodes) { m_SelectedNodes = selectedNodes; } void QmitkDataNodeContextMenu::InitNodeDescriptors() { auto nodeDescriptorManager = QmitkNodeDescriptorManager::GetInstance(); m_UnknownDataNodeDescriptor = nodeDescriptorManager->GetUnknownDataNodeDescriptor(); m_ImageDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("Image"); m_MultiComponentImageDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("MultiComponentImage"); m_DiffusionImageDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("DiffusionImage"); m_FiberBundleDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("FiberBundle"); m_PeakImageDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("PeakImage"); m_SegmentDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("Segment"); m_SurfaceDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("Surface"); m_PointSetNodeDescriptor = nodeDescriptorManager->GetDescriptor("PointSet"); m_PlanarLineNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarLine"); m_PlanarCircleNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarCircle"); m_PlanarEllipseNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarEllipse"); m_PlanarAngleNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarAngle"); m_PlanarFourPointAngleNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarFourPointAngle"); m_PlanarRectangleNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarRectangle"); m_PlanarPolygonNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarPolygon"); m_PlanarPathNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarPath"); m_PlanarDoubleEllipseNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarDoubleEllipse"); m_PlanarBezierCurveNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarBezierCurve"); m_PlanarSubdivisionPolygonNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarSubdivisionPolygon"); } void QmitkDataNodeContextMenu::InitDefaultActions() { auto workbenchPartSite = m_WorkbenchPartSite.Lock(); m_GlobalReinitAction = new QmitkDataNodeGlobalReinitAction(m_Parent, workbenchPartSite); m_GlobalReinitAction->setIcon(QIcon(":/org.mitk.gui.qt.datamanager/Refresh_48.png")); m_UnknownDataNodeDescriptor->AddAction(m_GlobalReinitAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_GlobalReinitAction)); m_ReinitAction = new QmitkDataNodeReinitAction(m_Parent, workbenchPartSite); m_ReinitAction->setIcon(QIcon(":/org.mitk.gui.qt.datamanager/Refresh_48.png")); m_UnknownDataNodeDescriptor->AddAction(m_ReinitAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_ReinitAction)); + m_ResetGeometryAction = new QmitkDataNodeResetGeometryAction(m_Parent, workbenchPartSite); + m_ResetGeometryAction->setIcon(QIcon(":/org.mitk.gui.qt.datamanager/Refresh_48.png")); + m_UnknownDataNodeDescriptor->AddAction(m_ResetGeometryAction, true); + m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_ResetGeometryAction)); + QAction* saveAction = new QmitkFileSaveAction(QIcon(":/org.mitk.gui.qt.datamanager/Save_48.png"), workbenchPartSite->GetWorkbenchWindow()); m_UnknownDataNodeDescriptor->AddAction(saveAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, saveAction)); m_RemoveAction = new QmitkDataNodeRemoveAction(m_Parent, workbenchPartSite); m_RemoveAction->setIcon(QIcon(":/org.mitk.gui.qt.datamanager/Remove_48.png")); m_UnknownDataNodeDescriptor->AddAction(m_RemoveAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_RemoveAction)); m_ShowSelectedNodesAction = new QmitkDataNodeShowSelectedNodesAction(m_Parent, workbenchPartSite); m_RemoveAction->setIcon(QIcon(":/org.mitk.gui.qt.datamanager/ShowSelectedNode_48.png")); m_UnknownDataNodeDescriptor->AddAction(m_ShowSelectedNodesAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_ShowSelectedNodesAction)); m_ToggleVisibilityAction = new QmitkDataNodeToggleVisibilityAction(m_Parent, workbenchPartSite); m_ToggleVisibilityAction->setIcon(QIcon(":/org.mitk.gui.qt.datamanager/InvertShowSelectedNode_48.png")); m_UnknownDataNodeDescriptor->AddAction(m_ToggleVisibilityAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_ToggleVisibilityAction)); m_ShowDetailsAction = new QmitkDataNodeShowDetailsAction(m_Parent, workbenchPartSite); m_ShowDetailsAction->setIcon(QIcon(":/org.mitk.gui.qt.datamanager/ShowDataInfo_48.png")); m_UnknownDataNodeDescriptor->AddAction(m_ShowDetailsAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_ShowDetailsAction)); m_OpacityAction = new QmitkDataNodeOpacityAction(m_Parent, workbenchPartSite); m_UnknownDataNodeDescriptor->AddAction(m_OpacityAction, false); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_OpacityAction)); m_ColorAction = new QmitkDataNodeColorAction(m_Parent, workbenchPartSite); this->AddColorAction(m_ColorAction); m_ColormapAction = new QmitkDataNodeColorMapAction(m_Parent, workbenchPartSite); m_ImageDataNodeDescriptor->AddAction(m_ColormapAction); m_DescriptorActionList.push_back(std::make_pair(m_ImageDataNodeDescriptor, m_ColormapAction)); if (nullptr != m_DiffusionImageDataNodeDescriptor) { m_DiffusionImageDataNodeDescriptor->AddAction(m_ColormapAction, false); m_DescriptorActionList.push_back(std::make_pair(m_DiffusionImageDataNodeDescriptor, m_ColormapAction)); } m_ComponentAction = new QmitkDataNodeComponentAction(m_Parent, workbenchPartSite); m_MultiComponentImageDataNodeDescriptor->AddAction(m_ComponentAction, false); m_DescriptorActionList.push_back(std::make_pair(m_MultiComponentImageDataNodeDescriptor, m_ComponentAction)); if (nullptr != m_DiffusionImageDataNodeDescriptor) { m_DiffusionImageDataNodeDescriptor->AddAction(m_ComponentAction, false); m_DescriptorActionList.push_back(std::make_pair(m_DiffusionImageDataNodeDescriptor, m_ComponentAction)); } m_TextureInterpolationAction = new QmitkDataNodeTextureInterpolationAction(m_Parent, workbenchPartSite); m_ImageDataNodeDescriptor->AddAction(m_TextureInterpolationAction, false); m_DescriptorActionList.push_back(std::make_pair(m_ImageDataNodeDescriptor, m_TextureInterpolationAction)); if (nullptr != m_DiffusionImageDataNodeDescriptor) { m_DiffusionImageDataNodeDescriptor->AddAction(m_TextureInterpolationAction, false); m_DescriptorActionList.push_back(std::make_pair(m_DiffusionImageDataNodeDescriptor, m_TextureInterpolationAction)); } if (nullptr != m_SegmentDataNodeDescriptor) { m_SegmentDataNodeDescriptor->AddAction(m_TextureInterpolationAction, false); m_DescriptorActionList.push_back(std::make_pair(m_SegmentDataNodeDescriptor, m_TextureInterpolationAction)); } m_SurfaceRepresentationAction = new QmitkDataNodeSurfaceRepresentationAction(m_Parent, workbenchPartSite); m_SurfaceDataNodeDescriptor->AddAction(m_SurfaceRepresentationAction, false); m_DescriptorActionList.push_back(std::make_pair(m_SurfaceDataNodeDescriptor, m_SurfaceRepresentationAction)); } void QmitkDataNodeContextMenu::InitExtensionPointActions() { auto extensionPointService = berry::Platform::GetExtensionRegistry(); auto customMenuConfigs = extensionPointService->GetConfigurationElementsFor("org.mitk.gui.qt.datamanager.contextMenuActions"); DescriptorActionListType descriptorActionList; m_ConfigElements.clear(); for (const auto& customMenuConfig : qAsConst(customMenuConfigs)) { auto descriptorName = customMenuConfig->GetAttribute("nodeDescriptorName"); auto actionLabel = customMenuConfig->GetAttribute("label"); auto actionClass = customMenuConfig->GetAttribute("class"); if (descriptorName.isEmpty() || actionLabel.isEmpty() || actionClass.isEmpty()) continue; auto descriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(descriptorName); if (nullptr == descriptor) { MITK_WARN << "Cannot add action \"" << actionLabel << "\" to non-existent descriptor \"" << descriptorName << "\"."; continue; } QAction* action = nullptr; auto actionIcon = customMenuConfig->GetAttribute("icon"); if (!actionIcon.isEmpty()) { QIcon icon = !QFile::exists(actionIcon) ? berry::AbstractUICTKPlugin::ImageDescriptorFromPlugin(customMenuConfig->GetContributor()->GetName(), actionIcon) : QIcon(actionIcon); action = new QAction(icon, actionLabel, m_Parent); } else { action = new QAction(actionLabel, m_Parent); } if (nullptr != action) { // See T26938. We do not know why but without the lambda function indirection, the // connection is lost after the content menu was shown for the first time. connect(action, &QAction::triggered, [action, this]() { this->OnExtensionPointActionTriggered(action); }); m_ConfigElements[action] = customMenuConfig; descriptorActionList.push_back(std::make_pair(descriptor, action)); } } this->AddDescriptorActionList(descriptorActionList); } void QmitkDataNodeContextMenu::InitServiceActions() { } void QmitkDataNodeContextMenu::OnContextMenuRequested(const QPoint& /*pos*/) { auto workbenchPartSite = m_WorkbenchPartSite.Lock(); if (workbenchPartSite.IsNull()) return; auto selection = workbenchPartSite->GetWorkbenchWindow()->GetSelectionService()->GetSelection() .Cast(); if (selection.IsNull() || selection->IsEmpty()) return; m_SelectedNodes = QList::fromStdList(selection->GetSelectedDataNodes()); if (!m_SelectedNodes.isEmpty()) { this->clear(); auto actions = m_SelectedNodes.size() == 1 ? this->GetActions(m_SelectedNodes.front()) : this->GetActions(m_SelectedNodes); for (auto& action : actions) { auto dataNodeAction = dynamic_cast(action); if (nullptr != dataNodeAction) dataNodeAction->SetSelectedNodes(m_SelectedNodes); } this->addActions(actions); this->popup(QCursor::pos()); } } void QmitkDataNodeContextMenu::OnExtensionPointActionTriggered(QAction* action) { auto configElementIter = m_ConfigElements.find(action); if (m_ConfigElements.end() == configElementIter) { MITK_WARN << "Associated configuration element for action \"" << action->text() << "\" not found."; return; } auto configElement = configElementIter->second; auto contextMenuAction = configElement->CreateExecutableExtension("class"); auto dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNotNull()) contextMenuAction->SetDataStorage(dataStorage); if ("QmitkCreatePolygonModelAction" == configElement->GetAttribute("class")) { contextMenuAction->SetSmoothed("true" == configElement->GetAttribute("smoothed")); contextMenuAction->SetDecimated(m_SurfaceDecimation); } contextMenuAction->Run(m_SelectedNodes); } void QmitkDataNodeContextMenu::AddColorAction(QWidgetAction* colorAction) { if (nullptr != m_ImageDataNodeDescriptor) { m_ImageDataNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_ImageDataNodeDescriptor, colorAction)); } if (nullptr != m_MultiComponentImageDataNodeDescriptor) { m_MultiComponentImageDataNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_MultiComponentImageDataNodeDescriptor, colorAction)); } if (nullptr != m_DiffusionImageDataNodeDescriptor) { m_DiffusionImageDataNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_DiffusionImageDataNodeDescriptor, colorAction)); } if (nullptr != m_FiberBundleDataNodeDescriptor) { m_FiberBundleDataNodeDescriptor->AddAction(colorAction, false); m_DescriptorActionList.push_back(std::make_pair(m_FiberBundleDataNodeDescriptor, colorAction)); } if (nullptr != m_PeakImageDataNodeDescriptor) { m_PeakImageDataNodeDescriptor->AddAction(colorAction, false); m_DescriptorActionList.push_back(std::make_pair(m_PeakImageDataNodeDescriptor, colorAction)); } if (nullptr != m_SegmentDataNodeDescriptor) { m_SegmentDataNodeDescriptor->AddAction(colorAction, false); m_DescriptorActionList.push_back(std::make_pair(m_SegmentDataNodeDescriptor, colorAction)); } if (nullptr != m_SurfaceDataNodeDescriptor) { m_SurfaceDataNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_SurfaceDataNodeDescriptor, colorAction)); } if (nullptr != m_PointSetNodeDescriptor) { m_PointSetNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PointSetNodeDescriptor, colorAction)); } if (nullptr != m_PlanarLineNodeDescriptor) { m_PlanarLineNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarLineNodeDescriptor, colorAction)); } if (nullptr != m_PlanarCircleNodeDescriptor) { m_PlanarCircleNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarCircleNodeDescriptor, colorAction)); } if (nullptr != m_PlanarEllipseNodeDescriptor) { m_PlanarEllipseNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarEllipseNodeDescriptor, colorAction)); } if (nullptr != m_PlanarAngleNodeDescriptor) { m_PlanarAngleNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarAngleNodeDescriptor, colorAction)); } if (nullptr != m_PlanarFourPointAngleNodeDescriptor) { m_PlanarFourPointAngleNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarFourPointAngleNodeDescriptor, colorAction)); } if (nullptr != m_PlanarRectangleNodeDescriptor) { m_PlanarRectangleNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarRectangleNodeDescriptor, colorAction)); } if (nullptr != m_PlanarPolygonNodeDescriptor) { m_PlanarPolygonNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarPolygonNodeDescriptor, colorAction)); } if (nullptr != m_PlanarPathNodeDescriptor) { m_PlanarPathNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarPathNodeDescriptor, colorAction)); } if (nullptr != m_PlanarDoubleEllipseNodeDescriptor) { m_PlanarDoubleEllipseNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarDoubleEllipseNodeDescriptor, colorAction)); } if (nullptr != m_PlanarBezierCurveNodeDescriptor) { m_PlanarBezierCurveNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarBezierCurveNodeDescriptor, colorAction)); } if (nullptr != m_PlanarSubdivisionPolygonNodeDescriptor) { m_PlanarSubdivisionPolygonNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarSubdivisionPolygonNodeDescriptor, colorAction)); } } void QmitkDataNodeContextMenu::AddDescriptorActionList(DescriptorActionListType& descriptorActionList) { using ListItem = std::pair; std::sort(descriptorActionList.begin(), descriptorActionList.end(), [](const ListItem& left, const ListItem& right) -> bool { return left.second->text() < right.second->text(); }); for (auto& descriptorActionPair : descriptorActionList) { descriptorActionPair.first->AddAction(descriptorActionPair.second); m_DescriptorActionList.push_back(descriptorActionPair); } } QList QmitkDataNodeContextMenu::GetActions(const mitk::DataNode* node) { QList actions; for(const auto& descriptorActionPair : m_DescriptorActionList) { if (descriptorActionPair.first->CheckNode(node) || "Unknown" == descriptorActionPair.first->GetNameOfClass()) actions.append(descriptorActionPair.second); } return actions; } QList QmitkDataNodeContextMenu::GetActions(const QList& nodes) { QList actions; for (const auto& descriptorActionPair : m_DescriptorActionList) { for (const auto& node : nodes) { if (descriptorActionPair.first->CheckNode(node) || "Unknown" == descriptorActionPair.first->GetNameOfClass()) { auto batchActions = descriptorActionPair.first->GetBatchActions(); if (std::find(batchActions.begin(), batchActions.end(), descriptorActionPair.second) != batchActions.end()) actions.append(descriptorActionPair.second); break; } } } return actions; } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeContextMenu.h b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeContextMenu.h index 2dcf8ba901..858cf6123e 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeContextMenu.h +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeContextMenu.h @@ -1,136 +1,138 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QMITKDATANODECONTEXTMENU_H #define QMITKDATANODECONTEXTMENU_H #include // qt widgets module #include "QmitkDataNodeGlobalReinitAction.h" #include "QmitkDataNodeReinitAction.h" +#include "QmitkDataNodeResetGeometryAction.h" #include "QmitkDataNodeRemoveAction.h" #include "QmitkDataNodeShowSelectedNodesAction.h" #include "QmitkDataNodeToggleVisibilityAction.h" #include "QmitkDataNodeShowDetailsAction.h" #include "QmitkDataNodeOpacityAction.h" #include "QmitkDataNodeColorAction.h" #include "QmitkDataNodeColorMapAction.h" #include "QmitkDataNodeComponentAction.h" #include "QmitkDataNodeTextureInterpolationAction.h" #include "QmitkDataNodeSurfaceRepresentationAction.h" #include "QmitkNodeDescriptor.h" // mitk core #include #include #include // blueberry ui qt plugin #include #include // qt #include class MITK_QT_APP QmitkDataNodeContextMenu : public QMenu { Q_OBJECT public: QmitkDataNodeContextMenu(berry::IWorkbenchPartSite::Pointer workbenchPartSite, QWidget* parent = nullptr); ~QmitkDataNodeContextMenu() override; void SetDataStorage(mitk::DataStorage* dataStorage); void SetBaseRenderer(mitk::BaseRenderer* baseRenderer); void SetSurfaceDecimation(bool surfaceDecimation); void SetSelectedNodes(const QList& selectedNodes); public Q_SLOTS: void OnContextMenuRequested(const QPoint& pos); void OnExtensionPointActionTriggered(QAction* action); private: using DescriptorActionListType = std::vector>; using ConfigurationElementsType = std::map; void InitNodeDescriptors(); void InitDefaultActions(); void InitExtensionPointActions(); void InitServiceActions(); void AddColorAction(QWidgetAction* colorAction); void AddDescriptorActionList(DescriptorActionListType& descriptorActionList); QList GetActions(const mitk::DataNode* node); QList GetActions(const QList& nodes); QWidget* m_Parent; berry::IWorkbenchPartSite::WeakPtr m_WorkbenchPartSite; mitk::WeakPointer m_DataStorage; mitk::WeakPointer m_BaseRenderer; QList m_SelectedNodes; // store a list of all actions to remove them on menu destruction DescriptorActionListType m_DescriptorActionList; // stores the configuration elements for the context menu actions from extension points ConfigurationElementsType m_ConfigElements; QmitkNodeDescriptor* m_UnknownDataNodeDescriptor; QmitkNodeDescriptor* m_ImageDataNodeDescriptor; QmitkNodeDescriptor* m_MultiComponentImageDataNodeDescriptor; QmitkNodeDescriptor* m_DiffusionImageDataNodeDescriptor; QmitkNodeDescriptor* m_FiberBundleDataNodeDescriptor; QmitkNodeDescriptor* m_PeakImageDataNodeDescriptor; QmitkNodeDescriptor* m_SegmentDataNodeDescriptor; QmitkNodeDescriptor* m_SurfaceDataNodeDescriptor; QmitkNodeDescriptor* m_PointSetNodeDescriptor; QmitkNodeDescriptor* m_PlanarLineNodeDescriptor; QmitkNodeDescriptor* m_PlanarCircleNodeDescriptor; QmitkNodeDescriptor* m_PlanarEllipseNodeDescriptor; QmitkNodeDescriptor* m_PlanarAngleNodeDescriptor; QmitkNodeDescriptor* m_PlanarFourPointAngleNodeDescriptor; QmitkNodeDescriptor* m_PlanarRectangleNodeDescriptor; QmitkNodeDescriptor* m_PlanarPolygonNodeDescriptor; QmitkNodeDescriptor* m_PlanarPathNodeDescriptor; QmitkNodeDescriptor* m_PlanarDoubleEllipseNodeDescriptor; QmitkNodeDescriptor* m_PlanarBezierCurveNodeDescriptor; QmitkNodeDescriptor* m_PlanarSubdivisionPolygonNodeDescriptor; ////////////////////////////////////////////////////////////////////////// // default actions ////////////////////////////////////////////////////////////////////////// QmitkDataNodeGlobalReinitAction* m_GlobalReinitAction; QmitkDataNodeReinitAction* m_ReinitAction; + QmitkDataNodeResetGeometryAction* m_ResetGeometryAction; QmitkDataNodeRemoveAction* m_RemoveAction; QmitkDataNodeShowSelectedNodesAction* m_ShowSelectedNodesAction; QmitkDataNodeToggleVisibilityAction* m_ToggleVisibilityAction; QmitkDataNodeShowDetailsAction* m_ShowDetailsAction; QmitkDataNodeOpacityAction* m_OpacityAction; QmitkDataNodeColorAction* m_ColorAction; QmitkDataNodeColorMapAction* m_ColormapAction; QmitkDataNodeComponentAction* m_ComponentAction; QmitkDataNodeTextureInterpolationAction* m_TextureInterpolationAction; QmitkDataNodeSurfaceRepresentationAction* m_SurfaceRepresentationAction; bool m_SurfaceDecimation; }; #endif // QMITKDATANODECONTEXTMENU_H diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeResetGeometryAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeResetGeometryAction.cpp new file mode 100644 index 0000000000..25cdf72dc0 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeResetGeometryAction.cpp @@ -0,0 +1,113 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include + +// mitk core +#include +#include + +// mitk gui common plugin +#include + +// namespace that contains the concrete action +namespace ResetGeometryAction +{ + void Run(berry::IWorkbenchPartSite::Pointer workbenchPartSite, const mitk::TimeGeometry* referenceGeometry, mitk::BaseRenderer* baseRenderer /*= nullptr*/) + { + + if (workbenchPartSite.IsNull()) + { + return; + } + + auto* renderWindowPart = mitk::WorkbenchUtil::GetRenderWindowPart(workbenchPartSite->GetPage(), mitk::WorkbenchUtil::NONE); + if (nullptr == renderWindowPart) + { + renderWindowPart = mitk::WorkbenchUtil::OpenRenderWindowPart(workbenchPartSite->GetPage(), false); + if (nullptr == renderWindowPart) + { + // no render window available + return; + } + } + + if (nullptr == referenceGeometry) + { + return; + } + + mitk::TimeStepType imageTimeStep = 0; + + // store the current position to set it again later, if the camera should not be reset + mitk::Point3D currentPosition = renderWindowPart->GetSelectedPosition(); + + // store the current time step to set it again later, if the camera should not be reset + auto* renderingManager = mitk::RenderingManager::GetInstance(); + const auto currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint(); + if (referenceGeometry->IsValidTimePoint(currentTimePoint)) + { + imageTimeStep = referenceGeometry->TimePointToTimeStep(currentTimePoint); + } + + if (nullptr == baseRenderer) + { + renderingManager->InitializeViews(referenceGeometry, mitk::RenderingManager::REQUEST_UPDATE_ALL, false); + } + else + { + renderingManager->InitializeView(baseRenderer->GetRenderWindow(), referenceGeometry, false); + } + + renderWindowPart->SetSelectedPosition(currentPosition); + renderingManager->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); + } +} // namespace ResetGeometryAction + +QmitkDataNodeResetGeometryAction::QmitkDataNodeResetGeometryAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchpartSite) + : QAction(parent) + , QmitkAbstractDataNodeAction(workbenchpartSite) +{ + this->setText(tr("Reset geometry")); + this->InitializeAction(); +} + +QmitkDataNodeResetGeometryAction::QmitkDataNodeResetGeometryAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchpartSite) + : QmitkDataNodeResetGeometryAction(parent, berry::IWorkbenchPartSite::Pointer(workbenchpartSite)) +{ +} + +void QmitkDataNodeResetGeometryAction::InitializeAction() +{ + connect(this, &QmitkDataNodeResetGeometryAction::triggered, this, &QmitkDataNodeResetGeometryAction::OnActionTriggered); +} + +void QmitkDataNodeResetGeometryAction::OnActionTriggered(bool /*checked*/) +{ + auto workbenchPartSite = m_WorkbenchPartSite.Lock(); + + if (workbenchPartSite.IsNull()) + { + return; + } + + auto baseRenderer = this->GetBaseRenderer(); + + auto selectedNode = this->GetSelectedNode(); + mitk::Image::ConstPointer selectedImage = dynamic_cast(selectedNode->GetData()); + if (selectedImage.IsNull()) + { + return; + } + + ResetGeometryAction::Run(workbenchPartSite, selectedImage->GetTimeGeometry(), baseRenderer); +} diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeResetGeometryAction.h b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeResetGeometryAction.h new file mode 100644 index 0000000000..8bd5f8295a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeResetGeometryAction.h @@ -0,0 +1,49 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef QMITKDATANODERESETGEOMETRYACTION_H +#define QMITKDATANODERESETGEOMETRYACTION_H + +#include + +#include + +// qt +#include + +namespace ResetGeometryAction +{ + MITK_QT_APP void Run(berry::IWorkbenchPartSite::Pointer workbenchPartSite, + const QList& selectedNodes = QList(), + mitk::BaseRenderer* baseRenderer = nullptr); +} + +class MITK_QT_APP QmitkDataNodeResetGeometryAction : public QAction, public QmitkAbstractDataNodeAction +{ + Q_OBJECT + +public: + + QmitkDataNodeResetGeometryAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); + QmitkDataNodeResetGeometryAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); + +private Q_SLOTS: + + void OnActionTriggered(bool); + +protected: + + void InitializeAction() override; + +}; + +#endif // QMITKDATANODEREINITVIEWACTION_H diff --git a/Plugins/org.mitk.gui.qt.dicombrowser/CMakeLists.txt b/Plugins/org.mitk.gui.qt.dicombrowser/CMakeLists.txt index 2939dc3c5c..f8c21fc0fe 100644 --- a/Plugins/org.mitk.gui.qt.dicombrowser/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.dicombrowser/CMakeLists.txt @@ -1,28 +1,29 @@ project(org_mitk_gui_qt_dicombrowser) # Note: # If we use an installed version of DCMTK then DCMTK_DIR points to the subdirectory # of the installation directory (share/dcmtk) that contains DCMTKConfig.cmake. # Therefore we look for the the storescp command in the '../../bin' directory, too. find_program(DCMTK_STORESCP NAMES storescp storescp${DCMTK_CMAKE_DEBUG_POSTFIX} storescp${CMAKE_DEBUG_POSTFIX} PATHS "${MITK_EXTERNAL_PROJECT_PREFIX}/bin" PATH_SUFFIXES Release Debug DOC "Dcmtk storage provider which is used to store dicom files which are transfered over network." NO_DEFAULT_PATH ) mark_as_advanced(DCMTK_STORESCP) if(NOT EXISTS ${DCMTK_STORESCP}) message(WARNING "Couldn't find program storescp: Query/retrieve of the DICOM plugin won't work!") else() configure_file( org_mitk_gui_qt_dicombrowser_config.h.in org_mitk_gui_qt_dicombrowser_config.h @ONLY) MITK_INSTALL_HELPER_APP(EXECUTABLES ${DCMTK_STORESCP}) mitk_create_plugin( EXPORT_DIRECTIVE DICOM_EXPORT EXPORTED_INCLUDE_SUFFIXES src MODULE_DEPENDS MitkContourModel MitkQtWidgetsExt MitkDICOMUI MitkRT MitkRTUI MitkDICOM + PACKAGE_DEPENDS CTK|CTKDICOMWidgets ) endif() diff --git a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationFlowControlView.cpp b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationFlowControlView.cpp index fcf0290514..5fbb4736b0 100644 --- a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationFlowControlView.cpp +++ b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationFlowControlView.cpp @@ -1,183 +1,208 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "org_mitk_gui_qt_flow_segmentation_Activator.h" // Blueberry #include #include #include #include #include //MITK #include #include #include #include #include #include #include +#include // Qmitk #include "QmitkSegmentationFlowControlView.h" #include // Qt #include #include - -#include +#include const std::string QmitkSegmentationFlowControlView::VIEW_ID = "org.mitk.views.flow.control"; QmitkSegmentationFlowControlView::QmitkSegmentationFlowControlView() : m_Controls(new Ui::SegmentationFlowControlView), m_Parent(nullptr) { auto notHelperObject = mitk::NodePredicateNot::New( mitk::NodePredicateProperty::New("helper object")); m_SegmentationPredicate = mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), notHelperObject); m_SegmentationTaskListPredicate = mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), notHelperObject); berry::PlatformUI::GetWorkbench()->AddWorkbenchListener(this); } QmitkSegmentationFlowControlView::~QmitkSegmentationFlowControlView() { berry::PlatformUI::GetWorkbench()->RemoveWorkbenchListener(this); } bool QmitkSegmentationFlowControlView::PreShutdown(berry::IWorkbench*, bool) { return m_Controls->segmentationTaskListWidget != nullptr ? m_Controls->segmentationTaskListWidget->OnPreShutdown() : true; } void QmitkSegmentationFlowControlView::SetFocus() { m_Controls->btnStoreAndAccept->setFocus(); } void QmitkSegmentationFlowControlView::CreateQtPartControl(QWidget* parent) { m_Controls->setupUi(parent); m_Parent = parent; using Self = QmitkSegmentationFlowControlView; connect(m_Controls->btnStore, &QPushButton::clicked, this, &Self::OnStoreButtonClicked); connect(m_Controls->btnStoreAndAccept, &QPushButton::clicked, this, &Self::OnAcceptButtonClicked); connect(m_Controls->segmentationTaskListWidget, &QmitkSegmentationTaskListWidget::ActiveTaskChanged, this, &Self::OnActiveTaskChanged); connect(m_Controls->segmentationTaskListWidget, &QmitkSegmentationTaskListWidget::CurrentTaskChanged, this, &Self::OnCurrentTaskChanged); + auto* prevShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_S), parent); + connect(prevShortcut, &QShortcut::activated, this, &Self::OnStoreInterimResultShortcutActivated); + + auto* nextShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_A), parent); + connect(nextShortcut, &QShortcut::activated, this, &Self::OnAcceptSegmentationShortcutActivated); + m_Controls->btnStore->setIcon(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg"))); m_Controls->btnStore->setVisible(false); m_Controls->segmentationTaskListWidget->setVisible(false); m_Controls->labelStored->setVisible(false); this->UpdateControls(); m_OutputDir = QString::fromStdString(mitk::BaseApplication::instance().config().getString("flow.outputdir", itksys::SystemTools::GetCurrentWorkingDirectory())); m_OutputDir = QDir::fromNativeSeparators(m_OutputDir); m_FileExtension = QString::fromStdString(mitk::BaseApplication::instance().config().getString("flow.outputextension", "nrrd")); } void QmitkSegmentationFlowControlView::OnStoreButtonClicked() { m_Controls->segmentationTaskListWidget->SaveActiveTask(true); } void QmitkSegmentationFlowControlView::OnAcceptButtonClicked() { if (m_Controls->segmentationTaskListWidget->isVisible()) { + auto* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); + int activeToolId = -1; + + if (toolManager != nullptr) + activeToolId = toolManager->GetActiveToolID(); + m_Controls->segmentationTaskListWidget->SaveActiveTask(); m_Controls->segmentationTaskListWidget->LoadNextUnfinishedTask(); + + if (toolManager != nullptr) + toolManager->ActivateTool(activeToolId); } else { auto nodes = this->GetDataStorage()->GetSubset(m_SegmentationPredicate); for (auto node : *nodes) { QString outputpath = m_OutputDir + "/" + QString::fromStdString(node->GetName()) + "." + m_FileExtension; outputpath = QDir::toNativeSeparators(QDir::cleanPath(outputpath)); mitk::IOUtil::Save(node->GetData(), outputpath.toStdString()); } m_Controls->labelStored->setVisible(true); } } void QmitkSegmentationFlowControlView::OnActiveTaskChanged(const std::optional&) { this->UpdateControls(); } void QmitkSegmentationFlowControlView::OnCurrentTaskChanged(const std::optional&) { this->UpdateControls(); } void QmitkSegmentationFlowControlView::UpdateControls() { auto dataStorage = this->GetDataStorage(); auto hasTaskList = !dataStorage->GetSubset(m_SegmentationTaskListPredicate)->empty(); m_Controls->segmentationTaskListWidget->setVisible(hasTaskList); if (hasTaskList) { auto activeTaskIsShown = m_Controls->segmentationTaskListWidget->ActiveTaskIsShown(); m_Controls->btnStore->setVisible(activeTaskIsShown); m_Controls->btnStoreAndAccept->setEnabled(activeTaskIsShown); } else { auto hasSegmentation = !dataStorage->GetSubset(m_SegmentationPredicate)->empty(); m_Controls->btnStore->setVisible(false); m_Controls->btnStoreAndAccept->setEnabled(hasSegmentation); } } void QmitkSegmentationFlowControlView::NodeAdded(const mitk::DataNode* node) { if (dynamic_cast(node->GetData()) != nullptr) this->UpdateControls(); } void QmitkSegmentationFlowControlView::NodeChanged(const mitk::DataNode* node) { if (dynamic_cast(node->GetData()) != nullptr) this->UpdateControls(); } void QmitkSegmentationFlowControlView::NodeRemoved(const mitk::DataNode* node) { if (dynamic_cast(node->GetData()) != nullptr) this->UpdateControls(); } + +void QmitkSegmentationFlowControlView::OnStoreInterimResultShortcutActivated() +{ + m_Controls->btnStore->click(); +} + +void QmitkSegmentationFlowControlView::OnAcceptSegmentationShortcutActivated() +{ + m_Controls->btnStoreAndAccept->click(); +} diff --git a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationFlowControlView.h b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationFlowControlView.h index beeffc7401..8d3a9bf48a 100644 --- a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationFlowControlView.h +++ b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationFlowControlView.h @@ -1,87 +1,89 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __Q_MITK_MATCHPOINT_MAPPER_H #define __Q_MITK_MATCHPOINT_MAPPER_H #include #include #include #include "mitkNodePredicateBase.h" #include namespace Ui { class SegmentationFlowControlView; } /*! \brief QmitkSegmentationFlowControlView Class that "controls" the segmentation view. It offers the possibility to accept a segmentation. Accepting the segmentation stores the segmentation to the given working directory. The working directory is specified by command line arguments. If no commandline flag is set the current working directory will be used. */ class QmitkSegmentationFlowControlView : public QmitkAbstractView, public berry::IWorkbenchListener { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; /** * Creates smartpointer typedefs */ berryObjectMacro(QmitkSegmentationFlowControlView) QmitkSegmentationFlowControlView(); ~QmitkSegmentationFlowControlView() override; void CreateQtPartControl(QWidget *parent) override; protected slots: void OnStoreButtonClicked(); void OnAcceptButtonClicked(); void OnActiveTaskChanged(const std::optional& index); void OnCurrentTaskChanged(const std::optional& index); + void OnStoreInterimResultShortcutActivated(); + void OnAcceptSegmentationShortcutActivated(); protected: void SetFocus() override; void NodeAdded(const mitk::DataNode* node) override; void NodeChanged(const mitk::DataNode* node) override; void NodeRemoved(const mitk::DataNode* node) override; bool PreShutdown(berry::IWorkbench*, bool) override; void UpdateControls(); Ui::SegmentationFlowControlView* m_Controls; private: QWidget *m_Parent; mitk::NodePredicateBase::Pointer m_SegmentationPredicate; mitk::NodePredicateBase::Pointer m_SegmentationTaskListPredicate; QString m_OutputDir; QString m_FileExtension; }; #endif // MatchPoint_h diff --git a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskListWidget.cpp b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskListWidget.cpp index b85e6e8659..2b24622e95 100644 --- a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskListWidget.cpp +++ b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskListWidget.cpp @@ -1,787 +1,813 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkSegmentationTaskListWidget.h" #include "org_mitk_gui_qt_flow_segmentation_Activator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include -#include +#include namespace { berry::IPreferences::Pointer GetSegmentationPreferences() { return berry::Platform::GetPreferencesService()->GetSystemPreferences()->Node("/org.mitk.views.segmentation"); } mitk::DataStorage* GetDataStorage() { auto* pluginContext = org_mitk_gui_qt_flow_segmentation_Activator::GetContext(); auto dataStorageServiceReference = pluginContext->getServiceReference(); if (dataStorageServiceReference) { auto* dataStorageService = pluginContext->getService(dataStorageServiceReference); if (dataStorageService != nullptr) { auto dataStorageReference = dataStorageService->GetDataStorage(); pluginContext->ungetService(dataStorageServiceReference); return dataStorageReference->GetDataStorage(); } } return nullptr; } - fs::path GetInputLocation(const mitk::BaseData* data) + std::filesystem::path GetInputLocation(const mitk::BaseData* data) { std::string result; if (data != nullptr) data->GetPropertyList()->GetStringProperty("MITK.IO.reader.inputlocation", result); return result; } QString ColorString(const QString& string, const QColor& color, const QColor& backgroundColor = QColor::Invalid) { if (!color.isValid() && !backgroundColor.isValid()) return string; auto result = QStringLiteral("%1").arg(string); return result; } } /* This constructor has three objectives: * 1. Do widget initialization that cannot be done in the .ui file * 2. Connect signals and slots * 3. Explicitly trigger a reset to a valid initial widget state */ QmitkSegmentationTaskListWidget::QmitkSegmentationTaskListWidget(QWidget* parent) : QWidget(parent), m_Ui(new Ui::QmitkSegmentationTaskListWidget), m_FileSystemWatcher(new QFileSystemWatcher(this)), m_UnsavedChanges(false) { m_Ui->setupUi(this); m_Ui->selectionWidget->SetDataStorage(GetDataStorage()); m_Ui->selectionWidget->SetSelectionIsOptional(true); m_Ui->selectionWidget->SetEmptyInfo(QStringLiteral("Select a segmentation task list")); m_Ui->selectionWidget->SetAutoSelectNewNodes(true); m_Ui->selectionWidget->SetNodePredicate(mitk::TNodePredicateDataType::New()); m_Ui->progressBar->setStyleSheet(QString("QProgressBar::chunk { background-color: %1; }").arg(QmitkStyleManager::GetIconAccentColor())); using Self = QmitkSegmentationTaskListWidget; connect(m_Ui->selectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &Self::OnSelectionChanged); connect(m_Ui->previousButton, &QToolButton::clicked, this, &Self::OnPreviousButtonClicked); connect(m_Ui->nextButton, &QToolButton::clicked, this, &Self::OnNextButtonClicked); connect(m_Ui->loadButton, &QPushButton::clicked, this, &Self::OnLoadButtonClicked); connect(m_FileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &Self::OnResultDirectoryChanged); + auto* prevShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_P), this); + connect(prevShortcut, &QShortcut::activated, this, &Self::OnPreviousTaskShortcutActivated); + + auto* nextShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_N), this); + connect(nextShortcut, &QShortcut::activated, this, &Self::OnNextTaskShortcutActivated); + + auto* loadShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key::Key_L), this); + connect(loadShortcut, &QShortcut::activated, this, &Self::OnLoadTaskShortcutActivated); + this->OnSelectionChanged(m_Ui->selectionWidget->GetSelectedNodes()); } QmitkSegmentationTaskListWidget::~QmitkSegmentationTaskListWidget() { } void QmitkSegmentationTaskListWidget::OnUnsavedChangesSaved() { if (m_UnsavedChanges) { m_UnsavedChanges = false; if (this->ActiveTaskIsShown()) this->UpdateDetailsLabel(); } } /* Make sure that the widget transitions into a valid state whenever the * selection changes. */ void QmitkSegmentationTaskListWidget::OnSelectionChanged(const QmitkSingleNodeSelectionWidget::NodeList& nodes) { this->UnloadTasks(); this->ResetControls(); if (!nodes.empty()) { m_TaskListNode = nodes.front(); auto taskList = dynamic_cast(m_TaskListNode->GetData()); if (taskList != nullptr) { this->OnTaskListChanged(taskList); return; } } this->SetTaskList(nullptr); m_TaskListNode = nullptr; } /* Reset all controls to a default state as a common basis for further * adjustments. */ void QmitkSegmentationTaskListWidget::ResetControls() { m_Ui->progressBar->setEnabled(false); m_Ui->progressBar->setFormat(""); m_Ui->progressBar->setValue(0); m_Ui->progressBar->setMaximum(1); m_Ui->previousButton->setEnabled(false); m_Ui->nextButton->setEnabled(false); this->UpdateLoadButton(); this->UpdateDetailsLabel(); } /* If the segmentation task changed, reset all member variables to expected * default values and reset the file system watcher. */ void QmitkSegmentationTaskListWidget::SetTaskList(mitk::SegmentationTaskList* taskList) { if (m_TaskList != taskList) { m_TaskList = taskList; if (taskList != nullptr) { this->SetCurrentTaskIndex(0); } else { this->SetCurrentTaskIndex(std::nullopt); } this->ResetFileSystemWatcher(); } } void QmitkSegmentationTaskListWidget::ResetFileSystemWatcher() { auto paths = m_FileSystemWatcher->directories(); if (!paths.empty()) m_FileSystemWatcher->removePaths(paths); if (m_TaskList.IsNotNull()) { for (const auto& task : *m_TaskList) { auto resultPath = m_TaskList->GetAbsolutePath(task.GetResult()).remove_filename(); - if (!fs::exists(resultPath)) + if (!std::filesystem::exists(resultPath)) { try { - fs::create_directories(resultPath); + std::filesystem::create_directories(resultPath); } - catch (const fs::filesystem_error& e) + catch (const std::filesystem::filesystem_error& e) { MITK_ERROR << e.what(); } } - if (fs::exists(resultPath)) + if (std::filesystem::exists(resultPath)) m_FileSystemWatcher->addPath(QString::fromStdString(resultPath.string())); } } } void QmitkSegmentationTaskListWidget::OnResultDirectoryChanged(const QString&) { // TODO: If a segmentation was modified ("Unsaved changes"), saved ("Done"), and then the file is deleted, the status should be "Unsaved changes" instead of "Not done". this->UpdateProgressBar(); this->UpdateDetailsLabel(); } void QmitkSegmentationTaskListWidget::UpdateProgressBar() { int progress = 0; for (size_t i = 0; i < m_TaskList->GetNumberOfTasks(); ++i) { if (m_TaskList->IsDone(i)) ++progress; } m_Ui->progressBar->setValue(progress); } /* Provided that a valid segmentation task list is currently selected and the * widget is in its default state, update all controls accordingly. */ void QmitkSegmentationTaskListWidget::OnTaskListChanged(mitk::SegmentationTaskList* taskList) { this->SetTaskList(taskList); const auto numTasks = taskList->GetNumberOfTasks(); m_Ui->progressBar->setMaximum(numTasks); m_Ui->progressBar->setFormat(QStringLiteral("%v/%m Task(s) done")); m_Ui->progressBar->setEnabled(true); this->UpdateProgressBar(); m_Ui->loadButton->setEnabled(true); if (numTasks > 1) m_Ui->nextButton->setEnabled(true); } /* If possible, change the currently displayed task to the previous task. * Enable/disable navigation buttons according to the task's position. */ void QmitkSegmentationTaskListWidget::OnPreviousButtonClicked() { auto current = m_CurrentTaskIndex.value(); if (current != 0) this->SetCurrentTaskIndex(current - 1); this->UpdateNavigationButtons(); } /* If possible, change the currently displayed task to the next task. * Enable/disable navigation buttons according to the task's position. */ void QmitkSegmentationTaskListWidget::OnNextButtonClicked() { + MITK_INFO << "NEXT"; const auto maxIndex = m_TaskList->GetNumberOfTasks() - 1; auto current = m_CurrentTaskIndex.value(); if (current < maxIndex) this->SetCurrentTaskIndex(current + 1); this->UpdateNavigationButtons(); } void QmitkSegmentationTaskListWidget::UpdateNavigationButtons() { const auto maxIndex = m_TaskList->GetNumberOfTasks() - 1; const auto current = m_CurrentTaskIndex.value(); m_Ui->previousButton->setEnabled(current != 0); m_Ui->nextButton->setEnabled(current != maxIndex); } /* Update affected controls when the currently displayed task changed. */ void QmitkSegmentationTaskListWidget::OnCurrentTaskChanged() { this->UpdateLoadButton(); this->UpdateNavigationButtons(); this->UpdateDetailsLabel(); } /* Update the load button according to the currently displayed task. */ void QmitkSegmentationTaskListWidget::UpdateLoadButton() { auto text = !this->ActiveTaskIsShown() ? QStringLiteral("Load task") : QStringLiteral("Task"); if (m_CurrentTaskIndex.has_value()) { const auto current = m_CurrentTaskIndex.value(); if (m_TaskList.IsNotNull()) { text += QString(" %1/%2").arg(current + 1).arg(m_TaskList->GetNumberOfTasks()); if (m_TaskList->HasName(current)) text += QStringLiteral(":\n") + QString::fromStdString(m_TaskList->GetName(current)); } m_Ui->loadButton->setDisabled(this->ActiveTaskIsShown()); } else { m_Ui->loadButton->setEnabled(false); } m_Ui->loadButton->setText(text); } /* Update the details label according to the currently display task. * The text is composed of the status of the task and a variable number * of text blocks according to the optional values provided by the task. */ void QmitkSegmentationTaskListWidget::UpdateDetailsLabel() { if (!m_CurrentTaskIndex.has_value()) { m_Ui->detailsLabel->clear(); return; } const auto current = m_CurrentTaskIndex.value(); bool isDone = m_TaskList->IsDone(current); auto details = QString("

Status: %1 / ").arg(this->ActiveTaskIsShown() ? ColorString("Active", Qt::white, QColor(Qt::green).darker()) : ColorString("Inactive", Qt::white, QColor(Qt::red).darker())); if (m_UnsavedChanges && this->ActiveTaskIsShown()) { details += QString("%1

").arg(ColorString("Unsaved changes", Qt::white, QColor(Qt::red).darker())); } else { details += QString("%1

").arg(isDone ? ColorString("Done", Qt::white, QColor(Qt::green).darker()) : ColorString("Not done", Qt::white, QColor(Qt::red).darker())); } if (m_TaskList->HasDescription(current)) details += QString("

Description: %1

").arg(QString::fromStdString(m_TaskList->GetDescription(current))); QStringList stringList; if (m_TaskList->HasImage(current)) stringList << QString::fromStdString("Image: " + m_TaskList->GetImage(current).string()); if (m_TaskList->HasSegmentation(current)) stringList << QString::fromStdString("Segmentation: " + m_TaskList->GetSegmentation(current).string()); if (m_TaskList->HasLabelName(current)) stringList << QString::fromStdString("Label name: " + m_TaskList->GetLabelName(current)); if (m_TaskList->HasLabelNameSuggestions(current)) stringList << QString::fromStdString("Label name suggestions: " + m_TaskList->GetLabelNameSuggestions(current).string()); if (m_TaskList->HasPreset(current)) stringList << QString::fromStdString("Label set preset: " + m_TaskList->GetPreset(current).string()); if (m_TaskList->HasDynamic(current)) stringList << QString("Segmentation type: %1").arg(m_TaskList->GetDynamic(current) ? "Dynamic" : "Static"); if (!stringList.empty()) details += QString("

%1

").arg(stringList.join(QStringLiteral("
"))); m_Ui->detailsLabel->setText(details); } /* Load/activate the currently displayed task. Unload all data nodes from * previously active tasks first, but spare and reuse the image if possible. */ void QmitkSegmentationTaskListWidget::OnLoadButtonClicked() { if (!this->HandleUnsavedChanges() || m_UnsavedChanges) return; m_Ui->loadButton->setEnabled(false); QApplication::setOverrideCursor(Qt::BusyCursor); this->LoadTask(this->GetImageDataNode(m_CurrentTaskIndex.value())); QApplication::restoreOverrideCursor(); } /* If present, return the image data node for the task with the specified * index. Otherwise, return nullptr. */ mitk::DataNode* QmitkSegmentationTaskListWidget::GetImageDataNode(size_t index) const { const auto imagePath = m_TaskList->GetAbsolutePath(m_TaskList->GetImage(index)); auto imageNodes = GetDataStorage()->GetDerivations(m_TaskListNode, mitk::NodePredicateFunction::New([imagePath](const mitk::DataNode* node) { return imagePath == GetInputLocation(node->GetData()); })); return !imageNodes->empty() ? imageNodes->front() : nullptr; } /* If present, return the segmentation data node for the task with the * specified index. Otherwise, return nullptr. */ mitk::DataNode* QmitkSegmentationTaskListWidget::GetSegmentationDataNode(size_t index) const { const auto* imageNode = this->GetImageDataNode(index); if (imageNode != nullptr) { auto segmentations = GetDataStorage()->GetDerivations(imageNode, mitk::TNodePredicateDataType::New()); if (!segmentations->empty()) return segmentations->front(); } return nullptr; } /* Unload all task data nodes but spare the passed image data node. */ void QmitkSegmentationTaskListWidget::UnloadTasks(const mitk::DataNode* skip) { this->UnsubscribeFromActiveSegmentation(); if (m_TaskListNode.IsNotNull()) { mitk::DataStorage::Pointer dataStorage = GetDataStorage(); auto imageNodes = dataStorage->GetDerivations(m_TaskListNode, mitk::TNodePredicateDataType::New()); for (auto imageNode : *imageNodes) { dataStorage->Remove(dataStorage->GetDerivations(imageNode, nullptr, false)); if (imageNode != skip) dataStorage->Remove(imageNode); } } this->SetActiveTaskIndex(std::nullopt); } void QmitkSegmentationTaskListWidget::LoadNextUnfinishedTask() { const auto current = m_CurrentTaskIndex.value(); const auto numTasks = m_TaskList->GetNumberOfTasks(); for (size_t unboundNext = current; unboundNext < current + numTasks; ++unboundNext) { auto next = unboundNext % numTasks; if (!m_TaskList->IsDone(next)) { this->SetCurrentTaskIndex(next); this->OnLoadButtonClicked(); break; } } } /* Load/activate the currently displayed task. The task must specify * an image. The segmentation is either created from scratch with an optional * name for the first label, possibly based on a label set preset specified by * the task, or loaded as specified by the task. If a result file does * exist, it is chosen as segmentation instead. */ void QmitkSegmentationTaskListWidget::LoadTask(mitk::DataNode::Pointer imageNode) { this->UnloadTasks(imageNode); const auto current = m_CurrentTaskIndex.value(); mitk::Image::Pointer image; mitk::LabelSetImage::Pointer segmentation; try { if (imageNode.IsNull()) { const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetImage(current)); image = mitk::IOUtil::Load(path.string()); } const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetResult(current)); const auto interimPath = m_TaskList->GetInterimPath(path); - if (fs::exists(path)) + if (std::filesystem::exists(path)) { segmentation = mitk::IOUtil::Load(path.string()); } - else if (fs::exists(interimPath)) + else if (std::filesystem::exists(interimPath)) { segmentation = mitk::IOUtil::Load(interimPath.string()); } else if (m_TaskList->HasSegmentation(current)) { const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetSegmentation(current)); segmentation = mitk::IOUtil::Load(path.string()); } } catch (const mitk::Exception&) { return; } auto dataStorage = GetDataStorage(); if (imageNode.IsNull()) { imageNode = mitk::DataNode::New(); imageNode->SetData(image); dataStorage->Add(imageNode, m_TaskListNode); mitk::RenderingManager::GetInstance()->InitializeViews(image->GetTimeGeometry()); } else { image = static_cast(imageNode->GetData()); } auto name = "Task " + std::to_string(current + 1); imageNode->SetName(name); if (segmentation.IsNull()) { mitk::Image::ConstPointer templateImage = image; if (templateImage->GetDimension() > 3) { if (m_TaskList->HasDynamic(current)) { if (!m_TaskList->GetDynamic(current)) templateImage = mitk::SegmentationHelper::GetStaticSegmentationTemplate(image); } else { QmitkStaticDynamicSegmentationDialog dialog(this); dialog.SetReferenceImage(templateImage); dialog.exec(); templateImage = dialog.GetSegmentationTemplate(); } } auto segmentationNode = mitk::LabelSetImageHelper::CreateNewSegmentationNode(imageNode, templateImage, name); segmentation = static_cast(segmentationNode->GetData()); if (m_TaskList->HasPreset(current)) { const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetPreset(current)); mitk::LabelSetIOHelper::LoadLabelSetImagePreset(path.string(), segmentation); } else { auto label = mitk::LabelSetImageHelper::CreateNewLabel(segmentation); if (m_TaskList->HasLabelName(current)) label->SetName(m_TaskList->GetLabelName(current)); segmentation->GetActiveLabelSet()->AddLabel(label); } dataStorage->Add(segmentationNode, imageNode); } else { auto segmentationNode = mitk::DataNode::New(); segmentationNode->SetName(name); segmentationNode->SetData(segmentation); dataStorage->Add(segmentationNode, imageNode); } auto prefs = GetSegmentationPreferences(); if (prefs.IsNotNull()) { if (m_TaskList->HasLabelNameSuggestions(current)) { auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetLabelNameSuggestions(current)); prefs->PutBool("default label naming", false); prefs->Put("label suggestions", QString::fromStdString(path.string())); prefs->PutBool("replace standard suggestions", true); prefs->PutBool("suggest once", true); } else { prefs->PutBool("default label naming", true); prefs->Put("label suggestions", ""); } } m_UnsavedChanges = false; this->SetActiveTaskIndex(current); this->SubscribeToActiveSegmentation(); this->OnCurrentTaskChanged(); } void QmitkSegmentationTaskListWidget::SubscribeToActiveSegmentation() { if (m_ActiveTaskIndex.has_value()) { auto segmentationNode = this->GetSegmentationDataNode(m_ActiveTaskIndex.value()); if (segmentationNode != nullptr) { auto segmentation = static_cast(segmentationNode->GetData()); auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationTaskListWidget::OnSegmentationModified); m_SegmentationModifiedObserverTag = segmentation->AddObserver(itk::ModifiedEvent(), command); } } } void QmitkSegmentationTaskListWidget::UnsubscribeFromActiveSegmentation() { if (m_ActiveTaskIndex.has_value() && m_SegmentationModifiedObserverTag.has_value()) { auto segmentationNode = this->GetSegmentationDataNode(m_ActiveTaskIndex.value()); if (segmentationNode != nullptr) { auto segmentation = static_cast(segmentationNode->GetData()); segmentation->RemoveObserver(m_SegmentationModifiedObserverTag.value()); } m_SegmentationModifiedObserverTag.reset(); } } void QmitkSegmentationTaskListWidget::OnSegmentationModified() { if (!m_UnsavedChanges) { m_UnsavedChanges = true; if (m_ActiveTaskIndex.value() == m_CurrentTaskIndex) this->UpdateDetailsLabel(); } } void QmitkSegmentationTaskListWidget::SetActiveTaskIndex(const std::optional& index) { if (m_ActiveTaskIndex != index) { m_ActiveTaskIndex = index; emit ActiveTaskChanged(m_ActiveTaskIndex); } } void QmitkSegmentationTaskListWidget::SetCurrentTaskIndex(const std::optional& index) { if (m_CurrentTaskIndex != index) { m_CurrentTaskIndex = index; this->OnCurrentTaskChanged(); emit CurrentTaskChanged(m_CurrentTaskIndex); } } bool QmitkSegmentationTaskListWidget::ActiveTaskIsShown() const { return m_ActiveTaskIndex.has_value() && m_CurrentTaskIndex.has_value() && m_ActiveTaskIndex == m_CurrentTaskIndex; } bool QmitkSegmentationTaskListWidget::HandleUnsavedChanges(const QString& alternativeTitle) { if (m_UnsavedChanges) { const auto active = m_ActiveTaskIndex.value(); const auto current = m_CurrentTaskIndex.value(); QString title; if (alternativeTitle.isEmpty()) { title = QString("Load task %1").arg(current + 1); if (m_TaskList->HasName(current)) title += ": " + QString::fromStdString(m_TaskList->GetName(current)); } else { title = alternativeTitle; } auto text = QString("The currently active task %1 ").arg(active + 1); if (m_TaskList->HasName(active)) text += "(" + QString::fromStdString(m_TaskList->GetName(active)) + ") "; text += "has unsaved changes."; auto reply = QMessageBox::question(this, title, text, QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel); switch (reply) { case QMessageBox::Save: - this->SaveActiveTask(!fs::exists(m_TaskList->GetResult(active))); + this->SaveActiveTask(!std::filesystem::exists(m_TaskList->GetResult(active))); break; case QMessageBox::Discard: m_UnsavedChanges = false; break; default: return false; } } return true; } void QmitkSegmentationTaskListWidget::SaveActiveTask(bool saveAsIntermediateResult) { if (!m_ActiveTaskIndex.has_value()) return; QApplication::setOverrideCursor(Qt::BusyCursor); try { const auto active = m_ActiveTaskIndex.value(); m_TaskList->SaveTask(active, this->GetSegmentationDataNode(active)->GetData(), saveAsIntermediateResult); this->OnUnsavedChangesSaved(); } catch (const mitk::Exception& e) { MITK_ERROR << e; } QApplication::restoreOverrideCursor(); } bool QmitkSegmentationTaskListWidget::OnPreShutdown() { return this->HandleUnsavedChanges(QStringLiteral("Application shutdown")); } + +void QmitkSegmentationTaskListWidget::OnPreviousTaskShortcutActivated() +{ + m_Ui->previousButton->click(); +} + +void QmitkSegmentationTaskListWidget::OnNextTaskShortcutActivated() +{ + m_Ui->nextButton->click(); +} + +void QmitkSegmentationTaskListWidget::OnLoadTaskShortcutActivated() +{ + m_Ui->loadButton->click(); +} diff --git a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskListWidget.h b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskListWidget.h index e1e11c235a..4ce97a712b 100644 --- a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskListWidget.h +++ b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskListWidget.h @@ -1,87 +1,90 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkSegmentationTaskListWidget_h #define QmitkSegmentationTaskListWidget_h #include #include #include #include #include class QFileSystemWatcher; namespace Ui { class QmitkSegmentationTaskListWidget; } class QmitkSegmentationTaskListWidget : public QWidget { Q_OBJECT public: explicit QmitkSegmentationTaskListWidget(QWidget* parent = nullptr); ~QmitkSegmentationTaskListWidget() override; bool ActiveTaskIsShown() const; void LoadNextUnfinishedTask(); void SaveActiveTask(bool saveAsIntermediateResult = false); bool OnPreShutdown(); signals: void ActiveTaskChanged(const std::optional& index); void CurrentTaskChanged(const std::optional& index); private: void OnSelectionChanged(const QmitkSingleNodeSelectionWidget::NodeList& nodes); void ResetControls(); void SetTaskList(mitk::SegmentationTaskList* task); void ResetFileSystemWatcher(); void OnResultDirectoryChanged(const QString&); void UpdateProgressBar(); void OnTaskListChanged(mitk::SegmentationTaskList* task); void OnPreviousButtonClicked(); void OnNextButtonClicked(); void OnCurrentTaskChanged(); void UpdateLoadButton(); void UpdateNavigationButtons(); void UpdateDetailsLabel(); void OnLoadButtonClicked(); mitk::DataNode* GetImageDataNode(size_t index) const; void UnloadTasks(const mitk::DataNode* skip = nullptr); void LoadTask(mitk::DataNode::Pointer imageNode = nullptr); void SubscribeToActiveSegmentation(); void UnsubscribeFromActiveSegmentation(); void OnSegmentationModified(); void SetActiveTaskIndex(const std::optional& index); void SetCurrentTaskIndex(const std::optional& index); bool HandleUnsavedChanges(const QString& alternativeTitle = QString()); mitk::DataNode* GetSegmentationDataNode(size_t index) const; void OnUnsavedChangesSaved(); + void OnPreviousTaskShortcutActivated(); + void OnNextTaskShortcutActivated(); + void OnLoadTaskShortcutActivated(); Ui::QmitkSegmentationTaskListWidget* m_Ui; QFileSystemWatcher* m_FileSystemWatcher; mitk::SegmentationTaskList::Pointer m_TaskList; mitk::DataNode::Pointer m_TaskListNode; std::optional m_CurrentTaskIndex; std::optional m_ActiveTaskIndex; std::optional m_SegmentationModifiedObserverTag; bool m_UnsavedChanges; }; #endif diff --git a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp index c22961b79f..b0423f5a91 100644 --- a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp +++ b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp @@ -1,612 +1,563 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkImageNavigatorView.h" #include #include #include #include #include #include #include #include #include #include #include #include const std::string QmitkImageNavigatorView::VIEW_ID = "org.mitk.views.imagenavigator"; QmitkImageNavigatorView::QmitkImageNavigatorView() : m_AxialStepper(nullptr) , m_SagittalStepper(nullptr) , m_CoronalStepper(nullptr) , m_TimeStepper(nullptr) , m_Parent(nullptr) , m_IRenderWindowPart(nullptr) { } QmitkImageNavigatorView::~QmitkImageNavigatorView() { } void QmitkImageNavigatorView::CreateQtPartControl(QWidget *parent) { // create GUI widgets m_Parent = parent; m_Controls.setupUi(parent); connect(m_Controls.m_XWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); connect(m_Controls.m_YWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); connect(m_Controls.m_ZWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); m_Parent->setEnabled(false); mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); this->RenderWindowPartActivated(renderPart); } void QmitkImageNavigatorView::SetFocus () { m_Controls.m_XWorldCoordinateSpinBox->setFocus(); } void QmitkImageNavigatorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (this->m_IRenderWindowPart != renderWindowPart) { this->m_IRenderWindowPart = renderWindowPart; this->m_Parent->setEnabled(true); QmitkRenderWindow* renderWindow = renderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { if (m_AxialStepper) m_AxialStepper->deleteLater(); m_AxialStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorAxial, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorAxialFromSimpleExample"); m_Controls.m_SliceNavigatorAxial->setEnabled(true); m_Controls.m_AxialLabel->setEnabled(true); m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(true); connect(m_AxialStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); connect(m_AxialStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorAxial->setEnabled(false); m_Controls.m_AxialLabel->setEnabled(false); m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(false); } renderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal"); if (renderWindow) { if (m_SagittalStepper) m_SagittalStepper->deleteLater(); m_SagittalStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorSagittal, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorSagittalFromSimpleExample"); m_Controls.m_SliceNavigatorSagittal->setEnabled(true); m_Controls.m_SagittalLabel->setEnabled(true); m_Controls.m_YWorldCoordinateSpinBox->setEnabled(true); connect(m_SagittalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); connect(m_SagittalStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorSagittal->setEnabled(false); m_Controls.m_SagittalLabel->setEnabled(false); m_Controls.m_YWorldCoordinateSpinBox->setEnabled(false); } renderWindow = renderWindowPart->GetQmitkRenderWindow("coronal"); if (renderWindow) { if (m_CoronalStepper) m_CoronalStepper->deleteLater(); m_CoronalStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorCoronal, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorCoronalFromSimpleExample"); m_Controls.m_SliceNavigatorCoronal->setEnabled(true); m_Controls.m_CoronalLabel->setEnabled(true); m_Controls.m_XWorldCoordinateSpinBox->setEnabled(true); connect(m_CoronalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); connect(m_CoronalStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorCoronal->setEnabled(false); m_Controls.m_CoronalLabel->setEnabled(false); m_Controls.m_XWorldCoordinateSpinBox->setEnabled(false); } mitk::SliceNavigationController* timeController = renderWindowPart->GetTimeNavigationController(); if (timeController) { if (m_TimeStepper) m_TimeStepper->deleteLater(); m_TimeStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorTime, timeController->GetTime(), "sliceNavigatorTimeFromSimpleExample"); m_Controls.m_SliceNavigatorTime->setEnabled(true); m_Controls.m_TimeLabel->setEnabled(true); connect(m_TimeStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorTime->setEnabled(false); m_Controls.m_TimeLabel->setEnabled(false); } this->OnRefetch(); this->UpdateStatusBar(); } } void QmitkImageNavigatorView::UpdateStatusBar() { if (m_IRenderWindowPart != nullptr) { mitk::Point3D position = m_IRenderWindowPart->GetSelectedPosition(); mitk::BaseRenderer::Pointer baseRenderer = mitk::BaseRenderer::GetInstance(m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetVtkRenderWindow()); auto globalCurrentTimePoint = baseRenderer->GetTime(); mitk::TNodePredicateDataType::Pointer isImageData = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = GetDataStorage()->GetSubset(isImageData).GetPointer(); if (nodes.IsNotNull()) { mitk::Image::Pointer image3D; mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; int component = 0; node = mitk::FindTopmostVisibleNode(nodes, position, globalCurrentTimePoint, baseRenderer); if (node.IsNotNull()) { bool isBinary(false); node->GetBoolProperty("binary", isBinary); if (isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = GetDataStorage()->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, position, globalCurrentTimePoint, baseRenderer); } if (topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } // get the position and pixel value from the image and build up status bar text auto statusBar = mitk::StatusBar::GetInstance(); if (image3D.IsNotNull() && statusBar != nullptr) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(position, p); auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); if (pixelType == itk::IOPixelEnum::RGB || pixelType == itk::IOPixelEnum::RGBA) { std::string pixelValue = "Pixel RGB(A) value: "; pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); statusBar->DisplayImageInfo(position, p, globalCurrentTimePoint, pixelValue.c_str()); } else if (pixelType == itk::IOPixelEnum::DIFFUSIONTENSOR3D || pixelType == itk::IOPixelEnum::SYMMETRICSECONDRANKTENSOR) { std::string pixelValue = "See ODF Details view. "; statusBar->DisplayImageInfo(position, p, globalCurrentTimePoint, pixelValue.c_str()); } else { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(position, p); mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5( mitk::FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(image3D->GetTimeGeometry()->TimePointToTimeStep(globalCurrentTimePoint)), p, pixelValue, component); statusBar->DisplayImageInfo(position, p, globalCurrentTimePoint, pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } } } void QmitkImageNavigatorView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_IRenderWindowPart = nullptr; m_Parent->setEnabled(false); } int QmitkImageNavigatorView::GetSizeFlags(bool width) { if(!width) { return berry::Constants::MIN | berry::Constants::MAX | berry::Constants::FILL; } else { return 0; } } int QmitkImageNavigatorView::ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult) { if(width==false) { return 200; } else { return preferredResult; } } int QmitkImageNavigatorView::GetClosestAxisIndex(mitk::Vector3D normal) { // cos(theta) = normal . axis // cos(theta) = (a, b, c) . (d, e, f) // cos(theta) = (a, b, c) . (1, 0, 0) = a // cos(theta) = (a, b, c) . (0, 1, 0) = b // cos(theta) = (a, b, c) . (0, 0, 1) = c double absCosThetaWithAxis[3]; for (int i = 0; i < 3; i++) { absCosThetaWithAxis[i] = fabs(normal[i]); } int largestIndex = 0; double largestValue = absCosThetaWithAxis[0]; for (int i = 1; i < 3; i++) { if (absCosThetaWithAxis[i] > largestValue) { largestValue = absCosThetaWithAxis[i]; largestIndex = i; } } return largestIndex; } void QmitkImageNavigatorView::SetBorderColors() { if (m_IRenderWindowPart) { QString decoColor; QmitkRenderWindow* renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("sagittal"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("coronal"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } } } QString QmitkImageNavigatorView::GetDecorationColorOfGeometry(QmitkRenderWindow* renderWindow) { QColor color; float rgb[3] = {1.0f, 1.0f, 1.0f}; float rgbMax = 255.0f; mitk::BaseRenderer::GetInstance(renderWindow->GetVtkRenderWindow())->GetCurrentWorldPlaneGeometryNode()->GetColor(rgb); color.setRed(static_cast(rgb[0]*rgbMax + 0.5)); color.setGreen(static_cast(rgb[1]*rgbMax + 0.5)); color.setBlue(static_cast(rgb[2]*rgbMax + 0.5)); QString colorAsString = QString(color.name()); return colorAsString; } void QmitkImageNavigatorView::SetBorderColor(int axis, QString colorAsStyleSheetString) { if (axis == 0) { this->SetBorderColor(m_Controls.m_XWorldCoordinateSpinBox, colorAsStyleSheetString); } else if (axis == 1) { this->SetBorderColor(m_Controls.m_YWorldCoordinateSpinBox, colorAsStyleSheetString); } else if (axis == 2) { this->SetBorderColor(m_Controls.m_ZWorldCoordinateSpinBox, colorAsStyleSheetString); } } void QmitkImageNavigatorView::SetBorderColor(QDoubleSpinBox *spinBox, QString colorAsStyleSheetString) { assert(spinBox); spinBox->setStyleSheet(QString("border: 2px solid ") + colorAsStyleSheetString + ";"); } -void QmitkImageNavigatorView::SetStepSizes() -{ - this->SetStepSize(0); - this->SetStepSize(1); - this->SetStepSize(2); -} - -void QmitkImageNavigatorView::SetStepSize(int axis) -{ - if (m_IRenderWindowPart) - { - mitk::BaseGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D(); - - if (geometry.IsNotNull()) - { - mitk::Point3D crossPositionInIndexCoordinates; - mitk::Point3D crossPositionInIndexCoordinatesPlus1; - mitk::Point3D crossPositionInMillimetresPlus1; - mitk::Vector3D transformedAxisDirection; - - mitk::Point3D crossPositionInMillimetres = m_IRenderWindowPart->GetSelectedPosition(); - geometry->WorldToIndex(crossPositionInMillimetres, crossPositionInIndexCoordinates); - - crossPositionInIndexCoordinatesPlus1 = crossPositionInIndexCoordinates; - crossPositionInIndexCoordinatesPlus1[axis] += 1; - - geometry->IndexToWorld(crossPositionInIndexCoordinatesPlus1, crossPositionInMillimetresPlus1); - - transformedAxisDirection = crossPositionInMillimetresPlus1 - crossPositionInMillimetres; - - int closestAxisInMillimetreSpace = this->GetClosestAxisIndex(transformedAxisDirection); - double stepSize = transformedAxisDirection.GetNorm(); - this->SetStepSize(closestAxisInMillimetreSpace, stepSize); - } - } -} - -void QmitkImageNavigatorView::SetStepSize(int axis, double stepSize) -{ - if (axis == 0) - { - m_Controls.m_XWorldCoordinateSpinBox->setSingleStep(stepSize); - } - else if (axis == 1) - { - m_Controls.m_YWorldCoordinateSpinBox->setSingleStep(stepSize); - } - else if (axis == 2) - { - m_Controls.m_ZWorldCoordinateSpinBox->setSingleStep(stepSize); - } -} - void QmitkImageNavigatorView::OnMillimetreCoordinateValueChanged() { if (m_IRenderWindowPart) { mitk::TimeGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); if (geometry.IsNotNull()) { mitk::Point3D positionInWorldCoordinates; positionInWorldCoordinates[0] = m_Controls.m_XWorldCoordinateSpinBox->value(); positionInWorldCoordinates[1] = m_Controls.m_YWorldCoordinateSpinBox->value(); positionInWorldCoordinates[2] = m_Controls.m_ZWorldCoordinateSpinBox->value(); m_IRenderWindowPart->SetSelectedPosition(positionInWorldCoordinates); } } } void QmitkImageNavigatorView::OnRefetch() { - if (m_IRenderWindowPart) + if (nullptr == m_IRenderWindowPart) { - mitk::BaseGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D(); - mitk::TimeGeometry::ConstPointer timeGeometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); + return; + } - if (geometry.IsNull() && timeGeometry.IsNotNull()) - { - mitk::TimeStepType timeStep = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetTime()->GetPos(); - geometry = timeGeometry->GetGeometryForTimeStep(timeStep); - SetVisibilityOfTimeSlider(timeGeometry->CountTimeSteps()); - } + mitk::TimeGeometry::ConstPointer timeGeometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); - if (geometry.IsNotNull()) - { - mitk::BoundingBox::BoundsArrayType bounds = geometry->GetBounds(); + if (timeGeometry.IsNull()) + { + return; + } + + SetVisibilityOfTimeSlider(timeGeometry->CountTimeSteps()); + mitk::TimeStepType timeStep = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetTime()->GetPos(); + mitk::BaseGeometry::Pointer geometry = timeGeometry->GetGeometryForTimeStep(timeStep); + if (geometry.IsNotNull()) + { + mitk::BoundingBox::BoundsArrayType bounds = geometry->GetBounds(); - mitk::Point3D cornerPoint1InIndexCoordinates; - cornerPoint1InIndexCoordinates[0] = bounds[0]; - cornerPoint1InIndexCoordinates[1] = bounds[2]; - cornerPoint1InIndexCoordinates[2] = bounds[4]; + mitk::Point3D cornerPoint1InIndexCoordinates; + cornerPoint1InIndexCoordinates[0] = bounds[0]; + cornerPoint1InIndexCoordinates[1] = bounds[2]; + cornerPoint1InIndexCoordinates[2] = bounds[4]; - mitk::Point3D cornerPoint2InIndexCoordinates; - cornerPoint2InIndexCoordinates[0] = bounds[1]; - cornerPoint2InIndexCoordinates[1] = bounds[3]; - cornerPoint2InIndexCoordinates[2] = bounds[5]; + mitk::Point3D cornerPoint2InIndexCoordinates; + cornerPoint2InIndexCoordinates[0] = bounds[1]; + cornerPoint2InIndexCoordinates[1] = bounds[3]; + cornerPoint2InIndexCoordinates[2] = bounds[5]; - if (!geometry->GetImageGeometry()) - { - cornerPoint1InIndexCoordinates[0] += 0.5; - cornerPoint1InIndexCoordinates[1] += 0.5; - cornerPoint1InIndexCoordinates[2] += 0.5; - cornerPoint2InIndexCoordinates[0] -= 0.5; - cornerPoint2InIndexCoordinates[1] -= 0.5; - cornerPoint2InIndexCoordinates[2] -= 0.5; - } + if (!geometry->GetImageGeometry()) + { + cornerPoint1InIndexCoordinates[0] += 0.5; + cornerPoint1InIndexCoordinates[1] += 0.5; + cornerPoint1InIndexCoordinates[2] += 0.5; + cornerPoint2InIndexCoordinates[0] -= 0.5; + cornerPoint2InIndexCoordinates[1] -= 0.5; + cornerPoint2InIndexCoordinates[2] -= 0.5; + } + + mitk::Point3D crossPositionInWorldCoordinates = m_IRenderWindowPart->GetSelectedPosition(); - mitk::Point3D crossPositionInWorldCoordinates = m_IRenderWindowPart->GetSelectedPosition(); + mitk::Point3D cornerPoint1InWorldCoordinates; + mitk::Point3D cornerPoint2InWorldCoordinates; - mitk::Point3D cornerPoint1InWorldCoordinates; - mitk::Point3D cornerPoint2InWorldCoordinates; + geometry->IndexToWorld(cornerPoint1InIndexCoordinates, cornerPoint1InWorldCoordinates); + geometry->IndexToWorld(cornerPoint2InIndexCoordinates, cornerPoint2InWorldCoordinates); - geometry->IndexToWorld(cornerPoint1InIndexCoordinates, cornerPoint1InWorldCoordinates); - geometry->IndexToWorld(cornerPoint2InIndexCoordinates, cornerPoint2InWorldCoordinates); + m_Controls.m_XWorldCoordinateSpinBox->blockSignals(true); + m_Controls.m_YWorldCoordinateSpinBox->blockSignals(true); + m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(true); - m_Controls.m_XWorldCoordinateSpinBox->blockSignals(true); - m_Controls.m_YWorldCoordinateSpinBox->blockSignals(true); - m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(true); + m_Controls.m_XWorldCoordinateSpinBox->setMinimum( + std::min(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); + m_Controls.m_YWorldCoordinateSpinBox->setMinimum( + std::min(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); + m_Controls.m_ZWorldCoordinateSpinBox->setMinimum( + std::min(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); + m_Controls.m_XWorldCoordinateSpinBox->setMaximum( + std::max(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); + m_Controls.m_YWorldCoordinateSpinBox->setMaximum( + std::max(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); + m_Controls.m_ZWorldCoordinateSpinBox->setMaximum( + std::max(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); - m_Controls.m_XWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); - m_Controls.m_YWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); - m_Controls.m_ZWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); - m_Controls.m_XWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); - m_Controls.m_YWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); - m_Controls.m_ZWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); + m_Controls.m_XWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[0]); + m_Controls.m_YWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[1]); + m_Controls.m_ZWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[2]); - m_Controls.m_XWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[0]); - m_Controls.m_YWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[1]); - m_Controls.m_ZWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[2]); + m_Controls.m_XWorldCoordinateSpinBox->blockSignals(false); + m_Controls.m_YWorldCoordinateSpinBox->blockSignals(false); + m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(false); - m_Controls.m_XWorldCoordinateSpinBox->blockSignals(false); - m_Controls.m_YWorldCoordinateSpinBox->blockSignals(false); - m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(false); + /// Calculating 'inverse direction' property. - /// Calculating 'inverse direction' property. + mitk::AffineTransform3D::MatrixType matrix = geometry->GetIndexToWorldTransform()->GetMatrix(); + matrix.GetVnlMatrix().normalize_columns(); + mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); - mitk::AffineTransform3D::MatrixType matrix = geometry->GetIndexToWorldTransform()->GetMatrix(); - matrix.GetVnlMatrix().normalize_columns(); - mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); + for (int worldAxis = 0; worldAxis < 3; ++worldAxis) + { + QmitkRenderWindow *renderWindow = worldAxis == 0 ? m_IRenderWindowPart->GetQmitkRenderWindow("sagittal") : + worldAxis == 1 ? m_IRenderWindowPart->GetQmitkRenderWindow("coronal") : + m_IRenderWindowPart->GetQmitkRenderWindow("axial"); - for (int worldAxis = 0; worldAxis < 3; ++worldAxis) + if (renderWindow) { - QmitkRenderWindow* renderWindow = - worldAxis == 0 ? m_IRenderWindowPart->GetQmitkRenderWindow("sagittal") : - worldAxis == 1 ? m_IRenderWindowPart->GetQmitkRenderWindow("coronal") : - m_IRenderWindowPart->GetQmitkRenderWindow("axial"); + const mitk::BaseGeometry *rendererGeometry = renderWindow->GetRenderer()->GetCurrentWorldGeometry(); - if (renderWindow) + /// Because of some problems with the current way of event signalling, + /// 'Modified' events are sent out from the stepper while the renderer + /// does not have a geometry yet. Therefore, we do a nullptr check here. + /// See bug T22122. This check can be resolved after T22122 got fixed. + if (rendererGeometry) { - const mitk::BaseGeometry* rendererGeometry = renderWindow->GetRenderer()->GetCurrentWorldGeometry(); - - /// Because of some problems with the current way of event signalling, - /// 'Modified' events are sent out from the stepper while the renderer - /// does not have a geometry yet. Therefore, we do a nullptr check here. - /// See bug T22122. This check can be resolved after T22122 got fixed. - if (rendererGeometry) - { - int dominantAxis = itk::Function::Max3( - inverseMatrix[0][worldAxis], - inverseMatrix[1][worldAxis], - inverseMatrix[2][worldAxis]); - - bool referenceGeometryAxisInverted = inverseMatrix[dominantAxis][worldAxis] < 0; - bool rendererZAxisInverted = rendererGeometry->GetAxisVector(2)[worldAxis] < 0; - - /// `referenceGeometryAxisInverted` tells if the direction of the corresponding axis - /// of the reference geometry is flipped compared to the 'world direction' or not. - /// - /// `rendererZAxisInverted` tells if direction of the renderer geometry z axis is - /// flipped compared to the 'world direction' or not. This is the same as the indexing - /// direction in the slice navigation controller and matches the 'top' property when - /// initialising the renderer planes. (If 'top' was true then the direction is - /// inverted.) - /// - /// The world direction can be +1 ('up') that means right, anterior or superior, or - /// it can be -1 ('down') that means left, posterior or inferior, respectively. - /// - /// If these two do not match, we have to invert the index between the slice navigation - /// controller and the slider navigator widget, so that the user can see and control - /// the index according to the reference geometry, rather than the slice navigation - /// controller. The index in the slice navigation controller depends on in which way - /// the reference geometry has been resliced for the renderer, and it does not necessarily - /// match neither the world direction, nor the direction of the corresponding axis of - /// the reference geometry. Hence, it is a merely internal information that should not - /// be exposed to the GUI. - /// - /// So that one can navigate in the same world direction by dragging the slider - /// right, regardless of the direction of the corresponding axis of the reference - /// geometry, we invert the direction of the controls if the reference geometry axis - /// is inverted but the direction is not ('inversDirection' is false) or the other - /// way around. - - bool inverseDirection = referenceGeometryAxisInverted != rendererZAxisInverted; - - QmitkSliderNavigatorWidget* navigatorWidget = - worldAxis == 0 ? m_Controls.m_SliceNavigatorSagittal : - worldAxis == 1 ? m_Controls.m_SliceNavigatorCoronal : - m_Controls.m_SliceNavigatorAxial; - - navigatorWidget->SetInverseDirection(inverseDirection); - - // This should be a preference (see T22254) - // bool invertedControls = referenceGeometryAxisInverted != inverseDirection; - // navigatorWidget->SetInvertedControls(invertedControls); - } + int dominantAxis = + itk::Function::Max3(inverseMatrix[0][worldAxis], inverseMatrix[1][worldAxis], inverseMatrix[2][worldAxis]); + + bool referenceGeometryAxisInverted = inverseMatrix[dominantAxis][worldAxis] < 0; + bool rendererZAxisInverted = rendererGeometry->GetAxisVector(2)[worldAxis] < 0; + + /// `referenceGeometryAxisInverted` tells if the direction of the corresponding axis + /// of the reference geometry is flipped compared to the 'world direction' or not. + /// + /// `rendererZAxisInverted` tells if direction of the renderer geometry z axis is + /// flipped compared to the 'world direction' or not. This is the same as the indexing + /// direction in the slice navigation controller and matches the 'top' property when + /// initialising the renderer planes. (If 'top' was true then the direction is + /// inverted.) + /// + /// The world direction can be +1 ('up') that means right, anterior or superior, or + /// it can be -1 ('down') that means left, posterior or inferior, respectively. + /// + /// If these two do not match, we have to invert the index between the slice navigation + /// controller and the slider navigator widget, so that the user can see and control + /// the index according to the reference geometry, rather than the slice navigation + /// controller. The index in the slice navigation controller depends on in which way + /// the reference geometry has been resliced for the renderer, and it does not necessarily + /// match neither the world direction, nor the direction of the corresponding axis of + /// the reference geometry. Hence, it is a merely internal information that should not + /// be exposed to the GUI. + /// + /// So that one can navigate in the same world direction by dragging the slider + /// right, regardless of the direction of the corresponding axis of the reference + /// geometry, we invert the direction of the controls if the reference geometry axis + /// is inverted but the direction is not ('inversDirection' is false) or the other + /// way around. + + bool inverseDirection = referenceGeometryAxisInverted != rendererZAxisInverted; + + QmitkSliderNavigatorWidget* navigatorWidget = + worldAxis == 0 ? m_Controls.m_SliceNavigatorSagittal : + worldAxis == 1 ? m_Controls.m_SliceNavigatorCoronal : + m_Controls.m_SliceNavigatorAxial; + + navigatorWidget->SetInverseDirection(inverseDirection); + + // This should be a preference (see T22254) + // bool invertedControls = referenceGeometryAxisInverted != inverseDirection; + // navigatorWidget->SetInvertedControls(invertedControls); } } } - - this->SetBorderColors(); - } + + this->SetBorderColors(); } void QmitkImageNavigatorView::SetVisibilityOfTimeSlider(std::size_t timeSteps) { m_Controls.m_SliceNavigatorTime->setVisible(timeSteps > 1); m_Controls.m_TimeLabel->setVisible(timeSteps > 1); } diff --git a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.h b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.h index a9d687bf67..110ef123c4 100644 --- a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.h +++ b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.h @@ -1,100 +1,95 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#ifndef _QMITKIMAGENAVIGATORVIEW_H_INCLUDED -#define _QMITKIMAGENAVIGATORVIEW_H_INCLUDED +#ifndef QMITKIMAGENAVIGATORVIEW_H +#define QMITKIMAGENAVIGATORVIEW_H #include #include #include #include "ui_QmitkImageNavigatorViewControls.h" - class QmitkStepperAdapter; /*! * \ingroup org_mitk_gui_qt_imagenavigator_internal * * \class QmitkImageNavigatorView * * \brief Provides a means to scan quickly through a dataset via Axial, * Coronal and Sagittal sliders, displaying millimetre location and stepper position. * * For images, the stepper position corresponds to a voxel index. For other datasets * such as a surface, it corresponds to a sub-division of the bounding box. * * \sa QmitkAbstractView */ class QmitkImageNavigatorView : public QmitkAbstractView, public mitk::IRenderWindowPartListener, public berry::ISizeProvider { - // this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) Q_OBJECT public: static const std::string VIEW_ID; QmitkImageNavigatorView(); ~QmitkImageNavigatorView() override; void CreateQtPartControl(QWidget *parent) override; int GetSizeFlags(bool width) override; int ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult) override; protected slots: void OnMillimetreCoordinateValueChanged(); void OnRefetch(); void UpdateStatusBar(); protected: void SetFocus() override; void RenderWindowPartActivated(mitk::IRenderWindowPart *renderWindowPart) override; void RenderWindowPartDeactivated(mitk::IRenderWindowPart *renderWindowPart) override; void SetBorderColors(); void SetBorderColor(QDoubleSpinBox *spinBox, QString colorAsStyleSheetString); void SetBorderColor(int axis, QString colorAsStyleSheetString); - void SetStepSizes(); - void SetStepSize(int axis); - void SetStepSize(int axis, double stepSize); int GetClosestAxisIndex(mitk::Vector3D normal); void SetVisibilityOfTimeSlider(std::size_t timeSteps); Ui::QmitkImageNavigatorViewControls m_Controls; QmitkStepperAdapter* m_AxialStepper; QmitkStepperAdapter* m_SagittalStepper; QmitkStepperAdapter* m_CoronalStepper; QmitkStepperAdapter* m_TimeStepper; QWidget* m_Parent; mitk::IRenderWindowPart* m_IRenderWindowPart; /** * @brief GetDecorationColorOfGeometry helper method to get the color of a helper geometry node. * @param renderWindow The renderwindow of the geometry * @return the color for decoration in QString format (#RRGGBB). */ QString GetDecorationColorOfGeometry(QmitkRenderWindow *renderWindow); }; -#endif // _QMITKIMAGENAVIGATORVIEW_H_INCLUDED +#endif // QMITKIMAGENAVIGATORVIEW_H 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 82dcb5a5b7..8fff9e1c8c 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox +++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox @@ -1,405 +1,415 @@ /** \page org_mitk_views_segmentation The Segmentation View \imageMacro{segmentation-dox.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_segmentationoverview 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. A multilabel segmentation can contain more than one label and 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 MITK segmentation plugin allows you to create multilabel segmentations of anatomical and pathological structures in medical images. The plugin consists of two views:
  • Segmentation View: Manual and (semi-)automatic segmentation
  • \subpage org_mitk_views_segmentationutilities : Segmentation post-processing
In this documentation, the features and usage of the segmentation view will be described. For an introduction to the segmentation utilities 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 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
  • Show only selected nodes: Enable if only the selected segmentation and the reference image should be visible
  • Smoothed surface creation: Set certain smoothing parameters for surface creation
  • Default label set preset: Start a new segmentation with this preset instead of a default label
  • Label creation: Assign default names and colors to new labels or ask users for them
  • Label suggestions: Specify custom suggestions for label names and colors
\section org_mitk_views_segmentationtechnicalissues 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 multilabel images of the same extent as the original image.
\section org_mitk_views_segmentationdataselection Data selection & creating new segmentations To select a reference image for the segmentation, click on the Selected image selection widget and choose a suitable image from the selection available in the data manager. Once an image is selected, a new segmentation can be created on this reference image by clicking the button to the right of the Selected segmentation selection widget. A new multilabel segmentation with an initial label is automatically generated. The new segmentation will be added to the data manager as sub-node of the reference image. This item is then automatically selected in the data selection, which allows to start editing the new segmentation right away. \imageMacro{"QmitkSegmentation_DataSelection.png","Data selection",12} Alternatively to creating a new segmentation, an existing one can be edited as well. If a reference image is selected for which a segmentation already exists in the data manager, the auto selection mode will automatically select a fitting segmentation. Clicking on the segmentation selection widget a drop down list will open, containing all suitable segmentations for the selected reference dataset available in the data manager. \section org_mitk_views_segmentationlayers Segmentation layers For each multilabel segmentation different layers can be added or deleted. The layers can be used independently and layers can be switched using the left and right arrows. 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. \imageMacro{"QmitkSegmentation_LayerSelection.png","Layer selection",12} \section org_mitk_views_segmentationlabels Segmentation labels For each layer, one or more labels can be added. Pressing the double arrow on the right, all created labels are shown in the 'Label 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{"QmitkSegmentation_LabelTable.png","The 'Label Table' shows all labels in the current segmentation session",12} The 'New Label' button can be used to add a new label. This will automatically add a new label with a distinct name and color to the list of available labels.\n 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_segmentationlabelsuggestions Label name and color suggestions When renaming labels or creating new labels with enforced manual naming in the Segmentation preferences, entering names is supported by auto-completion for common label names. The list of predefined label names and colors for the auto-completion feature can be either extented or replaced by a custom list of label name and color suggestions. This custom list must be specified as a JSON file, just containing an array of objects, each with a mandatory "name" string and an optional "color" string. The JSON file can be set in the Segmentation preferences as well as a few options on how to apply these suggestions. \subsection org_mitk_views_segmentationlabelpresets Saving and loading label set presets Label set presets are useful to share a certain style or scheme between different segmentation sessions or to provide templates for new segmentation sessions. The properties of all labels in all layers like their names, colors, and visibilities are saved as a label set preset by clicking on the 'Save label set preset' button. Label set presets are applied to any segmentation session by clicking on the 'Load label set preset' button. If a label for a certain value already exists, its properties are overridden by the preset. If a label for a certain value does not yet exist, an empty label with the label properties of the preset is created. The actual segmentations of labels are unaffected as label set presets only store label properties. \subsubsection org_mitk_views_segmentationdefaultlabelpresets Applying label set presets by default If you work on a repetetive segmentation task, manually loading the same label set preset for each and every new segmentation can be tedious. To streamline your workflow, you can set a default label set preset in the Segmentation preferences (Ctrl+P). When set, this label set preset will be applied to all new segmentations instead of creating the default red "New label 1" label. \subsection org_mitk_views_segmentationlabelsearch Searching for 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 a label by providing assistance for label name completion. If the label is found, it will become the active one after pressing 'enter'. To start editing a label needs to be activated by clicking on the corresponding row in the 'Label Table'. Only one label can be active at the time. Then the segmentation tools in the toolbox can be used for mask generation. \subsection org_mitk_views_multilabelsegmentationoperationsonlabels Operations on labels Depending on the selection in the 'Label Table', several actions are offered: \subsubsection org_mitk_views_segmentationoperationssingleselection Operations with single label selection Right-clicking on any label opens a pop-up menu that 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{"QmitkSegmentation_OperationsSingleSelection.png","Context menu for single label selection",12} \subsubsection org_mitk_views_segmentationoperationsmultiselection Operations with multiple label selection Shift-clickink on multiple labels allows to select more than one label. If more than one label is selected, different options will appear in the menu:
  • 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.
\imageMacro{"QmitkSegmentation_OperationsMultiSelection.png","Context menu for multiple label selection",12} \section org_mitk_views_segmentationtooloverview Segmentation tool overview MITK offers a comprehensive set of slice-based 2D and (semi-)automated 3D segmentation tools. The manual 2D tools require some user interaction and can only be applied to a single image slice whereas the 3D tools operate on the whole image. The 3D tools usually only require a small amount of user interaction, i.e. placing seed points or setting / adjusting parameters. You can switch between the different toolsets by selecting the 2D or 3D tab in the segmentation view. \imageMacro{QmitkSegmentation_ToolOverview.png,"An overview of the existing 2D and 3D tools in MITK.",5.50} \section org_mitk_views_segmentation2dsegmentation 2D segmentation tools With 2D manual contouring you define which voxels are part of the segmentation and which are not. This allows you to create segmentations of any structures of interest in an image. You can also use manual contouring to correct segmentations that result from sub-optimal automatic methods. The drawback of manual contouring is that you might need to define contours on many 2D slices. However, this is mitigated by the interpolation feature, which will make suggestions for a segmentation. To start using one of the editing tools, click its button from the displayed toolbox. The selected editing tool will be active and its corresponding button will stay pressed until you click the button again. Selecting a different tool also deactivates the previous one.\n If you have to delineate a lot of images, shortcuts to switch between tools becomes convenient. For that, just hit the first letter of each tool to activate it (A for Add, S for Subtract, etc.). All of the editing tools work by the same principle: using the mouse (left button) to click anywhere in a 2D window (any of the orientations axial, sagittal, or coronal), moving the mouse while holding the mouse button and releasing the button to finish the editing action. Multi-step undo and redo is fully supported by all editing tools by using the application-wide undo / redo buttons in the toolbar. Remark: Clicking and moving the mouse in any of the 2D render windows will move the crosshair that defines what part of the image is displayed. This behavior is disabled as long as any of the manual segmentation tools are active - otherwise you might have a hard time concentrating on the contour you are drawing. \subsection org_mitk_views_segmentationaddsubtracttools Add and subtract tools \imageMacro{QmitkSegmentation_IMGIconAddSubtract.png,"Add and subtract tools",7.70} Use the left mouse button to draw a closed contour. When releasing the mouse button, the contour will be added (Add tool) to or removed (Subtract tool) from the current segmentation. Adding and subtracting voxels can be iteratively repeated for the same segmentation. Holding CTRL / CMD while drawing will invert the current tool's behavior (i.e. instead of adding voxels, they will be subtracted). +\subsection org_mitk_views_segmentationlassotool Lasso tool +\imageMacro{QmitkSegmentation_Lasso.png,"Lasso tool",7.70} + +The tool is a more advanced version of the add/substract tool. It offers you the following features: +
    +
  1. Generating a polygon segmentation (click left mouse button to set ancor point) +
  2. Freehand contouring (like the add tool; press left mouse button while moving the mouse) +
  3. Move ancor points (select an ancor point, press left mouse button while moving the mouse) +
  4. Add new ancor points (press CTRL while click left mouse to add an ancor to the contour) +
  5. Delete an ancor point (press Del while ancor point is selected) +
  6. Segmentation can be added to the label (Add mode) or substracted (Substract mode) +
+To start a segmentation double left click where the first ancor point should be. To end the segmentation double left click where the last ancor point should be. +Please note that: +
    +
  • feature 3-6 are only available, if auto confirm is *not* activated +
  • feature 3-5 is not available for freehand contour segments +
+ \subsection org_mitk_views_segmentationpaintwipetools 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_segmentationregiongrowingtool Region growing tool \imageMacro{QmitkSegmentation_IMGIconRegionGrowing.png,"Region growing tool",3.81} Click at one point in a 2D slice widget to add an image region to the segmentation with the region growing tool. Region Growing selects all pixels around the mouse cursor that have a similar gray value as the pixel below the mouse cursor. This allows to quickly create segmentations of structures that have a good contrast to surrounding tissue. The tool operates based on the current level window, so changing the level window to optimize the contrast for the ROI is encouraged. Moving the mouse up / down is different from left / right: Moving up the cursor while holding the left mouse button widens the range for the included grey values; moving it down narrows it. Moving the mouse left and right will shift the range. The tool will select more or less pixels, corresponding to the changing gray value range. \if THISISNOTIMPLEMENTEDATTHEMOMENT A common issue with region growing is the so called "leakage" which happens when the structure of interest is connected to other pixels, of similar gray values, through a narrow "bridge" at the border of the structure. The Region Growing tool comes with a "leakage detection/removal" feature. If leakage happens, you can left-click into the leakage region and the tool will try to automatically remove this region (see illustration below). \imageMacro{QmitkSegmentation_Leakage.png,"Leakage correction feature of the region growing tool",11.28} \endif \subsection org_mitk_views_segmentationfilltool Fill tool \imageMacro{QmitkSegmentation_IMGIconFill.png,"Fill tool",3.81} Left-click inside a segmentation with holes to completely fill all holes. Left-click inside a hole to fill only this specific hole. \subsection org_mitk_views_segmentationerasetool Erase tool \imageMacro{QmitkSegmentation_IMGIconErase.png,"Erase tool",3.79} This tool removes a connected part of pixels that form a segmentation. You may use it to remove single segmentations (left-click on specific segmentation) or to clear a whole slice at once (left-click outside a segmentation). \subsection org_mitk_views_segmentationlivewiretool Live wire tool \imageMacro{QmitkSegmentation_IMGIconLiveWire.png,"Live wire tool",3.01} The Live Wire Tool acts as a magnetic lasso with a contour snapping to edges of objects. \imageMacro{QmitkSegmentation_IMGLiveWireUsage.PNG,"Steps for using the Live Wire Tool",16.00} -
    -
  • (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. +The tool handling is the same like the Lasso tool (see for more info), except it generates live wire contours instead of straight lines. \subsection org_mitk_views_segmentationinterpolation 2D and 3D Interpolation Creating segmentations using 2D manual contouring for large image volumes may be very time-consuming, because structures of interest may cover a large range of slices. Note: Interpolation is currently disabled for segmentations containing more than one label. 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. 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. 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 undo / redo is not yet working for 3D interpolation. The current state of the 3D interpolation can be saved across application restart. For that, just click on save project during the interpolation is active. After restarting the application and load your project you can click on "Reinit Interpolation" within the 3D interpolation GUI area. \section org_mitk_views_segmentation3dsegmentation 3D segmentation tools The 3D tools operate on the whole image and require usually a small amount of interaction like placing seed-points or specifying certain parameters. All 3D tools provide an immediate segmentation feedback, which is displayed as a transparent green overlay. For accepting a preview you have to press the Confirm button of the selected tool. The following 3D tools are available: \subsection org_mitk_views_segmentation3dthresholdtool 3D Threshold tool The thresholding tool simply applies a 3D threshold to the patient image. All pixels with values equal or above the selected threshold are labeled as part of the segmentation. You can change the threshold by either moving the slider of setting a certain value in the spinbox. \imageMacro{QmitkSegmentation_3DThresholdTool.png,"3D Threshold tool",10.00} \subsection org_mitk_views_segmentation3dulthresholdTool 3D upper / lower threshold tool The Upper/Lower Thresholding tool works similar to the simple 3D threshold tool but allows you to define an upper and lower threshold. All pixels with values within this threshold interval will be labeled as part of the segmentation. \imageMacro{QmitkSegmentation_3DULThresholdTool.png,"3D upper / lower threshold tool",10.00} \subsection org_mitk_views_segmentation3dotsutool 3D Otsu tool The 3D Otsu tool provides a more sophisticated thresholding algorithm. It allows you to define a number of regions. Based on the image histogram the pixels will then be divided into different regions. The more regions you define the longer the calculation will take. You can select afterwards which of these regions you want to confirm as segmentation. \imageMacro{QmitkSegmentation_3DOtsuTool.png,"3D Otsu tool",10.00} \subsection org_mitk_views_segmentation3drgtool 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_segmentationpickingtool Picking Tool The Picking tool offers two modes that allow you to manipulate "islands" within your segmentation. This is especially useful if e.g. a thresholding provided you with several areas within your image but you are just interested in one special region. - Picking mode: Allows you to select certain "islands". When the pick is confirmed, the complete content of the active label will be removed except the pick. This mode is beneficial if you have a lot segmentation noise and want to pick the relevant parts and dismiss the rest. Hint: You can also pick from other labels, but this will only work if these labels are unlocked. - Relabel mode: Allows you to select certain "islands". When the pick is confirmed, it will be relabeled and added to the active label content. Hint: This mode ignores the locks of other labels, hence you do not need to unlock them explicitly. \imageMacro{QmitkSegmentation_PickingTool.png,"Picking tool",10.00} \subsection org_mitk_views_segmentationnnUNetTool nnU-Net Tool (Ubuntu only) \imageMacro{QmitkSegmentation_nnUnetTool.png,"nnUNet tool",10.00} This tool provides a GUI to the deep learning-based segmentation algorithm called the nnUNet. With this tool, you can get a segmentation mask predicted for the loaded image in MITK. Be ready with the pre-trained weights (a.k.a RESULTS_FOLDER) for your organ or task concerned, before using the tool. For a detailed explanation of the parameters and pre-trained weights folder structure etc., please refer to https://github.com/MIC-DKFZ/nnUNet.
Remark: The tool assumes that you have a Python3 environment with nnUNet (pip) installed. Your machine should be also equipped with a CUDA enabled GPU. \subsubsection org_mitk_views_segmentationnnUNetToolWorkflow Workflow: -# Select the "Python Path" drop-down to see if MITK has automatically detected other Python environments. Click on a fitting environment for the nnUNet inference or click "Select" in the dropdown to choose an unlisted python environment. Note that, while selecting an arbitrary environment folder, only select the base folder, e.g. "myenv". No need to select all the way until "../myenv/bin/python", for example. -# Click on the "nnUNet Results Folder" directory icon to navigate to the results folder on your hard disk. This is equivalent to setting the RESULTS_FOLDER environment variable. If your results folder is as per the nnUNet required folder structure, the configuration, trainers, tasks and folds are automatically parsed and correspondingly loaded in the drop-down boxes as shown below. Note that MITK automatically checks for the RESULTS_FOLDER environment variable value and, if found, auto parses that directory when the tool is started. \imageMacro{QmitkSegmentation_nnUNet_Settings.png,"nnUNet Segmentation Settings",10} -# Choose your required Task-Configuration-Trainer-Planner-Fold parameters, sequentially. By default, all entries are selected inside the "Fold" dropdown (shown: "All"). Note that, even if you uncheck all entries from the "Fold" dropdown (shown: "None"), then too, all folds would be considered for inferencing. -# For ensemble predictions, you will get the option to select parameters irrespective on postprocessing files available in the ensembles folder of RESULTS_FOLDER. Note that, if a postprocessing json file exists for the selected combination then it will used for ensembling, by default. To choose not to, uncheck the "Use PostProcessing JSON" in the "Advanced" section. \imageMacro{QmitkSegmentation_nnUNet_ensemble.png,"nnUNet Segmentation Settings",10} -# If your task is trained with multi-modal inputs, then "Multi-Modal" checkbox is checked and the no.of modalities are preloaded and shown next to "Required Modalities". Instantly, as much node selectors with corresponding modality names should appear below to select the Data Manager along including a selector with preselected with the reference node. Now, select the image nodes in the node selectors accordingly for accurate inferencing. \imageMacro{QmitkSegmentation_nnUNet_multimodal.png,"nnUNet Multi Modal Settings",10.00} -# Click on "Preview". -# In the "Advanced" section, you can also activate other options like "Mixed Precision" and "Enable Mirroring" (for test time data augmentation) pertaining to nnUNet. \imageMacro{QmitkSegmentation_nnUNet_Advanced.png,"nnUNet Advanced Settings",10.00} -# Use "Advanced" > "GPU Id" combobox to change the preferred GPU for inferencing. This is internally equivalent to setting the CUDA_VISIBLE_DEVICES environment variable. -# Every inferred segmentation is cached to prevent a redundant computation. In case, a user doesn't wish to cache a Preview, uncheck the "Enable Caching" in the "Advanced" section. This will ensure that the current parameters will neither be checked against the existing cache nor a segmentation be loaded from it when Preview is clicked. -# You may always clear all the cached segmentations by clicking "Clear Cache" button. \subsubsection org_mitk_views_segmentationnnUNetToolMisc Miscellaneous: -# In case you want to reload/reparse the folders in the "nnUNet Results Folder", eg. after adding new tasks into it, you may do so without reselecting the folder again by clicking the "Refresh Results Folder" button. -# The "Advanced" > "GPU Id" combobox lists the Nvidia GPUs available by parsing the nvidia-smi utility output. In case your machine has Nvidia CUDA enabled GPUs but the nvidia-smi fails for some reason, the "GPU Id" combobox will show no entries. In such a situation, it's still possible to execute inferencing by manually entering the preferred GPU Id, eg. 0 in the combobox. -# The "Advanced" > "Available Models" lists the available pre-trained tasks for download. Make sure you have internet connection. Then, choose a Task from the dropdown and click the Download button. The pre-trained models for the selected Task will be downloaded and placed to the RESULTS_FOLDER directory automatically. -# In the RESULTS_FOLDER directory, inside the trainer-planner folder of every task, MITK keeps a "mitk_export.json" file for fast loading for multi-modal information. It is recommended not to delete this file(s) for a fast responsive UI. Tip: If multi-modal information shown on MITK is not correct for a given task, you may modify this JSON file and try again. \section org_mitk_views_segmentationpostprocessing Additional things you can do with segmentations Segmentations are never an end in themselves. Consequently, the segmentation view adds a couple of "post-processing" actions, accessible through the context-menu of the data manager. \imageMacro{QmitkSegmentation_IMGDataManagerContextMenu.png,"Context menu items for segmentations",10.58}
  • 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_segmentationtechnicaldetail Technical information for developers For technical specifications see \subpage QmitkSegmentationTechnicalPage and for information on the extensions of the tools system \subpage toolextensions. */ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_Lasso.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_Lasso.png new file mode 100644 index 0000000000..3336c07b3c Binary files /dev/null and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_Lasso.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.cpp index 0742129dfa..ee964c22aa 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.cpp @@ -1,1147 +1,1157 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkLabelSetWidget.h" // mitk #include #include #include #include #include #include #include #include #include #include #include // Qmitk #include #include #include // Qt #include #include #include #include #include #include #include +#include #include #include // itk #include QmitkLabelSetWidget::QmitkLabelSetWidget(QWidget *parent) : QWidget(parent), m_DataStorage(nullptr), m_Completer(nullptr), m_ToolManager(nullptr), m_ProcessingManualSelection(false) { m_Controls.setupUi(this); m_ColorSequenceRainbow.GoToBegin(); m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); m_Controls.m_LabelSearchBox->setAlwaysShowClearIcon(true); m_Controls.m_LabelSearchBox->setShowSearchIcon(true); QStringList completionList; completionList << ""; m_Completer = new QCompleter(completionList, this); m_Completer->setCaseSensitivity(Qt::CaseInsensitive); m_Controls.m_LabelSearchBox->setCompleter(m_Completer); connect(m_Controls.m_LabelSearchBox, SIGNAL(returnPressed()), this, SLOT(OnSearchLabel())); + auto* renameLabelShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_R), this); + connect(renameLabelShortcut, &QShortcut::activated, this, &QmitkLabelSetWidget::OnRenameLabelShortcutActivated); + QStringListModel *completeModel = static_cast(m_Completer->model()); completeModel->setStringList(GetLabelStringList()); m_Controls.m_LabelSearchBox->setEnabled(false); m_Controls.m_lblCaption->setText(""); InitializeTableWidget(); } QmitkLabelSetWidget::~QmitkLabelSetWidget() {} void QmitkLabelSetWidget::OnTableViewContextMenuRequested(const QPoint & /*pos*/) { int pixelValue = GetPixelValueOfSelectedItem(); if (-1 == pixelValue) return; QMenu *menu = new QMenu(m_Controls.m_LabelSetTableWidget); if (m_Controls.m_LabelSetTableWidget->selectedItems().size() > 1) { QAction *mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge selection on current label", this); mergeAction->setEnabled(true); QObject::connect(mergeAction, SIGNAL(triggered(bool)), this, SLOT(OnMergeLabels(bool))); menu->addAction(mergeAction); QAction *removeLabelsAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove selected labels", this); removeLabelsAction->setEnabled(true); QObject::connect(removeLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnRemoveLabels(bool))); menu->addAction(removeLabelsAction); QAction *eraseLabelsAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase selected labels", this); eraseLabelsAction->setEnabled(true); QObject::connect(eraseLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnEraseLabels(bool))); menu->addAction(eraseLabelsAction); } else { QAction *renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "Rename...", this); renameAction->setEnabled(true); QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool))); menu->addAction(renameAction); QAction *removeAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove...", this); removeAction->setEnabled(true); QObject::connect(removeAction, SIGNAL(triggered(bool)), this, SLOT(OnRemoveLabel(bool))); menu->addAction(removeAction); QAction *eraseAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase...", this); eraseAction->setEnabled(true); QObject::connect(eraseAction, SIGNAL(triggered(bool)), this, SLOT(OnEraseLabel(bool))); menu->addAction(eraseAction); QAction *randomColorAction = new QAction(QIcon(":/Qmitk/RandomColor.png"), "Random color", this); randomColorAction->setEnabled(true); QObject::connect(randomColorAction, SIGNAL(triggered(bool)), this, SLOT(OnRandomColor(bool))); menu->addAction(randomColorAction); QAction *viewOnlyAction = new QAction(QIcon(":/Qmitk/visible.png"), "View only", this); viewOnlyAction->setEnabled(true); QObject::connect(viewOnlyAction, SIGNAL(triggered(bool)), this, SLOT(OnSetOnlyActiveLabelVisible(bool))); menu->addAction(viewOnlyAction); QAction *viewAllAction = new QAction(QIcon(":/Qmitk/visible.png"), "View all", this); viewAllAction->setEnabled(true); QObject::connect(viewAllAction, SIGNAL(triggered(bool)), this, SLOT(OnSetAllLabelsVisible(bool))); menu->addAction(viewAllAction); QAction *hideAllAction = new QAction(QIcon(":/Qmitk/invisible.png"), "Hide all", this); hideAllAction->setEnabled(true); QObject::connect(hideAllAction, SIGNAL(triggered(bool)), this, SLOT(OnSetAllLabelsInvisible(bool))); menu->addAction(hideAllAction); QAction *lockAllAction = new QAction(QIcon(":/Qmitk/lock.png"), "Lock all", this); lockAllAction->setEnabled(true); QObject::connect(lockAllAction, SIGNAL(triggered(bool)), this, SLOT(OnLockAllLabels(bool))); menu->addAction(lockAllAction); QAction *unlockAllAction = new QAction(QIcon(":/Qmitk/unlock.png"), "Unlock all", this); unlockAllAction->setEnabled(true); QObject::connect(unlockAllAction, SIGNAL(triggered(bool)), this, SLOT(OnUnlockAllLabels(bool))); menu->addAction(unlockAllAction); QAction *createSurfaceAction = new QAction(QIcon(":/Qmitk/CreateSurface.png"), "Create surface", this); createSurfaceAction->setEnabled(true); createSurfaceAction->setMenu(new QMenu()); QAction *tmp1 = createSurfaceAction->menu()->addAction(QString("Detailed")); QAction *tmp2 = createSurfaceAction->menu()->addAction(QString("Smoothed")); QObject::connect(tmp1, SIGNAL(triggered(bool)), this, SLOT(OnCreateDetailedSurface(bool))); QObject::connect(tmp2, SIGNAL(triggered(bool)), this, SLOT(OnCreateSmoothedSurface(bool))); menu->addAction(createSurfaceAction); QAction *createMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create mask", this); createMaskAction->setEnabled(true); QObject::connect(createMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateMask(bool))); menu->addAction(createMaskAction); QAction *createCroppedMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create cropped mask", this); createCroppedMaskAction->setEnabled(true); QObject::connect(createCroppedMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateCroppedMask(bool))); menu->addAction(createCroppedMaskAction); QSlider *opacitySlider = new QSlider; opacitySlider->setMinimum(0); opacitySlider->setMaximum(100); opacitySlider->setOrientation(Qt::Horizontal); QObject::connect(opacitySlider, SIGNAL(valueChanged(int)), this, SLOT(OnOpacityChanged(int))); QLabel *_OpacityLabel = new QLabel("Opacity: "); QVBoxLayout *_OpacityWidgetLayout = new QVBoxLayout; _OpacityWidgetLayout->setContentsMargins(4, 4, 4, 4); _OpacityWidgetLayout->addWidget(_OpacityLabel); _OpacityWidgetLayout->addWidget(opacitySlider); QWidget *_OpacityWidget = new QWidget; _OpacityWidget->setLayout(_OpacityWidgetLayout); QWidgetAction *OpacityAction = new QWidgetAction(this); OpacityAction->setDefaultWidget(_OpacityWidget); // QObject::connect( m_OpacityAction, SIGNAL( changed() ), this, SLOT( OpacityActionChanged() ) ); auto workingImage = this->GetWorkingImage(); auto activeLayer = workingImage->GetActiveLayer(); auto label = workingImage->GetLabel(pixelValue, activeLayer); if (nullptr != label) { auto opacity = label->GetOpacity(); opacitySlider->setValue(static_cast(opacity * 100)); } menu->addAction(OpacityAction); } menu->popup(QCursor::pos()); } void QmitkLabelSetWidget::OnUnlockAllLabels(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsLocked(false); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnLockAllLabels(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsLocked(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnSetAllLabelsVisible(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsVisible(true); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnSetAllLabelsInvisible(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsVisible(false); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnSetOnlyActiveLabelVisible(bool /*value*/) { mitk::LabelSetImage *workingImage = GetWorkingImage(); int pixelValue = GetPixelValueOfSelectedItem(); workingImage->GetActiveLabelSet()->SetAllLabelsVisible(false); workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->SetVisible(true); workingImage->GetActiveLabelSet()->UpdateLookupTable(pixelValue); this->WaitCursorOn(); const mitk::Point3D &pos = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetCenterOfMassCoordinates(); this->WaitCursorOff(); if (pos.GetVnlVector().max_value() > 0.0) { emit goToLabel(pos); } UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnEraseLabel(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QString question = "Do you really want to erase the contents of label \""; question.append( QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName())); question.append("\"?"); QMessageBox::StandardButton answerButton = QMessageBox::question(this, "Erase label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { this->WaitCursorOn(); GetWorkingImage()->EraseLabel(pixelValue); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnRemoveLabel(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QString question = "Do you really want to remove label \""; question.append( QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName())); question.append("\"?"); QMessageBox::StandardButton answerButton = QMessageBox::question(this, "Remove label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { this->WaitCursorOn(); GetWorkingImage()->RemoveLabel(pixelValue, GetWorkingImage()->GetActiveLayer()); this->WaitCursorOff(); } ResetAllTableWidgetItems(); } void QmitkLabelSetWidget::OnRenameLabel(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QmitkNewSegmentationDialog dialog(this, this->GetWorkingImage(), QmitkNewSegmentationDialog::RenameLabel); dialog.SetColor(GetWorkingImage()->GetActiveLabelSet()->GetLabel(pixelValue)->GetColor()); dialog.SetName(QString::fromStdString(GetWorkingImage()->GetActiveLabelSet()->GetLabel(pixelValue)->GetName())); if (dialog.exec() == QDialog::Rejected) { return; } QString segmentationName = dialog.GetName(); if (segmentationName.isEmpty()) { segmentationName = "Unnamed"; } GetWorkingImage()->GetActiveLabelSet()->RenameLabel(pixelValue, segmentationName.toStdString(), dialog.GetColor()); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); UpdateAllTableWidgetItems(); } +void QmitkLabelSetWidget::OnRenameLabelShortcutActivated() +{ + if (m_Controls.m_LabelSetTableWidget->selectedItems().size() == 1) + this->OnRenameLabel(true); +} + void QmitkLabelSetWidget::OnCombineAndCreateMask(bool /*value*/) { m_Controls.m_LabelSetTableWidget->selectedRanges(); // ...to do... // } void QmitkLabelSetWidget::OnCreateMasks(bool /*value*/) { m_Controls.m_LabelSetTableWidget->selectedRanges(); // ..to do.. // } void QmitkLabelSetWidget::OnCombineAndCreateSurface(bool /*value*/) { m_Controls.m_LabelSetTableWidget->selectedRanges(); // ..to do.. // } void QmitkLabelSetWidget::OnEraseLabels(bool /*value*/) { QString question = "Do you really want to erase the selected labels?"; QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Erase selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (ranges.isEmpty()) return; std::vector VectorOfLablePixelValues; foreach (QTableWidgetSelectionRange a, ranges) for (int i = a.topRow(); i <= a.bottomRow(); i++) VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt()); this->WaitCursorOn(); GetWorkingImage()->EraseLabels(VectorOfLablePixelValues); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnRemoveLabels(bool /*value*/) { QString question = "Do you really want to remove the selected labels?"; QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Remove selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (ranges.isEmpty()) { return; } std::vector VectorOfLablePixelValues; foreach (QTableWidgetSelectionRange a, ranges) { for (int i = a.topRow(); i <= a.bottomRow(); ++i) { VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt()); } } this->WaitCursorOn(); GetWorkingImage()->RemoveLabels(VectorOfLablePixelValues, GetWorkingImage()->GetActiveLayer()); this->WaitCursorOff(); } ResetAllTableWidgetItems(); } void QmitkLabelSetWidget::OnMergeLabels(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QString question = "Do you really want to merge selected labels into \""; question.append( QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName())); question.append("\"?"); QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Merge selected label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (ranges.isEmpty()) { return; } std::vector vectorOfSourcePixelValues; foreach (QTableWidgetSelectionRange a, ranges) { for (int i = a.topRow(); i <= a.bottomRow(); ++i) { vectorOfSourcePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt()); } } this->WaitCursorOn(); GetWorkingImage()->MergeLabels(pixelValue, vectorOfSourcePixelValues, GetWorkingImage()->GetActiveLayer()); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnLockedButtonClicked() { int row = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, LOCKED_COL)) { row = i; } } if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) { int pixelValue = m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt(); GetWorkingImage() ->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer()) ->SetLocked(!GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetLocked()); } } void QmitkLabelSetWidget::OnVisibleButtonClicked() { int row = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, VISIBLE_COL)) { row = i; break; } } if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) { QTableWidgetItem *item = m_Controls.m_LabelSetTableWidget->item(row, 0); int pixelValue = item->data(Qt::UserRole).toInt(); GetWorkingImage() ->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer()) ->SetVisible(!GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetVisible()); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnColorButtonClicked() { int row = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, COLOR_COL)) { row = i; } } if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) { int pixelValue = m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt(); const mitk::Color &color = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetColor(); QColor initial(color.GetRed() * 255, color.GetGreen() * 255, color.GetBlue() * 255); QColor qcolor = QColorDialog::getColor(initial, nullptr, QString("Change color")); if (!qcolor.isValid()) { return; } QPushButton *button = static_cast(m_Controls.m_LabelSetTableWidget->cellWidget(row, COLOR_COL)); if (!button) { return; } button->setAutoFillBackground(true); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(qcolor.red())); styleSheet.append(","); styleSheet.append(QString::number(qcolor.green())); styleSheet.append(","); styleSheet.append(QString::number(qcolor.blue())); styleSheet.append("); border: 0;"); button->setStyleSheet(styleSheet); mitk::Color newColor; newColor.SetRed(qcolor.red() / 255.0); newColor.SetGreen(qcolor.green() / 255.0); newColor.SetBlue(qcolor.blue() / 255.0); GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetColor(newColor); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); } } void QmitkLabelSetWidget::OnRandomColor(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); GetWorkingImage() ->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer()) ->SetColor(m_ColorSequenceRainbow.GetNextColor()); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnActiveLabelChanged(int pixelValue) { mitk::LabelSetImage *workingImage = GetWorkingImage(); assert(workingImage); workingImage->GetActiveLabelSet()->SetActiveLabel(pixelValue); // MITK_INFO << "Active Label set to << " << pixelValue; mitk::SurfaceBasedInterpolationController *interpolator = mitk::SurfaceBasedInterpolationController::GetInstance(); if (interpolator) { interpolator->SetActiveLabel(pixelValue); } workingImage->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnItemClicked(QTableWidgetItem *item) { if (!item) return; int pixelValue = item->data(Qt::UserRole).toInt(); QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (!ranges.empty() && ranges.back().rowCount() == 1) { m_ProcessingManualSelection = true; OnActiveLabelChanged(pixelValue); m_ProcessingManualSelection = false; mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnItemDoubleClicked(QTableWidgetItem *item) { if (!item) return; int pixelValue = item->data(Qt::UserRole).toInt(); // OnItemClicked(item); <<-- Double click first call OnItemClicked WaitCursorOn(); mitk::LabelSetImage *workingImage = GetWorkingImage(); workingImage->UpdateCenterOfMass(pixelValue, workingImage->GetActiveLayer()); const mitk::Point3D &pos = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetCenterOfMassCoordinates(); WaitCursorOff(); if (pos.GetVnlVector().max_value() > 0.0) { emit goToLabel(pos); } workingImage->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::SelectLabelByPixelValue(mitk::Label::PixelType pixelValue) { if (m_ProcessingManualSelection || !GetWorkingImage()->ExistLabel(pixelValue)) return; for (int row = 0; row < m_Controls.m_LabelSetTableWidget->rowCount(); row++) { if (m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt() == pixelValue) { m_Controls.m_LabelSetTableWidget->clearSelection(); m_Controls.m_LabelSetTableWidget->selectRow(row); m_Controls.m_LabelSetTableWidget->scrollToItem(m_Controls.m_LabelSetTableWidget->item(row, 0)); return; } } } void QmitkLabelSetWidget::InsertTableWidgetItem(mitk::Label *label) { const mitk::Color &color = label->GetColor(); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color[0] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[1] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[2] * 255)); styleSheet.append("); border: 0;"); QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL) - 2; QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth); QTableWidgetItem *nameItem = new QTableWidgetItem(text); nameItem->setTextAlignment(Qt::AlignCenter | Qt::AlignLeft); // ---!--- // IMPORTANT: ADD PIXELVALUE TO TABLEWIDGETITEM.DATA nameItem->setData(Qt::UserRole, QVariant(label->GetValue())); // ---!--- QPushButton *pbColor = new QPushButton(tableWidget); pbColor->setFixedSize(24, 24); pbColor->setCheckable(false); pbColor->setAutoFillBackground(true); pbColor->setToolTip("Change label color"); pbColor->setStyleSheet(styleSheet); connect(pbColor, SIGNAL(clicked()), this, SLOT(OnColorButtonClicked())); QString transparentStyleSheet = QLatin1String("background-color: transparent; border: 0;"); QPushButton *pbLocked = new QPushButton(tableWidget); pbLocked->setFixedSize(24, 24); QIcon *iconLocked = new QIcon(); auto lockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg")); auto unlockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg")); iconLocked->addPixmap(lockIcon.pixmap(64), QIcon::Normal, QIcon::Off); iconLocked->addPixmap(unlockIcon.pixmap(64), QIcon::Normal, QIcon::On); pbLocked->setIcon(*iconLocked); pbLocked->setIconSize(QSize(24, 24)); pbLocked->setCheckable(true); pbLocked->setToolTip("Lock/unlock label"); pbLocked->setChecked(!label->GetLocked()); pbLocked->setStyleSheet(transparentStyleSheet); connect(pbLocked, SIGNAL(clicked()), this, SLOT(OnLockedButtonClicked())); QPushButton *pbVisible = new QPushButton(tableWidget); pbVisible->setFixedSize(24, 24); pbVisible->setAutoRepeat(false); QIcon *iconVisible = new QIcon(); auto visibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg")); auto invisibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/invisible.svg")); iconVisible->addPixmap(visibleIcon.pixmap(64), QIcon::Normal, QIcon::Off); iconVisible->addPixmap(invisibleIcon.pixmap(64), QIcon::Normal, QIcon::On); pbVisible->setIcon(*iconVisible); pbVisible->setIconSize(QSize(24, 24)); pbVisible->setCheckable(true); pbVisible->setToolTip("Show/hide label"); pbVisible->setChecked(!label->GetVisible()); pbVisible->setStyleSheet(transparentStyleSheet); connect(pbVisible, SIGNAL(clicked()), this, SLOT(OnVisibleButtonClicked())); int row = tableWidget->rowCount(); tableWidget->insertRow(row); tableWidget->setRowHeight(row, 24); tableWidget->setItem(row, 0, nameItem); tableWidget->setCellWidget(row, 1, pbLocked); tableWidget->setCellWidget(row, 2, pbColor); tableWidget->setCellWidget(row, 3, pbVisible); tableWidget->selectRow(row); // m_LabelSetImage->SetActiveLabel(label->GetPixelValue()); // m_ToolManager->WorkingDataModified.Send(); // emit activeLabelChanged(label->GetPixelValue()); if (row == 0) { tableWidget->hideRow(row); // hide exterior label } } void QmitkLabelSetWidget::UpdateAllTableWidgetItems() { mitk::LabelSetImage *workingImage = GetWorkingImage(); if (!workingImage) return; // add all labels QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; m_LabelStringList.clear(); for (int i = 0; i < tableWidget->rowCount(); ++i) { UpdateTableWidgetItem(tableWidget->item(i, 0)); m_LabelStringList.append(tableWidget->item(i, 0)->text()); } OnLabelListModified(m_LabelStringList); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::UpdateTableWidgetItem(QTableWidgetItem *item) { mitk::LabelSetImage *workingImage = GetWorkingImage(); mitk::Label *label = workingImage->GetLabel(item->data(Qt::UserRole).toInt(), workingImage->GetActiveLayer()); const mitk::Color &color = label->GetColor(); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color[0] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[1] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[2] * 255)); styleSheet.append("); border: 0;"); QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL) - 2; QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth); item->setText(text); QPushButton *pbLocked = dynamic_cast(tableWidget->cellWidget(item->row(), 1)); pbLocked->setChecked(!label->GetLocked()); QPushButton *pbColor = dynamic_cast(tableWidget->cellWidget(item->row(), 2)); pbColor->setStyleSheet(styleSheet); QPushButton *pbVisible = dynamic_cast(tableWidget->cellWidget(item->row(), 3)); pbVisible->setChecked(!label->GetVisible()); if (item->row() == 0) { tableWidget->hideRow(item->row()); // hide exterior label } } void QmitkLabelSetWidget::ResetAllTableWidgetItems() { QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; // remove all rows while (tableWidget->rowCount()) { tableWidget->removeRow(0); } mitk::DataNode * workingNode = GetWorkingNode(); auto workingImage = dynamic_cast(workingNode->GetData()); if (nullptr == workingImage) { return; } // add all labels m_LabelStringList.clear(); mitk::LabelSet::LabelContainerConstIteratorType it = workingImage->GetActiveLabelSet()->IteratorConstBegin(); mitk::LabelSet::LabelContainerConstIteratorType end = workingImage->GetActiveLabelSet()->IteratorConstEnd(); int pixelValue = -1; while (it != end) { InsertTableWidgetItem(it->second); if (workingImage->GetActiveLabel(workingImage->GetActiveLayer()) == it->second) // get active pixelValue = it->first; m_LabelStringList.append(QString(it->second->GetName().c_str())); it++; } SelectLabelByPixelValue(pixelValue); OnLabelListModified(m_LabelStringList); std::stringstream captionText; captionText << "Number of labels: " << workingImage->GetNumberOfLabels(workingImage->GetActiveLayer()) - 1; m_Controls.m_lblCaption->setText(captionText.str().c_str()); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); emit LabelSetWidgetReset(); } int QmitkLabelSetWidget::GetPixelValueOfSelectedItem() { if (m_Controls.m_LabelSetTableWidget->currentItem()) { return m_Controls.m_LabelSetTableWidget->currentItem()->data(Qt::UserRole).toInt(); } return -1; } QStringList &QmitkLabelSetWidget::GetLabelStringList() { return m_LabelStringList; } void QmitkLabelSetWidget::InitializeTableWidget() { QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; tableWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); tableWidget->setTabKeyNavigation(false); tableWidget->setAlternatingRowColors(false); tableWidget->setFocusPolicy(Qt::NoFocus); tableWidget->setColumnCount(4); tableWidget->resizeColumnToContents(NAME_COL); tableWidget->setColumnWidth(LOCKED_COL, 25); tableWidget->setColumnWidth(COLOR_COL, 25); tableWidget->setColumnWidth(VISIBLE_COL, 25); tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); tableWidget->setContextMenuPolicy(Qt::CustomContextMenu); tableWidget->horizontalHeader()->hide(); tableWidget->setSortingEnabled(false); tableWidget->verticalHeader()->hide(); tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); connect(tableWidget, SIGNAL(itemClicked(QTableWidgetItem *)), this, SLOT(OnItemClicked(QTableWidgetItem *))); connect( tableWidget, SIGNAL(itemDoubleClicked(QTableWidgetItem *)), this, SLOT(OnItemDoubleClicked(QTableWidgetItem *))); connect(tableWidget, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(OnTableViewContextMenuRequested(const QPoint &))); } void QmitkLabelSetWidget::OnOpacityChanged(int value) { int pixelValue = GetPixelValueOfSelectedItem(); float opacity = static_cast(value) / 100.0f; GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetOpacity(opacity); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); } void QmitkLabelSetWidget::setEnabled(bool enabled) { QWidget::setEnabled(enabled); UpdateControls(); } void QmitkLabelSetWidget::SetDataStorage(mitk::DataStorage *storage) { m_DataStorage = storage; } void QmitkLabelSetWidget::OnSearchLabel() { std::string text = m_Controls.m_LabelSearchBox->text().toStdString(); int pixelValue = -1; int row = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (m_Controls.m_LabelSetTableWidget->item(i, 0)->text().toStdString().compare(text) == 0) { pixelValue = m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt(); row = i; break; } } if (pixelValue == -1) { return; } GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue); QTableWidgetItem *nameItem = m_Controls.m_LabelSetTableWidget->item(row, NAME_COL); if (!nameItem) { return; } m_Controls.m_LabelSetTableWidget->clearSelection(); m_Controls.m_LabelSetTableWidget->selectRow(row); m_Controls.m_LabelSetTableWidget->scrollToItem(nameItem); GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue); this->WaitCursorOn(); mitk::Point3D pos = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates(); m_ToolManager->WorkingDataChanged(); if (pos.GetVnlVector().max_value() > 0.0) { emit goToLabel(pos); } else { GetWorkingImage()->UpdateCenterOfMass(pixelValue, GetWorkingImage()->GetActiveLayer()); mitk::Point3D pos = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates(); emit goToLabel(pos); } this->WaitCursorOff(); } void QmitkLabelSetWidget::OnLabelListModified(const QStringList &list) { QStringListModel *completeModel = static_cast(m_Completer->model()); completeModel->setStringList(list); } mitk::LabelSetImage *QmitkLabelSetWidget::GetWorkingImage() { mitk::DataNode *workingNode = GetWorkingNode(); mitk::LabelSetImage *workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); return workingImage; } mitk::DataNode *QmitkLabelSetWidget::GetWorkingNode() { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); return workingNode; } void QmitkLabelSetWidget::UpdateControls() { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); bool hasWorkingData = (workingNode != nullptr); m_Controls.m_LabelSetTableWidget->setEnabled(hasWorkingData); m_Controls.m_LabelSearchBox->setEnabled(hasWorkingData); if (!hasWorkingData) return; QStringListModel *completeModel = static_cast(m_Completer->model()); completeModel->setStringList(GetLabelStringList()); } void QmitkLabelSetWidget::OnCreateCroppedMask(bool) { m_ToolManager->ActivateTool(-1); mitk::LabelSetImage *workingImage = GetWorkingImage(); mitk::Image::Pointer maskImage; int pixelValue = GetPixelValueOfSelectedItem(); try { this->WaitCursorOn(); mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New(); cropFilter->SetInput(workingImage->CreateLabelMask(pixelValue)); cropFilter->SetBackgroundValue(0); cropFilter->SetMarginFactor(1.15); cropFilter->Update(); maskImage = cropFilter->GetOutput(); this->WaitCursorOff(); } catch (mitk::Exception &e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } if (maskImage.IsNull()) { QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } mitk::DataNode::Pointer maskNode = mitk::DataNode::New(); std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName(); name += "-mask"; maskNode->SetName(name); maskNode->SetData(maskImage); maskNode->SetBoolProperty("binary", true); maskNode->SetBoolProperty("outline binary", true); maskNode->SetBoolProperty("outline binary shadow", true); maskNode->SetFloatProperty("outline width", 2.0); maskNode->SetColor(workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetColor()); maskNode->SetOpacity(1.0); m_DataStorage->Add(maskNode, GetWorkingNode()); } void QmitkLabelSetWidget::OnCreateMask(bool /*triggered*/) { m_ToolManager->ActivateTool(-1); mitk::LabelSetImage *workingImage = GetWorkingImage(); mitk::Image::Pointer maskImage; int pixelValue = GetPixelValueOfSelectedItem(); try { this->WaitCursorOn(); maskImage = workingImage->CreateLabelMask(pixelValue); this->WaitCursorOff(); } catch (mitk::Exception &e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } if (maskImage.IsNull()) { QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } mitk::DataNode::Pointer maskNode = mitk::DataNode::New(); std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName(); name += "-mask"; maskNode->SetName(name); maskNode->SetData(maskImage); maskNode->SetBoolProperty("binary", true); maskNode->SetBoolProperty("outline binary", true); maskNode->SetBoolProperty("outline binary shadow", true); maskNode->SetFloatProperty("outline width", 2.0); maskNode->SetColor(workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetColor()); maskNode->SetOpacity(1.0); m_DataStorage->Add(maskNode, GetWorkingNode()); } void QmitkLabelSetWidget::OnToggleOutline(bool value) { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); workingNode->SetBoolProperty("labelset.contour.active", value); workingNode->GetData()->Modified(); // fixme: workaround to force data-type rendering (and not only property-type) mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnCreateSmoothedSurface(bool /*triggered*/) { m_ToolManager->ActivateTool(-1); mitk::DataNode::Pointer workingNode = GetWorkingNode(); mitk::LabelSetImage *workingImage = GetWorkingImage(); int pixelValue = GetPixelValueOfSelectedItem(); mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New(); itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand); itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand); mitk::DataNode::Pointer groupNode = workingNode; surfaceFilter->SetPointerParameter("Group node", groupNode); surfaceFilter->SetPointerParameter("Input", workingImage); surfaceFilter->SetParameter("RequestedLabel", pixelValue); surfaceFilter->SetParameter("Smooth", true); surfaceFilter->SetDataStorage(*m_DataStorage); mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background..."); try { surfaceFilter->StartAlgorithm(); } catch (mitk::Exception &e) { MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Surface", "Could not create a surface mesh out of the selected label. See error log for details.\n"); } } void QmitkLabelSetWidget::OnCreateDetailedSurface(bool /*triggered*/) { m_ToolManager->ActivateTool(-1); mitk::DataNode::Pointer workingNode = GetWorkingNode(); mitk::LabelSetImage *workingImage = GetWorkingImage(); int pixelValue = GetPixelValueOfSelectedItem(); mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New(); itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand); itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand); mitk::DataNode::Pointer groupNode = workingNode; surfaceFilter->SetPointerParameter("Group node", groupNode); surfaceFilter->SetPointerParameter("Input", workingImage); surfaceFilter->SetParameter("RequestedLabel", pixelValue); surfaceFilter->SetParameter("Smooth", false); surfaceFilter->SetDataStorage(*m_DataStorage); mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background..."); try { surfaceFilter->StartAlgorithm(); } catch (mitk::Exception &e) { MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Surface", "Could not create a surface mesh out of the selected label. See error log for details.\n"); } } void QmitkLabelSetWidget::WaitCursorOn() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); } void QmitkLabelSetWidget::WaitCursorOff() { this->RestoreOverrideCursor(); } void QmitkLabelSetWidget::RestoreOverrideCursor() { QApplication::restoreOverrideCursor(); } void QmitkLabelSetWidget::OnThreadedCalculationDone() { mitk::StatusBar::GetInstance()->Clear(); } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.h b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.h index 9fb7a1c0e8..a1d6146b4d 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkLabelSetWidget.h @@ -1,165 +1,166 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkLabelSetWidget_h #define QmitkLabelSetWidget_h #include #include #include #include #include class QmitkDataStorageComboBox; class QCompleter; namespace mitk { class LabelSetImage; class LabelSet; class Label; class DataStorage; class ToolManager; class DataNode; } class MITK_QT_SEGMENTATION QmitkLabelSetWidget : public QWidget { Q_OBJECT public: explicit QmitkLabelSetWidget(QWidget *parent = nullptr); ~QmitkLabelSetWidget() override; void SetDataStorage(mitk::DataStorage *storage); void UpdateControls(); virtual void setEnabled(bool enabled); QStringList &GetLabelStringList(); signals: /// \brief Send a signal when it was requested to go to a label. void goToLabel(const mitk::Point3D &); void LabelSetWidgetReset(); public slots: /** * @brief Updates the current labels in the label set widget table. For each label (widget item) the 'UpdateTableWidgetItem' is called. * * Updating means setting the color box of the table, setting the column with and fill it with the label name. * Furthermore the two push buttons for locking and showing/hiding the layer are checked/unchecked. * This functions only changes the appearance of the table widget and no render window update is necessary. */ void UpdateAllTableWidgetItems(); /** * @brief Resets the current labels in the label set widget table. For each label a widget item is inserted into the table. * * Resetting means removing all rows of the widget table and inserting new rows (labels) from the active label set (= layer) of the current working node. * The currently active label is selected and 'Number of labels' is set. * As this function is typically used after one label has been removed or the reference node has been changed (e.g.) the render windows have to be updated. */ void ResetAllTableWidgetItems(); void SelectLabelByPixelValue(mitk::Label::PixelType pixelValue); private slots: // LabelSet dependent void OnOpacityChanged(int); void OnUnlockAllLabels(bool); void OnLockAllLabels(bool); void OnSetAllLabelsVisible(bool); void OnSetAllLabelsInvisible(bool); void OnSetOnlyActiveLabelVisible(bool); void OnRandomColor(bool); void OnRemoveLabel(bool); void OnRemoveLabels(bool); void OnRenameLabel(bool); + void OnRenameLabelShortcutActivated(); void OnLockedButtonClicked(); void OnVisibleButtonClicked(); void OnColorButtonClicked(); void OnItemClicked(QTableWidgetItem *item); void OnItemDoubleClicked(QTableWidgetItem *item); void OnTableViewContextMenuRequested(const QPoint &); void InsertTableWidgetItem(mitk::Label *label); void UpdateTableWidgetItem(QTableWidgetItem *item); // reaction to "returnPressed" signal from ... void OnSearchLabel(); // reaction to the button "Change Label" void OnActiveLabelChanged(int pixelValue); // LabelSetImage Dependet void OnCreateDetailedSurface(bool); void OnCreateSmoothedSurface(bool); // reaction to the signal "createMask" from QmitkLabelSetTableWidget void OnCreateMask(bool); void OnCreateMasks(bool); // reaction to the signal "createCroppedMask" from QmitkLabelSetTableWidget void OnCreateCroppedMask(bool); void OnCombineAndCreateMask(bool); void OnCombineAndCreateSurface(bool); void OnEraseLabel(bool); void OnEraseLabels(bool); void OnMergeLabels(bool); // reaction to signal "labelListModified" from QmitkLabelSetTableWidget void OnLabelListModified(const QStringList &list); // reaction to the signal "toggleOutline" from QmitkLabelSetTableWidget void OnToggleOutline(bool); private: enum TableColumns { NAME_COL = 0, LOCKED_COL, COLOR_COL, VISIBLE_COL }; void WaitCursorOn(); void WaitCursorOff(); void RestoreOverrideCursor(); void OnThreadedCalculationDone(); void InitializeTableWidget(); int GetPixelValueOfSelectedItem(); mitk::LabelSetImage *GetWorkingImage(); mitk::DataNode *GetWorkingNode(); Ui::QmitkLabelSetWidgetControls m_Controls; mitk::ColorSequenceRainbow m_ColorSequenceRainbow; mitk::DataStorage *m_DataStorage; QCompleter *m_Completer; mitk::ToolManager *m_ToolManager; QStringList m_OrganColors; QStringList m_LabelStringList; bool m_ProcessingManualSelection; }; #endif diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.cpp index 845bc60741..b4b03fff35 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.cpp @@ -1,311 +1,328 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkNewSegmentationDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { // Get standard label name and color suggestions from embedded XML preset file for anatomical structures. QmitkNewSegmentationDialog::SuggestionsType GetStandardSuggestions() { QmitkNewSegmentationDialog::SuggestionsType standardSuggestions; vtkNew presets; presets->LoadPreset(); for (const auto& preset : presets->GetColorPresets()) { auto name = QString::fromStdString(preset.first); auto color = QColor::fromRgbF(preset.second.GetRed(), preset.second.GetGreen(), preset.second.GetBlue()); standardSuggestions.emplace(name, color); } return standardSuggestions; } // Parse label name and color suggestions from a JSON file. An array of objects is expected, each consisting // of a "name" string and an optional "color" string. If present, the "color" string must follow the conventions // of QColor::setNamedColor(), i.e., #rrggbb or any SVG color keyword name like CornflowerBlue. Everything else // in the JSON file is simply ignored. In case of any error, an empty map is returned. QmitkNewSegmentationDialog::SuggestionsType ParseSuggestions(const std::string& filename) { std::ifstream file(filename); if (!file.is_open()) { MITK_ERROR << "Could not open \"" << filename << "\"!"; return {}; } auto json = nlohmann::json::parse(file, nullptr, false); if (json.is_discarded() || !json.is_array()) { MITK_ERROR << "Could not parse \"" << filename << "\" as JSON array!"; return {}; } QmitkNewSegmentationDialog::SuggestionsType parsedSuggestions; for (const auto& obj : json) { if (!obj.is_object() || !obj.contains("name")) continue; auto name = QString::fromStdString(obj["name"]); QColor color(QColor::Invalid); if (obj.contains("color")) color.setNamedColor(QString::fromStdString(obj["color"])); parsedSuggestions.emplace(name, color); } if (parsedSuggestions.empty()) MITK_WARN << "Could not parse any suggestions from \"" << filename << "\"!"; return parsedSuggestions; } struct Preferences { QString labelSuggestions; bool replaceStandardSuggestions; bool suggestOnce; }; // Get all relevant preferences and consider command-line arguments overrides. Preferences GetPreferences() { auto nodePrefs = berry::Platform::GetPreferencesService()->GetSystemPreferences()->Node("/org.mitk.views.segmentation"); Preferences prefs; prefs.labelSuggestions = QString::fromStdString(mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString(), "")); if (prefs.labelSuggestions.isEmpty()) prefs.labelSuggestions = nodePrefs->Get("label suggestions", ""); prefs.replaceStandardSuggestions = nodePrefs->GetBool("replace standard suggestions", true); prefs.suggestOnce = nodePrefs->GetBool("suggest once", true); return prefs; } // Get names of all labels in all layers of a LabelSetImage. QStringList GetExistingLabelNames(mitk::LabelSetImage* labelSetImage) { QStringList existingLabelNames; existingLabelNames.reserve(labelSetImage->GetTotalNumberOfLabels()); const auto numLayers = labelSetImage->GetNumberOfLayers(); for (std::remove_const_t layerIndex = 0; layerIndex < numLayers; ++layerIndex) { const auto* labelSet = labelSetImage->GetLabelSet(layerIndex); for (auto labelIter = labelSet->IteratorConstBegin(); labelIter != labelSet->IteratorConstEnd(); ++labelIter) { if (0 == labelIter->first) continue; // Ignore background label auto name = QString::fromStdString(labelIter->second->GetName()); if (!name.isEmpty()) // Potential duplicates do not matter for our purpose existingLabelNames.push_back(name); } } return existingLabelNames; } // Remove blacklisted suggestions. QmitkNewSegmentationDialog::SuggestionsType FilterSuggestions(const QmitkNewSegmentationDialog::SuggestionsType& suggestions, const QStringList& blacklist) { QmitkNewSegmentationDialog::SuggestionsType filteredSuggestions; std::remove_copy_if(suggestions.begin(), suggestions.end(), std::inserter(filteredSuggestions, filteredSuggestions.end()), [&blacklist](const auto& suggestion) { return blacklist.contains(suggestion.first); }); return filteredSuggestions; } } QmitkNewSegmentationDialog::QmitkNewSegmentationDialog(QWidget *parent, mitk::LabelSetImage* labelSetImage, Mode mode) : QDialog(parent), m_Ui(new Ui::QmitkNewSegmentationDialog), m_SuggestOnce(true), m_Color(Qt::red) { m_Ui->setupUi(this); if (RenameLabel == mode) { this->setWindowTitle("Rename Label"); m_Ui->label->setText("New name and color of the label"); m_Ui->buttonBox->button(QDialogButtonBox::Ok)->setText("Rename label"); } else { m_Ui->buttonBox->button(QDialogButtonBox::Ok)->setText("Create label"); } - m_Ui->nameComboBox->setFocus(); + m_Ui->nameLineEdit->setFocus(); connect(m_Ui->colorButton, &QToolButton::clicked, this, &QmitkNewSegmentationDialog::OnColorButtonClicked); - connect(m_Ui->nameComboBox, &QComboBox::currentTextChanged, this, &QmitkNewSegmentationDialog::OnSuggestionSelected); + connect(m_Ui->nameLineEdit, &QLineEdit::textChanged, this, &QmitkNewSegmentationDialog::OnTextChanged); + connect(m_Ui->nameList, &QListWidget::itemSelectionChanged, this, &QmitkNewSegmentationDialog::OnSuggestionSelected); + connect(m_Ui->nameList, &QListWidget::itemDoubleClicked, this, &QmitkNewSegmentationDialog::OnAccept); connect(m_Ui->buttonBox, &QDialogButtonBox::accepted, this, &QmitkNewSegmentationDialog::OnAccept); this->UpdateColorButtonBackground(); auto prefs = GetPreferences(); if (!prefs.labelSuggestions.isEmpty()) { auto suggestions = ParseSuggestions(prefs.labelSuggestions.toStdString()); this->SetSuggestions(suggestions, prefs.replaceStandardSuggestions && !suggestions.empty()); } else { this->SetSuggestions(GetStandardSuggestions(), true); } if (nullptr != labelSetImage && prefs.suggestOnce) { auto existingLabelNames = GetExistingLabelNames(labelSetImage); m_Suggestions = FilterSuggestions(m_Suggestions, existingLabelNames); - this->UpdateCompleterModel(); + this->UpdateNameList(); } } QmitkNewSegmentationDialog::~QmitkNewSegmentationDialog() { } void QmitkNewSegmentationDialog::UpdateColorButtonBackground() { m_Ui->colorButton->setStyleSheet("background-color:" + m_Color.name()); } QString QmitkNewSegmentationDialog::GetName() const { return m_Name; } mitk::Color QmitkNewSegmentationDialog::GetColor() const { mitk::Color color; if (m_Color.isValid()) { color.SetRed(m_Color.redF()); color.SetGreen(m_Color.greenF()); color.SetBlue(m_Color.blueF()); } else { color.Set(1.0f, 0.0f, 0.0f); } return color; } void QmitkNewSegmentationDialog::SetName(const QString& name) { - m_Ui->nameComboBox->setEditText(name); + m_Ui->nameLineEdit->setText(name); } void QmitkNewSegmentationDialog::SetColor(const mitk::Color& color) { m_Color.setRgbF(color.GetRed(), color.GetGreen(), color.GetBlue()); this->UpdateColorButtonBackground(); } void QmitkNewSegmentationDialog::SetSuggestions(const SuggestionsType& suggestions, bool replaceStandardSuggestions) { if (replaceStandardSuggestions) { m_Suggestions = suggestions; } else { m_Suggestions = GetStandardSuggestions(); for (const auto& [name, color] : suggestions) { if (m_Suggestions.end() == m_Suggestions.find(name)) m_Suggestions[name] = color; } } - this->UpdateCompleterModel(); + this->UpdateNameList(); } -void QmitkNewSegmentationDialog::UpdateCompleterModel() +void QmitkNewSegmentationDialog::UpdateNameList() { QStringList names; for (const auto& suggestion : m_Suggestions) names << suggestion.first; - m_Ui->nameComboBox->clear(); - m_Ui->nameComboBox->addItems(names); + m_Ui->nameList->clear(); + m_Ui->nameList->addItems(names); } void QmitkNewSegmentationDialog::OnAccept() { - m_Name = m_Ui->nameComboBox->currentText(); + m_Name = m_Ui->nameLineEdit->text(); this->accept(); } +void QmitkNewSegmentationDialog::OnTextChanged(const QString& text) +{ + auto finding = m_Ui->nameList->findItems(text, Qt::MatchFlag::MatchExactly); + + if (!finding.isEmpty()) + m_Ui->nameList->setCurrentItem(finding.first()); +} + void QmitkNewSegmentationDialog::OnColorButtonClicked() { auto color = QColorDialog::getColor(m_Color); if (color.isValid()) { m_Color = color; this->UpdateColorButtonBackground(); } } -void QmitkNewSegmentationDialog::OnSuggestionSelected(const QString &name) +void QmitkNewSegmentationDialog::OnSuggestionSelected() { + const auto* currentItem = m_Ui->nameList->currentItem(); + + if (currentItem == nullptr) + return; + + const auto name = currentItem->text(); + m_Ui->nameLineEdit->setText(name); auto color = m_Suggestions[name]; if (color.isValid()) { m_Color = color; this->UpdateColorButtonBackground(); } } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.h b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.h index 9f188540d1..a2dae77786 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.h @@ -1,80 +1,81 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkNewSegmentationDialog_h #define QmitkNewSegmentationDialog_h #include #include #include #include #include #include namespace mitk { class LabelSetImage; } namespace Ui { class QmitkNewSegmentationDialog; } /** \brief Dialog for naming labels. */ class MITK_QT_SEGMENTATION QmitkNewSegmentationDialog : public QDialog { Q_OBJECT public: using SuggestionsType = std::map; enum Mode { NewLabel, RenameLabel }; explicit QmitkNewSegmentationDialog(QWidget *parent = nullptr, mitk::LabelSetImage* labelSetImage = nullptr, Mode mode = NewLabel); ~QmitkNewSegmentationDialog() override; QString GetName() const; mitk::Color GetColor() const; void SetName(const QString& name); void SetColor(const mitk::Color& color); private: void OnAccept(); - void OnSuggestionSelected(const QString& name); + void OnSuggestionSelected(); void OnColorButtonClicked(); + void OnTextChanged(const QString& text); void SetSuggestions(const SuggestionsType& suggestions, bool replaceStandardSuggestions = false); void UpdateColorButtonBackground(); - void UpdateCompleterModel(); + void UpdateNameList(); Ui::QmitkNewSegmentationDialog* m_Ui; bool m_SuggestOnce; QString m_Name; QColor m_Color; SuggestionsType m_Suggestions; }; #endif diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.ui b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.ui index d75e20262c..107b350957 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.ui +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkNewSegmentationDialog.ui @@ -1,105 +1,92 @@ QmitkNewSegmentationDialog 0 0 - 250 - 105 + 308 + 235 Create Label - + Name and color of the label - + + + + + + + + - - - - true - - - QComboBox::NoInsert - - + + - - - - Qt::Vertical - - - - 20 - 40 - - - - Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() QmitkNewSegmentationDialog accept() 248 254 157 274 buttonBox rejected() QmitkNewSegmentationDialog reject() 316 260 286 274 diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.cpp index 9e354d92ae..656bd0c271 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.cpp @@ -1,196 +1,205 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkLabelsWidget.h" #include // mitk #include #include #include #include #include // Qmitk #include #include #include #include "../QmitkSaveMultiLabelPresetAction.h" #include "../QmitkLoadMultiLabelPresetAction.h" // Qt #include +#include QmitkLabelsWidget::QmitkLabelsWidget(QWidget *parent) : QWidget(parent) , m_Controls(new Ui::QmitkLabelsWidgetControls) , m_ToolManager(nullptr) , m_DefaultLabelNaming(true) { m_Controls->setupUi(this); m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); m_Controls->savePresetButton->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg"))); m_Controls->loadPresetButton->setIcon(QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg"))); connect(m_Controls->newLabelButton, &QToolButton::clicked, this, &QmitkLabelsWidget::OnNewLabel); connect(m_Controls->lockExteriorButton, &QToolButton::toggled, this, &QmitkLabelsWidget::OnLockExterior); connect(m_Controls->savePresetButton, &QToolButton::clicked, this, &QmitkLabelsWidget::OnSavePreset); connect(m_Controls->loadPresetButton, &QToolButton::clicked, this, &QmitkLabelsWidget::OnLoadPreset); connect(m_Controls->showLabelTableButton, &QToolButton::toggled, this, &QmitkLabelsWidget::ShowLabelTable); + auto* newLabelShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_N), this); + connect(newLabelShortcut, &QShortcut::activated, this, &QmitkLabelsWidget::OnNewLabelShortcutActivated); + this->UpdateGUI(); } QmitkLabelsWidget::~QmitkLabelsWidget() { delete m_Controls; } void QmitkLabelsWidget::UpdateGUI() { m_Controls->newLabelButton->setEnabled(false); m_Controls->lockExteriorButton->setEnabled(false); m_Controls->lockExteriorButton->setChecked(false); m_Controls->savePresetButton->setEnabled(false); m_Controls->loadPresetButton->setEnabled(false); m_Controls->showLabelTableButton->setEnabled(false); m_Controls->showLabelTableButton->setChecked(false); mitk::LabelSetImage* workingImage = this->GetWorkingImage(); if (nullptr == workingImage) { return; } int activeLayer = workingImage->GetActiveLayer(); m_Controls->lockExteriorButton->setEnabled(true); m_Controls->lockExteriorButton->setChecked(workingImage->GetLabel(0, activeLayer)->GetLocked()); m_Controls->showLabelTableButton->setEnabled(true); m_Controls->showLabelTableButton->setChecked(true); m_Controls->newLabelButton->setEnabled(true); m_Controls->savePresetButton->setEnabled(true); m_Controls->loadPresetButton->setEnabled(true); } void QmitkLabelsWidget::SetDefaultLabelNaming(bool defaultLabelNaming) { m_DefaultLabelNaming = defaultLabelNaming; } mitk::LabelSetImage* QmitkLabelsWidget::GetWorkingImage() { mitk::DataNode* workingNode = this->GetWorkingNode(); if (nullptr == workingNode) { return nullptr; } auto workingImage = dynamic_cast(workingNode->GetData()); return workingImage; } mitk::DataNode* QmitkLabelsWidget::GetWorkingNode() { mitk::DataNode* referenceNode = m_ToolManager->GetWorkingData(0); return referenceNode; } void QmitkLabelsWidget::OnNewLabel() { m_ToolManager->ActivateTool(-1); mitk::DataNode* workingNode = this->GetWorkingNode(); if (nullptr == workingNode) { return; } auto workingImage = dynamic_cast(workingNode->GetData()); if (nullptr == workingImage) { return; } mitk::Label::Pointer newLabel = mitk::LabelSetImageHelper::CreateNewLabel(workingImage); if (!m_DefaultLabelNaming) { QmitkNewSegmentationDialog dialog(this, workingImage); dialog.SetName(QString::fromStdString(newLabel->GetName())); dialog.SetColor(newLabel->GetColor()); if (QDialog::Rejected == dialog.exec()) return; auto name = dialog.GetName(); if (!name.isEmpty()) newLabel->SetName(name.toStdString()); newLabel->SetColor(dialog.GetColor()); } workingImage->GetActiveLabelSet()->AddLabel(newLabel); this->UpdateGUI(); emit LabelsChanged(); } +void QmitkLabelsWidget::OnNewLabelShortcutActivated() +{ + m_Controls->newLabelButton->click(); +} + void QmitkLabelsWidget::OnLockExterior(bool checked) { auto workingImage = this->GetWorkingImage(); if (nullptr == workingImage) { return; } workingImage->GetLabel(0)->SetLocked(checked); } void QmitkLabelsWidget::OnSavePreset() { auto workingNode = this->GetWorkingNode(); QmitkAbstractNodeSelectionWidget::NodeList nodes; nodes.append(workingNode); QmitkSaveMultiLabelPresetAction action; action.Run(nodes); } void QmitkLabelsWidget::OnLoadPreset() { auto workingNode = this->GetWorkingNode(); QmitkAbstractNodeSelectionWidget::NodeList nodes; nodes.append(workingNode); QmitkLoadMultiLabelPresetAction action; action.Run(nodes); } void QmitkLabelsWidget::WaitCursorOn() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); } void QmitkLabelsWidget::WaitCursorOff() { this->RestoreOverrideCursor(); } void QmitkLabelsWidget::RestoreOverrideCursor() { QApplication::restoreOverrideCursor(); } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.h index 127775d686..5b18994b6a 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/Common/QmitkLabelsWidget.h @@ -1,86 +1,87 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QMITKLABELSWIDGET_H #define QMITKLABELSWIDGET_H // mitk core #include // Qt #include namespace Ui { class QmitkLabelsWidgetControls; } namespace mitk { class DataNode; class Image; class LabelSetImage; class ToolManager; } class QmitkLabelsWidget : public QWidget { Q_OBJECT public: explicit QmitkLabelsWidget(QWidget* parent = nullptr); ~QmitkLabelsWidget() override; void UpdateGUI(); void SetDefaultLabelNaming(bool defaultLabelNaming); Q_SIGNALS: void LabelsChanged(); void ShowLabelTable(bool); private: mitk::LabelSetImage* GetWorkingImage(); mitk::DataNode* GetWorkingNode(); // reaction to button "New Label" void OnNewLabel(); + void OnNewLabelShortcutActivated(); // reaction to the button "Lock exterior" void OnLockExterior(bool); // reaction to button "Save Preset" void OnSavePreset(); // reaction to button "Load Preset" void OnLoadPreset(); void WaitCursorOn(); void WaitCursorOff(); void RestoreOverrideCursor(); Ui::QmitkLabelsWidgetControls* m_Controls; mitk::ToolManager* m_ToolManager; bool m_DefaultLabelNaming; }; #endif diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp index 98faec74c8..be1d26e65a 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp @@ -1,989 +1,989 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkSegmentationView.h" #include "mitkPluginActivator.h" // blueberry #include // mitk #include #include #include #include #include #include #include #include #include #include #include #include #include // Qmitk #include #include #include // us #include #include // Qt #include #include #include #include const std::string QmitkSegmentationView::VIEW_ID = "org.mitk.views.segmentation"; QmitkSegmentationView::QmitkSegmentationView() : m_Parent(nullptr) , m_Controls(nullptr) , m_RenderWindowPart(nullptr) , m_ToolManager(nullptr) , m_ReferenceNode(nullptr) , m_WorkingNode(nullptr) , m_DrawOutline(true) , m_SelectionMode(false) , m_MouseCursorSet(false) , m_DefaultLabelNaming(true) { auto isImage = mitk::TNodePredicateDataType::New(); auto isDwi = mitk::NodePredicateDataType::New("DiffusionImage"); auto isDti = mitk::NodePredicateDataType::New("TensorImage"); auto isOdf = mitk::NodePredicateDataType::New("OdfImage"); auto isSegment = mitk::NodePredicateDataType::New("Segment"); auto validImages = mitk::NodePredicateOr::New(); validImages->AddPredicate(mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isSegment))); validImages->AddPredicate(isDwi); validImages->AddPredicate(isDti); validImages->AddPredicate(isOdf); auto isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); auto isMask = mitk::NodePredicateAnd::New(isBinary, isImage); auto validSegmentations = mitk::NodePredicateOr::New(); validSegmentations->AddPredicate(mitk::TNodePredicateDataType::New()); validSegmentations->AddPredicate(isMask); m_SegmentationPredicate = mitk::NodePredicateAnd::New(); m_SegmentationPredicate->AddPredicate(validSegmentations); m_SegmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); m_SegmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object"))); m_ReferencePredicate = mitk::NodePredicateAnd::New(); m_ReferencePredicate->AddPredicate(validImages); m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(m_SegmentationPredicate)); m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object"))); } QmitkSegmentationView::~QmitkSegmentationView() { if (nullptr != m_Controls) { OnLooseLabelSetConnection(); // deactivate all tools m_ToolManager->ActivateTool(-1); // removing all observers for (NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter) { (*dataIter).first->GetProperty("visible")->RemoveObserver((*dataIter).second); } m_WorkingDataObserverTags.clear(); mitk::RenderingManager::GetInstance()->RemoveObserver(m_RenderingManagerObserverTag); ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); service->RemoveAllPlanePositions(); context->ungetService(ppmRef); m_ToolManager->SetReferenceData(nullptr); m_ToolManager->SetWorkingData(nullptr); } delete m_Controls; } /**********************************************************************/ /* private Q_SLOTS */ /**********************************************************************/ void QmitkSegmentationView::OnReferenceSelectionChanged(QList nodes) { m_ToolManager->ActivateTool(-1); if (nodes.empty()) { m_Controls->workingNodeSelector->SetNodePredicate(m_SegmentationPredicate); m_ReferenceNode = nullptr; m_ToolManager->SetReferenceData(m_ReferenceNode); this->UpdateGUI(); return; } m_ReferenceNode = nodes.first(); m_ToolManager->SetReferenceData(m_ReferenceNode); if (m_ReferenceNode.IsNotNull()) { // set a predicate such that a segmentation fits the selected reference image geometry auto segPredicate = mitk::NodePredicateAnd::New(m_SegmentationPredicate.GetPointer(), mitk::NodePredicateSubGeometry::New(m_ReferenceNode->GetData()->GetGeometry())); m_Controls->workingNodeSelector->SetNodePredicate(segPredicate); if (m_SelectionMode) { // hide all image nodes to later show only the automatically selected ones mitk::DataStorage::SetOfObjects::ConstPointer imageNodes = this->GetDataStorage()->GetSubset(m_ReferencePredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = imageNodes->begin(); iter != imageNodes->end(); ++iter) { (*iter)->SetVisibility(false); } } m_ReferenceNode->SetVisibility(true); } this->UpdateGUI(); } void QmitkSegmentationView::OnSegmentationSelectionChanged(QList nodes) { m_ToolManager->ActivateTool(-1); // Remove observer if one was registered auto finding = m_WorkingDataObserverTags.find(m_WorkingNode); if (finding != m_WorkingDataObserverTags.end()) { m_WorkingNode->GetProperty("visible")->RemoveObserver(m_WorkingDataObserverTags[m_WorkingNode]); m_WorkingDataObserverTags.erase(m_WorkingNode); } if (nodes.empty()) { m_WorkingNode = nullptr; m_ToolManager->SetWorkingData(m_WorkingNode); this->UpdateGUI(); return; } if (m_ReferenceNode.IsNull()) { this->UpdateGUI(); return; } mitk::Image::ConstPointer referenceImage = dynamic_cast(m_ReferenceNode->GetData()); if (referenceImage.IsNull()) { this->UpdateGUI(); return; } m_WorkingNode = nodes.first(); m_ToolManager->SetWorkingData(m_WorkingNode); if (m_WorkingNode.IsNotNull()) { if (m_SelectionMode) { // hide all segmentation nodes to later show only the selected ones mitk::DataStorage::SetOfObjects::ConstPointer segmentationNodes = this->GetDataStorage()->GetSubset(m_SegmentationPredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = segmentationNodes->begin(); iter != segmentationNodes->end(); ++iter) { (*iter)->SetVisibility(false); } } m_WorkingNode->SetVisibility(true); this->OnEstablishLabelSetConnection(); m_Controls->labelSetWidget->ResetAllTableWidgetItems(); auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::ValidateSelectionInput); m_WorkingDataObserverTags.insert(std::pair(m_WorkingNode, m_WorkingNode->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command))); this->InitializeRenderWindows(referenceImage->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, false); } this->UpdateGUI(); } void QmitkSegmentationView::OnVisibilityShortcutActivated() { if (m_WorkingNode.IsNull()) { return; } bool isVisible = false; m_WorkingNode->GetBoolProperty("visible", isVisible); m_WorkingNode->SetVisibility(!isVisible); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::OnLabelToggleShortcutActivated() { if (m_WorkingNode.IsNull()) { return; } auto workingImage = dynamic_cast(m_WorkingNode->GetData()); if (nullptr == workingImage) { return; } this->WaitCursorOn(); workingImage->GetActiveLabelSet()->SetNextActiveLabel(); workingImage->Modified(); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::OnNewSegmentation() { m_ToolManager->ActivateTool(-1); if (m_ReferenceNode.IsNull()) { MITK_ERROR << "'Create new segmentation' button should never be clickable unless a reference image is selected."; return; } mitk::Image::ConstPointer referenceImage = dynamic_cast(m_ReferenceNode->GetData()); if (referenceImage.IsNull()) { QMessageBox::information( m_Parent, "New segmentation", "Please load and select an image before starting some action."); return; } if (referenceImage->GetDimension() <= 1) { QMessageBox::information( m_Parent, "New segmentation", "Segmentation is currently not supported for 2D images"); return; } auto segTemplateImage = referenceImage; if (referenceImage->GetDimension() > 3) { QmitkStaticDynamicSegmentationDialog dialog(m_Parent); dialog.SetReferenceImage(referenceImage.GetPointer()); dialog.exec(); segTemplateImage = dialog.GetSegmentationTemplate(); } mitk::DataNode::Pointer newSegmentationNode; try { this->WaitCursorOn(); newSegmentationNode = mitk::LabelSetImageHelper::CreateNewSegmentationNode(m_ReferenceNode, segTemplateImage); this->WaitCursorOff(); } catch (mitk::Exception& e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::warning(m_Parent, "New segmentation", "Could not create a new segmentation."); return; } auto newLabelSetImage = dynamic_cast(newSegmentationNode->GetData()); if (nullptr == newLabelSetImage) { // something went wrong return; } const auto labelSetPreset = this->GetDefaultLabelSetPreset(); if (labelSetPreset.empty() || !mitk::LabelSetIOHelper::LoadLabelSetImagePreset(labelSetPreset, newLabelSetImage)) { auto newLabel = mitk::LabelSetImageHelper::CreateNewLabel(newLabelSetImage); if (!m_DefaultLabelNaming) { QmitkNewSegmentationDialog dialog(m_Parent); dialog.SetName(QString::fromStdString(newLabel->GetName())); dialog.SetColor(newLabel->GetColor()); if (QDialog::Rejected == dialog.exec()) return; auto name = dialog.GetName(); if (!name.isEmpty()) newLabel->SetName(name.toStdString()); newLabel->SetColor(dialog.GetColor()); } newLabelSetImage->GetActiveLabelSet()->AddLabel(newLabel); } if (!this->GetDataStorage()->Exists(newSegmentationNode)) { this->GetDataStorage()->Add(newSegmentationNode, m_ReferenceNode); } if (m_ToolManager->GetWorkingData(0)) { m_ToolManager->GetWorkingData(0)->SetSelected(false); } newSegmentationNode->SetSelected(true); m_Controls->workingNodeSelector->SetCurrentSelectedNode(newSegmentationNode); } std::string QmitkSegmentationView::GetDefaultLabelSetPreset() const { auto labelSetPreset = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABELSET_PRESET.toStdString(), ""); if (labelSetPreset.empty()) labelSetPreset = m_LabelSetPresetPreference.toStdString(); return labelSetPreset; } void QmitkSegmentationView::OnManualTool2DSelected(int id) { this->ResetMouseCursor(); mitk::StatusBar::GetInstance()->DisplayText(""); if (id >= 0) { std::string text = "Active Tool: \""; text += m_ToolManager->GetToolById(id)->GetName(); text += "\""; mitk::StatusBar::GetInstance()->DisplayText(text.c_str()); us::ModuleResource resource = m_ToolManager->GetToolById(id)->GetCursorIconResource(); this->SetMouseCursor(resource, 0, 0); } } void QmitkSegmentationView::OnShowMarkerNodes(bool state) { mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = m_ToolManager->GetTools().size(); for (unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(m_ToolManager->GetToolById(i)); if (nullptr == manualSegmentationTool) { continue; } manualSegmentationTool->SetShowMarkerNodes(state); } } void QmitkSegmentationView::OnLayersChanged() { this->OnEstablishLabelSetConnection(); m_Controls->labelSetWidget->ResetAllTableWidgetItems(); } void QmitkSegmentationView::OnShowLabelTable(bool value) { m_Controls->labelSetWidget->setVisible(value); } void QmitkSegmentationView::OnGoToLabel(const mitk::Point3D& pos) { if (m_RenderWindowPart) { m_RenderWindowPart->SetSelectedPosition(pos); } } void QmitkSegmentationView::OnLabelSetWidgetReset() { this->ValidateSelectionInput(); } /**********************************************************************/ /* private */ /**********************************************************************/ void QmitkSegmentationView::CreateQtPartControl(QWidget* parent) { m_Parent = parent; m_Controls = new Ui::QmitkSegmentationViewControls; m_Controls->setupUi(parent); // *------------------------ // * SHORTCUTS // *------------------------ - QShortcut* visibilityShortcut = new QShortcut(QKeySequence("CTRL+H"), parent); + QShortcut* visibilityShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_H), parent); connect(visibilityShortcut, &QShortcut::activated, this, &QmitkSegmentationView::OnVisibilityShortcutActivated); - QShortcut* labelToggleShortcut = new QShortcut(QKeySequence("CTRL+L"), parent); + QShortcut* labelToggleShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_I), parent); connect(labelToggleShortcut, &QShortcut::activated, this, &QmitkSegmentationView::OnLabelToggleShortcutActivated); // *------------------------ // * DATA SELECTION WIDGETS // *------------------------ m_Controls->referenceNodeSelector->SetDataStorage(GetDataStorage()); m_Controls->referenceNodeSelector->SetNodePredicate(m_ReferencePredicate); m_Controls->referenceNodeSelector->SetInvalidInfo("Select an image"); m_Controls->referenceNodeSelector->SetPopUpTitel("Select an image"); m_Controls->referenceNodeSelector->SetPopUpHint("Select an image that should be used to define the geometry and bounds of the segmentation."); m_Controls->workingNodeSelector->SetDataStorage(GetDataStorage()); m_Controls->workingNodeSelector->SetNodePredicate(m_SegmentationPredicate); m_Controls->workingNodeSelector->SetInvalidInfo("Select a segmentation"); m_Controls->workingNodeSelector->SetPopUpTitel("Select a segmentation"); m_Controls->workingNodeSelector->SetPopUpHint("Select a segmentation that should be modified. Only segmentation with the same geometry and within the bounds of the reference image are selected."); connect(m_Controls->referenceNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkSegmentationView::OnReferenceSelectionChanged); connect(m_Controls->workingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkSegmentationView::OnSegmentationSelectionChanged); // *------------------------ // * TOOLMANAGER // *------------------------ m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); m_ToolManager->SetDataStorage(*(this->GetDataStorage())); m_ToolManager->InitializeTools(); - QString segTools2D = tr("Add Subtract Fill Erase Paint Wipe 'Region Growing' 'Live Wire'"); + QString segTools2D = tr("Add Subtract Lasso Fill Erase Paint Wipe 'Region Growing' 'Live Wire'"); QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Region Growing 3D' Picking"); #ifdef __linux__ segTools3D.append(" nnUNet"); // plugin not enabled for MacOS / Windows #endif std::regex extSegTool2DRegEx("SegTool2D$"); std::regex extSegTool3DRegEx("SegTool3D$"); auto tools = m_ToolManager->GetTools(); for (const auto &tool : tools) { if (std::regex_search(tool->GetNameOfClass(), extSegTool2DRegEx)) { segTools2D.append(QString(" '%1'").arg(tool->GetName())); } else if (std::regex_search(tool->GetNameOfClass(), extSegTool3DRegEx)) { segTools3D.append(QString(" '%1'").arg(tool->GetName())); } } // setup 2D tools m_Controls->toolSelectionBox2D->SetToolManager(*m_ToolManager); m_Controls->toolSelectionBox2D->SetGenerateAccelerators(true); m_Controls->toolSelectionBox2D->SetToolGUIArea(m_Controls->toolGUIArea2D); m_Controls->toolSelectionBox2D->SetDisplayedToolGroups(segTools2D.toStdString()); m_Controls->toolSelectionBox2D->SetLayoutColumns(3); m_Controls->toolSelectionBox2D->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); connect(m_Controls->toolSelectionBox2D, &QmitkToolSelectionBox::ToolSelected, this, &QmitkSegmentationView::OnManualTool2DSelected); // setup 3D Tools m_Controls->toolSelectionBox3D->SetToolManager(*m_ToolManager); m_Controls->toolSelectionBox3D->SetGenerateAccelerators(true); m_Controls->toolSelectionBox3D->SetToolGUIArea(m_Controls->toolGUIArea3D); m_Controls->toolSelectionBox3D->SetDisplayedToolGroups(segTools3D.toStdString()); m_Controls->toolSelectionBox3D->SetLayoutColumns(3); m_Controls->toolSelectionBox3D->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); m_Controls->slicesInterpolator->SetDataStorage(this->GetDataStorage()); // create general signal / slot connections connect(m_Controls->newSegmentationButton, &QToolButton::clicked, this, &QmitkSegmentationView::OnNewSegmentation); connect(m_Controls->slicesInterpolator, &QmitkSlicesInterpolator::SignalShowMarkerNodes, this, &QmitkSegmentationView::OnShowMarkerNodes); connect(m_Controls->layersWidget, &QmitkLayersWidget::LayersChanged, this, &QmitkSegmentationView::OnLayersChanged); connect(m_Controls->labelsWidget, &QmitkLabelsWidget::ShowLabelTable, this, &QmitkSegmentationView::OnShowLabelTable); // *------------------------ // * LABELSETWIDGET // *------------------------ connect(m_Controls->labelSetWidget, &QmitkLabelSetWidget::goToLabel, this, &QmitkSegmentationView::OnGoToLabel); connect(m_Controls->labelSetWidget, &QmitkLabelSetWidget::LabelSetWidgetReset, this, &QmitkSegmentationView::OnLabelSetWidgetReset); m_Controls->labelSetWidget->SetDataStorage(this->GetDataStorage()); m_Controls->labelSetWidget->hide(); auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::ValidateSelectionInput); m_RenderingManagerObserverTag = mitk::RenderingManager::GetInstance()->AddObserver(mitk::RenderingManagerViewsInitializedEvent(), command); m_RenderWindowPart = this->GetRenderWindowPart(); if (nullptr != m_RenderWindowPart) { this->RenderWindowPartActivated(m_RenderWindowPart); } // Make sure the GUI notices if appropriate data is already present on creation. // Should be done last, if everything else is configured because it triggers the autoselection of data. m_Controls->referenceNodeSelector->SetAutoSelectNewNodes(true); m_Controls->workingNodeSelector->SetAutoSelectNewNodes(true); this->UpdateGUI(); } void QmitkSegmentationView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_RenderWindowPart != renderWindowPart) { m_RenderWindowPart = renderWindowPart; } if (nullptr != m_Parent) { m_Parent->setEnabled(true); } if (nullptr == m_Controls) { return; } // tell the interpolation about tool manager, data storage and render window part if (nullptr != m_RenderWindowPart) { QList controllers; controllers.push_back(m_RenderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()); controllers.push_back(m_RenderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()); controllers.push_back(m_RenderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()); m_Controls->slicesInterpolator->Initialize(m_ToolManager, controllers); } } void QmitkSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_RenderWindowPart = nullptr; if (nullptr != m_Parent) { m_Parent->setEnabled(false); } } void QmitkSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences* prefs) { auto labelSuggestions = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString(), ""); m_DefaultLabelNaming = labelSuggestions.empty() ? prefs->GetBool("default label naming", true) : false; // No default label naming when label suggestions are enforced via command-line argument if (nullptr != m_Controls) { m_Controls->labelsWidget->SetDefaultLabelNaming(m_DefaultLabelNaming); bool slimView = prefs->GetBool("slim view", false); m_Controls->toolSelectionBox2D->SetShowNames(!slimView); m_Controls->toolSelectionBox3D->SetShowNames(!slimView); } m_DrawOutline = prefs->GetBool("draw outline", true); m_SelectionMode = prefs->GetBool("selection mode", false); m_LabelSetPresetPreference = prefs->Get("label set preset", ""); this->ApplyDisplayOptions(); } void QmitkSegmentationView::NodeAdded(const mitk::DataNode* node) { if (m_SegmentationPredicate->CheckNode(node)) { this->ApplyDisplayOptions(const_cast(node)); } } void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node) { if (!m_SegmentationPredicate->CheckNode(node)) { return; } // remove all possible contour markers of the segmentation mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations( node, mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true))); ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* 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; mitk::Image* image = dynamic_cast(node->GetData()); mitk::SurfaceInterpolationController::GetInstance()->RemoveInterpolationSession(image); } void QmitkSegmentationView::OnEstablishLabelSetConnection() { if (m_WorkingNode.IsNull()) { return; } auto workingImage = dynamic_cast(m_WorkingNode->GetData()); if (nullptr == workingImage) { return; } workingImage->GetActiveLabelSet()->AddLabelEvent += mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->RemoveLabelEvent += mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->ModifyLabelEvent += mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent += mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->ActiveLabelEvent += mitk::MessageDelegate1(m_Controls->labelSetWidget, &QmitkLabelSetWidget::SelectLabelByPixelValue); // 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, &QmitkSegmentationView::UpdateGUI); } void QmitkSegmentationView::OnLooseLabelSetConnection() { if (m_WorkingNode.IsNull()) { return; } auto workingImage = dynamic_cast(m_WorkingNode->GetData()); if (nullptr == workingImage) { return; } // Reset LabelSetWidget Events workingImage->GetActiveLabelSet()->AddLabelEvent -= mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->ModifyLabelEvent -= mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent -= mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->ActiveLabelEvent -= mitk::MessageDelegate1(m_Controls->labelSetWidget, &QmitkLabelSetWidget::SelectLabelByPixelValue); // 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, &QmitkSegmentationView::UpdateGUI); } void QmitkSegmentationView::ApplyDisplayOptions() { if (nullptr == m_Parent) { return; } if (nullptr == m_Controls) { return; // might happen on initialization (preferences loaded) } mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDataStorage()->GetSubset(m_SegmentationPredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { this->ApplyDisplayOptions(*iter); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::ApplyDisplayOptions(mitk::DataNode* node) { if (nullptr == node) { return; } auto labelSetImage = dynamic_cast(node->GetData()); if (nullptr != labelSetImage) { // node is a multi label segmentation // its outline property can be set in the segmentation preference page node->SetProperty("labelset.contour.active", mitk::BoolProperty::New(m_DrawOutline)); // force render window update to show outline node->GetData()->Modified(); } else if (nullptr != node->GetData()) { // node is a legacy binary segmentation bool isBinary = false; node->GetBoolProperty("binary", isBinary); if (isBinary) { node->SetProperty("outline binary", mitk::BoolProperty::New(m_DrawOutline)); node->SetProperty("outline width", mitk::FloatProperty::New(2.0)); // force render window update to show outline node->GetData()->Modified(); } } } void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode* node) { QmitkRenderWindow* selectedRenderWindow = nullptr; auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); auto* axialRenderWindow = renderWindowPart->GetQmitkRenderWindow("axial"); auto* sagittalRenderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal"); auto* coronalRenderWindow = renderWindowPart->GetQmitkRenderWindow("coronal"); auto* threeDRenderWindow = renderWindowPart->GetQmitkRenderWindow("3d"); bool PlanarFigureInitializedWindow = false; // find initialized renderwindow if (node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, axialRenderWindow->GetRenderer())) { selectedRenderWindow = axialRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, sagittalRenderWindow->GetRenderer())) { selectedRenderWindow = sagittalRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, coronalRenderWindow->GetRenderer())) { selectedRenderWindow = coronalRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, threeDRenderWindow->GetRenderer())) { selectedRenderWindow = threeDRenderWindow; } // make node visible if (nullptr != selectedRenderWindow) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1; ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id)); context->ungetService(ppmRef); selectedRenderWindow->GetRenderer()->GetCameraController()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSegmentationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& nodes) { if (0 == nodes.size()) { return; } std::string markerName = "Position"; unsigned int numberOfNodes = nodes.size(); std::string nodeName = nodes.at(0)->GetName(); if ((numberOfNodes == 1) && (nodeName.find(markerName) == 0)) { this->OnContourMarkerSelected(nodes.at(0)); return; } } void QmitkSegmentationView::ResetMouseCursor() { if (m_MouseCursorSet) { mitk::ApplicationCursor::GetInstance()->PopCursor(); m_MouseCursorSet = false; } } void QmitkSegmentationView::SetMouseCursor(const us::ModuleResource& resource, int hotspotX, int hotspotY) { // Remove previously set mouse cursor if (m_MouseCursorSet) { this->ResetMouseCursor(); } if (resource) { us::ModuleResourceStream cursor(resource, std::ios::binary); mitk::ApplicationCursor::GetInstance()->PushCursor(cursor, hotspotX, hotspotY); m_MouseCursorSet = true; } } void QmitkSegmentationView::UpdateGUI() { mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); bool hasReferenceNode = referenceNode != nullptr; mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); bool hasWorkingNode = workingNode != nullptr; m_Controls->newSegmentationButton->setEnabled(false); if (hasReferenceNode) { m_Controls->newSegmentationButton->setEnabled(true); } if (hasWorkingNode && hasReferenceNode) { int layer = -1; referenceNode->GetIntProperty("layer", layer); workingNode->SetIntProperty("layer", layer + 1); } m_Controls->layersWidget->UpdateGUI(); m_Controls->labelsWidget->UpdateGUI(); this->ValidateSelectionInput(); } void QmitkSegmentationView::ValidateSelectionInput() { this->UpdateWarningLabel(""); m_Controls->layersWidget->setEnabled(false); m_Controls->labelsWidget->setEnabled(false); m_Controls->labelSetWidget->setEnabled(false); // the argument is actually not used // enable status depends on the tool manager selection m_Controls->toolSelectionBox2D->setEnabled(false); m_Controls->toolSelectionBox3D->setEnabled(false); m_Controls->slicesInterpolator->setEnabled(false); m_Controls->interpolatorWarningLabel->hide(); mitk::DataNode* referenceNode = m_Controls->referenceNodeSelector->GetSelectedNode(); mitk::DataNode* workingNode = m_Controls->workingNodeSelector->GetSelectedNode(); if (nullptr == referenceNode) { return; } if (nullptr == workingNode) { return; } mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); auto workingNodeIsVisible = renderWindowPart && workingNode->IsVisible(renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer()); if (!workingNodeIsVisible) { this->UpdateWarningLabel(tr("The selected segmentation is currently not visible!")); return; } /* * Here we check whether the geometry of the selected segmentation image is aligned with the worldgeometry. * At the moment it is not supported to use a geometry different from the selected image for reslicing. * For further information see Bug 16063 */ const mitk::BaseGeometry* workingNodeGeo = workingNode->GetData()->GetGeometry(); const mitk::BaseGeometry* worldGeo = renderWindowPart->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D(); if (nullptr != workingNodeGeo && nullptr != worldGeo) { if (mitk::Equal(*workingNodeGeo->GetBoundingBox(), *worldGeo->GetBoundingBox(), mitk::eps, true)) { m_ToolManager->SetReferenceData(referenceNode); m_ToolManager->SetWorkingData(workingNode); m_Controls->layersWidget->setEnabled(true); m_Controls->labelsWidget->setEnabled(true); m_Controls->labelSetWidget->setEnabled(true); m_Controls->toolSelectionBox2D->setEnabled(true); m_Controls->toolSelectionBox3D->setEnabled(true); auto labelSetImage = dynamic_cast(workingNode->GetData()); if (nullptr != labelSetImage) { int numberOfLabels = labelSetImage->GetNumberOfLabels(labelSetImage->GetActiveLayer()); if (2 == numberOfLabels) // fix for T27319: exterior is label 0, first label is label 1 { m_Controls->slicesInterpolator->setEnabled(true); } else { m_Controls->interpolatorWarningLabel->show(); m_Controls->interpolatorWarningLabel->setText("Interpolation only works for single label segmentations."); } } return; } } m_ToolManager->SetReferenceData(referenceNode); m_ToolManager->SetWorkingData(nullptr); this->UpdateWarningLabel(tr("Please perform a reinit on the segmentation image!")); } void QmitkSegmentationView::UpdateWarningLabel(QString text) { if (text.size() == 0) { m_Controls->selectionWarningLabel->hide(); } else { m_Controls->selectionWarningLabel->show(); m_Controls->selectionWarningLabel->setText("" + text + ""); } } diff --git a/README.md b/README.md index 7285997530..f2c8c8469b 100644 --- a/README.md +++ b/README.md @@ -1,110 +1,110 @@ ![MITK Logo][logo] The [Medical Imaging Interaction Toolkit][mitk] (MITK) is a free open-source software system for development of interactive medical image processing software. MITK combines the [Insight Toolkit][itk] (ITK) and the [Visualization Toolkit][vtk] (VTK) with an application framework. The links below provide high-level and reference documentation targeting different usage scenarios: - Get a [high-level overview][mitk-overview] about MITK with pointers to further documentation - End-users looking for help with MITK applications should read the [MITK User Manual][mitk-usermanual] - Developers contributing to or using MITK, please see the [MITK Developer Manual][mitk-devmanual] as well as the [MITK API Reference][mitk-apiref] See the [MITK homepage][mitk] for details. Supported platforms ------------------- MITK is a cross-platform C++ toolkit and officially supports: - Windows - Linux - macOS For details, please read the [Supported Platforms][platforms] page. ### Build status of develop branch [![Windows][windows-build-status]][cdash] -[![Ubuntu 18.04][ubuntu-18.04-build-status]][cdash] [![Ubuntu 20.04][ubuntu-20.04-build-status]][cdash] +[![Ubuntu 22.04][ubuntu-22.04-build-status]][cdash] [![macOS 10.15 Catalina][macos-10.15-build-status]][cdash] [![macOS 11 Big Sur][macos-11-build-status]][cdash] We highly recommend to use the stable **master** branch instead. It is updated 1-2 times per month accompanied by curated [changelogs][changelog] and [snapshot installers][snapshot-installers]. License ------- Copyright (c) [German Cancer Research Center (DKFZ)][dkfz]. All rights reserved. MITK is available as free open-source software under a [3-clause BSD license][license]. Download -------- The MITK source code and binaries for the *MitkWorkbench* application are released regularly according to the [MITK release cycle][release-cycle]. See the [Download][download] page for a list of releases. The official MITK source code is available in the [MITK Git repository][phab_repo]. The Git clone command is git clone https://phabricator.mitk.org/source/mitk.git MITK Active development takes place in the MITK develop branch and its usage is advised for advanced users only. How to contribute ----------------- Contributions of all kind are happily accepted. However, to make the contribution process as smooth as possible, please read the [How to contribute to MITK][contribute] page if you plan to contribute to MITK. Build instructions ------------------ MITK uses [CMake][cmake] to configure a build tree. The following is a crash course about cloning, configuring, and building MITK on a Linux/Unix system: git clone https://phabricator.mitk.org/source/mitk.git MITK mkdir MITK-build cd MITK-build cmake ../MITK make -j4 Read the comprehensive [build instructions][build] page for details. Useful links ------------ - [Homepage][mitk] - [Download][download] - [Mailing list][mailinglist] - [Issue tracker][bugs] [logo]: https://github.com/MITK/MITK/raw/master/mitk.png [mitk]: https://www.mitk.org [itk]: https://itk.org [vtk]: https://vtk.org [mitk-overview]: https://docs.mitk.org/nightly/ [mitk-usermanual]: https://docs.mitk.org/nightly/UserManualPortal.html [mitk-devmanual]: https://docs.mitk.org/nightly/DeveloperManualPortal.html [mitk-apiref]: https://docs.mitk.org/nightly/usergroup0.html [platforms]: https://docs.mitk.org/nightly/SupportedPlatformsPage.html [dkfz]: https://www.dkfz.de [license]: https://github.com/MITK/MITK/blob/master/LICENSE [release-cycle]: https://www.mitk.org/MitkReleaseCycle [download]: https://www.mitk.org/Download [phab_repo]: https://phabricator.mitk.org/source/mitk/ [contribute]: https://www.mitk.org/How_to_contribute [cmake]: https://www.cmake.org [build]: https://docs.mitk.org/nightly/BuildInstructionsPage.html [mailinglist]: https://www.mitk.org/Mailinglist [bugs]: https://phabricator.mitk.org/maniphest/ [cdash]: https://cdash.mitk.org/index.php?project=MITK [changelog]: https://phabricator.mitk.org/w/mitk/changelog/ [snapshot-installers]: https://www.mitk.org/download/ci/snapshots/ [windows-build-status]: https://ci.mitk.org/buildStatus/icon?job=MITK%2FContinuous%2FWindows&subject=Windows -[ubuntu-18.04-build-status]: https://ci.mitk.org/buildStatus/icon?job=MITK%2FContinuous%2FUbuntu+18.04&subject=Ubuntu+18.04 +[ubuntu-22.04-build-status]: https://ci.mitk.org/buildStatus/icon?job=MITK%2FContinuous%2FUbuntu+22.04&subject=Ubuntu+22.04 [ubuntu-20.04-build-status]: https://ci.mitk.org/buildStatus/icon?job=MITK%2FContinuous%2FUbuntu+20.04&subject=Ubuntu+20.04 [macOS-10.15-build-status]: https://ci.mitk.org/buildStatus/icon?job=MITK%2FContinuous%2FmacOS+Catalina&subject=macOS+10.15+Catalina [macOS-11-build-status]: https://ci.mitk.org/buildStatus/icon?job=MITK%2FContinuous%2FmacOS+Big+Sur&subject=macOS+11+Big+Sur