diff --git a/CMakeLists.txt b/CMakeLists.txt index 95428c4..93633ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,298 +1,294 @@ #----------------------------------------------------------------------------- # This is the root RTToolbox CMakeList file. #----------------------------------------------------------------------------- PROJECT(RTToolbox) CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11.2) IF(CMAKE_BACKWARDS_COMPATIBILITY GREATER 2.8.11.2) SET(CMAKE_BACKWARDS_COMPATIBILITY 2.8.11.2 CACHE STRING "Latest version of CMake when this project was released." FORCE) ENDIF(CMAKE_BACKWARDS_COMPATIBILITY GREATER 2.8.11.2) # RTToolbox version number. SET(RTToolbox_VERSION_MAJOR "4") SET(RTToolbox_VERSION_MINOR "1") -SET(RTToolbox_VERSION_PATCH "0") +SET(RTToolbox_VERSION_PATCH "1") # Version string should not include patch level. The major.minor is # enough to distinguish available features of the toolbox. SET(RTToolbox_VERSION_STRING "${RTToolbox_VERSION_MAJOR}.${RTToolbox_VERSION_MINOR}") SET(RTToolbox_FULL_VERSION_STRING "${RTToolbox_VERSION_MAJOR}.${RTToolbox_VERSION_MINOR}.${RTToolbox_VERSION_PATCH}") # default build type SET(CMAKE_BUILD_TYPE Release) MARK_AS_ADVANCED(BUILD_SHARED_LIBS) IF (WIN32) IF (MSVC_VERSION LESS 1600) MESSAGE(FATAL_ERROR "RTToolbox requires at least Visual Studio 2010.") ENDIF(MSVC_VERSION LESS 1600) add_definitions(-D_SCL_SECURE_NO_WARNINGS) ELSE (WIN32) IF (CMAKE_COMPILER_IS_GNUCC) IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 5.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.0) MESSAGE(AUTHOR_WARNING "RTToolbox was only tested with GCC 4.6 and GCC 4.9. You are using GCC " ${CMAKE_CXX_COMPILER_VERSION} ". This compiler version might not work.") ENDIF() ENDIF() ENDIF(WIN32) IF (NOT (CMAKE_MAJOR_VERSION LESS 3)) IF(COMMAND CMAKE_POLICY) # Enable old CMake behaviour when dealing with export_library_dependencies(). # This is necessary to avoid warnings in CMake versions # greater than 3.0 # See http://www.cmake.org/cmake/help/v3.0/policy/CMP0033.html CMAKE_POLICY(SET CMP0033 OLD) ENDIF(COMMAND CMAKE_POLICY) ENDIF() #----------------------------------------------------------------------------- # CMake Function(s) and Macro(s) #----------------------------------------------------------------------------- include(cmake/MacroParseArguments.cmake) include(cmake/rttbMacroCreateModuleConf.cmake) include(cmake/rttbMacroCreateModule.cmake) include(cmake/rttbMacroCreateApplication.cmake) include(cmake/rttbMacroCheckModule.cmake) include(cmake/rttbMacroUseModule.cmake) include(cmake/rttbMacroCreateTestModule.cmake) include(cmake/rttbFunctionOrganizeSources.cmake) include(cmake/rttbMacroCreateApplicationTests.cmake) #----------------------------------------------------------------------------- # Basis config RTTB module infrastructure #----------------------------------------------------------------------------- set(RTTB_MODULES_CONF_DIR ${RTToolbox_BINARY_DIR}/modulesConf CACHE INTERNAL "Modules Conf") set(RTTB_MODULES_PACKAGE_DEPENDS_DIR ${RTToolbox_SOURCE_DIR}/cmake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${RTTB_MODULES_PACKAGE_DEPENDS_DIR}) #----------------------------------------------------------------------------- # Testing setup # Configure Dart testing support. This should be done before any # MESSAGE(FATAL_ERROR ...) commands are invoked. #----------------------------------------------------------------------------- SET(CTEST_NEW_FORMAT 1) INCLUDE(CTest) ENABLE_TESTING() IF(BUILD_TESTING) CONFIGURE_FILE(${RTToolbox_SOURCE_DIR}/cmake/RemoveTemporaryFiles.cmake.in ${RTToolbox_BINARY_DIR}/cmake/RemoveTemporaryFiles.cmake @ONLY IMMEDIATE) CONFIGURE_FILE(${RTToolbox_SOURCE_DIR}/cmake/rttbSampleBuildTest.cmake.in ${RTToolbox_BINARY_DIR}/cmake/rttbSampleBuildTest.cmake @ONLY) CONFIGURE_FILE(${RTToolbox_SOURCE_DIR}/cmake/CTestCustom.ctest.in ${RTToolbox_BINARY_DIR}/cmake/CTestCustom.ctest @ONLY) FILE(WRITE ${RTToolbox_BINARY_DIR}/CTestCustom.cmake "INCLUDE(\"${RTToolbox_BINARY_DIR}/cmake/CTestCustom.ctest\")\n") SET(BUILDNAME "${BUILDNAME}" CACHE STRING "Name of build on the dashboard") MARK_AS_ADVANCED(BUILDNAME) ENDIF(BUILD_TESTING) #----------------------------------------------------------------------------- # Output directories. #----------------------------------------------------------------------------- IF(NOT LIBRARY_OUTPUT_PATH) SET (LIBRARY_OUTPUT_PATH ${RTToolbox_BINARY_DIR}/bin CACHE PATH "Single output directory for building all libraries.") ENDIF(NOT LIBRARY_OUTPUT_PATH) IF(NOT EXECUTABLE_OUTPUT_PATH) SET (EXECUTABLE_OUTPUT_PATH ${RTToolbox_BINARY_DIR}/bin CACHE PATH "Single output directory for building all executables.") ENDIF(NOT EXECUTABLE_OUTPUT_PATH) MARK_AS_ADVANCED(EXECUTABLE_OUTPUT_PATH LIBRARY_OUTPUT_PATH) MARK_AS_ADVANCED(LIBRARY_OUTPUT_PATH EXECUTABLE_OUTPUT_PATH) SET(RTToolbox_LIBRARY_PATH "${LIBRARY_OUTPUT_PATH}") SET(RTToolbox_EXECUTABLE_PATH "${EXECUTABLE_OUTPUT_PATH}") #----------------------------------------------------------------------------- # Find Doxygen. #----------------------------------------------------------------------------- FIND_PROGRAM(DOXYGEN_EXECUTABLE "doxygen") #----------------------------------------------------------------------------- # Installation vars. # RTToolbox_INSTALL_BIN_DIR - binary dir (executables) # RTToolbox_INSTALL_LIB_DIR - library dir (libs) # RTToolbox_INSTALL_INCLUDE_DIR - include dir (headers) # RTToolbox_INSTALL_NO_DEVELOPMENT - do not install development files # RTToolbox_INSTALL_NO_RUNTIME - do not install runtime files # RTToolbox_INSTALL_NO_DOCUMENTATION - do not install documentation files # Remark: needs directory are stored with no leading slash (CMake 2.4 and newer) #----------------------------------------------------------------------------- IF(NOT RTTOOLBOX_INSTALL_BIN_DIR) SET(RTTOOLBOX_INSTALL_BIN_DIR "bin") ENDIF(NOT RTTOOLBOX_INSTALL_BIN_DIR) IF(NOT RTTOOLBOX_INSTALL_LIB_DIR) SET(RTTOOLBOX_INSTALL_LIB_DIR "lib") ENDIF(NOT RTTOOLBOX_INSTALL_LIB_DIR) IF(NOT RTTOOLBOX_INSTALL_PACKAGE_DIR) SET(RTTOOLBOX_INSTALL_PACKAGE_DIR "lib") ENDIF(NOT RTTOOLBOX_INSTALL_PACKAGE_DIR) IF(NOT RTTOOLBOX_INSTALL_INCLUDE_DIR) SET(RTTOOLBOX_INSTALL_INCLUDE_DIR "include") ENDIF(NOT RTTOOLBOX_INSTALL_INCLUDE_DIR) IF(NOT RTTOOLBOX_INSTALL_NO_DEVELOPMENT) SET(RTTOOLBOX_INSTALL_NO_DEVELOPMENT 0) ENDIF(NOT RTTOOLBOX_INSTALL_NO_DEVELOPMENT) IF(NOT RTTOOLBOX_INSTALL_NO_RUNTIME) SET(RTTOOLBOX_INSTALL_NO_RUNTIME 0) ENDIF(NOT RTTOOLBOX_INSTALL_NO_RUNTIME) IF(NOT RTTOOLBOX_INSTALL_NO_DOCUMENTATION) SET(RTTOOLBOX_INSTALL_NO_DOCUMENTATION 0) ENDIF(NOT RTTOOLBOX_INSTALL_NO_DOCUMENTATION) SET(RTTOOLBOX_INSTALL_NO_LIBRARIES) IF(RTTOOLBOX_BUILD_SHARED_LIBS) IF(RTTOOLBOX_INSTALL_NO_RUNTIME AND RTTOOLBOX_INSTALL_NO_DEVELOPMENT) SET(RTTOOLBOX_INSTALL_NO_LIBRARIES 1) ENDIF(RTTOOLBOX_INSTALL_NO_RUNTIME AND RTTOOLBOX_INSTALL_NO_DEVELOPMENT) ELSE(RTTOOLBOX_BUILD_SHARED_LIBS) IF(RTTOOLBOX_INSTALL_NO_DEVELOPMENT) SET(RTTOOLBOX_INSTALL_NO_LIBRARIES 1) ENDIF(RTTOOLBOX_INSTALL_NO_DEVELOPMENT) ENDIF(RTTOOLBOX_BUILD_SHARED_LIBS) # set RTToolbox_DIR so it can be used by subprojects SET(RTToolbox_DIR "${CMAKE_BINARY_DIR}" CACHE INTERNAL "RTToolbox dir to be used by subprojects") #----------------------------------------------------------------------------- # DCMTK MT-Flag treat #----------------------------------------------------------------------------- option(RTTB_DCMTK_COMPLIANCE_ENFORCE_MT "This enforces the whole RTToolbox to be compiled with /MT,/MTd to be compliant with DCMTK" OFF) string(FIND ${CMAKE_GENERATOR} "Visual Studio" RTTB_VS_USED) if(RTTB_DCMTK_COMPLIANCE_ENFORCE_MT AND RTTB_VS_USED EQUAL 0) message(STATUS "Enforce DCMTK compliance: /MT and /MTd flags are used") string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG}) message(STATUS "CMAKE_C_FLAGS_DEBUG set to: ${CMAKE_C_FLAGS_DEBUG}") string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE}) message(STATUS "CMAKE_C_FLAGS_RELEASE set to: ${CMAKE_C_FLAGS_RELEASE}") string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_MINSIZEREL ${CMAKE_C_FLAGS_MINSIZEREL}) message(STATUS "CMAKE_C_FLAGS_MINSIZEREL set to: ${CMAKE_C_FLAGS_MINSIZEREL}") string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELWITHDEBINFO ${CMAKE_C_FLAGS_RELWITHDEBINFO}) message(STATUS "CMAKE_C_FLAGS_RELWITHDEBINFO set to: ${CMAKE_C_FLAGS_RELWITHDEBINFO}") string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) message(STATUS "CMAKE_CXX_FLAGS_DEBUG set to: ${CMAKE_CXX_FLAGS_DEBUG}") string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) message(STATUS "CMAKE_CXX_FLAGS_RELEASE set to: ${CMAKE_CXX_FLAGS_RELEASE}") string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_MINSIZEREL ${CMAKE_CXX_FLAGS_MINSIZEREL}) message(STATUS "CMAKE_CXX_FLAGS_MINSIZEREL set to: ${CMAKE_CXX_FLAGS_MINSIZEREL}") string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}) message(STATUS "CMAKE_CXX_FLAGS_RELWITHDEBINFO set to: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") endif() #----------------------------------------------------------------------------- # Advanced RTToolbox configuration #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- # RTToolbox build configuration options. IF (WIN32) OPTION(BUILD_SHARED_LIBS "Build RTToolbox with shared libraries." OFF) ELSE (WIN32) OPTION(BUILD_SHARED_LIBS "Build RTToolbox with shared libraries." ON) ENDIF (WIN32) -IF(BUILD_SHARED_LIBS) - IF(WIN32) - MESSAGE(FATAL_ERROR "RTToolbox currently does not support a dynamic build on Windows. We are working on that...") - ENDIF(WIN32) -ELSE(BUILD_SHARED_LIBS) +IF(NOT BUILD_SHARED_LIBS) IF(UNIX) MESSAGE(FATAL_ERROR "RTToolbox currently does not support a static build on unix like systems. We are working on that...") ENDIF(UNIX) -ENDIF(BUILD_SHARED_LIBS) +ENDIF(NOT BUILD_SHARED_LIBS) SET(RTToolbox_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) IF(NOT RTToolbox_NO_LIBRARY_VERSION) # This setting of SOVERSION assumes that any API change # will increment either the minor or major version number of RTToolbox. SET(RTToolbox_LIBRARY_PROPERTIES VERSION "${RTToolbox_VERSION_MAJOR}.${RTToolbox_VERSION_MINOR}.${RTToolbox_VERSION_PATCH}" SOVERSION "${RTToolbox_VERSION_MAJOR}.${RTToolbox_VERSION_MINOR}" ) ENDIF(NOT RTToolbox_NO_LIBRARY_VERSION) #----------------------------------------------------------------------------- # Configure files with settings for use by the build. # #----------------------------------------------------------------------------- CONFIGURE_FILE(${RTToolbox_SOURCE_DIR}/RTToolboxConfigure.h.in ${RTToolbox_BINARY_DIR}/RTToolboxConfigure.h) IF(NOT RTTOOLBOX_INSTALL_NO_DEVELOPMENT) INSTALL(FILES ${RTToolbox_BINARY_DIR}/RTToolboxConfigure.h DESTINATION ${RTTOOLBOX_INSTALL_INCLUDE_DIR} COMPONENT Development) ENDIF(NOT RTTOOLBOX_INSTALL_NO_DEVELOPMENT) #----------------------------------------------------------------------------- # The entire RTToolbox tree should use the same include path #----------------------------------------------------------------------------- #Default include dir. Others dirs will be defined by activated subprojects INCLUDE_DIRECTORIES(${RTToolbox_BINARY_DIR}) LINK_DIRECTORIES(${LIBARY_OUTPUT_PATH}) #Prepare the correct target information export by the subprojects SET(RTToolbox_TARGETS_FILE "${RTToolbox_BINARY_DIR}/RTToolboxTargets.cmake") FILE(WRITE ${RTToolbox_TARGETS_FILE} "# Generated by CMake, do not edit!") #----------------------------------------------------------------------------- # Dispatch the build into the proper subdirectories. #----------------------------------------------------------------------------- MESSAGE (STATUS "generating Project RTToolbox") ADD_SUBDIRECTORY (code) ADD_SUBDIRECTORY (demoapps) IF (BUILD_TESTING) ADD_SUBDIRECTORY (testing) ENDIF (BUILD_TESTING) ADD_SUBDIRECTORY (documentation) #----------------------------------------------------------------------------- # Help other projects use RTToolbox. #----------------------------------------------------------------------------- EXPORT(PACKAGE RTToolbox) # Copy the UseRTToolbox.cmake file to the binary tree for backward compatability. CONFIGURE_FILE(${RTToolbox_SOURCE_DIR}/UseRTToolbox.cmake.in ${RTToolbox_BINARY_DIR}/UseRTToolbox.cmake COPYONLY IMMEDIATE) # Save library dependencies. EXPORT_LIBRARY_DEPENDENCIES(${RTToolbox_BINARY_DIR}/RTToolboxLibraryDepends.cmake) # Create the RTToolboxConfig.cmake file containing the RTToolbox configuration. INCLUDE (${RTToolbox_SOURCE_DIR}/rttbGenerateRTToolboxConfig.cmake) IF(NOT RTToolbox_INSTALL_NO_DEVELOPMENT) INSTALL(FILES ${RTToolbox_BINARY_DIR}/RTToolboxConfig.cmake ${RTToolbox_BINARY_DIR}/RTToolboxTargets.cmake ${RTToolbox_BINARY_DIR}/RTToolboxLibraryDepends.cmake ${RTToolbox_BINARY_DIR}/UseRTToolbox.cmake DESTINATION ${RTTOOLBOX_INSTALL_PACKAGE_DIR} COMPONENT Development ) ENDIF(NOT RTToolbox_INSTALL_NO_DEVELOPMENT) diff --git a/cmake/PackageDepends/RTTB_BoostBinaries_Config.cmake b/cmake/PackageDepends/RTTB_BoostBinaries_Config.cmake index 0f8f727..239183e 100644 --- a/cmake/PackageDepends/RTTB_BoostBinaries_Config.cmake +++ b/cmake/PackageDepends/RTTB_BoostBinaries_Config.cmake @@ -1,29 +1,34 @@ IF(NOT BoostBinaries_FOUND) IF(DEFINED Boost_INCLUDE_DIR) IF(NOT IS_ABSOLUTE ${Boost_INCLUDE_DIR}) SET(Boost_INCLUDE_DIR "${RTToolbox_BINARY_DIR}/${Boost_INCLUDE_DIR}") ENDIF(NOT IS_ABSOLUTE ${Boost_INCLUDE_DIR}) ENDIF(DEFINED Boost_INCLUDE_DIR) - - IF(BUILD_SHARED_LIBS) + + IF(NOT DEFINED RTTB_USE_MITK_BOOST) + OPTION(RTTB_USE_MITK_BOOST "RTTB should use a boost which is available in the MITK superbuild external projects structure." OFF) + MARK_AS_ADVANCED(RTTB_USE_MITK_BOOST) + ENDIF(NOT DEFINED RTTB_USE_MITK_BOOST) + + IF(BUILD_SHARED_LIBS OR RTTB_USE_MITK_BOOST) SET(Boost_USE_STATIC_LIBS OFF) - ELSE(BUILD_SHARED_LIBS) + ADD_DEFINITIONS(-DBOOST_ALL_DYN_LINK) + ELSE(BUILD_SHARED_LIBS OR RTTB_USE_MITK_BOOST) SET(Boost_USE_STATIC_LIBS ON) - ENDIF(BUILD_SHARED_LIBS) + ENDIF(BUILD_SHARED_LIBS OR RTTB_USE_MITK_BOOST) SET(BOOST_MIN_VERSION "1.56.0") - FIND_PACKAGE(Boost ${BOOST_MIN_VERSION} REQUIRED COMPONENTS filesystem regex program_options system) - + FIND_PACKAGE(Boost ${BOOST_MIN_VERSION} REQUIRED COMPONENTS filesystem regex system ${RTTB_Boost_ADDITIONAL_COMPONENT}) LIST(APPEND ALL_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIRS}) LIST(APPEND ALL_LIBRARIES ${Boost_LIBRARIES}) link_directories(${Boost_LIBRARY_DIRS}) MARK_AS_ADVANCED(CLEAR Boost_INCLUDE_DIR) SET(BoostBinaries_FOUND TRUE) ENDIF(NOT BoostBinaries_FOUND) diff --git a/cmake/PackageDepends/RTTB_DCMTK_Config.cmake b/cmake/PackageDepends/RTTB_DCMTK_Config.cmake index 50d13d2..bd30a9e 100644 --- a/cmake/PackageDepends/RTTB_DCMTK_Config.cmake +++ b/cmake/PackageDepends/RTTB_DCMTK_Config.cmake @@ -1,218 +1,230 @@ #----------------------------------------------------------------------------- # Find DCMTK #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- # This part is based on the findDCMTK of CMake 2.8.8 # it was patched because: # 1. the find_library statement had an incomplete set # of search paths. If building DCMTK with Visual # Studio the libs are to be found in /lib/release # or /lib/debug # 2. the module DCMRT was not included (but needed by RTToolbox) # IF(DEFINED DCMTK_DIR) IF(NOT IS_ABSOLUTE ${DCMTK_DIR}) SET(DCMTK_DIR "${RTToolbox_BINARY_DIR}/${DCMTK_DIR}") ENDIF(NOT IS_ABSOLUTE ${DCMTK_DIR}) ENDIF(DEFINED DCMTK_DIR) if(NOT DCMTK_FOUND AND NOT DCMTK_DIR) IF (NOT WIN32) set(DCMTK_DIR "/usr/include/dcmtk/") ENDIF(NOT WIN32) endif() #ensure that we always have the variable as cache, independent from #setting it via gui or command line set(DCMTK_DIR ${DCMTK_DIR} CACHE PATH "Root of DCMTK tree.") IF(DEFINED DCMTK_SOURCE_DIR) IF(NOT IS_ABSOLUTE ${DCMTK_SOURCE_DIR}) SET(DCMTK_SOURCE_DIR "${RTToolbox_BINARY_DIR}/${DCMTK_SOURCE_DIR}") ENDIF(NOT IS_ABSOLUTE ${DCMTK_SOURCE_DIR}) ENDIF(DEFINED DCMTK_SOURCE_DIR) if(NOT DCMTK_FOUND AND NOT DCMTK_SOURCE_DIR) set(DCMTK_SOURCE_DIR ${DCMTK_DIR}) endif() #ensure that we always have the variable as cache, independent from #setting it via gui or command line set(DCMTK_SOURCE_DIR ${DCMTK_SOURCE_DIR} CACHE PATH "Root of DCMTK tree.") +IF(NOT DEFINED RTTB_USE_MITK_DCMTK) + OPTION(RTTB_USE_MITK_DCMTK "RTTB should use a DCMTK which is available in the MITK superbuild external projects structure." OFF) + MARK_AS_ADVANCED(RTTB_USE_MITK_DCMTK) +ENDIF(NOT DEFINED RTTB_USE_MITK_DCMTK) + OPTION(RTTB_USE_ML_DCMTK "RTTB should use a DCMTK which is available in the MeVisLab package structure." OFF) MARK_AS_ADVANCED(RTTB_USE_ML_DCMTK) if(NOT DCMTK_FOUND) set(DCMTK_FOUND True) set(DCMTK_DEBUG_LIB_SEARCH_PATH ${DCMTK_DIR}/${lib}/libsrc/Debug ${DCMTK_DIR}/${lib}/Debug ${DCMTK_DIR}/lib/Debug ) set(DCMTK_LIB_SEARCH_PATH ${DCMTK_DIR}/${lib}/libsrc ${DCMTK_DIR}/${lib}/libsrc/Release ${DCMTK_DIR}/${lib}/Release ${DCMTK_DIR}/lib/Release ${DCMTK_DIR}/lib ) set(DCMTK_LIB_SEARCH_NAMES dcmdata dcmimage dcmimgle dcmjpeg dcmnet dcmpstat dcmqrdb dcmdsig dcmsr dcmtls dcmrt ijg12 ijg16 ijg8 ofstd oflog ) if(${RTTB_USE_ML_DCMTK}) set(DCMTK_DEBUG_LIB_SEARCH_PATH ${DCMTK_DIR}/lib ) set(DCMTK_LIB_SEARCH_PATH ${DCMTK_DIR}/lib ) set(DCMTK_LIB_SEARCH_NAMES ${DCMTK_LIB_SEARCH_NAMES} zlib) endif(${RTTB_USE_ML_DCMTK}) foreach(lib ${DCMTK_LIB_SEARCH_NAMES}) - if(${RTTB_USE_ML_DCMTK}) - find_library(DCMTK_${lib}_DEBUG_LIBRARY - ${lib}_d - PATHS ${DCMTK_DEBUG_LIB_SEARCH_PATH}) - else(${RTTB_USE_ML_DCMTK}) - find_library(DCMTK_${lib}_DEBUG_LIBRARY - ${lib} - PATHS ${DCMTK_DEBUG_LIB_SEARCH_PATH}) - endif(${RTTB_USE_ML_DCMTK}) - + set(debuglib ${lib}) + + if(${RTTB_USE_MITK_DCMTK}) + set(debuglib ${lib}d) + elseif(${RTTB_USE_ML_DCMTK}) + set(debuglib ${lib}_d) + endif(${RTTB_USE_MITK_DCMTK}) + + find_library(DCMTK_${lib}_DEBUG_LIBRARY + ${debuglib} + PATHS ${DCMTK_DEBUG_LIB_SEARCH_PATH}) + find_library(DCMTK_${lib}_LIBRARY ${lib} PATHS ${DCMTK_LIB_SEARCH_PATH}) if((${UNIX}) AND (NOT DCMTK_${lib}_DEBUG_LIBRARY)) set(DCMTK_${lib}_DEBUG_LIBRARY ${DCMTK_${lib}_LIBRARY} CACHE PATH "Path to a library" FORCE) endif((${UNIX}) AND (NOT DCMTK_${lib}_DEBUG_LIBRARY)) mark_as_advanced(DCMTK_${lib}_LIBRARY) mark_as_advanced(DCMTK_${lib}_DEBUG_LIBRARY) add_library(${lib} STATIC IMPORTED) set_target_properties(${lib} PROPERTIES IMPORTED_LOCATION ${DCMTK_${lib}_LIBRARY} IMPORTED_LOCATION_DEBUG ${DCMTK_${lib}_DEBUG_LIBRARY}) - if(DCMTK_${lib}_LIBRARY) + if(DCMTK_${lib}_LIBRARY OR DCMTK_${lib}_DEBUG_LIBRARY) list(APPEND DCMTK_LIBRARIES ${lib}) endif() if(NOT DCMTK_${lib}_LIBRARY) message(WARNING "Cannot find library DCMTK_${lib}_LIBRARY") endif() if(NOT DCMTK_${lib}_DEBUG_LIBRARY) message(WARNING "Cannot find library DCMTK_${lib}_DEBUG_LIBRARY") endif() endforeach() set(DCMTK_config_TEST_HEADER osconfig.h) set(DCMTK_dcmdata_TEST_HEADER dctypes.h) set(DCMTK_dcmimage_TEST_HEADER dicoimg.h) set(DCMTK_dcmimgle_TEST_HEADER dcmimage.h) set(DCMTK_dcmjpeg_TEST_HEADER djdecode.h) set(DCMTK_dcmnet_TEST_HEADER assoc.h) set(DCMTK_dcmpstat_TEST_HEADER dcmpstat.h) set(DCMTK_dcmqrdb_TEST_HEADER dcmqrdba.h) set(DCMTK_dcmrt_TEST_HEADER drmdose.h) set(DCMTK_dcmsign_TEST_HEADER sicert.h) set(DCMTK_dcmsr_TEST_HEADER dsrtree.h) set(DCMTK_dcmtls_TEST_HEADER tlslayer.h) set(DCMTK_ofstd_TEST_HEADER ofstdinc.h) set(DCMTK_oflog_TEST_HEADER oflog.h) foreach(dir config dcmdata dcmimage dcmimgle dcmjpeg dcmnet dcmpstat dcmqrdb dcmrt dcmsign dcmsr dcmtls ofstd oflog) find_path(DCMTK_${dir}_INCLUDE_DIR ${DCMTK_${dir}_TEST_HEADER} PATHS ${DCMTK_DIR}/${dir}/include ${DCMTK_DIR}/${dir} ${DCMTK_DIR}/include/${dir} ${DCMTK_DIR}/include/dcmtk/${dir} ${DCMTK_DIR}/${dir}/include/dcmtk/${dir} ${DCMTK_SOURCE_DIR}/${dir}/include ${DCMTK_SOURCE_DIR}/${dir} + ${DCMTK_SOURCE_DIR}/dcmtk/${dir} ${DCMTK_SOURCE_DIR}/include/${dir} ${DCMTK_SOURCE_DIR}/include/dcmtk/${dir} ${DCMTK_SOURCE_DIR}/${dir}/include/dcmtk/${dir} ) mark_as_advanced(DCMTK_${dir}_INCLUDE_DIR) if(DCMTK_${dir}_INCLUDE_DIR) list(APPEND DCMTK_INCLUDE_DIRS ${DCMTK_${dir}_INCLUDE_DIR} ${DCMTK_SOURCE_DIR}/${dir}/include ${DCMTK_DIR}/${dir}/include) endif() endforeach() if(WIN32) list(APPEND DCMTK_LIBRARIES netapi32 wsock32 ws2_32) endif() if(UNIX) list(APPEND DCMTK_LIBRARIES pthread) endif() if(DCMTK_ofstd_INCLUDE_DIR) get_filename_component(DCMTK_dcmtk_INCLUDE_DIR ${DCMTK_ofstd_INCLUDE_DIR} PATH CACHE) list(APPEND DCMTK_INCLUDE_DIRS ${DCMTK_dcmtk_INCLUDE_DIR}) mark_as_advanced(DCMTK_dcmtk_INCLUDE_DIR) endif() endif() #----------------------------------------------------------------------------- # RTTB part to use the found DCMTK # IF(NOT DCMTK_FOUND) MESSAGE(SEND_ERROR "DCMTK development files not found.\n Please check variables (e.g. DCMTK_DIR) for include directories and libraries.\nYou may set environment variable DCMTK_DIR before pressing 'configure'") ENDIF(NOT DCMTK_FOUND) IF( NOT WIN32 ) SET(MISSING_LIBS_REQUIRED_BY_DCMTK wrap tiff) ENDIF( NOT WIN32 ) LIST(APPEND ALL_INCLUDE_DIRECTORIES ${DCMTK_INCLUDE_DIRS} ${DCMTK_DIR}/include) +IF(DEFINED RTTB_USE_MITK_DCMTK) + LIST(APPEND ALL_INCLUDE_DIRECTORIES ${DCMTK_SOURCE_DIR}) +ENDIF(DEFINED RTTB_USE_MITK_DCMTK) + LIST(APPEND ALL_LIBRARIES ${DCMTK_LIBRARIES} ${MISSING_LIBS_REQUIRED_BY_DCMTK}) diff --git a/cmake/PackageDepends/RTTB_ITK_Config.cmake b/cmake/PackageDepends/RTTB_ITK_Config.cmake index 527ce0e..31cb3d6 100644 --- a/cmake/PackageDepends/RTTB_ITK_Config.cmake +++ b/cmake/PackageDepends/RTTB_ITK_Config.cmake @@ -1,18 +1,29 @@ #----------------------------------------------------------------------------- # Find ITK. #----------------------------------------------------------------------------- FIND_PACKAGE(ITK 4.4 REQUIRED) IF(ITK_FOUND) INCLUDE(${ITK_USE_FILE}) ELSE(ITK_FOUND) MESSAGE(FATAL_ERROR "Cannot build without ITK. Please set ITK_DIR.") ENDIF(ITK_FOUND) LIST(APPEND ALL_INCLUDE_DIRECTORIES ${ITK_INCLUDE_DIRS}) LIST(APPEND ALL_LIBRARIES ${ITK_LIBRARIES}) LINK_DIRECTORIES(${ITK_LIBRARY_DIRS}) +OPTION(RTTB_USE_SYSTEM_GDCM "Activate checker to choose a GDCM installation that should be linked with ITK. (This is needed e.g. when building against an ITK that is distributed with MITK. MITK uses its own GDCM and not the one distributed with ITK.)" OFF) + +IF (RTTB_USE_SYSTEM_GDCM) + MESSAGE (STATUS "RTTB uses system GDCM instead of ITK distribution.") + FIND_PACKAGE(GDCM PATHS ${ITK_GDCM_DIR} ${GDCM_DIR} REQUIRED) + INCLUDE(${GDCM_USE_FILE}) + LIST(APPEND ALL_INCLUDE_DIRECTORIES ${GDCM_INCLUDE_DIRS}) + LIST(APPEND ALL_LIBRARIES ${GDCM_LIBRARIES}) +ENDIF (RTTB_USE_SYSTEM_GDCM) + + CONFIGURE_FILE(${RTToolbox_SOURCE_DIR}/cmake/ITKConfig.cmake.in ${RTTB_MODULES_CONF_DIR}/ITKConfig.cmake @ONLY) diff --git a/code/algorithms/rttbDoseStatistics.cpp b/code/algorithms/rttbDoseStatistics.cpp index 14860e3..0c28329 100644 --- a/code/algorithms/rttbDoseStatistics.cpp +++ b/code/algorithms/rttbDoseStatistics.cpp @@ -1,344 +1,363 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include "rttbDoseStatistics.h" #include "rttbDataNotAvailableException.h" namespace rttb { namespace algorithms { DoseStatistics::DoseStatistics(DoseStatisticType minimum, DoseStatisticType maximum, DoseStatisticType mean, - DoseStatisticType stdDeviation, unsigned int numVoxels, VolumeType volume, + DoseStatisticType stdDeviation, VoxelNumberType numVoxels, VolumeType volume, ResultListPointer maximumVoxelPositions /*= ResultListPointer()*/, ResultListPointer minimumVoxelPositions /*= ResultListPointer()*/, VolumeToDoseFunctionType Dx /*= std::map()*/, DoseToVolumeFunctionType Vx /*= std::map()*/, VolumeToDoseFunctionType MOHx /*= std::map()*/, VolumeToDoseFunctionType MOCx /*= std::map()*/, VolumeToDoseFunctionType MaxOHx /*= std::map()*/, VolumeToDoseFunctionType MinOCx /*= std::map()*/, DoseTypeGy referenceDose /*=-1*/): _minimum(minimum), _maximum(maximum), _mean(mean), _stdDeviation(stdDeviation), _numVoxels(numVoxels), _volume(volume), - _maximumVoxelPositions(maximumVoxelPositions), _minimumVoxelPositions(minimumVoxelPositions), _Dx(Dx), _Vx(Vx), _MOHx(MOHx), _MOCx(MOCx), _MaxOHx(MaxOHx), _MinOCx(MinOCx) { + if (maximumVoxelPositions == NULL) + { + _maximumVoxelPositions = boost::make_shared > > + (std::vector >()); + } + else + { + _maximumVoxelPositions = maximumVoxelPositions; + } + + if (minimumVoxelPositions == NULL) + { + _minimumVoxelPositions = boost::make_shared > > + (std::vector >()); + } + else + { + _minimumVoxelPositions = minimumVoxelPositions; + } + if (referenceDose <= 0) { _referenceDose = _maximum; } else { _referenceDose = referenceDose; } } DoseStatistics::~DoseStatistics() { } void DoseStatistics::setMinimumVoxelPositions(ResultListPointer minimumVoxelPositions) { _minimumVoxelPositions = minimumVoxelPositions; } void DoseStatistics::setMaximumVoxelPositions(ResultListPointer maximumVoxelPositions) { _maximumVoxelPositions = maximumVoxelPositions; } void DoseStatistics::setDx(const DoseToVolumeFunctionType& DxValues) { _Dx = DxValues; } void DoseStatistics::setVx(const VolumeToDoseFunctionType& VxValues) { _Vx = VxValues; } void DoseStatistics::setMOHx(const VolumeToDoseFunctionType& MOHxValues) { _MOHx = MOHxValues; } void DoseStatistics::setMOCx(const VolumeToDoseFunctionType& MOCxValues) { _MOCx = MOCxValues; } void DoseStatistics::setMaxOHx(const VolumeToDoseFunctionType& MaxOHValues) { _MaxOHx = MaxOHValues; } void DoseStatistics::setMinOCx(const VolumeToDoseFunctionType& MinOCValues) { _MinOCx = MinOCValues; } void DoseStatistics::setReferenceDose(DoseTypeGy referenceDose) { if (referenceDose <= 0) { _referenceDose = _maximum; } else { _referenceDose = referenceDose; } } - unsigned int DoseStatistics::getNumberOfVoxels() const + VoxelNumberType DoseStatistics::getNumberOfVoxels() const { return _numVoxels; } VolumeType DoseStatistics::getVolume() const { return _volume; } DoseTypeGy DoseStatistics::getReferenceDose() const { return _referenceDose; } DoseStatisticType DoseStatistics::getMaximum() const { return _maximum; } DoseStatisticType DoseStatistics::getMinimum() const { return _minimum; } DoseStatisticType DoseStatistics::getMean() const { return _mean; } DoseStatisticType DoseStatistics::getStdDeviation() const { return _stdDeviation; } DoseStatisticType DoseStatistics::getVariance() const { return _stdDeviation * _stdDeviation; } VolumeType DoseStatistics::getVx(DoseTypeGy xDoseAbsolute, bool findNearestValue, DoseTypeGy& nearestXDose) const { return getValue(_Vx, xDoseAbsolute, findNearestValue, nearestXDose); } VolumeType DoseStatistics::getVx(DoseTypeGy xDoseAbsolute) const { DoseTypeGy dummy; return getValue(_Vx, xDoseAbsolute, false, dummy); } DoseTypeGy DoseStatistics::getDx(VolumeType xVolumeAbsolute, bool findNearestValue, VolumeType& nearestXVolume) const { return getValue(_Dx, xVolumeAbsolute, findNearestValue, nearestXVolume); } DoseTypeGy DoseStatistics::getDx(VolumeType xVolumeAbsolute) const { VolumeType dummy; return getValue(_Dx, xVolumeAbsolute, false, dummy); } DoseTypeGy DoseStatistics::getMOHx(VolumeType xVolumeAbsolute, bool findNearestValue, VolumeType& nearestXVolume) const { return getValue(_MOHx, xVolumeAbsolute, findNearestValue, nearestXVolume); } DoseTypeGy DoseStatistics::getMOHx(VolumeType xVolumeAbsolute) const { VolumeType dummy; return getValue(_MOHx, xVolumeAbsolute, false, dummy); } DoseTypeGy DoseStatistics::getMOCx(VolumeType xVolumeAbsolute, bool findNearestValue, VolumeType& maximumDistanceFromOriginalVolume) const { return getValue(_MOCx, xVolumeAbsolute, findNearestValue, maximumDistanceFromOriginalVolume); } DoseTypeGy DoseStatistics::getMOCx(VolumeType xVolumeAbsolute) const { VolumeType dummy; return getValue(_MOCx, xVolumeAbsolute, false, dummy); } DoseTypeGy DoseStatistics::getMaxOHx(VolumeType xVolumeAbsolute, bool findNearestValue, VolumeType& maximumDistanceFromOriginalVolume) const { return getValue(_MaxOHx, xVolumeAbsolute, findNearestValue, maximumDistanceFromOriginalVolume); } DoseTypeGy DoseStatistics::getMaxOHx(VolumeType xVolumeAbsolute) const { VolumeType dummy; return getValue(_MaxOHx, xVolumeAbsolute, false, dummy); } DoseTypeGy DoseStatistics::getMinOCx(VolumeType xVolumeAbsolute, bool findNearestValue, VolumeType& maximumDistanceFromOriginalVolume) const { return getValue(_MinOCx, xVolumeAbsolute, findNearestValue, maximumDistanceFromOriginalVolume); } DoseTypeGy DoseStatistics::getMinOCx(VolumeType xVolumeAbsolute) const { VolumeType dummy; return getValue(_MinOCx, xVolumeAbsolute, false, dummy); } double DoseStatistics::getValue(const std::map& aMap, double key, bool findNearestValueInstead, double& storedKey) const { if (aMap.find(key) != std::end(aMap)) { return aMap.find(key)->second; } else { //value not in map. We have to find the nearest value if (aMap.empty()) { throw core::DataNotAvailableException("No Vx values are defined"); } else { if (findNearestValueInstead) { auto iterator = findNearestKeyInMap(aMap, key); storedKey = iterator->first; return iterator->second; } else { throw core::DataNotAvailableException("No Vx value with required dose is defined"); } } } } std::map::const_iterator DoseStatistics::findNearestKeyInMap( const std::map& aMap, double key) const { double minDistance = 1e19; double minDistanceLast = 1e20; auto iterator = std::begin(aMap); while (iterator != std::end(aMap)) { minDistanceLast = minDistance; minDistance = fabs(iterator->first - key); if (minDistanceLast > minDistance) { ++iterator; } else { if (iterator != std::begin(aMap)) { --iterator; return iterator; } else { return std::begin(aMap); } } } --iterator; return iterator; } - DoseStatistics::ResultListPointer DoseStatistics::getMaximumPositions() const + DoseStatistics::ResultListPointer DoseStatistics::getMaximumVoxelPositions() const { return _maximumVoxelPositions; } - DoseStatistics::ResultListPointer DoseStatistics::getMinimumPositions() const + DoseStatistics::ResultListPointer DoseStatistics::getMinimumVoxelPositions() const { return _minimumVoxelPositions; } DoseStatistics::DoseToVolumeFunctionType DoseStatistics::getAllVx() const { return _Vx; } DoseStatistics::VolumeToDoseFunctionType DoseStatistics::getAllDx() const { return _Dx; } DoseStatistics::VolumeToDoseFunctionType DoseStatistics::getAllMOHx() const { return _MOHx; } DoseStatistics::VolumeToDoseFunctionType DoseStatistics::getAllMOCx() const { return _MOCx; } DoseStatistics::VolumeToDoseFunctionType DoseStatistics::getAllMaxOHx() const { return _MaxOHx; } DoseStatistics::VolumeToDoseFunctionType DoseStatistics::getAllMinOCx() const { return _MinOCx; } }//end namespace algorithms }//end namespace rttb diff --git a/code/algorithms/rttbDoseStatistics.h b/code/algorithms/rttbDoseStatistics.h index 726436d..4c80b34 100644 --- a/code/algorithms/rttbDoseStatistics.h +++ b/code/algorithms/rttbDoseStatistics.h @@ -1,220 +1,218 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __DOSE_STATISTICS_H #define __DOSE_STATISTICS_H #include #include #include #include "rttbBaseType.h" #include "rttbDoseIteratorInterface.h" namespace rttb { namespace algorithms { /*! @class DoseStatistics @brief This is a data class storing different statistical values from a rt dose distribution @sa DoseStatisticsCalculator */ class DoseStatistics { public: enum complexStatistics { Dx, Vx, MOHx, MOCx, MaxOHx, MinOCx }; typedef boost::shared_ptr > > ResultListPointer; typedef boost::shared_ptr DoseStatisticsPointer; typedef std::map DoseToVolumeFunctionType; typedef std::map VolumeToDoseFunctionType; private: double getValue(const std::map& aMap, double key, bool findNearestValueInstead, double& storedKey) const; std::map::const_iterator findNearestKeyInMap(const std::map& aMap, double key) const; DoseStatisticType _maximum; DoseStatisticType _minimum; ResultListPointer _maximumVoxelPositions; ResultListPointer _minimumVoxelPositions; DoseStatisticType _mean; DoseStatisticType _stdDeviation; - unsigned int _numVoxels; + VoxelNumberType _numVoxels; VolumeType _volume; DoseTypeGy _referenceDose; //for Vx computation VolumeToDoseFunctionType _Dx; DoseToVolumeFunctionType _Vx; VolumeToDoseFunctionType _MOHx; VolumeToDoseFunctionType _MOCx; VolumeToDoseFunctionType _MaxOHx; VolumeToDoseFunctionType _MinOCx; public: /*! @brief Standard Constructor */ //DoseStatistics(); /*! @brief Constructor @detail the dose statistic values are set. Complex values maximumVoxelLocation, maximumVoxelLocation, Dx, Vx, MOHx, MOCx, MaxOHx and MinOCx are optional */ DoseStatistics(DoseStatisticType minimum, DoseStatisticType maximum, DoseStatisticType mean, - DoseStatisticType stdDeviation, unsigned int numVoxels, VolumeType volume, + DoseStatisticType stdDeviation, VoxelNumberType numVoxels, VolumeType volume, ResultListPointer minimumVoxelPositions = - boost::make_shared > > - (std::vector >()), + NULL, ResultListPointer maximumVoxelPositions = - boost::make_shared > > - (std::vector >()), + NULL, VolumeToDoseFunctionType Dx = VolumeToDoseFunctionType(), DoseToVolumeFunctionType Vx = DoseToVolumeFunctionType(), VolumeToDoseFunctionType MOHx = VolumeToDoseFunctionType(), VolumeToDoseFunctionType MOCx = VolumeToDoseFunctionType(), VolumeToDoseFunctionType MaxOHx = VolumeToDoseFunctionType(), VolumeToDoseFunctionType MinOCx = VolumeToDoseFunctionType(), DoseTypeGy referenceDose = -1); ~DoseStatistics(); void setMinimumVoxelPositions(ResultListPointer minimumVoxelPositions); void setMaximumVoxelPositions(ResultListPointer maximumVoxelPositions); void setDx(const DoseToVolumeFunctionType& DxValues); void setVx(const VolumeToDoseFunctionType& VxValues); void setMOHx(const VolumeToDoseFunctionType& MOHxValues); void setMOCx(const VolumeToDoseFunctionType& MOCxValues); void setMaxOHx(const VolumeToDoseFunctionType& MaxOHxValues); void setMinOCx(const VolumeToDoseFunctionType& MinOCxValues); void setReferenceDose(DoseTypeGy referenceDose); /*! @brief Get number of voxels in doseIterator, with sub-voxel accuracy. */ - unsigned int getNumberOfVoxels() const; + VoxelNumberType getNumberOfVoxels() const; /*! @brief Get the volume of the voxels in doseIterator (in cm3), with sub-voxel accuracy */ VolumeType getVolume() const; /*! @brief Get the reference dose for Vx computation */ DoseTypeGy getReferenceDose() const; /*! @brief Get the maximum of the current dose distribution. @return Return the maximum dose in Gy */ DoseStatisticType getMaximum() const; /*! @brief Get a vector of the the maximum dose VoxelGridIDs together with their dose value in Gy @exception InvalidDoseException if the vector has not been set (i.e. is empty) */ - ResultListPointer getMaximumPositions() const; + ResultListPointer getMaximumVoxelPositions() const; /*! @brief Get the minimum of the current dose distribution. @return Return the minimum dose in Gy */ DoseStatisticType getMinimum() const; /*! @brief Get a vector of the the minimum dose VoxelGridIDs together with their dose value in Gy @exception InvalidDoseException if the vector has not been set (i.e. is empty) */ - ResultListPointer getMinimumPositions() const; + ResultListPointer getMinimumVoxelPositions() const; /*! @brief Get the mean of the current dose distribution. @return Return the mean dose in Gy */ DoseStatisticType getMean() const; /*! @brief Get the standard deviation of the current dose distribution. @return Return the standard deviation in Gy */ DoseStatisticType getStdDeviation() const; /*! @brief Get the variance of of the current dose distribution. @return Return the variance in Gy */ DoseStatisticType getVariance() const; /*! @brief Get Vx: the volume irradiated with a dose >= x. @return Return absolute volume in absolute cm^3. @exception NoDataException if the Vx values have not been set (i.e. the vector is empty) @exception NoDataException if the requested Dose is not in the vector */ VolumeType getVx(DoseTypeGy xDoseAbsolute) const; VolumeType getVx(DoseTypeGy xDoseAbsolute, bool findNearestValue, DoseTypeGy& nearestXDose) const; DoseToVolumeFunctionType getAllVx() const; /*! @brief Get Dx: the minimal dose delivered to part x of the current volume. @return Return dose value in Gy. @exception InvalidDoseException if the Dx values have not been set (i.e. the vector is empty) */ DoseTypeGy getDx(VolumeType xVolumeAbsolute, bool findNearestValue, VolumeType& nearestXVolume) const; DoseTypeGy getDx(VolumeType xVolumeAbsolute) const; VolumeToDoseFunctionType getAllDx() const; /*! @brief Get MOHx: mean dose of the hottest x voxels. @return Return dose value in Gy. @exception InvalidDoseException if the values have not been set (i.e. the vector is empty) */ DoseTypeGy getMOHx(VolumeType xVolumeAbsolute, bool findNearestValue, VolumeType& nearestXVolume) const; DoseTypeGy getMOHx(VolumeType xVolumeAbsolute) const; VolumeToDoseFunctionType getAllMOHx() const; /*! @brief Get MOCx: mean dose of the coldest x voxels. @return Return dose value in Gy. @exception InvalidDoseException if the values have not been set (i.e. the vector is empty) */ DoseTypeGy getMOCx(VolumeType xVolumeAbsolute, bool findNearestValue, VolumeType& nearestXVolume) const; DoseTypeGy getMOCx(VolumeType xVolumeAbsolute) const; VolumeToDoseFunctionType getAllMOCx() const; /*! @brief Get MaxOHx: Maximum outside of the hottest x voxels. @return Return dose value in Gy. @exception InvalidDoseException if the values have not been set (i.e. the vector is empty) */ DoseTypeGy getMaxOHx(VolumeType xVolumeAbsolute, bool findNearestValue, VolumeType& nearestXVolume) const; DoseTypeGy getMaxOHx(VolumeType xVolumeAbsolute) const; VolumeToDoseFunctionType getAllMaxOHx() const; /*! @brief Get MinOCx: Minimum outside of the coldest x voxels. @return Return dose value in Gy. @exception InvalidDoseException if the values have not been set (i.e. the vector is empty) */ DoseTypeGy getMinOCx(VolumeType xVolumeAbsolute, bool findNearestValue, VolumeType& nearestXVolume) const; DoseTypeGy getMinOCx(VolumeType xVolumeAbsolute) const; VolumeToDoseFunctionType getAllMinOCx() const; }; } } #endif diff --git a/code/core/CMakeLists.txt b/code/core/CMakeLists.txt index 5095f4e..eb85b02 100644 --- a/code/core/CMakeLists.txt +++ b/code/core/CMakeLists.txt @@ -1,2 +1,2 @@ -RTTB_CREATE_MODULE(RTTBCore PACKAGE_DEPENDS Boost) +RTTB_CREATE_MODULE(RTTBCore PACKAGE_DEPENDS BoostBinaries) diff --git a/code/core/rttbGeometricInfo.cpp b/code/core/rttbGeometricInfo.cpp index 8bfcfeb..336aeb0 100644 --- a/code/core/rttbGeometricInfo.cpp +++ b/code/core/rttbGeometricInfo.cpp @@ -1,344 +1,345 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include #include "rttbGeometricInfo.h" namespace rttb { namespace core { void GeometricInfo::setSpacing(const SpacingVectorType3D& aSpacingVector) { _spacing = aSpacingVector; } const SpacingVectorType3D& GeometricInfo::getSpacing() const { return _spacing; } void GeometricInfo::setImagePositionPatient(const WorldCoordinate3D& aImagePositionPatient) { _imagePositionPatient = aImagePositionPatient; } const WorldCoordinate3D& GeometricInfo::getImagePositionPatient() const { return _imagePositionPatient; } void GeometricInfo::setOrientationMatrix(const OrientationMatrix& anOrientationMatrix) { _orientationMatrix = anOrientationMatrix; computeInvertOrientation(); } bool GeometricInfo::computeInvertOrientation() { typedef boost::numeric::ublas::permutation_matrix pmatrix; boost::numeric::ublas::matrix A(_orientationMatrix); // create a permutation matrix for the LU-factorization pmatrix pm(A.size1()); size_t res = boost::numeric::ublas::lu_factorize(A, pm); if (res != 0) { return false; } _invertedOrientationMatrix.assign(boost::numeric::ublas::identity_matrix (A.size1())); // backsubstitute to get the inverse boost::numeric::ublas::lu_substitute(A, pm, _invertedOrientationMatrix); return true; } const ImageOrientation GeometricInfo::getImageOrientationRow() const { ImageOrientation _imageOrientationRow(0); _imageOrientationRow(0) = _orientationMatrix(0, 0); _imageOrientationRow(1) = _orientationMatrix(1, 0); _imageOrientationRow(2) = _orientationMatrix(2, 0); return _imageOrientationRow; } const ImageOrientation GeometricInfo::getImageOrientationColumn() const { ImageOrientation _imageOrientationColumn(0); _imageOrientationColumn(0) = _orientationMatrix(0, 1); _imageOrientationColumn(1) = _orientationMatrix(1, 1); _imageOrientationColumn(2) = _orientationMatrix(2, 1); return _imageOrientationColumn; } void GeometricInfo::setPixelSpacingRow(const GridVolumeType aValue) { _spacing(0) = aValue; } const GridVolumeType GeometricInfo::getPixelSpacingRow() const { return _spacing(0); } void GeometricInfo::setPixelSpacingColumn(const GridVolumeType aValue) { _spacing(1) = aValue; } const GridVolumeType GeometricInfo::getPixelSpacingColumn() const { return _spacing(1); } void GeometricInfo::setSliceThickness(const GridVolumeType aValue) { _spacing(2) = aValue; } const GridVolumeType GeometricInfo::getSliceThickness() const { return _spacing(2); } void GeometricInfo::setImageSize(const ImageSize& aSize) { setNumColumns(aSize(0)); setNumRows(aSize(1)); setNumSlices(aSize(2)); } const ImageSize GeometricInfo::getImageSize() const { return ImageSize(getNumColumns(), getNumRows(), getNumSlices()); } void GeometricInfo::setNumColumns(const VoxelGridDimensionType aValue) { _numberOfColumns = aValue; } const VoxelGridDimensionType GeometricInfo::getNumColumns() const { return _numberOfColumns; } void GeometricInfo::setNumRows(const VoxelGridDimensionType aValue) { _numberOfRows = aValue; } const VoxelGridDimensionType GeometricInfo::getNumRows() const { return _numberOfRows; } void GeometricInfo::setNumSlices(const VoxelGridDimensionType aValue) { _numberOfFrames = aValue; } const VoxelGridDimensionType GeometricInfo::getNumSlices() const { return _numberOfFrames; } bool operator==(const GeometricInfo& gInfo, const GeometricInfo& gInfo1) { return (gInfo.getImagePositionPatient() == gInfo1.getImagePositionPatient() && gInfo.getOrientationMatrix() == gInfo1.getOrientationMatrix() && gInfo.getSpacing() == gInfo1.getSpacing() && gInfo.getNumColumns() == gInfo1.getNumColumns() && gInfo.getNumRows() == gInfo1.getNumRows() && gInfo.getNumSlices() == gInfo1.getNumSlices()); } bool GeometricInfo::equalsAlmost(const GeometricInfo& another, double errorConstant /*= 1e-5*/) const { return (getImagePositionPatient().equalsAlmost(another.getImagePositionPatient(), errorConstant) && getOrientationMatrix().equalsAlmost(another.getOrientationMatrix(), errorConstant) && getSpacing().equalsAlmost(another.getSpacing(), errorConstant) && getNumColumns() == another.getNumColumns() && getNumRows() == another.getNumRows() && getNumSlices() == another.getNumSlices()); } bool GeometricInfo::worldCoordinateToGeometryCoordinate(const WorldCoordinate3D& aWorldCoordinate, DoubleVoxelGridIndex3D& aIndex) const { WorldCoordinate3D distanceToIP; distanceToIP = aWorldCoordinate - _imagePositionPatient; boost::numeric::ublas::vector result = boost::numeric::ublas::prod( _invertedOrientationMatrix, distanceToIP); boost::numeric::ublas::vector resultS = boost::numeric::ublas::element_div(result, _spacing); aIndex = DoubleVoxelGridIndex3D(resultS(0), resultS(1), resultS(2)); //check if it is inside VoxelGridIndex3D indexInt = VoxelGridIndex3D(GridIndexType(aIndex(0) + 0.5), GridIndexType(aIndex(1) + 0.5), GridIndexType(aIndex(2) + 0.5)); return isInside(indexInt); } bool GeometricInfo::worldCoordinateToIndex(const WorldCoordinate3D& aWorldCoordinate, VoxelGridIndex3D& aIndex) const { DoubleVoxelGridIndex3D doubleIndex; bool inside = worldCoordinateToGeometryCoordinate(aWorldCoordinate, doubleIndex); aIndex = VoxelGridIndex3D(GridIndexType(doubleIndex(0) + 0.5), GridIndexType(doubleIndex(1) + 0.5), GridIndexType(doubleIndex(2) + 0.5)); return inside; } bool GeometricInfo::geometryCoordinateToWorldCoordinate(const DoubleVoxelGridIndex3D& aIndex, WorldCoordinate3D& aWorldCoordinate) const { boost::numeric::ublas::vector resultS = boost::numeric::ublas::element_prod( aIndex, _spacing); boost::numeric::ublas::vector result = boost::numeric::ublas::prod( _orientationMatrix, resultS); aWorldCoordinate = result + _imagePositionPatient; VoxelGridIndex3D indexInt = VoxelGridIndex3D(GridIndexType(aIndex(0) + 0.5), GridIndexType(aIndex(1) + 0.5), GridIndexType(aIndex(2) + 0.5)); return isInside(indexInt); } bool GeometricInfo::indexToWorldCoordinate(const VoxelGridIndex3D& aIndex, WorldCoordinate3D& aWorldCoordinate) const { DoubleVoxelGridIndex3D indexDouble = DoubleVoxelGridIndex3D(aIndex(0) - 0.5, aIndex(1) - 0.5, aIndex(2) - 0.5); return geometryCoordinateToWorldCoordinate(indexDouble, aWorldCoordinate); } bool GeometricInfo::isInside(const VoxelGridIndex3D& aIndex) const { return (aIndex(0) >= 0 && aIndex(1) >= 0 && aIndex(2) >= 0 && aIndex(0) < static_cast(_numberOfColumns) && aIndex(1) < static_cast(_numberOfRows) && aIndex(2) < static_cast(_numberOfFrames)); } bool GeometricInfo::isInside(const WorldCoordinate3D& aWorldCoordinate) { VoxelGridIndex3D currentIndex; return (worldCoordinateToIndex(aWorldCoordinate, currentIndex)); } const GridSizeType GeometricInfo::getNumberOfVoxels() const { - return _numberOfRows * _numberOfColumns * _numberOfFrames; + GridSizeType nVoxels = static_cast(_numberOfRows * _numberOfColumns * _numberOfFrames); + return nVoxels; } bool GeometricInfo::convert(const VoxelGridID& gridID, VoxelGridIndex3D& gridIndex) const { if (validID(gridID)) { gridIndex(0) = gridID % getNumColumns(); VoxelGridID tempID = (gridID - gridIndex.x()) / getNumColumns(); gridIndex(1) = tempID % getNumRows(); gridIndex(2) = (tempID - gridIndex.y()) / getNumRows(); return true; } return false; } bool GeometricInfo::convert(const VoxelGridIndex3D& gridIndex, VoxelGridID& gridID) const { if ((gridIndex.x() >= static_cast(getNumColumns())) || (gridIndex.y() >= static_cast(getNumRows())) || (gridIndex.z() >= static_cast(getNumSlices()))) { return false; } else { gridID = gridIndex.z() * getNumColumns() * getNumRows() + gridIndex.y() * getNumColumns() + gridIndex.x(); return validID(gridID); } } bool GeometricInfo::validID(const VoxelGridID aID) const { return (aID >= 0 && aID < getNumberOfVoxels()); } bool GeometricInfo::validIndex(const VoxelGridIndex3D& aIndex) const { VoxelGridID aID; if (!convert(aIndex, aID)) { return false; } else { return validID(aID); } } std::ostream& operator<<(std::ostream& s, const GeometricInfo& anGeometricInfo) { s << "[ " << anGeometricInfo.getImagePositionPatient() << "; " << anGeometricInfo.getOrientationMatrix() << "; " << anGeometricInfo.getSpacing() << "; " << "; " << anGeometricInfo.getNumColumns() << "; " << anGeometricInfo.getNumRows() << "; " << anGeometricInfo.getNumSlices() << " ]"; return s; } }//end namespace core }//end namespace rttb diff --git a/code/core/rttbGeometricInfo.h b/code/core/rttbGeometricInfo.h index b3df594..3e457c5 100644 --- a/code/core/rttbGeometricInfo.h +++ b/code/core/rttbGeometricInfo.h @@ -1,190 +1,190 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __GEOMETRIC_INFO_NEW_H #define __GEOMETRIC_INFO_NEW_H #include #include #include #include #include "rttbBaseType.h" namespace rttb { namespace core { /*! @brief GeometricInfo objects contain all the information required for transformations between voxel grid coordinates and world coordinates. Corresponding converter functions are also available. @note ITK Pixel Indexing used (http://www.itk.org/Doxygen45/html/classitk_1_1Image.html): The Index type reverses the order so that with Index[0] = col, Index[1] = row, Index[2] = slice. */ class GeometricInfo { private: WorldCoordinate3D _imagePositionPatient; OrientationMatrix _orientationMatrix; OrientationMatrix _invertedOrientationMatrix; SpacingVectorType3D _spacing; VoxelGridDimensionType _numberOfColumns; VoxelGridDimensionType _numberOfRows; VoxelGridDimensionType _numberOfFrames; /* @brief Matrix inversion routine. Uses lu_factorize and lu_substitute in uBLAS to invert a matrix http://savingyoutime.wordpress.com/2009/09/21/c-matrix-inversion-boostublas/ */ bool computeInvertOrientation(); public: /*! @brief Constructor, initializes orientation matrix, spacing vector and patient position with zeros. */ GeometricInfo() : _imagePositionPatient(0),_orientationMatrix(0), _spacing(0), _numberOfColumns(0), _numberOfRows(0), _numberOfFrames(0) {} void setSpacing(const SpacingVectorType3D& aSpacingVector); const SpacingVectorType3D& getSpacing() const; void setImagePositionPatient(const WorldCoordinate3D& aImagePositionPatient); const WorldCoordinate3D& getImagePositionPatient() const; void setOrientationMatrix(const OrientationMatrix& anOrientationMatrix); const OrientationMatrix getOrientationMatrix() const { return _orientationMatrix; }; /*! @brief Returns the 1st row of the OrientationMatrix. @deprecated please use getOrientationMatrix() (x,0) instead*/ const ImageOrientation getImageOrientationRow() const; /*! @brief Returns the 2nd row of the OrientationMatrix. @deprecated please use getOrientationMatrix() (x,1) instead*/ const ImageOrientation getImageOrientationColumn() const; void setPixelSpacingRow(const GridVolumeType aValue); /*! @brief Returns the spacing in the x-dimension (rows) of the data grid. @deprecated please use getSpacing() (0) instead*/ const GridVolumeType getPixelSpacingRow() const; void setPixelSpacingColumn(const GridVolumeType aValue); /*! @brief Returns the spacing in the y-dimension (columns) of the data grid. @deprecated please use getSpacing() (1) instead*/ const GridVolumeType getPixelSpacingColumn() const; void setSliceThickness(const GridVolumeType aValue); /*! @brief Returns the spacing in the z-dimension (slices) of the data grid. @deprecated please use getSpacing() (2) instead*/ const GridVolumeType getSliceThickness() const; void setImageSize(const ImageSize& aSize); const ImageSize getImageSize() const; void setNumColumns(const VoxelGridDimensionType aValue); const VoxelGridDimensionType getNumColumns() const; void setNumRows(const VoxelGridDimensionType aValue); const VoxelGridDimensionType getNumRows() const; void setNumSlices(const VoxelGridDimensionType aValue); const VoxelGridDimensionType getNumSlices() const; /*! @brief determines equality of two GeometricInfo objects. */ friend bool operator==(const GeometricInfo& gInfo, const GeometricInfo& gInfo1); bool equalsAlmost(const GeometricInfo& another, double errorConstant = 1e-5) const; /*! @brief convert world coordinates to voxel grid index. The conversion of values is done even if the target index is not inside the given voxel grid (return false). If the target is inside the grid return true. */ bool worldCoordinateToIndex(const WorldCoordinate3D& aWorldCoordinate, VoxelGridIndex3D& aIndex) const; /*! @brief convert world coordinates to double geometry coordinate. The world coordinate of the image position patient (center of the first voxel) will be convert to the double voxel (0.0, 0.0, 0.0) The conversion of values is done even if the target index is not inside the given voxel grid (return false). If the target is inside the grid return true. */ bool worldCoordinateToGeometryCoordinate(const WorldCoordinate3D& aWorldCoordinate, DoubleVoxelGridIndex3D& aIndex) const; /*! @brief convert double geometry coordinate to world coordinates. The double voxel index (0.0, 0.0, 0.0) will be convert to the world coordinate of the image postion patient (center of the first voxel) The conversion of values is done even if the target is not inside the given voxel grid (return false). If the target is inside the voxel grid return true. */ bool geometryCoordinateToWorldCoordinate(const DoubleVoxelGridIndex3D& aIndex, WorldCoordinate3D& aWorldCoordinate) const; - /*! @brief convert int voxel grid index to world coordinates. + /*! @brief convert int voxel grid index to world coordinates. It is the upper left corner of the voxel. The conversion of values is done even if the target is not inside the given voxel grid (return false). If the target is inside the voxel grid return true. */ bool indexToWorldCoordinate(const VoxelGridIndex3D& aIndex, WorldCoordinate3D& aWorldCoordinate) const; /*! @brief check if a given voxel grid index is inside the given voxel grid.*/ bool isInside(const VoxelGridIndex3D& aIndex) const; /*! @brief check if a given world coordinate is inside the given voxel grid.*/ bool isInside(const WorldCoordinate3D& aWorldCoordinate); const GridSizeType getNumberOfVoxels() const; bool convert(const VoxelGridID& gridID, VoxelGridIndex3D& gridIndex) const; bool convert(const VoxelGridIndex3D& gridIndex, VoxelGridID& gridID) const; /*! @brief test if given ID is inside current dose grid */ bool validID(const VoxelGridID aID) const; /*! @brief test if given index is inside current dose grid */ bool validIndex(const VoxelGridIndex3D& aIndex) const; /*!@ brief generates string stream representation of the GeometricInfo object. */ friend std::ostream& operator<<(std::ostream& s, const GeometricInfo& anGeometricInfo); }; } } #endif diff --git a/code/core/rttbStrVectorStructureSetGenerator.cpp b/code/core/rttbStrVectorStructureSetGenerator.cpp index 8966d61..25c62ba 100644 --- a/code/core/rttbStrVectorStructureSetGenerator.cpp +++ b/code/core/rttbStrVectorStructureSetGenerator.cpp @@ -1,47 +1,65 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ - #include "rttbStrVectorStructureSetGenerator.h" +#include namespace rttb { namespace core { StrVectorStructureSetGenerator::StrVectorStructureSetGenerator(std::vector& aStructureVector, IDType aPatientUID) { _patientUID = aPatientUID; _strVector = aStructureVector; } StrVectorStructureSetGenerator::StructureSetPointer StrVectorStructureSetGenerator::generateStructureSet() { - return boost::make_shared(_strVector, _patientUID); + std::vector _filteredStructs = _strVector; + + if (this->getStructureLabelFilterActive()) + { + _filteredStructs.clear(); + + ::boost::regex e(this->getFilterRegEx()); + + for(auto aStruct : _strVector) + { + if (::boost::regex_match(aStruct->getLabel(), e)) + { + _filteredStructs.push_back(aStruct); + } + } + + } + + return boost::make_shared(_filteredStructs, _patientUID); } } }//end namespace rttb diff --git a/code/core/rttbStructureSet.cpp b/code/core/rttbStructureSet.cpp index 0cc4a3d..fd3306e 100644 --- a/code/core/rttbStructureSet.cpp +++ b/code/core/rttbStructureSet.cpp @@ -1,89 +1,89 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include #include #include #include "rttbStructureSet.h" #include "rttbInvalidParameterException.h" namespace rttb { namespace core { StructureSet::StructureSet() {} StructureSet::StructureSet(std::vector aStructureVector, IDType aPatientUID, IDType aUID) { _structureSetVector = aStructureVector; _patientUID = aPatientUID; _UID = aUID; if (_UID == "") { boost::uuids::uuid id; boost::uuids::random_generator generator; id = generator(); std::stringstream ss; ss << id; _UID = ss.str(); } } StructureSet::StructTypePointer StructureSet::getStructure(int aStructureNo) const { - int size = this->getNumberOfStructures() - 1; + int size = static_cast(this->getNumberOfStructures() - 1); if (aStructureNo < 0 || aStructureNo > size) { std::stringstream sstr; sstr << "aStructureNo must between 0 and " << size; throw InvalidParameterException(sstr.str()); } return _structureSetVector.at(aStructureNo); } StructureSet::NumberOfStructuresType StructureSet::getNumberOfStructures() const { return _structureSetVector.size(); } IDType StructureSet::getUID() { return _UID; } IDType StructureSet::getPatientUID() { return _patientUID; } }//end namespace core }//end namespace rttb diff --git a/code/core/rttbStructureSetGeneratorInterface.h b/code/core/rttbStructureSetGeneratorInterface.h index 6092c01..90e9813 100644 --- a/code/core/rttbStructureSetGeneratorInterface.h +++ b/code/core/rttbStructureSetGeneratorInterface.h @@ -1,64 +1,87 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __STRUCTURE_SET_GENERATOR_INTERFACE_H #define __STRUCTURE_SET_GENERATOR_INTERFACE_H #include #include "rttbStructureSet.h" namespace rttb { namespace core { /*! @class StructureSetGeneratorInterface @brief Interface for all structure set generating classes */ class StructureSetGeneratorInterface { public: typedef boost::shared_ptr StructureSetPointer; private: StructureSetGeneratorInterface(const StructureSetGeneratorInterface&); //not implemented on purpose -> non-copyable StructureSetGeneratorInterface& operator=(const StructureSetGeneratorInterface&);//not implemented on purpose -> non-copyable protected: - StructureSetGeneratorInterface() {}; + StructureSetGeneratorInterface(): _activeFilter(false) { }; virtual ~StructureSetGeneratorInterface() {}; + private: + bool _activeFilter; + std::string _filterRegEx; + public: + void setStructureLableFilterActive(bool active) + { + _activeFilter = active; + }; + + bool getStructureLabelFilterActive() const + { + return _activeFilter; + }; + + void setFilterRegEx(const std::string& filter) + { + _filterRegEx = filter; + }; + + std::string getFilterRegEx() const + { + return _filterRegEx; + }; /*! @brief Generate StructureSet @return Return shared pointer of StructureSet. */ virtual StructureSetPointer generateStructureSet() = 0; }; } } #endif diff --git a/code/io/dicom/rttbDicomFileDoseAccessorWriter.cpp b/code/io/dicom/rttbDicomFileDoseAccessorWriter.cpp index a7ec2a5..6629e73 100644 --- a/code/io/dicom/rttbDicomFileDoseAccessorWriter.cpp +++ b/code/io/dicom/rttbDicomFileDoseAccessorWriter.cpp @@ -1,259 +1,255 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include #include #include "rttbDicomFileDoseAccessorWriter.h" #include "rttbInvalidDoseException.h" #include "rttbGeometricInfo.h" #include "rttbGeometricInfo.h" #include "rttbGenericDoseIterator.h" #include "rttbDoseStatisticsCalculator.h" namespace rttb { namespace io { namespace dicom { DicomFileDoseAccessorWriter::DicomFileDoseAccessorWriter() { _doseIOD = boost::make_shared(); _dataset = _fileformat.getDataset(); } void DicomFileDoseAccessorWriter::setFileName(DICOMRTFileNameString aFileName) { _fileName = aFileName; } bool DicomFileDoseAccessorWriter::process() { OFCondition status; /* Prepare dcmtk */ DcmItem* dcm_item = 0; //get geometric info rttb::core::GeometricInfo geometricInfo = _doseAccessor->getGeometricInfo(); /* ----------------------------------------------------------------- */ /* Part 1 -- General header */ /* ----------------------------------------------------------------- */ OFString CreationUID(_doseAccessor->getUID().c_str()); _dataset->putAndInsertString(DCM_ImageType, "DERIVED\\SECONDARY\\REFORMATTED"); _dataset->putAndInsertOFStringArray(DCM_InstanceCreationDate, "");//Creation Date _dataset->putAndInsertOFStringArray(DCM_InstanceCreationTime, "");//Creation Time _dataset->putAndInsertOFStringArray(DCM_InstanceCreatorUID, CreationUID); _dataset->putAndInsertString(DCM_SOPClassUID, UID_RTDoseStorage); _dataset->putAndInsertString(DCM_SOPInstanceUID, _doseAccessor->getUID().c_str()); _dataset->putAndInsertOFStringArray(DCM_StudyDate, ""); _dataset->putAndInsertOFStringArray(DCM_StudyTime, ""); _dataset->putAndInsertOFStringArray(DCM_AccessionNumber, ""); _dataset->putAndInsertOFStringArray(DCM_Modality, "RTDOSE"); _dataset->putAndInsertString(DCM_Manufacturer, "RTToolbox"); _dataset->putAndInsertString(DCM_InstitutionName, ""); _dataset->putAndInsertString(DCM_ReferringPhysicianName, ""); _dataset->putAndInsertString(DCM_StationName, ""); _dataset->putAndInsertString(DCM_ManufacturerModelName, "RTToolbox"); /* (0008,1140) DCM_ReferencedImageSequence -- MIM likes this */ dcm_item = 0; _dataset->findOrCreateSequenceItem( DCM_ReferencedImageSequence, dcm_item, -2); dcm_item->putAndInsertString(DCM_ReferencedSOPClassUID, UID_CTImageStorage); dcm_item->putAndInsertString(DCM_ReferencedSOPInstanceUID, ""); _dataset->putAndInsertString(DCM_PatientName, ""); _dataset->putAndInsertString(DCM_PatientID, ""); _dataset->putAndInsertString(DCM_PatientBirthDate, ""); _dataset->putAndInsertString(DCM_PatientSex, "O"); _dataset->putAndInsertString(DCM_SliceThickness, boost::lexical_cast(geometricInfo.getSliceThickness()).c_str()); _dataset->putAndInsertString(DCM_SoftwareVersions, ""); _dataset->putAndInsertString(DCM_StudyInstanceUID, ""); _dataset->putAndInsertString(DCM_SeriesInstanceUID, ""); _dataset->putAndInsertString(DCM_StudyID, "10001"); _dataset->putAndInsertString(DCM_SeriesNumber, ""); _dataset->putAndInsertString(DCM_InstanceNumber, "1"); /* GCS FIX: PatientOrientation */ std::ostringstream sstr; sstr << geometricInfo.getImagePositionPatient().x() << "\\" << geometricInfo.getImagePositionPatient().y() << "\\" << geometricInfo.getImagePositionPatient().z(); _dataset->putAndInsertString(DCM_PatientOrientation, "L/P"); _dataset->putAndInsertString(DCM_ImagePositionPatient, sstr.str().c_str()); sstr.str(""); sstr << geometricInfo.getImageOrientationRow().x() << "\\" << geometricInfo.getImageOrientationRow().y() << "\\" << geometricInfo.getImageOrientationRow().z() << "\\" << geometricInfo.getImageOrientationColumn().x() << "\\" << geometricInfo.getImageOrientationColumn().y() << "\\" << geometricInfo.getImageOrientationColumn().z(); _dataset->putAndInsertString(DCM_ImageOrientationPatient, sstr.str().c_str()); _dataset->putAndInsertString(DCM_FrameOfReferenceUID, ""); _dataset->putAndInsertString(DCM_SamplesPerPixel, "1"); _dataset->putAndInsertString(DCM_PhotometricInterpretation, "MONOCHROME2"); sstr.str(""); sstr << geometricInfo.getNumSlices(); _dataset->putAndInsertString(DCM_NumberOfFrames, sstr.str().c_str()); /* GCS FIX: Add FrameIncrementPointer */ _dataset->putAndInsertString(DCM_FrameIncrementPointer, "(3004,000c)"); _dataset->putAndInsertUint16(DCM_Rows, geometricInfo.getNumRows()); _dataset->putAndInsertUint16(DCM_Columns, geometricInfo.getNumColumns()); sstr.str(""); sstr << geometricInfo.getSpacing()(1) << "\\" << geometricInfo.getSpacing()(0); _dataset->putAndInsertString(DCM_PixelSpacing, sstr.str().c_str()); _dataset->putAndInsertString(DCM_BitsAllocated, "32"); _dataset->putAndInsertString(DCM_BitsStored, "32"); _dataset->putAndInsertString(DCM_HighBit, "31"); _dataset->putAndInsertString(DCM_DoseUnits, "GY"); _dataset->putAndInsertString(DCM_DoseSummationType, "PLAN"); sstr.str("0"); for (int i = 1; i < geometricInfo.getNumSlices(); i++) { sstr << "\\" << i* geometricInfo.getSpacing()(2); } _dataset->putAndInsertString(DCM_GridFrameOffsetVector, sstr.str().c_str()); /* We need to convert image to uint16_t, but first we need to scale it so that the maximum dose fits in a 16-bit unsigned integer. Compute an appropriate scaling factor based on the maximum dose. */ /* Find the maximum value in the image */ boost::shared_ptr spTestDoseIterator = boost::make_shared(_doseAccessor); rttb::core::GenericDoseIterator::DoseIteratorPointer spDoseIterator(spTestDoseIterator); rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator(spDoseIterator); rttb::algorithms::DoseStatistics::DoseStatisticsPointer doseStatistics = myDoseStatsCalculator.calculateDoseStatistics(); - //rttb::algorithms::DoseStatistics doseStat(spDoseIterator); - //boost::shared_ptr< std::vector > > myResultPairs = - // boost::make_shared< std::vector > >(); - //rttb::algorithms::DoseStatistics::ResultListPointer spMyResultPairs(myResultPairs); double maxDose = doseStatistics->getMaximum(); /* Find scale factor */ - float dose_scale; + double dose_scale; dose_scale = maxDose / PixelDataMaxValue; /* Scale the image and add scale factor to _dataset */ sstr.str(""); sstr << dose_scale; _dataset->putAndInsertString(DCM_DoseGridScaling, sstr.str().c_str()); /* (300c,0002) ReferencedRTPlanSequence -- for future expansion */ dcm_item = 0; _dataset->findOrCreateSequenceItem( DCM_ReferencedRTPlanSequence, dcm_item, -2); dcm_item->putAndInsertString(DCM_ReferencedSOPClassUID, UID_RTPlanStorage); dcm_item->putAndInsertString(DCM_ReferencedSOPInstanceUID, ""); /* (300c,0060) DCM_ReferencedStructureSetSequence -- MIM likes this */ dcm_item = 0; _dataset->findOrCreateSequenceItem( DCM_ReferencedStructureSetSequence, dcm_item, -2); dcm_item->putAndInsertString(DCM_ReferencedSOPClassUID, UID_RTStructureSetStorage); dcm_item->putAndInsertString(DCM_ReferencedSOPInstanceUID, ""); /* Convert image bytes to integer, then add to _dataset */ Uint16* pixelData; - int pixelCount = geometricInfo.getNumRows() * geometricInfo.getNumColumns() * - geometricInfo.getNumSlices(); + unsigned int pixelCount = static_cast(geometricInfo.getNumRows() * geometricInfo.getNumColumns() * + geometricInfo.getNumSlices()); pixelData = new Uint16[pixelCount]; for (unsigned int i = 0; i < pixelCount; ++i) { double doseValue = _doseAccessor->getValueAt(i); double pixelValue = doseValue / dose_scale; if (pixelValue > PixelDataMaxValue) { pixelValue = PixelDataMaxValue; } pixelData[i] = boost::numeric_cast(pixelValue); } status = _dataset->putAndInsertUint16Array(DCM_PixelData, pixelData, pixelCount); if (!status.good()) { throw core::InvalidDoseException("Error: put and insert pixel data failed!"); } //Write dose to file status = _fileformat.saveFile(_fileName.c_str(), EXS_LittleEndianExplicit); if (status.bad()) { std::cerr << "Error: cannot write DICOM RTDOSE!" << std::endl; } return true; } }//end namespace itk }//end namespace io }//end namespace rttb diff --git a/code/io/dicom/rttbDicomFileStructureSetGenerator.cpp b/code/io/dicom/rttbDicomFileStructureSetGenerator.cpp index 7ef6906..3a64b1c 100644 --- a/code/io/dicom/rttbDicomFileStructureSetGenerator.cpp +++ b/code/io/dicom/rttbDicomFileStructureSetGenerator.cpp @@ -1,110 +1,112 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include #include #include "rttbInvalidParameterException.h" #include "rttbStructure.h" #include "rttbDicomFileStructureSetGenerator.h" #include "rttbDicomIODStructureSetGenerator.h" #include "rttbDcmrtException.h" #include "rttbDicomFileReaderHelper.h" namespace rttb { namespace io { namespace dicom { DicomFileStructureSetGenerator::DicomFileStructureSetGenerator(DICOMRTFileNameString aDICOMRTStrSetFileName) { _fileName = aDICOMRTStrSetFileName; } DicomFileStructureSetGenerator::~DicomFileStructureSetGenerator() { } DicomFileStructureSetGenerator::StructureSetPointer DicomFileStructureSetGenerator::generateStructureSet() { std::vector fileVector; //if a file if (isFile(_fileName)) { fileVector.push_back(_fileName); } //if a directory else if (isDirectory(_fileName)) { rttb::io::dicom::Modality strModality = {rttb::io::dicom::Modality::RTSTRUCT}; fileVector = getFileNamesWithSameUID(_fileName, strModality); } else { throw rttb::core::InvalidParameterException("Invalid file/directory name!"); } if (fileVector.size() < 1) { throw rttb::core::InvalidParameterException("There is no structure set files in the directory!"); } OFCondition status; DcmFileFormat fileformat; DRTStrSetIODPtr drtStrSetIODPtr = boost::make_shared(); //get the first structure set file status = fileformat.loadFile(fileVector.at(0).c_str()); if (!status.good()) { throw DcmrtException("Load rt structure set loadFile() failed!"); } status = drtStrSetIODPtr->read(*fileformat.getDataset()); if (!status.good()) { throw DcmrtException("Read DRTStructureSetIOD DRTStructureSetIOD.read() failed!"); } - return (boost::make_shared - (drtStrSetIODPtr))->generateStructureSet(); + io::dicom::DicomIODStructureSetGenerator iodGenerator(drtStrSetIODPtr); + iodGenerator.setStructureLableFilterActive(this->getStructureLabelFilterActive()); + iodGenerator.setFilterRegEx(this->getFilterRegEx()); + return iodGenerator.generateStructureSet(); } }//end namespace dicom }//end namespace io }//end namespace rttb diff --git a/code/io/dicom/rttbDicomIODStructureSetGenerator.cpp b/code/io/dicom/rttbDicomIODStructureSetGenerator.cpp index 27d1ce6..5172949 100644 --- a/code/io/dicom/rttbDicomIODStructureSetGenerator.cpp +++ b/code/io/dicom/rttbDicomIODStructureSetGenerator.cpp @@ -1,185 +1,198 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include #include +#include #include +#include #include "rttbNullPointerException.h" #include "rttbInvalidParameterException.h" #include "rttbStructure.h" #include "rttbDicomIODStructureSetGenerator.h" #include "rttbDcmrtException.h" namespace rttb { namespace io { namespace dicom { DicomIODStructureSetGenerator::DicomIODStructureSetGenerator(DRTStrSetIODPtr aDRTStructureSetIOD) { _drtStrSetIOD = aDRTStructureSetIOD; } void DicomIODStructureSetGenerator::readStrSet() { OFString uid; _drtStrSetIOD->getSeriesInstanceUID(uid); _UID = uid.c_str(); OFString uid2; _drtStrSetIOD->getPatientID(uid2); _patientUID = uid2.c_str(); DRTStructureSetROISequence* rois = &_drtStrSetIOD->getStructureSetROISequence(); + //generate map of relevant ROIs + std::map filteredROIs; + ::boost::regex e(this->getFilterRegEx()); + + for (unsigned long i = 0; i < rois->getNumberOfItems(); i++) + { + DRTStructureSetROISequence::Item* roisItem = &rois->getItem(i); + + OFString roiNumber; + roisItem->getROINumber(roiNumber); + OFString ofRoiName; + roisItem->getROIName(ofRoiName); + std::string roiName(ofRoiName.c_str()); + + if (!this->getStructureLabelFilterActive() || boost::regex_match(roiName, e)) + { + filteredROIs.insert(std::make_pair(roiNumber, roiName)); + } + } + /*A structure is a DRTROIContourSequence::Item. Each Item defines a roi. Each ROI contains a sequence of one or more contours, where a contour is either a single point (for a point ROI) or more than one point (representing an open or closed polygon). */ DRTROIContourSequence* rcs; rcs = &_drtStrSetIOD->getROIContourSequence(); DRTROIContourSequence::Item* rcsItem; long numberOfStructures = rcs->getNumberOfItems(); bool isEmpty = rcs->isEmpty(); if (numberOfStructures == 0 || isEmpty) { throw core::InvalidParameterException("Empty Structure Set!") ; } int structureNo = 0; for (rcs->gotoFirstItem(); (rcs->getCurrentItem(rcsItem)).good(); rcs->gotoNextItem()) { OFString refROINumber; rcsItem->getReferencedROINumber(refROINumber); + + //check if ROI number is in the filtered ROIS + if (filteredROIs.find(refROINumber) != filteredROIs.end()) + { DRTContourSequence* cs; cs = &rcsItem->getContourSequence(); unsigned long no2 = cs->getNumberOfItems(); PolygonSequenceType structureVector; for (unsigned long j = 0; j < no2; j++) { /*DRTContourSequence::Item represents a contour (either a single point (for a point ROI) or more than one point (representing an open or closed polygon))*/ DRTContourSequence::Item* csItem; csItem = &cs->getItem(j); OFString contourData; OFString numberOfContourPoints; csItem->getNumberOfContourPoints(numberOfContourPoints); unsigned int numberOfContourPointsInt; std::stringstream is(numberOfContourPoints.c_str()); is >> numberOfContourPointsInt; OFString countourNumber; csItem->getContourNumber(countourNumber); PolygonType contourVector; char* pEnd; for (unsigned int k = 0; k < numberOfContourPointsInt; k++) { WorldCoordinate3D point; for (unsigned int i = 0; i < 3; i++) { csItem->getContourData(contourData, k * 3 + i); WorldCoordinate value = strtod(contourData.c_str(), &pEnd); if (*pEnd != '\0') { throw core::InvalidParameterException("Contour data not readable!") ; } if (i == 0) { point(0) = value; } else if (i == 1) { point(1) = value; } else { point(2) = value; } } contourVector.push_back(point); } structureVector.push_back(contourVector); } boost::shared_ptr spStruct = boost::make_shared(structureVector); StructTypePointer str(spStruct); - for (unsigned long i = 0; i < rois->getNumberOfItems(); i++) - { - DRTStructureSetROISequence::Item* roisItem; - roisItem = &rois->getItem(i); - - OFString roiNumber; - roisItem->getROINumber(roiNumber); - - if (roiNumber == refROINumber) - { - OFString roiName; - roisItem->getROIName(roiName); - str->setLabel(roiName.c_str()); - std::cout << roiName.c_str() << std::endl; - break; - } - } + str->setLabel(filteredROIs[refROINumber]); + std::cout << filteredROIs[refROINumber].c_str() << std::endl; std::stringstream sstr; sstr << structureNo; str->setUID(sstr.str()); _strVector.push_back(str); - structureNo++; } + + ++structureNo; } + } DicomIODStructureSetGenerator::~DicomIODStructureSetGenerator() { } DicomIODStructureSetGenerator::StructureSetPointer DicomIODStructureSetGenerator::generateStructureSet() { this->readStrSet(); return boost::make_shared(_strVector, _patientUID, _UID); } }//end namespace dicom }//end namespace io }//end namespace rttb diff --git a/code/io/helax/rttbDicomHelaxDoseAccessor.cpp b/code/io/helax/rttbDicomHelaxDoseAccessor.cpp index af46a09..0157587 100644 --- a/code/io/helax/rttbDicomHelaxDoseAccessor.cpp +++ b/code/io/helax/rttbDicomHelaxDoseAccessor.cpp @@ -1,328 +1,329 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include "boost/filesystem/operations.hpp" #include "boost/filesystem/path.hpp" #include "boost/progress.hpp" #include #include "rttbDicomHelaxDoseAccessor.h" #include "rttbInvalidDoseException.h" #include "rttbDcmrtException.h" #include "rttbIndexOutOfBoundsException.h" #include "rttbInvalidParameterException.h" namespace rttb { namespace io { namespace helax { DicomHelaxDoseAccessor::~DicomHelaxDoseAccessor() { } DicomHelaxDoseAccessor::DicomHelaxDoseAccessor(std::vector aDICOMRTDoseVector) { for (size_t i = 0; i < aDICOMRTDoseVector.size(); i++) { _doseVector.push_back(aDICOMRTDoseVector.at(i)); } this->begin(); } bool DicomHelaxDoseAccessor::begin() { if (_doseVector.size() == 0) { throw core::InvalidParameterException(" The size of aDICOMRTDoseVector is 0!"); } assembleGeometricInfo(); _doseData.clear(); OFString doseGridScalingStr; _doseVector.at(0)->getDoseGridScaling( doseGridScalingStr);//get the first dose grid scaling as _doseGridScaling try { _doseGridScaling = boost::lexical_cast(doseGridScalingStr.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("Dose grid scaling not readable or = 0!") ; } for (size_t i = 0; i < _doseVector.size(); i++) { DRTDoseIODPtr dose = _doseVector.at(i); OFString currentDoseGridScalingStr; dose->getDoseGridScaling(currentDoseGridScalingStr); double currentDoseGridScaling; try { currentDoseGridScaling = boost::lexical_cast(currentDoseGridScalingStr.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("Dose grid scaling not readable or = 0!") ; } OFCondition status; DcmFileFormat fileformat; DcmItem doseitem; status = dose->write(doseitem); if (status.good()) { unsigned long count; const Uint16* pixelData; status = doseitem.findAndGetUint16Array(DcmTagKey(0x7fe0, 0x0010), pixelData, &count); if (status.good()) { for (unsigned int j = 0; j < static_cast(_geoInfo.getNumColumns()*_geoInfo.getNumRows()); j++) { - this->_doseData.push_back(pixelData[j]*currentDoseGridScaling / - _doseGridScaling); //recalculate dose data + Uint16 data = static_cast(pixelData[j] * currentDoseGridScaling / + _doseGridScaling); + this->_doseData.push_back(data); //recalculate dose data } } else { throw dicom::DcmrtException("Read Pixel Data (7FE0,0010) failed!"); } } else { throw dicom::DcmrtException("Read DICOM-RT Dose file failed!"); } } return true; } bool DicomHelaxDoseAccessor::assembleGeometricInfo() { DRTDoseIODPtr dose = _doseVector.at(0); Uint16 temp = 0; dose->getColumns(temp); _geoInfo.setNumColumns(temp); temp = 0; dose->getRows(temp); _geoInfo.setNumRows(temp); OFString numberOfFramesStr; dose->getNumberOfFrames(numberOfFramesStr); if (!numberOfFramesStr.empty()) { _geoInfo.setNumSlices(boost::lexical_cast(numberOfFramesStr.c_str())); } else { _geoInfo.setNumSlices((VoxelGridDimensionType)_doseVector.size()); } if (_geoInfo.getNumColumns() == 0 || _geoInfo.getNumRows() == 0 || _geoInfo.getNumSlices() == 0) { throw core::InvalidDoseException("Empty dicom dose!") ; } OFString imageOrientationRowX; dose->getImageOrientationPatient(imageOrientationRowX, 0); OFString imageOrientationRowY; dose->getImageOrientationPatient(imageOrientationRowY, 1); OFString imageOrientationRowZ; dose->getImageOrientationPatient(imageOrientationRowZ, 2); OFString imageOrientationColumnX; dose->getImageOrientationPatient(imageOrientationColumnX, 3); OFString imageOrientationColumnY; dose->getImageOrientationPatient(imageOrientationColumnY, 4); OFString imageOrientationColumnZ; dose->getImageOrientationPatient(imageOrientationColumnZ, 5); WorldCoordinate3D imageOrientationRow; WorldCoordinate3D imageOrientationColumn; try { imageOrientationRow(0) = boost::lexical_cast(imageOrientationRowX.c_str()); imageOrientationRow(1) = boost::lexical_cast(imageOrientationRowY.c_str()); imageOrientationRow(2) = boost::lexical_cast(imageOrientationRowZ.c_str()); imageOrientationColumn(0) = boost::lexical_cast(imageOrientationColumnX.c_str()); imageOrientationColumn(1) = boost::lexical_cast(imageOrientationColumnY.c_str()); imageOrientationColumn(2) = boost::lexical_cast(imageOrientationColumnZ.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("boost::lexical_cast ImageOrientation failed! Can not read image orientation X/Y/Z!") ; } OrientationMatrix orientation; orientation(0, 0) = imageOrientationRow.x(); orientation(1, 0) = imageOrientationRow.y(); orientation(2, 0) = imageOrientationRow.z(); orientation(0, 1) = imageOrientationColumn.x(); orientation(1, 1) = imageOrientationColumn.y(); orientation(2, 1) = imageOrientationColumn.z(); WorldCoordinate3D perpendicular = imageOrientationRow.cross(imageOrientationColumn); orientation(0, 2) = perpendicular.x(); orientation(1, 2) = perpendicular.y(); orientation(2, 2) = perpendicular.z(); _geoInfo.setOrientationMatrix(orientation); OFString imagePositionX; dose->getImagePositionPatient(imagePositionX, 0); OFString imagePositionY; dose->getImagePositionPatient(imagePositionY, 1); OFString imagePositionZ; dose->getImagePositionPatient(imagePositionZ, 2); WorldCoordinate3D imagePositionPatient; try { imagePositionPatient(0) = boost::lexical_cast(imagePositionX.c_str()); imagePositionPatient(1) = boost::lexical_cast(imagePositionY.c_str()); imagePositionPatient(2) = boost::lexical_cast(imagePositionZ.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("boost::lexical_cast ImagePosition failed! Can not read image position X/Y/Z!") ; } _geoInfo.setImagePositionPatient(imagePositionPatient); SpacingVectorType3D spacingVector; OFString pixelSpacingRowStr; dose->getPixelSpacing(pixelSpacingRowStr, 0); OFString pixelSpacingColumnStr; dose->getPixelSpacing(pixelSpacingColumnStr, 1); try { spacingVector(1) = boost::lexical_cast(pixelSpacingRowStr.c_str()); spacingVector(0) = boost::lexical_cast(pixelSpacingColumnStr.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("Can not read Pixel Spacing Row/Column!") ; } _geoInfo.setSpacing(spacingVector); if (_geoInfo.getPixelSpacingRow() == 0 || _geoInfo.getPixelSpacingColumn() == 0) { throw core::InvalidDoseException("Pixel spacing not readable or = 0!"); } OFString sliceThicknessStr; dose->getSliceThickness(sliceThicknessStr); try { spacingVector(2) = boost::lexical_cast(sliceThicknessStr.c_str()); } catch (boost::bad_lexical_cast&) { spacingVector(2) = 0 ;//if no information about slice thickness, set to 0 and calculate it using z coordinate difference between 1. and 2. dose } if (spacingVector(2) == 0) { if (_doseVector.size() > 1) { DRTDoseIODPtr dose2 = _doseVector.at(1);//get the 2. dose OFString imagePositionZ2; dose2->getImagePositionPatient(imagePositionZ2, 2); try { spacingVector(2) = boost::lexical_cast(imagePositionZ2.c_str()) - imagePositionPatient( 2); //caculate slicethickness } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("Can not read image position Z of the 2. dose!"); } } else { std::cerr << "sliceThickness == 0! It will be replaced with pixelSpacingRow=" << _geoInfo.getPixelSpacingRow() << "!" << std::endl; spacingVector(2) = spacingVector(0); } } _geoInfo.setSpacing(spacingVector); return true; } GenericValueType DicomHelaxDoseAccessor::getValueAt(const VoxelGridID aID) const { return _doseData.at(aID) * _doseGridScaling; } GenericValueType DicomHelaxDoseAccessor::getValueAt(const VoxelGridIndex3D& aIndex) const { VoxelGridID aVoxelGridID; if (_geoInfo.convert(aIndex, aVoxelGridID)) { return getValueAt(aVoxelGridID); } else { return -1; } } } } } diff --git a/code/io/itk/itkDoseAccessorImageFilter.cpp b/code/io/itk/itkDoseAccessorImageFilter.cpp index 183d2c8..748f776 100644 --- a/code/io/itk/itkDoseAccessorImageFilter.cpp +++ b/code/io/itk/itkDoseAccessorImageFilter.cpp @@ -1,89 +1,83 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include "itkDoseAccessorImageFilter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkProgressReporter.h" namespace itk { /** * Constructor */ DoseAccessorImageFilter ::DoseAccessorImageFilter() { this->SetNumberOfRequiredInputs(1); } void DoseAccessorImageFilter ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType threadId) { ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels()); - const unsigned int numberOfInputImages = - static_cast< unsigned int >(this->GetNumberOfIndexedInputs()); - - const unsigned int numberOfOutputImages = - static_cast< unsigned int >(this->GetNumberOfIndexedOutputs()); - typedef ImageRegionConstIteratorWithIndex< InputImageType > ImageRegionConstIteratorType; typedef ImageRegionIterator< OutputImageType > OutputImageRegionIteratorType; InputImagePointer inputPtr = dynamic_cast< InputImageType* >(ProcessObject::GetInput(0)); ImageRegionConstIteratorType inputItr; if (inputPtr) { inputItr = ImageRegionConstIteratorType(inputPtr, outputRegionForThread); } OutputImagePointer outputPtr = dynamic_cast< OutputImageType* >(ProcessObject::GetOutput(0)); OutputImageRegionIteratorType outputItr; if (outputPtr) { outputItr = OutputImageRegionIteratorType(outputPtr, outputRegionForThread); } if (inputPtr && outputPtr) { while (!(outputItr.IsAtEnd())) { ImageRegionConstIteratorType::IndexType index = inputItr.GetIndex(); rttb::VoxelGridIndex3D doseIndex(index[0], index[1], index[2]); outputItr.Set(m_Accessor->getValueAt(doseIndex)); ++outputItr; ++inputItr; progress.CompletedPixel(); } } } } // end namespace itk diff --git a/code/io/itk/rttbITKImageMaskAccessor.cpp b/code/io/itk/rttbITKImageMaskAccessor.cpp index 18ea303..129616c 100644 --- a/code/io/itk/rttbITKImageMaskAccessor.cpp +++ b/code/io/itk/rttbITKImageMaskAccessor.cpp @@ -1,175 +1,176 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include "rttbITKImageMaskAccessor.h" #include "rttbInvalidDoseException.h" namespace rttb { namespace io { namespace itk { ITKImageMaskAccessor::ITKImageMaskAccessor(ITKMaskImageType::ConstPointer aMaskImage) : _mask(aMaskImage) { if (_mask.IsNull()) { throw core::InvalidDoseException("Mask image = 0!") ; } assembleGeometricInfo(); } ITKImageMaskAccessor::~ITKImageMaskAccessor() { }; bool ITKImageMaskAccessor::assembleGeometricInfo() { _geoInfo = boost::make_shared(); _geoInfo->setSpacing(SpacingVectorType3D(_mask->GetSpacing()[0], _mask->GetSpacing()[1], _mask->GetSpacing()[2])); if (_geoInfo->getSpacing()[0] == 0 || _geoInfo->getSpacing()[1] == 0 || _geoInfo->getSpacing()[2] == 0) { throw core::InvalidDoseException("Pixel spacing = 0!"); } _geoInfo->setImagePositionPatient(WorldCoordinate3D(_mask->GetOrigin()[0], _mask->GetOrigin()[1], _mask->GetOrigin()[2])); OrientationMatrix OM(0); for (unsigned int col = 0; col < 3; ++col) { for (unsigned int row = 0; row < 3; ++row) { OM(col, row) = _mask->GetDirection()(col, row); } } _geoInfo->setOrientationMatrix(OM); _geoInfo->setNumColumns(_mask->GetLargestPossibleRegion().GetSize()[0]); _geoInfo->setNumRows(_mask->GetLargestPossibleRegion().GetSize()[1]); _geoInfo->setNumSlices(_mask->GetLargestPossibleRegion().GetSize()[2]); if (_geoInfo->getNumColumns() == 0 || _geoInfo->getNumRows() == 0 || _geoInfo->getNumSlices() == 0) { throw core::InvalidDoseException("Empty mask!") ; } return true; } void ITKImageMaskAccessor::updateMask() { return; } ITKImageMaskAccessor::MaskVoxelListPointer ITKImageMaskAccessor::getRelevantVoxelVector() { updateMask(); _relevantVoxelVector = getRelevantVoxelVector(0); return _relevantVoxelVector; } ITKImageMaskAccessor::MaskVoxelListPointer ITKImageMaskAccessor::getRelevantVoxelVector( float lowerThreshold) { MaskVoxelListPointer filteredVoxelVectorPointer(new MaskVoxelList); updateMask(); unsigned int size = _geoInfo->getNumColumns() * _geoInfo->getNumRows() * _geoInfo->getNumSlices(); filteredVoxelVectorPointer->reserve(size); for (unsigned int gridIndex = 0 ; gridIndex < size; gridIndex++) { core::MaskVoxel currentVoxel = core::MaskVoxel(gridIndex); if (getMaskAt(gridIndex, currentVoxel)) { if (currentVoxel.getRelevantVolumeFraction() > lowerThreshold) { filteredVoxelVectorPointer->push_back(currentVoxel); } } } return filteredVoxelVectorPointer; } bool ITKImageMaskAccessor::getMaskAt(VoxelGridID aID, core::MaskVoxel& voxel) const { VoxelGridIndex3D aVoxelGridIndex; if (_geoInfo->convert(aID, aVoxelGridIndex)) { return getMaskAt(aVoxelGridIndex, voxel); } else { return false; } } bool ITKImageMaskAccessor::getMaskAt(const VoxelGridIndex3D& aIndex, core::MaskVoxel& voxel) const { voxel.setRelevantVolumeFraction(0); if (_geoInfo->validIndex(aIndex)) { const ITKMaskImageType::IndexType pixelIndex = {{aIndex[0], aIndex[1], aIndex[2]}}; double value = _mask->GetPixel(pixelIndex); VoxelGridID gridId; _geoInfo->convert(aIndex, gridId); if (value >= 0 && value <= 1) { voxel.setRelevantVolumeFraction(value); + return true; } else { std::cerr << "The pixel value of the mask should be >=0 and <=1!" << std::endl; return false; } } else { return false; } } const core::GeometricInfo& ITKImageMaskAccessor::getGeometricInfo() const { return *_geoInfo; }; } } } diff --git a/code/io/itk/rttbITKImageMaskAccessor.h b/code/io/itk/rttbITKImageMaskAccessor.h index 2eeebf0..f3c7cb2 100644 --- a/code/io/itk/rttbITKImageMaskAccessor.h +++ b/code/io/itk/rttbITKImageMaskAccessor.h @@ -1,114 +1,114 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __ITK_IMAGE_MASK_ACCESSOR_H #define __ITK_IMAGE_MASK_ACCESSOR_H #include "rttbMaskAccessorInterface.h" #include "rttbGeometricInfo.h" #include "rttbBaseType.h" #include "itkImage.h" namespace rttb { namespace io { namespace itk { /*! @class ITKImageMaskAccessor @brief This class gives access to mask information stored in an itk image */ class ITKImageMaskAccessor: public core::MaskAccessorInterface { public: typedef ::itk::Image ITKMaskImageType; typedef ::itk::ImageBase<3> ITKImageBaseType; typedef core::MaskAccessorInterface::MaskVoxelList MaskVoxelList; typedef core::MaskAccessorInterface::MaskVoxelListPointer MaskVoxelListPointer; typedef boost::shared_ptr GeometricInfoPointer; private: /** @brief The mask as itkImage */ ITKMaskImageType::ConstPointer _mask; IDType _maskUID; GeometricInfoPointer _geoInfo; /*! vector containing list of mask voxels*/ MaskVoxelListPointer _relevantVoxelVector; /*! @brief get all required data from the itk image contained in _Mask @exception InvalidDoseException if PixelSpacing is 0 or size in any dimension is 0. */ bool assembleGeometricInfo(); public: ~ITKImageMaskAccessor(); ITKImageMaskAccessor(ITKMaskImageType::ConstPointer aMaskImage); /*! @brief voxelization of the given structures according to the original RTToolbox algorithm*/ void updateMask(); /*! @brief get vector conatining al relevant voxels that are inside the given structure*/ MaskVoxelListPointer getRelevantVoxelVector(); /*! @brief get vector conatining al relevant voxels that have a relevant volume above the given threshold and are inside the given structure*/ MaskVoxelListPointer getRelevantVoxelVector(float lowerThreshold); /*!@brief determine how a given voxel on the dose grid is masked * @param aID ID of the voxel in grid. * @param voxel Reference to the voxel. - * @post after a valid call voxel containes the information of the specified grid voxel. If aID is not valid, voxel values are undefined. + * @post after a valid call voxel contains the information of the specified grid voxel. If aID is not valid, voxel values are undefined. * The relevant volume fraction will be set to zero. - * @return Indicates of the voxel exists and therefore if parameter voxel containes valid values.*/ + * @return Indicates of the voxel exists and therefore if parameter voxel contains valid values.*/ bool getMaskAt(const VoxelGridID aID, core::MaskVoxel& voxel) const; bool getMaskAt(const VoxelGridIndex3D& aIndex, core::MaskVoxel& voxel) const; /*! @brief give access to GeometricInfo*/ const core::GeometricInfo& getGeometricInfo() const; /* @ brief is true if Mask is on a homogeneous grid */ // Inhomogeneous grids are not supported at the moment, but if they will // be supported in the future the interface does not need to change. bool isGridHomogeneous() const { return true; }; IDType getMaskUID() const { return _maskUID; }; }; } } } #endif diff --git a/code/io/other/rttbDVHTxtFileReader.cpp b/code/io/other/rttbDVHTxtFileReader.cpp index ba3590d..79b90c2 100644 --- a/code/io/other/rttbDVHTxtFileReader.cpp +++ b/code/io/other/rttbDVHTxtFileReader.cpp @@ -1,226 +1,219 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include #include #include #include "rttbDVHTxtFileReader.h" #include "rttbInvalidParameterException.h" #include "rttbBaseType.h" namespace rttb { namespace io { namespace other { DVHTxtFileReader::DVHTxtFileReader(FileNameString aFileName) { _fileName = aFileName; _resetFile = true; } DVHTxtFileReader::~DVHTxtFileReader() {} void DVHTxtFileReader::resetFileName(FileNameString aFileName) { _fileName = aFileName; _resetFile = true; } void DVHTxtFileReader::createDVH() { std::ifstream dvh_ifstr(this->_fileName.c_str(), std::ios::in); std::string structureLabel; std::string dvhType; - int numberOfBins; + unsigned int numberOfBins; DoseTypeGy prescribedDose; double estimated_max_dose_prescribed_dose_ratio; - int voxelsInStructure; std::deque dataDifferential; std::deque dataCumulative; DoseTypeGy deltaD = 0; DoseVoxelVolumeType deltaV = 0; IDType strID; IDType doseID; if (!dvh_ifstr.is_open()) { throw core::InvalidParameterException("DVH file name invalid: could not open the dvh file!"); } else { bool data_begin = false; while (!dvh_ifstr.eof()) { std::string line; std::getline(dvh_ifstr, line); if (line.find("DVH Data:") != std::string::npos) { data_begin = true; } if (data_begin && (line.find(",") != std::string::npos)) { std::stringstream ss(line.substr(line.find(",") + 1)); DoseCalcType dvh_i; ss >> dvh_i; if (dvhType == "DIFFERENTIAL") { dataDifferential.push_back(dvh_i); } else if (dvhType == "CUMULATIVE") { dataCumulative.push_back(dvh_i); } } if (line.find("DeltaD: ") != std::string::npos) { std::stringstream ss(line.substr(8)); ss >> deltaD; } if (line.find("DeltaV: ") != std::string::npos) { std::stringstream ss(line.substr(8)); ss >> deltaV; } if (line.find("StructureID: ") != std::string::npos) { std::stringstream ss(line.substr(13)); ss >> strID; } if (line.find("DoseID: ") != std::string::npos) { std::stringstream ss(line.substr(8)); ss >> doseID; } if (line.find("Number of bins: ") != std::string::npos) { std::stringstream ss(line.substr(16)); ss >> numberOfBins; } if (line.find("Structure Label: ") != std::string::npos) { std::stringstream ss(line.substr(17)); ss >> structureLabel; } if (line.find("DVH Type: ") != std::string::npos) { std::stringstream ss(line.substr(10)); ss >> dvhType; } if (line.find("Prescribed Dose: ") != std::string::npos) { std::stringstream ss(line.substr(17)); ss >> prescribedDose; } if (line.find("Estimated_max_dose_prescribed_dose_ratio: ") != std::string::npos) { std::stringstream ss(line.substr(42)); ss >> estimated_max_dose_prescribed_dose_ratio; } - - if (line.find("Voxels In Structure: ") != std::string::npos) - { - std::stringstream ss(line.substr(21)); - ss >> voxelsInStructure; - } } } - numberOfBins = std::max(dataDifferential.size(), dataCumulative.size()); + numberOfBins = static_cast(std::max(dataDifferential.size(), dataCumulative.size())); if (dvhType == "CUMULATIVE") { DoseCalcType differentialDVHi = 0; std::deque::iterator it; for (it = dataCumulative.begin(); it != dataCumulative.end(); ++it) { if (dataDifferential.size() == numberOfBins - 1) { differentialDVHi = *it; } else { differentialDVHi = *it - *(it + 1); } dataDifferential.push_back(differentialDVHi); } } if (numberOfBins == 0) { throw core::InvalidParameterException("Invalid dvh file: empty dvh data!"); } if (deltaD == 0) { deltaD = prescribedDose * estimated_max_dose_prescribed_dose_ratio / numberOfBins; } if (deltaV == 0) { deltaV = 0.027; } if (deltaD == 0 || deltaV == 0) { throw core::InvalidParameterException("Invalid dvh file: deltaD or deltaV must not be zero!"); } _dvh = boost::make_shared(dataDifferential, deltaD, deltaV, strID, doseID); _resetFile = false; } DVHTxtFileReader::DVHPointer DVHTxtFileReader::generateDVH() { if (_resetFile) { this->createDVH(); } return _dvh; } - }//end namepsace other + }//end namespace other }//end namespace io }//end namespace rttb diff --git a/code/io/other/rttbDVHXMLFileReader.cpp b/code/io/other/rttbDVHXMLFileReader.cpp index e9e8126..e40af6a 100644 --- a/code/io/other/rttbDVHXMLFileReader.cpp +++ b/code/io/other/rttbDVHXMLFileReader.cpp @@ -1,148 +1,145 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include #include #include #include #include "rttbDVHXMLFileReader.h" #include "rttbInvalidParameterException.h" #include "rttbBaseType.h" namespace rttb { namespace io { namespace other { DVHXMLFileReader::DVHXMLFileReader(FileNameString aFileName) { _fileName = aFileName; _resetFile = true; } DVHXMLFileReader::~DVHXMLFileReader() {} void DVHXMLFileReader::resetFileName(FileNameString aFileName) { _fileName = aFileName; _resetFile = true; } void DVHXMLFileReader::createDVH() { using boost::property_tree::ptree; ptree pt; // Load the XML file into the property tree. If reading fails // (cannot open file, parse error), an exception is thrown. try { read_xml(_fileName, pt); } catch (boost::property_tree::xml_parser_error& /*e*/) { throw rttb::core::InvalidParameterException("DVH file name invalid: could not open the xml file!"); } std::string dvhType; - int numberOfBins; std::deque dataDifferential, dataCumulative; DoseTypeGy deltaD = 0; DoseVoxelVolumeType deltaV = 0; IDType strID; IDType doseID; dvhType = pt.get("dvh.type"); deltaD = pt.get("dvh.deltaD"); deltaV = pt.get("dvh.deltaV"); strID = pt.get("dvh.structureID"); doseID = pt.get("dvh.doseID"); if (dvhType != "DIFFERENTIAL" && dvhType != "CUMULATIVE") { throw core::InvalidParameterException("DVH Type invalid! Only: DIFFERENTIAL/CUMULATIVE!"); } int count = 0; BOOST_FOREACH(boost::property_tree::ptree::value_type & v, pt.get_child("dvh.data")) { - if (count % 2 == 1) + if (dvhType == "DIFFERENTIAL") { - if (dvhType == "DIFFERENTIAL") - { - dataDifferential.push_back(boost::lexical_cast(v.second.data())); + dataDifferential.push_back(boost::lexical_cast(v.second.data())); - } - else if (dvhType == "CUMULATIVE") - { - dataCumulative.push_back(boost::lexical_cast(v.second.data())); - } + } + else if (dvhType == "CUMULATIVE") + { + dataCumulative.push_back(boost::lexical_cast(v.second.data())); } count++; } - numberOfBins = std::max(dataDifferential.size(), dataCumulative.size()); + unsigned int numberOfBins = static_cast(std::max(dataDifferential.size(), + dataCumulative.size())); if (dvhType == "CUMULATIVE") //dataDifferential should be calculated { DoseCalcType differentialDVHi = 0; std::deque::iterator it; for (it = dataCumulative.begin(); it != dataCumulative.end(); ++it) { if (dataDifferential.size() == numberOfBins - 1) { differentialDVHi = *it; } else { differentialDVHi = *it - *(it + 1); } dataDifferential.push_back(differentialDVHi); } } _dvh = boost::make_shared(dataDifferential, deltaD, deltaV, strID, doseID); _resetFile = false; } DVHXMLFileReader::DVHPointer DVHXMLFileReader::generateDVH() { if (_resetFile) { this->createDVH(); } return _dvh; } - }//end namepsace other + }//end namespace other }//end namespace io }//end namespace rttb diff --git a/code/io/other/rttbDVHXMLFileWriter.cpp b/code/io/other/rttbDVHXMLFileWriter.cpp index 90e547e..f8f416d 100644 --- a/code/io/other/rttbDVHXMLFileWriter.cpp +++ b/code/io/other/rttbDVHXMLFileWriter.cpp @@ -1,134 +1,140 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include "rttbDVHXMLFileWriter.h" #include "rttbNullPointerException.h" #include "rttbInvalidParameterException.h" #include "rttbException.h" #include #include namespace rttb { namespace io { namespace other { DVHXMLFileWriter::DVHXMLFileWriter(FileNameString aFileName, DVHType aDVHType) { this->setFileName(aFileName); this->setDVHType(aDVHType); } void DVHXMLFileWriter::setDVHType(DVHType aDVHType) { _dvhType = aDVHType; } FileNameString DVHXMLFileWriter::getFileName() const { return _fileName; } void DVHXMLFileWriter::setFileName(FileNameString aFileName) { _fileName = aFileName; } DVHType DVHXMLFileWriter::getDVHType() const { return _dvhType; } void DVHXMLFileWriter::writeDVH(DVHPointer aDvh) { if (!aDvh) { throw core::NullPointerException("aDvh must not be NULL! "); } using boost::property_tree::ptree; ptree pt; if (_dvhType.Type == DVHType::Differential) { pt.put("dvh.type", "DIFFERENTIAL"); } else if (_dvhType.Type == DVHType::Cumulative) { pt.put("dvh.type", "CUMULATIVE"); } else { throw core::InvalidParameterException("DVH Type not acceptable: Only: DIFFERENTIAL/CUMULATIVE!"); } DataDifferentialType dataDifferential = aDvh->getDataDifferential(); size_t numberOfBins = dataDifferential.size(); pt.put("dvh.deltaD", aDvh->getDeltaD()); pt.put("dvh.deltaV", aDvh->getDeltaV()); pt.put("dvh.structureID", aDvh->getStructureID()); pt.put("dvh.doseID", aDvh->getDoseID()); if (_dvhType.Type == DVHType::Differential) { for (size_t i = 0; i < numberOfBins; i++) { - pt.add("dvh.data.dosebin", i); - pt.add("dvh.data.volume", dataDifferential[i]); + boost::property_tree::ptree node; + node.put("", dataDifferential[i]); + node.put(".dosebin", i); + + pt.add_child("dvh.data.volume", node); } } else if (_dvhType.Type == DVHType::Cumulative) { DataDifferentialType dataCumulative = aDvh->calcCumulativeDVH(); for (size_t i = 0; i < numberOfBins; i++) { - pt.add("dvh.data.dosebin", i); - pt.add("dvh.data.volume", dataCumulative[i]); + boost::property_tree::ptree node; + node.put("", dataCumulative[i]); + node.put(".dosebin", i); + + pt.add_child("dvh.data.volume", node); } } try { boost::property_tree::xml_parser::xml_writer_settings settings = boost::property_tree::xml_writer_make_settings ('\t', 1); boost::property_tree::xml_parser::write_xml(_fileName, pt, std::locale(), settings); } catch (boost::property_tree::xml_parser_error& /*e*/) { throw core::InvalidParameterException("Write xml failed: xml_parser_error!"); } } } } } diff --git a/code/io/other/rttbDoseStatisticsXMLWriter.cpp b/code/io/other/rttbDoseStatisticsXMLWriter.cpp index c3693b9..dec5c5d 100644 --- a/code/io/other/rttbDoseStatisticsXMLWriter.cpp +++ b/code/io/other/rttbDoseStatisticsXMLWriter.cpp @@ -1,279 +1,284 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include "rttbDoseStatisticsXMLWriter.h" #include "rttbInvalidParameterException.h" #include "rttbDataNotAvailableException.h" namespace rttb { namespace io { namespace other { static const std::string xmlattrXTag = ".x"; static const std::string xmlattrNameTag = ".name"; static const std::string statisticsTag = "statistics.results"; static const std::string propertyTag = "property"; static const std::string columnSeparator = "@"; boost::property_tree::ptree writeDoseStatistics(DoseStatisticsPtr aDoseStatistics) { using boost::property_tree::ptree; ptree pt; ptree numberOfVoxelsNode = createNodeWithNameAttribute(aDoseStatistics->getNumberOfVoxels(), "numberOfVoxels"); pt.add_child(statisticsTag + "." + propertyTag, numberOfVoxelsNode); - ptree volumeNode = createNodeWithNameAttribute(aDoseStatistics->getVolume(), "volume"); + ptree volumeNode = createNodeWithNameAttribute(static_cast(aDoseStatistics->getVolume()), + "volume"); pt.add_child(statisticsTag + "." + propertyTag, volumeNode); - ptree minimumNode = createNodeWithNameAttribute(aDoseStatistics->getMinimum(), "minimum"); + ptree minimumNode = createNodeWithNameAttribute(static_cast(aDoseStatistics->getMinimum()), + "minimum"); - auto minimumPositions = aDoseStatistics->getMinimumPositions(); + auto minimumPositions = aDoseStatistics->getMinimumVoxelPositions(); std::vector >::iterator pairItMin = minimumPositions->begin(); int count = 0; for (; pairItMin != minimumPositions->end() && count < 100; ++pairItMin) //output max. 100 minimum { VoxelGridID voxelID = pairItMin->second; ptree voxelMinPositions; voxelMinPositions.add("voxelGridID", voxelID); minimumNode.add_child("voxel", voxelMinPositions); count++; } pt.add_child(statisticsTag + "." + propertyTag, minimumNode); - ptree maximumNode = createNodeWithNameAttribute(aDoseStatistics->getMaximum(), "maximum"); + ptree maximumNode = createNodeWithNameAttribute(static_cast(aDoseStatistics->getMaximum()), + "maximum"); - auto maximumPositions = aDoseStatistics->getMaximumPositions(); + auto maximumPositions = aDoseStatistics->getMaximumVoxelPositions(); std::vector >::iterator pairItMax = maximumPositions->begin(); count = 0; for (; pairItMax != maximumPositions->end() && count < 100; ++pairItMax) //output max. 100 maximum { VoxelGridID voxelID = pairItMax->second; ptree voxelMaxPositions; voxelMaxPositions.add("voxelGridID", voxelID); maximumNode.add_child("voxel", voxelMaxPositions); count++; } pt.add_child(statisticsTag + "." + propertyTag, maximumNode); - ptree meanNode = createNodeWithNameAttribute(aDoseStatistics->getMean(), "mean"); + ptree meanNode = createNodeWithNameAttribute(static_cast(aDoseStatistics->getMean()), + "mean"); pt.add_child(statisticsTag + "." + propertyTag, meanNode); - ptree sdNode = createNodeWithNameAttribute(aDoseStatistics->getStdDeviation(), "standardDeviation"); + ptree sdNode = createNodeWithNameAttribute(static_cast(aDoseStatistics->getStdDeviation()), + "standardDeviation"); pt.add_child(statisticsTag + "." + propertyTag, sdNode); - ptree varianceNode = createNodeWithNameAttribute(aDoseStatistics->getVariance(), "variance"); + ptree varianceNode = createNodeWithNameAttribute(static_cast(aDoseStatistics->getVariance()), + "variance"); pt.add_child(statisticsTag + "." + propertyTag, varianceNode); double absoluteVolume = aDoseStatistics->getVolume(); double referenceDose = aDoseStatistics->getReferenceDose(); rttb::algorithms::DoseStatistics::DoseToVolumeFunctionType AllVx = aDoseStatistics->getAllVx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllDx = aDoseStatistics->getAllDx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMOHx = aDoseStatistics->getAllMOHx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMOCx = aDoseStatistics->getAllMOCx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMaxOHx = aDoseStatistics->getAllMaxOHx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMinOCx = aDoseStatistics->getAllMinOCx(); rttb::algorithms::DoseStatistics::DoseToVolumeFunctionType::iterator vxIt; rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType::iterator it; - for (it = AllDx.begin(); it != AllDx.end(); it++) + for (it = AllDx.begin(); it != AllDx.end(); ++it) { - ptree DxNode = createNodeWithNameAndXAttribute((*it).second, "Dx", + ptree DxNode = createNodeWithNameAndXAttribute(static_cast((*it).second), "Dx", static_cast((*it).first / absoluteVolume * 100)); pt.add_child(statisticsTag + "." + propertyTag, DxNode); } - for (vxIt = AllVx.begin(); vxIt != AllVx.end(); vxIt++) + for (vxIt = AllVx.begin(); vxIt != AllVx.end(); ++vxIt) { - ptree VxNode = createNodeWithNameAndXAttribute((*vxIt).second, "Vx", + ptree VxNode = createNodeWithNameAndXAttribute(static_cast((*vxIt).second), "Vx", static_cast((*vxIt).first / referenceDose * 100)); pt.add_child(statisticsTag + "." + propertyTag, VxNode); } - for (it = AllMOHx.begin(); it != AllMOHx.end(); it++) + for (it = AllMOHx.begin(); it != AllMOHx.end(); ++it) { - ptree mohxNode = createNodeWithNameAndXAttribute((*it).second, "MOHx", + ptree mohxNode = createNodeWithNameAndXAttribute(static_cast((*it).second), "MOHx", static_cast((*it).first / absoluteVolume * 100)); pt.add_child(statisticsTag + "." + propertyTag, mohxNode); } - for (it = AllMOCx.begin(); it != AllMOCx.end(); it++) + for (it = AllMOCx.begin(); it != AllMOCx.end(); ++it) { - ptree mocxNode = createNodeWithNameAndXAttribute((*it).second, "MOCx", + ptree mocxNode = createNodeWithNameAndXAttribute(static_cast((*it).second), "MOCx", static_cast((*it).first / absoluteVolume * 100)); pt.add_child(statisticsTag + "." + propertyTag, mocxNode); } - for (it = AllMaxOHx.begin(); it != AllMaxOHx.end(); it++) + for (it = AllMaxOHx.begin(); it != AllMaxOHx.end(); ++it) { - ptree maxOhxNode = createNodeWithNameAndXAttribute((*it).second, "MaxOHx", + ptree maxOhxNode = createNodeWithNameAndXAttribute(static_cast((*it).second), "MaxOHx", static_cast((*it).first / absoluteVolume * 100)); pt.add_child(statisticsTag + "." + propertyTag, maxOhxNode); } - for (it = AllMinOCx.begin(); it != AllMinOCx.end(); it++) + for (it = AllMinOCx.begin(); it != AllMinOCx.end(); ++it) { - ptree minOCxNode = createNodeWithNameAndXAttribute((*it).second, "MinOCx", + ptree minOCxNode = createNodeWithNameAndXAttribute(static_cast((*it).second), "MinOCx", static_cast((*it).first / absoluteVolume * 100)); pt.add_child(statisticsTag + "." + propertyTag, minOCxNode); } return pt; } void writeDoseStatistics(DoseStatisticsPtr aDoseStatistics, FileNameString aFileName) { boost::property_tree::ptree pt = writeDoseStatistics(aDoseStatistics); try { boost::property_tree::xml_parser::write_xml(aFileName, pt, std::locale(), boost::property_tree::xml_writer_make_settings('\t', 1)); } catch (boost::property_tree::xml_parser_error& /*e*/) { throw core::InvalidParameterException("Write xml failed: xml_parser_error!"); } } XMLString writerDoseStatisticsToString(DoseStatisticsPtr aDoseStatistics) { boost::property_tree::ptree pt = writeDoseStatistics(aDoseStatistics); std::stringstream sstr; try { boost::property_tree::xml_parser::write_xml(sstr, pt, boost::property_tree::xml_writer_make_settings('\t', 1)); } catch (boost::property_tree::xml_parser_error& /*e*/) { throw core::InvalidParameterException("Write xml to string failed: xml_parser_error!"); } return sstr.str(); } StatisticsString writerDoseStatisticsToTableString(DoseStatisticsPtr aDoseStatistics) { std::stringstream sstr; - sstr << aDoseStatistics->getVolume() * 1000 << columnSeparator; // cm3 to mm3 - sstr << aDoseStatistics->getMaximum() << columnSeparator; - sstr << aDoseStatistics->getMinimum() << columnSeparator; + sstr << static_cast(aDoseStatistics->getVolume() * 1000) << columnSeparator; // cm3 to mm3 + sstr << static_cast(aDoseStatistics->getMaximum()) << columnSeparator; + sstr << static_cast(aDoseStatistics->getMinimum()) << columnSeparator; - sstr << aDoseStatistics->getMean() << columnSeparator; - sstr << aDoseStatistics->getStdDeviation() << columnSeparator; - sstr << aDoseStatistics->getVariance() << columnSeparator; + sstr << static_cast(aDoseStatistics->getMean()) << columnSeparator; + sstr << static_cast(aDoseStatistics->getStdDeviation()) << columnSeparator; + sstr << static_cast(aDoseStatistics->getVariance()) << columnSeparator; - double absoluteVolume = aDoseStatistics->getVolume(); rttb::algorithms::DoseStatistics::DoseToVolumeFunctionType AllVx = aDoseStatistics->getAllVx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllDx = aDoseStatistics->getAllDx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMOHx = aDoseStatistics->getAllMOHx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMOCx = aDoseStatistics->getAllMOCx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMaxOHx = aDoseStatistics->getAllMaxOHx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMinOCx = aDoseStatistics->getAllMinOCx(); rttb::algorithms::DoseStatistics::DoseToVolumeFunctionType::iterator vxIt; rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType::iterator it; - for (it = AllDx.begin(); it != AllDx.end(); it++) + for (it = AllDx.begin(); it != AllDx.end(); ++it) { - sstr << (*it).second << columnSeparator; + sstr << static_cast((*it).second) << columnSeparator; } - for (vxIt = AllVx.begin(); vxIt != AllVx.end(); vxIt++) + for (vxIt = AllVx.begin(); vxIt != AllVx.end(); ++vxIt) { // *1000 because of conversion cm3 to mm3 - sstr << (*vxIt).second * 1000 << columnSeparator; + sstr << static_cast((*vxIt).second * 1000) << columnSeparator; } - for (it = AllMOHx.begin(); it != AllMOHx.end(); it++) + for (it = AllMOHx.begin(); it != AllMOHx.end(); ++it) { - sstr << (*it).second << columnSeparator; + sstr << static_cast((*it).second) << columnSeparator; } - for (it = AllMOCx.begin(); it != AllMOCx.end(); it++) + for (it = AllMOCx.begin(); it != AllMOCx.end(); ++it) { - sstr << (*it).second << columnSeparator; + sstr << static_cast((*it).second) << columnSeparator; } - for (it = AllMaxOHx.begin(); it != AllMaxOHx.end(); it++) + for (it = AllMaxOHx.begin(); it != AllMaxOHx.end(); ++it) { - sstr << (*it).second << columnSeparator; + sstr << static_cast((*it).second) << columnSeparator; } - for (it = AllMinOCx.begin(); it != AllMinOCx.end(); it++) + for (it = AllMinOCx.begin(); it != AllMinOCx.end(); ++it) { - sstr << (*it).second << columnSeparator; + sstr << static_cast((*it).second) << columnSeparator; } return sstr.str(); } boost::property_tree::ptree createNodeWithNameAttribute(DoseTypeGy doseValue, const std::string& attributeName) { boost::property_tree::ptree node; node.put("", doseValue); node.put(xmlattrNameTag, attributeName); return node; } boost::property_tree::ptree createNodeWithNameAndXAttribute(DoseTypeGy doseValue, const std::string& attributeName, int xValue) { boost::property_tree::ptree node; node.put("", doseValue); node.put(xmlattrNameTag, attributeName); node.put(xmlattrXTag, xValue); return node; } }//end namespace other }//end namespace io }//end namespace rttb \ No newline at end of file diff --git a/code/io/other/rttbDoseStatisticsXMLWriter.h b/code/io/other/rttbDoseStatisticsXMLWriter.h index f97c1b3..48e944e 100644 --- a/code/io/other/rttbDoseStatisticsXMLWriter.h +++ b/code/io/other/rttbDoseStatisticsXMLWriter.h @@ -1,99 +1,103 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __DOSE_STATISTICS_XML_WRITER_H #define __DOSE_STATISTICS_XML_WRITER_H #include "rttbDoseStatistics.h" #include "rttbBaseType.h" /*boost includes*/ #include #include #include #include namespace rttb { namespace io { namespace other { typedef boost::shared_ptr DoseStatisticsPtr; typedef rttb::algorithms::DoseStatistics::ResultListPointer ResultListPointer; /*! @brief Write statistics to boost::property_tree::ptree. @param aReferenceDose A reference dose for the calculation of Vx @param aDoseStatistics DoseStatistics to write @pre aReferenceDose must >0 @exception InvalidParameterException Thrown if aReferenceDose<=0 or xml_parse_error + @note The precision is float */ boost::property_tree::ptree writeDoseStatistics(DoseStatisticsPtr aDoseStatistics); /*! @brief Write statistics to String. @param aReferenceDose A reference dose for the calculation of Vx @param aDoseStatistics DoseStatistics to write @pre aReferenceDose must >0 @exception InvalidParameterException Thrown if aReferenceDose<=0 or xml_parse_error + @note The precision is float */ XMLString writerDoseStatisticsToString(DoseStatisticsPtr aDoseStatistics); /*! @brief Write statistics to xml file, including numberOfVoxels, volume, minimum, maximum, mean, standard deviation, variance, D2,D5,D10,D90,D95,D98, V2,V5,V10,V90,V95,V98, MOH2,MOH5,MOH10, MOC2,MOC5,MOC10, MaxOH2,MaxOH5,MaxOH10, MinOC2,MinOC5,MinOC10. @param aReferenceDose A reference dose for the calculation of Vx @param aDoseStatistics DoseStatistics to write @param aFileName To write dose statistics @pre aReferenceDose must >0 @exception InvalidParameterException Thrown if aReferenceDose<=0 or xml_parse_error + @note The precision is float */ void writeDoseStatistics(DoseStatisticsPtr aDoseStatistics, FileNameString aFileName); boost::property_tree::ptree createNodeWithNameAttribute(DoseTypeGy doseValue, const std::string& attributeName); boost::property_tree::ptree createNodeWithNameAndXAttribute(DoseTypeGy doseValue, const std::string& attributeName, int xValue); /*! @brief Write statistics to String to generate a table: "Volume mm3@Max@Min@Mean@Std.Dev.@Variance@D2@D5@D10@D90@D95@D98@V2@V5@V10@V90@V95@V98@MOH2@MOH5@MOH10@MOC2@MOC5@MOC10@MaxOH2@MaxOH5@MaxOH10@MinOC2@MinOC5@MinOC10" @param aReferenceDose A reference dose for the calculation of Vx @param aDoseStatistics DoseStatistics to write @pre aReferenceDose must >0 @exception InvalidParameterException Thrown if aReferenceDose<=0 or xml_parse_error @note is used for the Mevislab-Linking of RTTB + @note The precision is float */ StatisticsString writerDoseStatisticsToTableString(DoseStatisticsPtr aDoseStatistics); } } } #endif diff --git a/code/io/virtuos/rttbVirtuosFileStructureSetGenerator.cpp b/code/io/virtuos/rttbVirtuosFileStructureSetGenerator.cpp index 6dcecd9..9aab674 100644 --- a/code/io/virtuos/rttbVirtuosFileStructureSetGenerator.cpp +++ b/code/io/virtuos/rttbVirtuosFileStructureSetGenerator.cpp @@ -1,241 +1,241 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include #include #include #include "rttbNullPointerException.h" #include "rttbInvalidParameterException.h" #include "rttbStructure.h" #include "rttbVirtuosFileStructureSetGenerator.h" namespace rttb { namespace io { namespace virtuos { VirtuosFileStructureSetGenerator::VirtuosFileStructureSetGenerator(FileNameType aVirtuosVDXFileName, FileNameType aVirtuosCTXFileName) : _pPointerOnVirtuosCube(new Cubeinfo*), _patient(NULL) { _VDXFileName = aVirtuosVDXFileName; _CTXFileName = aVirtuosCTXFileName; //check if file names are valid if (!boost::filesystem::exists(_VDXFileName)) { throw core::InvalidParameterException("invalid vdx file name"); } if (!boost::filesystem::exists(_CTXFileName)) { throw core::InvalidParameterException("invalid ctx file name"); } *_pPointerOnVirtuosCube = create_cubeinfo(0); this->initializeVirtuosCube(_CTXFileName); } VirtuosFileStructureSetGenerator::~VirtuosFileStructureSetGenerator() { if (this->_patient != NULL) { delete this->_patient; } closecube((*(this->_pPointerOnVirtuosCube))); nc_free_cubeinfo((*(this->_pPointerOnVirtuosCube))); delete this->_pPointerOnVirtuosCube; } void VirtuosFileStructureSetGenerator::importStructureSet(FileNameType aVirtuosVDXFileName, FileNameType aVirtuosCTXFileName) { //check file name if (aVirtuosCTXFileName.empty() || aVirtuosVDXFileName.empty()) { throw core::InvalidParameterException("Virtuos VDX/CTX file name must not be empty!"); } - int vdxPosition = aVirtuosVDXFileName.find(".vdx"); + size_t vdxPosition = aVirtuosVDXFileName.find(".vdx"); if (vdxPosition == std::string::npos) { throw core::InvalidParameterException("Virtuos VDX file name must be *.vdx!"); } //get patientFileName, patientDataPath for Virtuos function std::string patientFileName, patientName, patientDataPath; patientFileName.assign(aVirtuosVDXFileName, aVirtuosVDXFileName.find_last_of("/") + 1, aVirtuosVDXFileName.length()); patientName.assign(patientFileName, 0, patientFileName.find_first_of(".")); patientDataPath.assign(aVirtuosVDXFileName, 0, aVirtuosVDXFileName.find_last_of("/") + 1); //Virtuos: voi create voi model int errorcode = voi_create_voi_model_dirolab(patientName.c_str(), patientDataPath.c_str(), 0, this->_patient); if (errorcode != 0) { //throw std::string ("Virtuos Routines unable to create VOI Model! "); std::cerr << "voi_create_voi_model_dirolab error: error code " << errorcode << std::endl; } //Virtuos: voi read vdx errorcode = voi_read_vdx_version_2_for_DIROlab(patientFileName.c_str(), patientDataPath.c_str(), this->_patient); if (errorcode != 0) { //throw std::string ("voi_read_vdx_version_2_for_DIROlab failed! "); std::cerr << "voi_read_vdx_version_2_for_DIROlab error: error code " << errorcode << std::endl; } int numberOfVois = this->_patient->getNumberOfVois(); float firstSliceInFrame = (*_pPointerOnVirtuosCube)->pos_list[0].position; - double sliceThickness = (*_pPointerOnVirtuosCube)->slicedist; + float sliceThickness = (*_pPointerOnVirtuosCube)->slicedist; - float lastSliceInFrame = ((*_pPointerOnVirtuosCube)->dimz - 1) * sliceThickness + firstSliceInFrame; + float lastSliceInFrame = static_cast(((*_pPointerOnVirtuosCube)->dimz - 1) * sliceThickness + firstSliceInFrame); for (int currentVoiNumber = 0; currentVoiNumber < numberOfVois; currentVoiNumber++) { std::string voiName = ""; char tmpVoiName[1024]; voi_get_voi_name_by_index_dirolab(currentVoiNumber, 1024, tmpVoiName, this->_patient); voiName.assign(tmpVoiName); /* prepare contour extraction */ D3PointList* contours = NULL; contours = d3_list_create(1000000); D3Point origin = {0, 0, 0}, y_axis_point = {0, 0, 0}, x_axis_point = {0, 0, 0}; int maxNumberOfContours = 100000; int* pNoOFContours = &maxNumberOfContours; PolygonSequenceType polygonSequence; for (float z = firstSliceInFrame; z <= lastSliceInFrame; z += sliceThickness) { origin.x = 0.0; origin.y = 0.0; origin.z = z ; x_axis_point.x = 1.0; x_axis_point.y = 0.0; x_axis_point.z = z ; y_axis_point.x = 0.0; y_axis_point.y = 1.0; y_axis_point.z = z; *pNoOFContours = 100000; //<-- reason is the next function call voi_get_CT_contours_dirolab(voiName.c_str(), origin, x_axis_point, y_axis_point, pNoOFContours, &contours, 1, this->_patient); for (int i = 0; i < *pNoOFContours; i++) { PolygonType polygon; for (int j = 0; j < contours[i].used - 1; j++) //Virtuos polygon the last point is the same as the first point { WorldCoordinate3D point; point(0) = contours[i].points[j].x; point(1) = contours[i].points[j].y; point(2) = contours[i].points[j].z; polygon.push_back(point); }//end for j polygonSequence.push_back(polygon); }//end for i }//end for z boost::shared_ptr spStruct = boost::make_shared(polygonSequence); spStruct->setLabel(voiName); _strVector.push_back(spStruct); }//end for currentVoiNumber } void VirtuosFileStructureSetGenerator::initializeVirtuosCube(FileNameType aVirtuosCTXFileName) { if (aVirtuosCTXFileName.empty()) { throw core::InvalidParameterException("Empty File Name"); } - int gzPosition = aVirtuosCTXFileName.find(".gz"); + size_t gzPosition = aVirtuosCTXFileName.find(".gz"); if (gzPosition != std::string::npos) { aVirtuosCTXFileName.erase(gzPosition, aVirtuosCTXFileName.length()); } nc_init_cubeinfo(*_pPointerOnVirtuosCube); int opencubeErrorCode = opencube(aVirtuosCTXFileName.c_str() , *_pPointerOnVirtuosCube); if (opencubeErrorCode != 0) { std::stringstream opencubeErrorCodeAsStringStream; opencubeErrorCodeAsStringStream << opencubeErrorCode; throw core::InvalidParameterException(std::string( std::string("VirtuosIO::openCube returned error Code: ") + opencubeErrorCodeAsStringStream.str())); } if ((*_pPointerOnVirtuosCube)->dimx == 0 || (*_pPointerOnVirtuosCube)->dimy == 0 || (*_pPointerOnVirtuosCube)->dimz == 0) { throw core::InvalidParameterException("Invalid Cube dimension: dimX/dimY/dimZ must not be zero! "); } } VirtuosFileStructureSetGenerator::StructureSetPointer VirtuosFileStructureSetGenerator::generateStructureSet() { this->importStructureSet(_VDXFileName, _CTXFileName); return boost::make_shared(_strVector); } }//end namespace virtuos }//end namespace io }//end namespace rttb diff --git a/code/masks/boost/files.cmake b/code/masks/boost/files.cmake index 616ac4e..86bbcad 100644 --- a/code/masks/boost/files.cmake +++ b/code/masks/boost/files.cmake @@ -1,9 +1,17 @@ SET(CPP_FILES rttbBoostMask.cpp - rttbBoostMaskAccessor.cpp + rttbBoostMaskAccessor.cpp + rttbBoostMaskGenerateMaskVoxelListThread.cpp + rttbBoostMaskRedesign.cpp + rttbBoostMaskRedesignAccessor.cpp + rttbBoostMaskVoxelizationThread.cpp ) SET(H_FILES rttbBoostMask.h rttbBoostMaskAccessor.h + rttbBoostMaskGenerateMaskVoxelListThread.h + rttbBoostMaskRedesign.h + rttbBoostMaskRedesignAccessor.h + rttbBoostMaskVoxelizationThread.h ) diff --git a/code/masks/boost/rttbBoostMask.cpp b/code/masks/boost/rttbBoostMask.cpp index a9b44a5..912c1fa 100644 --- a/code/masks/boost/rttbBoostMask.cpp +++ b/code/masks/boost/rttbBoostMask.cpp @@ -1,490 +1,498 @@ #include #include #include #include #include #include #include #include #include "rttbBoostMask.h" #include "rttbNullPointerException.h" #include "rttbInvalidParameterException.h" namespace rttb { namespace masks { namespace boost { BoostMask::BoostMask(BoostMask::GeometricInfoPointer aDoseGeoInfo, BoostMask::StructPointer aStructure, bool strict) : _geometricInfo(aDoseGeoInfo), _structure(aStructure), _strict(strict), _voxelInStructure(::boost::make_shared()) { _isUpToDate = false; if (!_geometricInfo) { throw rttb::core::NullPointerException("Error: Geometric info is NULL!"); } else if (!_structure) { throw rttb::core::NullPointerException("Error: Structure is NULL!"); } } void BoostMask::calcMask() { if (hasSelfIntersections()) { if (_strict) { throw rttb::core::InvalidParameterException("Error: structure has self intersections!"); } else { std::cerr << _structure->getLabel() << " has self intersections! It may cause errors in the voxelization results!" << std::endl; } } std::vector intersectionSlicePolygonsVector;//store the polygons of intersection slice of each index z + unsigned int nSlices = static_cast(_geometricInfo->getNumSlices()); + //For each dose slice, get intersection structure slice and the contours on the structure slice - for (unsigned int indexZ = 0; indexZ < _geometricInfo->getNumSlices(); indexZ++) + for (unsigned int indexZ = 0; indexZ < nSlices; indexZ++) { BoostMask::BoostPolygonVector boostPolygons = getIntersectionSlicePolygons(indexZ); BoostMask::BoostPolygonVector::iterator it; for (it = boostPolygons.begin(); it != boostPolygons.end(); ++it) { ::boost::geometry::correct(*it); } intersectionSlicePolygonsVector.push_back(boostPolygons); } /* Use simple nearest neighbour interpolation (NNI) if dose and structure has different z spacing: If dose slice (indexZ) has no intersection with structure slice, but the last and the next do, that means the dose slice lies between two structure slices -> use the last slice intersection contours for this slice */ - for (unsigned int indexZ = 1; indexZ < _geometricInfo->getNumSlices() - 1; indexZ++) + for (unsigned int indexZ = 1; indexZ < nSlices - 1; indexZ++) { if (intersectionSlicePolygonsVector.at(indexZ).size() == 0 && intersectionSlicePolygonsVector.at(indexZ - 1).size() > 0 && intersectionSlicePolygonsVector.at(indexZ + 1).size() > 0) { intersectionSlicePolygonsVector.at(indexZ) = intersectionSlicePolygonsVector.at(indexZ - 1); } } - for (unsigned int indexZ = 0; indexZ < _geometricInfo->getNumSlices(); indexZ++) + for (unsigned int indexZ = 0; indexZ < nSlices; indexZ++) { BoostMask::BoostPolygonVector intersectionSlicePolygons = intersectionSlicePolygonsVector.at( indexZ); //Get bounding box of this dose slice VoxelIndexVector voxelList = getBoundingBox(indexZ, intersectionSlicePolygons); rttb::VoxelGridIndex3D gridIndex3D0 = voxelList.at(0); rttb::VoxelGridIndex3D gridIndex3D1 = voxelList.at(1); for (unsigned int indexX = gridIndex3D0[0]; indexX <= gridIndex3D1[0]; indexX++) { for (unsigned int indexY = gridIndex3D0[1]; indexY <= gridIndex3D1[1]; indexY++) { rttb::VoxelGridIndex3D currentIndex; currentIndex[0] = indexX; currentIndex[1] = indexY; currentIndex[2] = indexZ; rttb::VoxelGridID gridID; _geometricInfo->convert(currentIndex, gridID); //Get intersection polygons of the dose voxel and the structure BoostPolygonDeque polygons = getIntersections(currentIndex, intersectionSlicePolygons); //Calc areas of all intersection polygons double intersectionArea = calcArea(polygons); double voxelSize = _geometricInfo->getSpacing()[0] * _geometricInfo->getSpacing()[1]; if (intersectionArea > 0) { double volumeFraction = intersectionArea / voxelSize; if (volumeFraction > 1 && (volumeFraction - 1) <= 0.0001) { volumeFraction = 1; } core::MaskVoxel maskVoxel = core::MaskVoxel(gridID, (volumeFraction)); _voxelInStructure->push_back(maskVoxel);//push back the mask voxel in structure } } } } sort(_voxelInStructure->begin(), _voxelInStructure->end()); + + _isUpToDate = true; } bool BoostMask::hasSelfIntersections() { bool hasSelfIntersection = false; - for (unsigned int indexZ = 0; indexZ < _geometricInfo->getNumSlices(); indexZ++) + unsigned int nSlices = static_cast(_geometricInfo->getNumSlices()); + + for (unsigned int indexZ = 0; indexZ < nSlices; indexZ++) { rttb::VoxelGridIndex3D currentIndex(0, 0, indexZ); BoostMask::BoostPolygonVector boostPolygons = getIntersectionSlicePolygons(currentIndex[2]); BoostMask::BoostPolygonVector::iterator it; BoostMask::BoostPolygonVector::iterator it2; for (it = boostPolygons.begin(); it != boostPolygons.end(); ++it) { ::boost::geometry::correct(*it); //test if polygon has self intersection if (::boost::geometry::detail::overlay::has_self_intersections(*it, false)) { hasSelfIntersection = true; std::cerr << _structure->getLabel() << " has self intersection polygon! Slice: " << indexZ << ". " << std::endl; break; } //test if the polygons on the same slice has intersection for (it2 = boostPolygons.begin(); it2 != boostPolygons.end() && it2 != it; ++it2) { ::boost::geometry::correct(*it2); BoostPolygonDeque intersection; ::boost::geometry::intersection(*it, *it2, intersection); if (intersection.size() > 0) { //if no donut if (!(::boost::geometry::within(*it, *it2)) && !(::boost::geometry::within(*it2, *it))) { hasSelfIntersection = true; std::cerr << _structure->getLabel() << ": Two polygons on the same slice has intersection! Slice: " << indexZ << "." << std::endl; break; } } } } } return hasSelfIntersection; } /*Get the 4 voxel index of the bounding box of the polygon in the slice with sliceNumber*/ BoostMask::VoxelIndexVector BoostMask::getBoundingBox(unsigned int sliceNumber, const BoostPolygonVector& intersectionSlicePolygons) { - if (sliceNumber < 0 || sliceNumber >= _geometricInfo->getNumSlices()) + unsigned int nSlices = static_cast(_geometricInfo->getNumSlices()); + + if (sliceNumber < 0 || sliceNumber >= nSlices) { throw rttb::core::InvalidParameterException(std::string("Error: slice number is invalid!")); } rttb::VoxelGridIndex3D currentIndex(0, 0, sliceNumber); double xMin = 0; double yMin = 0; double xMax = 0; double yMax = 0; BoostPolygonVector::const_iterator it; for (it = intersectionSlicePolygons.begin(); it != intersectionSlicePolygons.end(); ++it) { ::boost::geometry::model::box box; ::boost::geometry::envelope(*it, box); BoostPoint2D minPoint = box.min_corner(); BoostPoint2D maxPoint = box.max_corner(); if (it == intersectionSlicePolygons.begin()) { xMin = minPoint.x(); yMin = minPoint.y(); xMax = maxPoint.x(); yMax = maxPoint.y(); } xMin = std::min(xMin, minPoint.x()); yMin = std::min(yMin, minPoint.y()); xMax = std::max(xMax, maxPoint.x()); yMax = std::max(yMax, maxPoint.y()); } rttb::WorldCoordinate3D nullWorldCoord; _geometricInfo->indexToWorldCoordinate(VoxelGridIndex3D(0, 0, 0), nullWorldCoord); rttb::WorldCoordinate3D minWorldCoord(xMin, yMin, nullWorldCoord.z()); rttb::WorldCoordinate3D maxWorldCoord(xMax, yMax, nullWorldCoord.z()); rttb::VoxelGridIndex3D index0; rttb::VoxelGridIndex3D index1; _geometricInfo->worldCoordinateToIndex(minWorldCoord, index0); _geometricInfo->worldCoordinateToIndex(maxWorldCoord, index1); VoxelIndexVector voxelList; voxelList.push_back(index0); voxelList.push_back(index1); return voxelList; } /*Get intersection polygons of the contour and a voxel polygon*/ BoostMask::BoostPolygonDeque BoostMask::getIntersections(const rttb::VoxelGridIndex3D& aVoxelIndex3D, const BoostPolygonVector& intersectionSlicePolygons) { BoostMask::BoostPolygonDeque polygonDeque; BoostRing2D voxelPolygon = get2DContour(aVoxelIndex3D); ::boost::geometry::correct(voxelPolygon); BoostPolygonVector::const_iterator it; for (it = intersectionSlicePolygons.begin(); it != intersectionSlicePolygons.end(); ++it) { BoostPolygon2D contour = *it; ::boost::geometry::correct(contour); BoostPolygonDeque intersection; ::boost::geometry::intersection(voxelPolygon, contour, intersection); polygonDeque.insert(polygonDeque.end(), intersection.begin(), intersection.end()); } return polygonDeque; } /*Calculate the intersection area*/ double BoostMask::calcArea(const BoostPolygonDeque& aPolygonDeque) { double area = 0; BoostPolygonDeque::const_iterator it; for (it = aPolygonDeque.begin(); it != aPolygonDeque.end(); ++it) { area += ::boost::geometry::area(*it); } return area; } VoxelGridIndex3D BoostMask::getGridIndex3D(const core::MaskVoxel& aMaskVoxel) { rttb::VoxelGridIndex3D gridIndex3D; _geometricInfo->convert(aMaskVoxel.getVoxelGridID(), gridIndex3D); return gridIndex3D; } BoostMask::MaskVoxelListPointer BoostMask::getRelevantVoxelVector() { if (!_isUpToDate) { calcMask(); } return _voxelInStructure; } BoostMask::BoostRing2D BoostMask::convert(const rttb::PolygonType& aRTTBPolygon) { BoostMask::BoostRing2D polygon2D; BoostPoint2D firstPoint; for (unsigned int i = 0; i < aRTTBPolygon.size(); i++) { rttb::WorldCoordinate3D rttbPoint = aRTTBPolygon.at(i); BoostPoint2D boostPoint(rttbPoint[0], rttbPoint[1]); if (i == 0) { firstPoint = boostPoint; } ::boost::geometry::append(polygon2D, boostPoint); } ::boost::geometry::append(polygon2D, firstPoint); return polygon2D; } BoostMask::BoostPolygonVector BoostMask::getIntersectionSlicePolygons( const rttb::GridIndexType aVoxelGridIndexZ) { BoostMask::BoostRingVector boostRingVector; rttb::PolygonSequenceType polygonSequence = _structure->getStructureVector(); //get the polygons in the slice and convert to boost rings rttb::PolygonSequenceType::iterator it; for (it = polygonSequence.begin(); it != polygonSequence.end(); ++it) { PolygonType rttbPolygon = *it; if (!rttbPolygon.empty()) { double polygonZCoor = rttbPolygon.at(0)[2]; rttb::WorldCoordinate3D polygonPoint(0, 0, polygonZCoor); rttb::VoxelGridIndex3D polygonPointIndex3D; _geometricInfo->worldCoordinateToIndex(polygonPoint, polygonPointIndex3D); //if the z if (aVoxelGridIndexZ == polygonPointIndex3D[2]) { boostRingVector.push_back(convert(rttbPolygon)); } } } return checkDonutAndConvert(boostRingVector); } BoostMask::BoostRing2D BoostMask::get2DContour(const rttb::VoxelGridIndex3D& aVoxelGrid3D) { BoostMask::BoostRing2D polygon; rttb::WorldCoordinate3D rttbPoint; _geometricInfo->indexToWorldCoordinate(aVoxelGrid3D, rttbPoint); BoostPoint2D point1(rttbPoint[0], rttbPoint[1]); ::boost::geometry::append(polygon, point1); double xSpacing = _geometricInfo->getSpacing()[0]; double ySpacing = _geometricInfo->getSpacing()[1]; BoostPoint2D point2(rttbPoint[0] + xSpacing, rttbPoint[1]); ::boost::geometry::append(polygon, point2); BoostPoint2D point3(rttbPoint[0] + xSpacing, rttbPoint[1] + ySpacing); ::boost::geometry::append(polygon, point3); BoostPoint2D point4(rttbPoint[0], rttbPoint[1] + ySpacing); ::boost::geometry::append(polygon, point4); ::boost::geometry::append(polygon, point1); return polygon; } BoostMask::BoostPolygonVector BoostMask::checkDonutAndConvert(const BoostMask::BoostRingVector& aRingVector) { //check donut BoostMask::BoostRingVector::const_iterator it1; BoostMask::BoostRingVector::const_iterator it2; BoostMask::BoostPolygonVector boostPolygonVector; std::vector donutIndexVector;//store the outer and inner ring index BoostMask::BoostPolygonVector donutVector;//store new generated donut polygon //Get donut index and donut polygon unsigned int index1 = 0; - for (it1 = aRingVector.begin(); it1 != aRingVector.end(); it1++, index1++) + for (it1 = aRingVector.begin(); it1 != aRingVector.end(); ++it1, ++index1) { bool it1IsDonut = false; //check if the ring is already determined as a donut for (unsigned int i = 0; i < donutIndexVector.size(); i++) { if (donutIndexVector.at(i) == index1) { it1IsDonut = true; break; } } //if not jet, check now if (!it1IsDonut) { bool it2IsDonut = false; unsigned int index2 = 0; - for (it2 = aRingVector.begin(); it2 != aRingVector.end(); it2++, index2++) + for (it2 = aRingVector.begin(); it2 != aRingVector.end(); ++it2, ++index2) { if (it2 != it1) { BoostMask::BoostPolygon2D polygon2D; if (::boost::geometry::within(*it1, *it2)) { ::boost::geometry::append(polygon2D, *it2);//append an outer ring to the polygon ::boost::geometry::interior_rings(polygon2D).resize(1);//create an interior ring ::boost::geometry::append(polygon2D, *it1, 0);//append a ring to the interior ring it2IsDonut = true; } //if donut else if (::boost::geometry::within(*it2, *it1)) { ::boost::geometry::append(polygon2D, *it1);//append an outer ring to the polygon ::boost::geometry::interior_rings(polygon2D).resize(1);//create an interior ring ::boost::geometry::append(polygon2D, *it2, 0);//append a ring to the interior ring it2IsDonut = true; } if (it2IsDonut) { donutIndexVector.push_back(index1); donutIndexVector.push_back(index2); donutVector.push_back(polygon2D);//store donut polygon break;//Only store the first donut! } } } } } //Store no donut polygon to boostPolygonVector index1 = 0; - for (it1 = aRingVector.begin(); it1 != aRingVector.end(); it1++, index1++) + for (it1 = aRingVector.begin(); it1 != aRingVector.end(); ++it1, ++index1) { bool it1IsDonut = false; //check if the ring is the outer or inner of a donut for (unsigned int i = 0; i < donutIndexVector.size(); i++) { if (donutIndexVector.at(i) == index1) { it1IsDonut = true; break; } } if (!it1IsDonut) { BoostMask::BoostPolygon2D polygon2D; ::boost::geometry::append(polygon2D, *it1); boostPolygonVector.push_back(polygon2D);//insert the ring, which is not a part of donut } } //Append donut polygon to boostPolygonVector BoostMask::BoostPolygonVector::iterator itDonut; - for (itDonut = donutVector.begin(); itDonut != donutVector.end(); itDonut++) + for (itDonut = donutVector.begin(); itDonut != donutVector.end(); ++itDonut) { boostPolygonVector.push_back(*itDonut);//append donuts } return boostPolygonVector; } } } } \ No newline at end of file diff --git a/code/masks/boost/rttbBoostMaskAccessor.cpp b/code/masks/boost/rttbBoostMaskAccessor.cpp index 9b30567..9050fbb 100644 --- a/code/masks/boost/rttbBoostMaskAccessor.cpp +++ b/code/masks/boost/rttbBoostMaskAccessor.cpp @@ -1,163 +1,164 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include "rttbBoostMaskAccessor.h" #include "rttbBoostMask.h" #include #include #include #include namespace rttb { namespace masks { namespace boost { BoostMaskAccessor::BoostMaskAccessor(StructTypePointer aStructurePointer, const core::GeometricInfo& aGeometricInfo, bool strict) : _spStructure(aStructurePointer), _geoInfo(aGeometricInfo), _strict(strict) { _spRelevantVoxelVector = MaskVoxelListPointer(); //generate new structure set uid ::boost::uuids::uuid id; ::boost::uuids::random_generator generator; id = generator(); std::stringstream ss; ss << id; _maskUID = "BoostMask_" + ss.str(); } BoostMaskAccessor::~BoostMaskAccessor() { }; void BoostMaskAccessor::updateMask() { MaskVoxelList newRelevantVoxelVector; if (_spRelevantVoxelVector) { return; // already calculated } - BoostMask mask(::boost::make_shared(_geoInfo), _spStructure, _strict); + BoostMask mask(::boost::make_shared(_geoInfo), + _spStructure, _strict); _spRelevantVoxelVector = mask.getRelevantVoxelVector(); return; } BoostMaskAccessor::MaskVoxelListPointer BoostMaskAccessor::getRelevantVoxelVector() { // if not already generated start voxelization here updateMask(); return _spRelevantVoxelVector; } BoostMaskAccessor::MaskVoxelListPointer BoostMaskAccessor::getRelevantVoxelVector( float lowerThreshold) { MaskVoxelListPointer filteredVoxelVectorPointer(new MaskVoxelList); updateMask(); // filter relevant voxels BoostMaskAccessor::MaskVoxelList::iterator it = _spRelevantVoxelVector->begin(); while (it != _spRelevantVoxelVector->end()) { if ((*it).getRelevantVolumeFraction() > lowerThreshold) { filteredVoxelVectorPointer->push_back(*it); } ++it; } // if mask calculation was not successful this is empty! return filteredVoxelVectorPointer; } bool BoostMaskAccessor::getMaskAt(VoxelGridID aID, core::MaskVoxel& voxel) const { //initialize return voxel voxel.setRelevantVolumeFraction(0); //check if ID is valid if (!_geoInfo.validID(aID)) { return false; } //determine how a given voxel on the dose grid is masked if (_spRelevantVoxelVector) { BoostMaskAccessor::MaskVoxelList::iterator it = _spRelevantVoxelVector->begin(); while (it != _spRelevantVoxelVector->end()) { if ((*it).getVoxelGridID() == aID) { voxel = (*it); return true; } ++it; } } // returns false if mask was not calculated without triggering calculation (otherwise not const!) else { return false; } return false; } bool BoostMaskAccessor::getMaskAt(const VoxelGridIndex3D& aIndex, core::MaskVoxel& voxel) const { //convert VoxelGridIndex3D to VoxelGridID VoxelGridID aVoxelGridID; if (_geoInfo.convert(aIndex, aVoxelGridID)) { return getMaskAt(aVoxelGridID, voxel); } else { return false; } } const core::GeometricInfo& BoostMaskAccessor::getGeometricInfo() const { return _geoInfo; }; } } } \ No newline at end of file diff --git a/code/masks/boost/rttbBoostMaskGenerateMaskVoxelListThread.cpp b/code/masks/boost/rttbBoostMaskGenerateMaskVoxelListThread.cpp new file mode 100644 index 0000000..eca3ca5 --- /dev/null +++ b/code/masks/boost/rttbBoostMaskGenerateMaskVoxelListThread.cpp @@ -0,0 +1,133 @@ + +#include "rttbBoostMaskGenerateMaskVoxelListThread.h" + + +#include "rttbInvalidParameterException.h" +#include + + +namespace rttb +{ + namespace masks + { + namespace boostRedesign + { + BoostMaskGenerateMaskVoxelListThread::BoostMaskGenerateMaskVoxelListThread( + const VoxelIndexVector& aGlobalBoundingBox, + GeometricInfoPointer aGeometricInfo, + const BoostArrayMap& aVoxelizationMap, + double aVoxelizationThickness, + unsigned int aBeginSlice, + unsigned int aEndSlice, + MaskVoxelQueuePointer aResultMaskVoxelQueue) : + _globalBoundingBox(aGlobalBoundingBox), _geometricInfo(aGeometricInfo), + _voxelizationMap(aVoxelizationMap), _voxelizationThickness(aVoxelizationThickness), + _beginSlice(aBeginSlice), _endSlice(aEndSlice), + _resultMaskVoxelQueue(aResultMaskVoxelQueue) + {} + + void BoostMaskGenerateMaskVoxelListThread::operator()() + { + rttb::VoxelGridIndex3D minIndex = _globalBoundingBox.at(0); + rttb::VoxelGridIndex3D maxIndex = _globalBoundingBox.at(1); + int globalBoundingBoxSize0 = maxIndex[0] - minIndex[0] + 1; + int globalBoundingBoxSize1 = maxIndex[1] - minIndex[1] + 1; + + for (unsigned int indexZ = _beginSlice; indexZ < _endSlice; ++indexZ) + { + //calculate weight vector + std::map weightVectorForZ; + calcWeightVector(indexZ, weightVectorForZ); + + //For each x,y, calc sum of all voxelization plane, use weight vector + for (unsigned int x = 0; x < globalBoundingBoxSize0; ++x) + { + for (unsigned int y = 0; y < globalBoundingBoxSize1; ++y) + { + rttb::VoxelGridIndex3D currentIndex; + currentIndex[0] = x + minIndex[0]; + currentIndex[1] = y + minIndex[1]; + currentIndex[2] = indexZ; + rttb::VoxelGridID gridID; + _geometricInfo->convert(currentIndex, gridID); + double volumeFraction = 0; + + std::map::iterator it; + BoostArrayMap::iterator itMap; + + for (it = weightVectorForZ.begin(), itMap = _voxelizationMap.begin(); it != weightVectorForZ.end() + && itMap != _voxelizationMap.end(); ++it, ++itMap) + { + double weight = (*it).second; + + BoostArray2D voxelizationArray = (*itMap).second; + //calc sum of all voxelization plane, use weight + volumeFraction += voxelizationArray[x][y] * weight; + } + + + if (volumeFraction > 1 && (volumeFraction - 1) <= errorConstant) + { + volumeFraction = 1; + } + else if (volumeFraction < 0 || (volumeFraction - 1) > errorConstant) + { + throw rttb::core::InvalidParameterException("Mask calculation failed! The volume fraction should >= 0 and <= 1!"); + } + + //insert mask voxel if volumeFraction > 0 + if (volumeFraction > 0) + { + core::MaskVoxel* maskVoxelPtr = new core::MaskVoxel(gridID, volumeFraction); + _resultMaskVoxelQueue->push(maskVoxelPtr);//push back the mask voxel in structure + } + } + + } + } + + } + + void BoostMaskGenerateMaskVoxelListThread::calcWeightVector(const rttb::VoxelGridID& aIndexZ, + std::map& weightVector) const + { + BoostArrayMap::const_iterator it; + + double indexZMin = aIndexZ - 0.5; + double indexZMax = aIndexZ + 0.5; + + for (it = _voxelizationMap.begin(); it != _voxelizationMap.end(); ++it) + { + double voxelizationPlaneIndexMin = (*it).first - 0.5 * _voxelizationThickness; + double voxelizationPlaneIndexMax = (*it).first + 0.5 * _voxelizationThickness; + double weight = 0; + + if ((voxelizationPlaneIndexMin < indexZMin) && (voxelizationPlaneIndexMax > indexZMin)) + { + if (voxelizationPlaneIndexMax < indexZMax) + { + weight = voxelizationPlaneIndexMax - indexZMin; + } + else + { + weight = 1; + } + } + else if ((voxelizationPlaneIndexMin >= indexZMin) && (voxelizationPlaneIndexMin < indexZMax)) + { + if (voxelizationPlaneIndexMax < indexZMax) + { + weight = _voxelizationThickness; + } + else + { + weight = indexZMax - voxelizationPlaneIndexMin; + } + } + + weightVector.insert(std::pair((*it).first, weight)); + } + } + } + } +} \ No newline at end of file diff --git a/code/masks/boost/rttbBoostMaskGenerateMaskVoxelListThread.h b/code/masks/boost/rttbBoostMaskGenerateMaskVoxelListThread.h new file mode 100644 index 0000000..9172498 --- /dev/null +++ b/code/masks/boost/rttbBoostMaskGenerateMaskVoxelListThread.h @@ -0,0 +1,88 @@ +// ----------------------------------------------------------------------- +// RTToolbox - DKFZ radiotherapy quantitative evaluation library +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See rttbCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/rttb/copyright.html +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notices for more information. +// +//------------------------------------------------------------------------ +/*! +// @file +// @version $Revision: 1127 $ (last changed revision) +// @date $Date: 2015-10-01 13:33:33 +0200 (Do, 01 Okt 2015) $ (last change date) +// @author $Author: hentsch $ (last changed by) +*/ + + +#ifndef __BOOST_MASK_GENERATE_MASK_VOXEL_LIST_H +#define __BOOST_MASK_GENERATE_MASK_VOXEL_LIST_H + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" +#include "rttbMaskVoxel.h" + +#include +#include +#include + +namespace rttb +{ + namespace masks + { + namespace boostRedesign + { + /*! @class BoostMaskGenerateMaskVoxelListThread + * + */ + class BoostMaskGenerateMaskVoxelListThread + { + + public: + typedef ::boost::shared_ptr GeometricInfoPointer; + typedef ::boost::multi_array BoostArray2D; + typedef std::map BoostArrayMap; + typedef ::boost::shared_ptr<::boost::lockfree::queue> MaskVoxelQueuePointer; + typedef std::vector VoxelIndexVector; + + BoostMaskGenerateMaskVoxelListThread(const VoxelIndexVector& aGlobalBoundingBox, + GeometricInfoPointer aGeometricInfo, + const BoostArrayMap& aVoxelizationMap, + double aVoxelizationThickness, + unsigned int aBeginSlice, + unsigned int aEndSlice, + MaskVoxelQueuePointer aResultMaskVoxelQueue); + void operator()(); + + private: + VoxelIndexVector _globalBoundingBox; + GeometricInfoPointer _geometricInfo; + BoostArrayMap _voxelizationMap; + //(for example, the first contour has the double grid index 0.1, the second 0.3, the third 0.5, then the thickness is 0.2) + double _voxelizationThickness; + + unsigned int _beginSlice; + /*! @brief _endSlice is the first index not to be processed (like a end iterator) + */ + unsigned int _endSlice; + + MaskVoxelQueuePointer _resultMaskVoxelQueue; + + /*! @brief For each dose grid index z, calculate the weight vector for each structure contour + */ + void calcWeightVector(const rttb::VoxelGridID& aIndexZ, + std::map& weightVector) const; + }; + + } + + + } +} + +#endif \ No newline at end of file diff --git a/code/masks/boost/rttbBoostMaskRedesign.cpp b/code/masks/boost/rttbBoostMaskRedesign.cpp new file mode 100644 index 0000000..bd1bf60 --- /dev/null +++ b/code/masks/boost/rttbBoostMaskRedesign.cpp @@ -0,0 +1,579 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "rttbBoostMaskRedesign.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbBoostMaskGenerateMaskVoxelListThread.h" +#include "rttbBoostMaskVoxelizationThread.h" + + + +namespace rttb +{ + namespace masks + { + namespace boostRedesign + { + + + BoostMask::BoostMask(BoostMask::GeometricInfoPointer aDoseGeoInfo, + BoostMask::StructPointer aStructure, bool strict, unsigned int numberOfThreads) + : _geometricInfo(aDoseGeoInfo), _structure(aStructure), + _strict(strict), _numberOfThreads(numberOfThreads), + _voxelInStructure(::boost::make_shared()) + { + + _isUpToDate = false; + + if (!_geometricInfo) + { + throw rttb::core::NullPointerException("Error: Geometric info is NULL!"); + } + else if (!_structure) + { + throw rttb::core::NullPointerException("Error: Structure is NULL!"); + } + + if (_numberOfThreads == 0) + { + _numberOfThreads = boost::thread::hardware_concurrency(); + } + + if (_numberOfThreads <= 0) + { + throw rttb::core::InvalidParameterException("Error: the given number of threads must > 0, or detection of the number of hardwore thread is not possible."); + } + + std::cout << "number of threads: " << _numberOfThreads << std::endl; + + } + + BoostMask::MaskVoxelListPointer BoostMask::getRelevantVoxelVector() + { + if (!_isUpToDate) + { + calcMask(); + } + + return _voxelInStructure; + } + + void BoostMask::calcMask() + { + preprocessing(); + voxelization(); + generateMaskVoxelList(); + _isUpToDate = true; + } + + void BoostMask::preprocessing() + { + rttb::PolygonSequenceType polygonSequence = _structure->getStructureVector(); + + //Convert world coordinate polygons to the polygons with geometry coordinate + rttb::PolygonSequenceType geometryCoordinatePolygonVector; + rttb::PolygonSequenceType::iterator it; + rttb::DoubleVoxelGridIndex3D globalMaxGridIndex(std::numeric_limits::min(), + std::numeric_limits::min(), std::numeric_limits::min()); + rttb::DoubleVoxelGridIndex3D globalMinGridIndex(_geometricInfo->getNumColumns(), + _geometricInfo->getNumRows(), 0); + + for (it = polygonSequence.begin(); it != polygonSequence.end(); ++it) + { + PolygonType rttbPolygon = *it; + PolygonType geometryCoordinatePolygon; + + //1. convert polygon to geometry coordinate polygons + //2. calculate global min/max + //3. check if polygon is planar + if (!preprocessingPolygon(rttbPolygon, geometryCoordinatePolygon, globalMinGridIndex, + globalMaxGridIndex, errorConstant)) + { + throw rttb::core::Exception("TiltedMaskPlaneException"); + } + + geometryCoordinatePolygonVector.push_back(geometryCoordinatePolygon); + } + + rttb::VoxelGridIndex3D minIndex = VoxelGridIndex3D(GridIndexType(globalMinGridIndex(0) + 0.5), + GridIndexType(globalMinGridIndex(1) + 0.5), GridIndexType(globalMinGridIndex(2) + 0.5)); + rttb::VoxelGridIndex3D maxIndex = VoxelGridIndex3D(GridIndexType(globalMaxGridIndex(0) + 0.5), + GridIndexType(globalMaxGridIndex(1) + 0.5), GridIndexType(globalMaxGridIndex(2) + 0.5)); + _globalBoundingBox.push_back(minIndex); + _globalBoundingBox.push_back(maxIndex); + + //convert rttb polygon sequence to a map of z index and a vector of boost ring 2d (without holes) + _ringMap = convertRTTBPolygonSequenceToBoostRingMap(geometryCoordinatePolygonVector); + + } + + void BoostMask::voxelization() + { + BoostPolygonMap::iterator it; + + if (_globalBoundingBox.size() < 2) + { + throw rttb::core::InvalidParameterException("Bounding box calculation failed! "); + } + + unsigned int ringMapSize = _ringMap.size(); + + BoostRingMap::iterator itMap; + + unsigned int mapSizeInAThread = _ringMap.size() / _numberOfThreads; + unsigned int count = 0; + unsigned int countThread = 0; + BoostPolygonMap polygonMap; + std::vector polygonMapVector; + + //check donut and convert to a map of z index and a vector of boost polygon 2d (with or without holes) + for (itMap = _ringMap.begin(); itMap != _ringMap.end(); ++itMap) + { + //the vector of all boost 2d polygons with the same z grid index(donut polygon is accepted). + BoostPolygonVector polygonVector = checkDonutAndConvert((*itMap).second); + + if (count == mapSizeInAThread && countThread < (_numberOfThreads - 1)) + { + polygonMapVector.push_back(polygonMap); + polygonMap.clear(); + count = 0; + countThread++; + } + + polygonMap.insert(std::pair((*itMap).first, + polygonVector)); + count++; + + } + + polygonMapVector.push_back(polygonMap); //insert the last one + + //generate voxelization map, multi-threading + ::boost::thread_group threads; + BoostMaskVoxelizationThread::VoxelizationQueuePointer resultQueue + = ::boost::make_shared<::boost::lockfree::queue> + (ringMapSize); + BoostMaskVoxelizationThread::VoxelizationIndexQueuePointer resultIndexQueue + = ::boost::make_shared<::boost::lockfree::queue> + (ringMapSize); + + for (int i = 0; i < polygonMapVector.size(); ++i) + { + BoostMaskVoxelizationThread t(polygonMapVector.at(i), _globalBoundingBox, resultIndexQueue, + resultQueue); + threads.create_thread(t); + } + + threads.join_all(); + + + BoostArray2D* array; + double index; + + //generate voxelization map using resultQueue and resultIndexQueue + while (resultIndexQueue->pop(index) && resultQueue->pop(array)) + { + _voxelizationMap.insert(std::pair(index, *array)); + } + + } + + void BoostMask::generateMaskVoxelList() + { + if (_globalBoundingBox.size() < 2) + { + throw rttb::core::InvalidParameterException("Bounding box calculation failed! "); + } + + //check homogeneus of the voxelization plane (the contours plane) + if (!calcVoxelizationThickness(_voxelizationThickness)) + { + throw rttb::core::InvalidParameterException("Error: The contour plane should be homogeneus!"); + } + + + ::boost::shared_ptr<::boost::lockfree::queue> resultQueue + = ::boost::make_shared<::boost::lockfree::queue> + (_geometricInfo->getNumberOfVoxels()); + + + ::boost::thread_group threads; + + unsigned int sliceNumberInAThread = _geometricInfo->getNumSlices() / _numberOfThreads; + + //generate mask voxel list, multi-threading + for (unsigned int i = 0; i < _numberOfThreads; ++i) + { + unsigned int beginSlice = i * sliceNumberInAThread; + unsigned int endSlice; + + if (i < _numberOfThreads - 1) + { + endSlice = (i + 1) * sliceNumberInAThread; + } + else + { + endSlice = _geometricInfo->getNumSlices(); + } + + + BoostMaskGenerateMaskVoxelListThread t(_globalBoundingBox, _geometricInfo, _voxelizationMap, + _voxelizationThickness, beginSlice, endSlice, + resultQueue); + + threads.create_thread(t); + + } + + threads.join_all(); + + core::MaskVoxel* voxel; + + //generate _voxelInStructure using resultQueue + while (resultQueue->pop(voxel)) + { + _voxelInStructure->push_back(*voxel);//push back the mask voxel in structure + } + + } + + bool BoostMask::preprocessingPolygon(const rttb::PolygonType& aRTTBPolygon, + rttb::PolygonType& geometryCoordinatePolygon, rttb::DoubleVoxelGridIndex3D& minimum, + rttb::DoubleVoxelGridIndex3D& maximum, double aErrorConstant) const + { + + double minZ = _geometricInfo->getNumSlices(); + double maxZ = 0.0; + + for (unsigned int i = 0; i < aRTTBPolygon.size(); i++) + { + rttb::WorldCoordinate3D worldCoordinatePoint = aRTTBPolygon.at(i); + + //convert to geometry coordinate polygon + rttb::DoubleVoxelGridIndex3D geometryCoordinatePoint; + _geometricInfo->worldCoordinateToGeometryCoordinate(worldCoordinatePoint, geometryCoordinatePoint); + + geometryCoordinatePolygon.push_back(geometryCoordinatePoint); + + //calculate the current global min/max + //min and max for x + if (geometryCoordinatePoint(0) < minimum(0)) + { + minimum(0) = geometryCoordinatePoint(0); + } + + if (geometryCoordinatePoint(0) > maximum(0)) + { + maximum(0) = geometryCoordinatePoint(0); + } + + //min and max for y + if (geometryCoordinatePoint(1) < minimum(1)) + { + minimum(1) = geometryCoordinatePoint(1); + } + + if (geometryCoordinatePoint(1) > maximum(1)) + { + maximum(1) = geometryCoordinatePoint(1); + } + + //min and max for z + if (geometryCoordinatePoint(2) < minimum(2)) + { + minimum(2) = geometryCoordinatePoint(2); + } + + if (geometryCoordinatePoint(2) > maximum(2)) + { + maximum(2) = geometryCoordinatePoint(2); + } + + //check planar + if (geometryCoordinatePoint(2) < minZ) + { + minZ = geometryCoordinatePoint(2); + } + + if (geometryCoordinatePoint(2) > maxZ) + { + maxZ = geometryCoordinatePoint(2); + } + + } + + return (abs(maxZ - minZ) <= aErrorConstant); + } + + + BoostMask::BoostRing2D BoostMask::convertRTTBPolygonToBoostRing(const rttb::PolygonType& + aRTTBPolygon) const + { + BoostMask::BoostRing2D polygon2D; + BoostPoint2D firstPoint; + + for (unsigned int i = 0; i < aRTTBPolygon.size(); i++) + { + rttb::WorldCoordinate3D rttbPoint = aRTTBPolygon.at(i); + BoostPoint2D boostPoint(rttbPoint[0], rttbPoint[1]); + + if (i == 0) + { + firstPoint = boostPoint; + } + + ::boost::geometry::append(polygon2D, boostPoint); + } + + ::boost::geometry::append(polygon2D, firstPoint); + return polygon2D; + } + + BoostMask::BoostRingMap BoostMask::convertRTTBPolygonSequenceToBoostRingMap( + const rttb::PolygonSequenceType& aRTTBPolygonVector) + { + rttb::PolygonSequenceType::const_iterator it; + BoostMask::BoostRingMap aRingMap; + + for (it = aRTTBPolygonVector.begin(); it != aRTTBPolygonVector.end(); ++it) + { + rttb::PolygonType rttbPolygon = *it; + double zIndex = rttbPolygon.at(0)[2];//get the first z index of the polygon + bool isFirstZ = true; + + if (!aRingMap.empty()) + { + BoostMask::BoostRingMap::iterator findIt = findNearestKey(aRingMap, zIndex, errorConstant); + + //if the z index is found (same slice), add the polygon to vector + if (findIt != aRingMap.end()) + { + //BoostRingVector ringVector = ; + (*findIt).second.push_back(convertRTTBPolygonToBoostRing(rttbPolygon)); + isFirstZ = false; + } + } + + //if it is the first z index in the map, insert vector with the polygon + if (isFirstZ) + { + BoostRingVector ringVector; + ringVector.push_back(convertRTTBPolygonToBoostRing(rttbPolygon)); + aRingMap.insert(std::pair(zIndex, ringVector)); + } + + } + + return aRingMap; + } + + BoostMask::BoostRingMap::iterator BoostMask::findNearestKey(BoostMask::BoostRingMap& + aBoostRingMap, double aIndex, double aErrorConstant) + { + BoostMask::BoostRingMap::iterator find = aBoostRingMap.find(aIndex); + + //if find a key equivalent to aIndex, found + if (find != aBoostRingMap.end()) + { + return find; + } + else + { + BoostMask::BoostRingMap::iterator lowerBound = aBoostRingMap.lower_bound(aIndex); + + //if all keys go before aIndex, check the last key + if (lowerBound == aBoostRingMap.end()) + { + lowerBound = --aBoostRingMap.end(); + } + + //if the lower bound very close to aIndex, found + if (abs((*lowerBound).first - aIndex) <= aErrorConstant) + { + return lowerBound; + } + else + { + //if the lower bound is the beginning, not found + if (lowerBound == aBoostRingMap.begin()) + { + return aBoostRingMap.end(); + } + else + { + BoostMask::BoostRingMap::iterator lowerBound1 = --lowerBound;//the key before the lower bound + + //if the key before the lower bound very close to a Index, found + if (abs((*lowerBound1).first - aIndex) <= aErrorConstant) + { + return lowerBound1; + } + //else, not found + else + { + return aBoostRingMap.end(); + } + } + } + + } + } + + BoostMask::BoostPolygonVector BoostMask::checkDonutAndConvert(const BoostMask::BoostRingVector& + aRingVector) const + { + //check donut + BoostMask::BoostRingVector::const_iterator it1; + BoostMask::BoostRingVector::const_iterator it2; + BoostMask::BoostPolygonVector boostPolygonVector; + std::vector donutIndexVector;//store the outer and inner ring index + BoostMask::BoostPolygonVector donutVector;//store new generated donut polygon + + //Get donut index and donut polygon + unsigned int index1 = 0; + + for (it1 = aRingVector.begin(); it1 != aRingVector.end(); it1++, index1++) + { + bool it1IsDonut = false; + + //check if the ring is already determined as a donut + for (unsigned int i = 0; i < donutIndexVector.size(); i++) + { + if (donutIndexVector.at(i) == index1) + { + it1IsDonut = true; + break; + } + } + + //if not jet, check now + if (!it1IsDonut) + { + bool it2IsDonut = false; + unsigned int index2 = 0; + + for (it2 = aRingVector.begin(); it2 != aRingVector.end(); it2++, index2++) + { + if (it2 != it1) + { + BoostMask::BoostPolygon2D polygon2D; + + if (::boost::geometry::within(*it1, *it2)) + { + ::boost::geometry::append(polygon2D, *it2);//append an outer ring to the polygon + ::boost::geometry::interior_rings(polygon2D).resize(1);//create an interior ring + ::boost::geometry::append(polygon2D, *it1, 0);//append a ring to the interior ring + it2IsDonut = true; + } + //if donut + else if (::boost::geometry::within(*it2, *it1)) + { + ::boost::geometry::append(polygon2D, *it1);//append an outer ring to the polygon + ::boost::geometry::interior_rings(polygon2D).resize(1);//create an interior ring + ::boost::geometry::append(polygon2D, *it2, 0);//append a ring to the interior ring + it2IsDonut = true; + } + + if (it2IsDonut) + { + donutIndexVector.push_back(index1); + donutIndexVector.push_back(index2); + donutVector.push_back(polygon2D);//store donut polygon + break;//Only store the first donut! + } + } + } + } + } + + //Store no donut polygon to boostPolygonVector + index1 = 0; + + for (it1 = aRingVector.begin(); it1 != aRingVector.end(); it1++, index1++) + { + bool it1IsDonut = false; + + //check if the ring is the outer or inner of a donut + for (unsigned int i = 0; i < donutIndexVector.size(); i++) + { + if (donutIndexVector.at(i) == index1) + { + it1IsDonut = true; + break; + } + } + + if (!it1IsDonut) + { + BoostMask::BoostPolygon2D polygon2D; + ::boost::geometry::append(polygon2D, *it1); + boostPolygonVector.push_back(polygon2D);//insert the ring, which is not a part of donut + } + } + + //Append donut polygon to boostPolygonVector + BoostMask::BoostPolygonVector::iterator itDonut; + + for (itDonut = donutVector.begin(); itDonut != donutVector.end(); itDonut++) + { + boostPolygonVector.push_back(*itDonut);//append donuts + } + + return boostPolygonVector; + } + + bool BoostMask::calcVoxelizationThickness(double& aThickness) const + { + BoostArrayMap::const_iterator it = _voxelizationMap.begin(); + BoostArrayMap::const_iterator it2 = ++_voxelizationMap.begin(); + + if (_voxelizationMap.size() <= 1) + { + aThickness = 1; + return true; + } + + double thickness = 0; + + for (; + it != _voxelizationMap.end(), it2 != _voxelizationMap.end(); ++it, ++it2) + { + if (thickness == 0) + { + thickness = (*it2).first - (*it).first; + } + else + { + //if no homogeneous, return false + if (thickness != ((*it2).first - (*it).first)) + { + return false; + } + } + + } + + if (thickness != 0) + { + aThickness = thickness; + } + else + { + aThickness = 1; + } + + return true; + } + } + } +} diff --git a/code/masks/boost/rttbBoostMaskRedesign.h b/code/masks/boost/rttbBoostMaskRedesign.h new file mode 100644 index 0000000..4844a4c --- /dev/null +++ b/code/masks/boost/rttbBoostMaskRedesign.h @@ -0,0 +1,210 @@ +// ----------------------------------------------------------------------- +// RTToolbox - DKFZ radiotherapy quantitative evaluation library +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See rttbCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/rttb/copyright.html +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notices for more information. +// +//------------------------------------------------------------------------ +/*! +// @file +// @version $Revision: 1127 $ (last changed revision) +// @date $Date: 2015-10-01 13:33:33 +0200 (Do, 01 Okt 2015) $ (last change date) +// @author $Author: hentsch $ (last changed by) +*/ + + +#ifndef __BOOST_MASK_R_H +#define __BOOST_MASK_R_H + +#include "rttbBaseType.h" +#include "rttbStructure.h" +#include "rttbGeometricInfo.h" +#include "rttbMaskVoxel.h" +#include "rttbMaskAccessorInterface.h" + +#include +#include +#include +#include +#include +#include + +namespace rttb +{ + namespace masks + { + namespace boostRedesign + { + /*! @class BoostMask + * @brief Implementation of voxelization using boost::geometry. + * @attention If "strict" is set to true, an exception will be thrown when the given structure has self intersection. + * (A structure without self interseciton means all contours of the structure have no self intersection, and + * the polygons on the same slice have no intersection between each other, unless the case of a donut. A donut is accepted.) + * If "strict" is set to false, debug information will be displayed when the given structure has self intersection. Self intersections will be ignored + * and the mask will be calculated, however, it may cause errors in the mask results. + */ + class BoostMask + { + + public: + typedef ::boost::shared_ptr GeometricInfoPointer; + typedef core::Structure::StructTypePointer StructPointer; + typedef core::MaskAccessorInterface::MaskVoxelList MaskVoxelList; + typedef core::MaskAccessorInterface::MaskVoxelListPointer MaskVoxelListPointer; + + /*! @brief Constructor + * @exception rttb::core::NullPointerException thrown if aDoseGeoInfo or aStructure is NULL + * @param strict indicates whether to allow self intersection in the structure. If it is set to true, an exception will be thrown when the given structure has self intersection. + * @param numberOfThreads number of threads used for voxelization. default value 0 means automatic detection, using the number of Hardware thread/cores + * @exception InvalidParameterException thrown if strict is true and the structure has self intersections + */ + BoostMask(GeometricInfoPointer aDoseGeoInfo, StructPointer aStructure, + bool strict = true, unsigned int numberOfThreads = 0); + + /*! @brief Generate mask and return the voxels in the mask + * @exception rttb::core::InvalidParameterException thrown if the structure has self intersections + */ + MaskVoxelListPointer getRelevantVoxelVector(); + + private: + typedef ::boost::geometry::model::d2::point_xy BoostPoint2D; + typedef ::boost::geometry::model::polygon< ::boost::geometry::model::d2::point_xy > + BoostPolygon2D; + typedef ::boost::geometry::model::ring< ::boost::geometry::model::d2::point_xy > + BoostRing2D; + typedef std::deque BoostPolygonDeque; + typedef std::vector BoostRingVector;//polygon without holes + typedef std::vector BoostPolygonVector;//polygon with or without holes + typedef std::vector VoxelIndexVector; + typedef std::map + BoostPolygonMap;//map of the z index with the vector of boost 2d polygon + typedef std::map + BoostRingMap;//map of the z index with the vector of boost 2d ring + typedef ::boost::multi_array BoostArray2D; + typedef std::map BoostArrayMap; + typedef ::boost::array BoostArray1D; + typedef std::vector > WeightVector; + + GeometricInfoPointer _geometricInfo; + + StructPointer _structure; + + /*! @brief The map of z index and a vector of boost ring 2d (without holes) + * Key: the double z grid index + * Value: the vector of boost ring 2d (without holes) + */ + BoostRingMap _ringMap; + + /*! @brief The min and max index of the global bounding box. + * The first index has the minimum for x/y/z of the global bounding box. + * The second index has the maximum for x/y/z of the global bounding index. + */ + VoxelIndexVector _globalBoundingBox; + + /*! @brief The voxelization map + * key: the converted double z grid index of a contour plane + * value: the 2d mask, array[i][j] = the mask value of the position (i,j) in the global bounding box, + * i: 0 - (_globalBoundingBoxSize0-1), j: 0 - (_globalBoundingBoxSize1-1) + */ + BoostArrayMap _voxelizationMap; + + //The thickness of the voxelization plane (the contour plane), in double dose grid index + //(for example, the first contour has the double grid index 0.1, the second 0.3, the third 0.5, then the thickness is 0.2) + double _voxelizationThickness; + + bool _strict; + + //vector of the MaskVoxel inside the structure + MaskVoxelListPointer _voxelInStructure; + + /*! @brief If the mask is up to date + */ + bool _isUpToDate; + + + /*! @brief The number of threads + */ + unsigned int _numberOfThreads; + + /*! @brief Voxelization and generate mask + */ + void calcMask(); + + /*! @brief The preprocessing step, wich consists of the following logic and Sub setps: + * For all contours in a struct: + * 1) Transfer the contour polygons into boost::geometry structures + * 1a) Convert the contur points from world coordinates into geometry coordinates. + * 1b) get min and max for x/y/z of a contour + * 2) Tilt check: if difference of z_min and z_max is larger then a tolerance value -> there is a tilt. Throw rttb::TiltedMaskPlaneException. + * 3) Get struct-bounding-box: get x_min_struct, y_min_struct, x_max_struct, y_max_struct to define the bounding box that containes all contours of a struct in x-y-dimensions. + */ + void preprocessing(); + + /*! @brief The voxelization step, wich computes the voxelization planes (in x/y) for all contours of an struct. + + * For each contour (that is in the z-Range of the reference geometry) of the struct: + * 1) Allocate result array (voxelization plane) based on the bounding box (see Preprocessing Step 3) + * 2) Generate voxelization plane for the contour (based on the x-y-raster of the reference geometry). + * 3) Add result Array (key is the z-Value of the contour) + */ + void voxelization(); + + /*! @final mask voxel Generation step which transfers the voxilization planes into the (z-)geometry of the reference geometry. + * It consists of following Sub steps : + * For all "slices" in the reference geometry : + * 1) generate weight vector for all voxelization planes for a given z - value of a slice + * Itterate over the bounding box of a struct.For each voxel : + * 2) Compute weighted sum of all voxelization planes(use weight vector, step 1) + * 2a) If sum > 0 : Add mask voxel for the current x / y(inner Loop) and z value(outer Loop). + * 3) return mask voxel list. + */ + void generateMaskVoxelList(); + + /*! @brief Convert the rttb polygon with world corrdinate to the rttb polygon with double geometry coordinate, calculate the current min/max + * and check if the polygon is planar + * @param minimum the current global minimum + * @param maximum the current global maximum + * @return Return true if the polygon is planar, which means that the minminal and maximal z-coordinate of the polygon is not larger than a error constant + */ + bool preprocessingPolygon(const rttb::PolygonType& aRTTBPolygon, + rttb::PolygonType& geometryCoordinatePolygon, rttb::DoubleVoxelGridIndex3D& minimum, + rttb::DoubleVoxelGridIndex3D& maximum, double aErrorConstant) const; + + /*! @brief Convert a rttb 3d polygon to a 2d boost ring*/ + BoostRing2D convertRTTBPolygonToBoostRing(const rttb::PolygonType& aRTTBPolygon) const; + + /*! @brief Convert a rttb 3d polygon to a map of z index with a vector of boost 2d ring, because of tilt check use the first z index of the polygon as the map key*/ + BoostRingMap convertRTTBPolygonSequenceToBoostRingMap(const rttb::PolygonSequenceType& + aRTTBPolygonVector); + + /*! @brief Find the key with error constant to aIndex + * @pre aBoostRingMap should not be empty + * @return Return aBoostRingMap.end() if the key is not found + */ + BoostMask::BoostRingMap::iterator findNearestKey(BoostMask::BoostRingMap& aBoostRingMap, + double aIndex, double aErrorConstant); + + /*! @brief If 2 rings in the vector build a donut, convert the 2 rings to a donut polygon, other rings unchanged*/ + BoostPolygonVector checkDonutAndConvert(const BoostRingVector& aRingVector) const; + + /*! @brief Calculate the voxelization thickness. + Return false, if the voxelization plane is not homogeneous + */ + bool calcVoxelizationThickness(double& aThickness) const; + + }; + + } + + + } +} + +#endif diff --git a/code/masks/boost/rttbBoostMaskAccessor.cpp b/code/masks/boost/rttbBoostMaskRedesignAccessor.cpp similarity index 88% copy from code/masks/boost/rttbBoostMaskAccessor.cpp copy to code/masks/boost/rttbBoostMaskRedesignAccessor.cpp index 9b30567..bf8fd80 100644 --- a/code/masks/boost/rttbBoostMaskAccessor.cpp +++ b/code/masks/boost/rttbBoostMaskRedesignAccessor.cpp @@ -1,163 +1,164 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file -// @version $Revision$ (last changed revision) -// @date $Date$ (last change date) -// @author $Author$ (last changed by) +// @version $Revision: 1127 $ (last changed revision) +// @date $Date: 2015-10-01 13:33:33 +0200 (Do, 01 Okt 2015) $ (last change date) +// @author $Author: hentsch $ (last changed by) */ -#include "rttbBoostMaskAccessor.h" -#include "rttbBoostMask.h" +#include "rttbBoostMaskRedesignAccessor.h" +#include "rttbBoostMaskRedesign.h" #include #include #include #include namespace rttb { namespace masks { - namespace boost + namespace boostRedesign { BoostMaskAccessor::BoostMaskAccessor(StructTypePointer aStructurePointer, const core::GeometricInfo& aGeometricInfo, bool strict) : _spStructure(aStructurePointer), _geoInfo(aGeometricInfo), _strict(strict) { _spRelevantVoxelVector = MaskVoxelListPointer(); //generate new structure set uid ::boost::uuids::uuid id; ::boost::uuids::random_generator generator; id = generator(); std::stringstream ss; ss << id; _maskUID = "BoostMask_" + ss.str(); } BoostMaskAccessor::~BoostMaskAccessor() { }; void BoostMaskAccessor::updateMask() { MaskVoxelList newRelevantVoxelVector; if (_spRelevantVoxelVector) { return; // already calculated } - BoostMask mask(::boost::make_shared(_geoInfo), _spStructure, _strict); + BoostMask mask(::boost::make_shared(_geoInfo), + _spStructure, _strict); _spRelevantVoxelVector = mask.getRelevantVoxelVector(); return; } BoostMaskAccessor::MaskVoxelListPointer BoostMaskAccessor::getRelevantVoxelVector() { // if not already generated start voxelization here updateMask(); return _spRelevantVoxelVector; } BoostMaskAccessor::MaskVoxelListPointer BoostMaskAccessor::getRelevantVoxelVector( float lowerThreshold) { MaskVoxelListPointer filteredVoxelVectorPointer(new MaskVoxelList); updateMask(); // filter relevant voxels BoostMaskAccessor::MaskVoxelList::iterator it = _spRelevantVoxelVector->begin(); while (it != _spRelevantVoxelVector->end()) { if ((*it).getRelevantVolumeFraction() > lowerThreshold) { filteredVoxelVectorPointer->push_back(*it); } ++it; } // if mask calculation was not successful this is empty! return filteredVoxelVectorPointer; } bool BoostMaskAccessor::getMaskAt(VoxelGridID aID, core::MaskVoxel& voxel) const { //initialize return voxel voxel.setRelevantVolumeFraction(0); //check if ID is valid if (!_geoInfo.validID(aID)) { return false; } //determine how a given voxel on the dose grid is masked if (_spRelevantVoxelVector) { BoostMaskAccessor::MaskVoxelList::iterator it = _spRelevantVoxelVector->begin(); while (it != _spRelevantVoxelVector->end()) { if ((*it).getVoxelGridID() == aID) { voxel = (*it); return true; } ++it; } } // returns false if mask was not calculated without triggering calculation (otherwise not const!) else { return false; } return false; } bool BoostMaskAccessor::getMaskAt(const VoxelGridIndex3D& aIndex, core::MaskVoxel& voxel) const { //convert VoxelGridIndex3D to VoxelGridID VoxelGridID aVoxelGridID; if (_geoInfo.convert(aIndex, aVoxelGridID)) { return getMaskAt(aVoxelGridID, voxel); } else { return false; } } const core::GeometricInfo& BoostMaskAccessor::getGeometricInfo() const { return _geoInfo; }; } } } \ No newline at end of file diff --git a/code/masks/boost/rttbBoostMaskRedesignAccessor.h b/code/masks/boost/rttbBoostMaskRedesignAccessor.h new file mode 100644 index 0000000..850c36a --- /dev/null +++ b/code/masks/boost/rttbBoostMaskRedesignAccessor.h @@ -0,0 +1,128 @@ +// ----------------------------------------------------------------------- +// RTToolbox - DKFZ radiotherapy quantitative evaluation library +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See rttbCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/rttb/copyright.html +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notices for more information. +// +//------------------------------------------------------------------------ +/*! +// @file +// @version $Revision: 1127 $ (last changed revision) +// @date $Date: 2015-10-01 13:33:33 +0200 (Do, 01 Okt 2015) $ (last change date) +// @author $Author: hentsch $ (last changed by) +*/ +#ifndef __BOOST_MASK_R_ACCESSOR__H +#define __BOOST_MASK_R_ACCESSOR__H + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" +#include "rttbMaskVoxel.h" +#include "rttbMaskAccessorInterface.h" +#include "rttbGenericDoseIterator.h" +#include "rttbStructure.h" + +#include + + +namespace rttb +{ + namespace masks + { + namespace boostRedesign + { + /*! @class BoostMaskAccessor + * @brief Using the voxelization based on boost::geometry and generate the mask accessor. + * @attention If "strict" is set to true, an exception will be thrown when the given structure has self intersection. + * (A structure without self interseciton means all contours of the structure have no self intersection, and + * the polygons on the same slice have no intersection between each other, unless the case of a donut. A donut is accepted.) + * If "strict" is set to false, debug information will be displayed when the given structure has self intersection. Self intersections will be ignored + * and the mask will be calculated, however, it may cause errors in the mask results. + */ + class BoostMaskAccessor: public core::MaskAccessorInterface + { + public: + typedef core::MaskAccessorInterface::MaskVoxelList MaskVoxelList; + typedef core::MaskAccessorInterface::MaskVoxelListPointer MaskVoxelListPointer; + + typedef core::Structure::StructTypePointer StructTypePointer; + + private: + + core::GeometricInfo _geoInfo; + + /*! vector containing list of mask voxels*/ + MaskVoxelListPointer _spRelevantVoxelVector; + + StructTypePointer _spStructure; + + IDType _maskUID; + + bool _strict; + + + public: + + + /*! @brief Constructor with a structure pointer and a geometric info pointer + * @param aStructurePointer smart pointer of the structure + * @param aGeometricInfoPtr smart pointer of the geometricinfo of the dose + * @param strict indicates whether to allow self intersection in the structure. If it is set to true, an exception will be thrown when the given structure has self intersection. + * @exception InvalidParameterException thrown if strict is true and the structure has self intersections + */ + BoostMaskAccessor(StructTypePointer aStructurePointer, const core::GeometricInfo& aGeometricInfo, + bool strict = true); + + /*! @brief destructor*/ + ~BoostMaskAccessor(); + + /*! @brief voxelization of the given structures using boost algorithms*/ + void updateMask(); + + /*! @brief get vector containing all relevant voxels that are inside the given structure*/ + MaskVoxelListPointer getRelevantVoxelVector(); + + /*! @brief get vector containing all relevant voxels that have a relevant volume above the given threshold and are inside the given structure*/ + MaskVoxelListPointer getRelevantVoxelVector(float lowerThreshold); + + /*!@brief determine how a given voxel on the dose grid is masked + * @param aID ID of the voxel in grid. + * @param voxel Reference to the voxel. + * @post after a valid call voxel containes the information of the specified grid voxel. If aID is not valid, voxel values are undefined. + * The relevant volume fraction will be set to zero. + * @return Indicates of the voxel exists and therefore if parameter voxel containes valid values.*/ + bool getMaskAt(const VoxelGridID aID, core::MaskVoxel& voxel) const; + + /*!@brief determine how a given voxel on the dose grid is masked + * @param aIndex 3d index of the voxel in grid. + * @param voxel Reference to the voxel. + * @return Indicates of the voxel exists and therefore if parameter voxel containes valid values.*/ + bool getMaskAt(const VoxelGridIndex3D& aIndex, core::MaskVoxel& voxel) const; + + /*! @brief give access to GeometricInfo*/ + const core::GeometricInfo& getGeometricInfo() const; + + /* @ brief is true if dose is on a homogeneous grid + * @remark Inhomogeneous grids are not supported at the moment, but if they will be supported in the future the interface does not need to change.*/ + bool isGridHomogeneous() const + { + return true; + }; + + IDType getMaskUID() const + { + return _maskUID; + }; + + }; + } + } +} + +#endif diff --git a/code/masks/boost/rttbBoostMaskVoxelizationThread.cpp b/code/masks/boost/rttbBoostMaskVoxelizationThread.cpp new file mode 100644 index 0000000..2d218b1 --- /dev/null +++ b/code/masks/boost/rttbBoostMaskVoxelizationThread.cpp @@ -0,0 +1,135 @@ +#include "rttbBoostMaskVoxelizationThread.h" + +namespace rttb +{ + namespace masks + { + namespace boostRedesign + { + BoostMaskVoxelizationThread::BoostMaskVoxelizationThread(const BoostPolygonMap& APolygonMap, + const VoxelIndexVector& aGlobalBoundingBox, VoxelizationIndexQueuePointer aResultIndexQueue, + VoxelizationQueuePointer aVoxelizationQueue) : _geometryCoordinateBoostPolygonMap(APolygonMap), + _globalBoundingBox(aGlobalBoundingBox), _resultIndexQueue(aResultIndexQueue), + _resultVoxelizationQueue(aVoxelizationQueue) + { + } + + + void BoostMaskVoxelizationThread::operator()() + { + rttb::VoxelGridIndex3D minIndex = _globalBoundingBox.at(0); + rttb::VoxelGridIndex3D maxIndex = _globalBoundingBox.at(1); + int globalBoundingBoxSize0 = maxIndex[0] - minIndex[0] + 1; + int globalBoundingBoxSize1 = maxIndex[1] - minIndex[1] + 1; + + BoostPolygonMap::iterator it; + + for (it = _geometryCoordinateBoostPolygonMap.begin(); + it != _geometryCoordinateBoostPolygonMap.end(); ++it) + { + BoostArray2D* maskArray = new BoostArray2D( + boost::extents[globalBoundingBoxSize0][globalBoundingBoxSize1]); + + BoostPolygonVector boostPolygonVec = (*it).second; + + for (unsigned int x = 0; x < globalBoundingBoxSize0; ++x) + { + for (unsigned int y = 0; y < globalBoundingBoxSize1; ++y) + { + rttb::VoxelGridIndex3D currentIndex; + currentIndex[0] = x + minIndex[0]; + currentIndex[1] = y + minIndex[1]; + currentIndex[2] = 0; + + //Get intersection polygons of the dose voxel and the structure + BoostPolygonDeque polygons = getIntersections(currentIndex, boostPolygonVec); + + //Calc areas of all intersection polygons + double volumeFraction = calcArea(polygons); + + if (volumeFraction > 1 && (volumeFraction - 1) <= errorConstant) + { + volumeFraction = 1; + } + else if (volumeFraction < 0 || (volumeFraction - 1) > errorConstant) + { + throw rttb::core::InvalidParameterException("Mask calculation failed! The volume fraction should >= 0 and <= 1!"); + } + + (*maskArray)[x][y] = volumeFraction; + } + } + + //insert into voxelization map + _resultIndexQueue->push((*it).first); + _resultVoxelizationQueue->push(maskArray); + } + + } + + /*Get intersection polygons of the contour and a voxel polygon*/ + BoostMaskVoxelizationThread::BoostPolygonDeque BoostMaskVoxelizationThread::getIntersections( + const rttb::VoxelGridIndex3D& + aVoxelIndex3D, const BoostPolygonVector& intersectionSlicePolygons) + { + BoostMaskVoxelizationThread::BoostPolygonDeque polygonDeque; + + BoostRing2D voxelPolygon = get2DContour(aVoxelIndex3D); + ::boost::geometry::correct(voxelPolygon); + + BoostPolygonVector::const_iterator it; + + for (it = intersectionSlicePolygons.begin(); it != intersectionSlicePolygons.end(); ++it) + { + BoostPolygon2D contour = *it; + ::boost::geometry::correct(contour); + BoostPolygonDeque intersection; + ::boost::geometry::intersection(voxelPolygon, contour, intersection); + + polygonDeque.insert(polygonDeque.end(), intersection.begin(), intersection.end()); + } + + return polygonDeque; + } + + BoostMaskVoxelizationThread::BoostRing2D BoostMaskVoxelizationThread::get2DContour( + const rttb::VoxelGridIndex3D& aVoxelGrid3D) + { + BoostRing2D polygon; + + + BoostPoint2D point1(aVoxelGrid3D[0] - 0.5, aVoxelGrid3D[1] - 0.5); + ::boost::geometry::append(polygon, point1); + + BoostPoint2D point2(aVoxelGrid3D[0] + 0.5, aVoxelGrid3D[1] - 0.5); + ::boost::geometry::append(polygon, point2); + + BoostPoint2D point3(aVoxelGrid3D[0] + 0.5, aVoxelGrid3D[1] + 0.5); + ::boost::geometry::append(polygon, point3); + + BoostPoint2D point4(aVoxelGrid3D[0] - 0.5, aVoxelGrid3D[1] + 0.5); + ::boost::geometry::append(polygon, point4); + + ::boost::geometry::append(polygon, point1); + + return polygon; + + } + + /*Calculate the intersection area*/ + double BoostMaskVoxelizationThread::calcArea(const BoostPolygonDeque& aPolygonDeque) + { + double area = 0; + + BoostPolygonDeque::const_iterator it; + + for (it = aPolygonDeque.begin(); it != aPolygonDeque.end(); ++it) + { + area += ::boost::geometry::area(*it); + } + + return area; + } + } + } +} \ No newline at end of file diff --git a/code/masks/boost/rttbBoostMaskVoxelizationThread.h b/code/masks/boost/rttbBoostMaskVoxelizationThread.h new file mode 100644 index 0000000..1bba438 --- /dev/null +++ b/code/masks/boost/rttbBoostMaskVoxelizationThread.h @@ -0,0 +1,114 @@ +// ----------------------------------------------------------------------- +// RTToolbox - DKFZ radiotherapy quantitative evaluation library +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See rttbCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/rttb/copyright.html +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notices for more information. +// +//------------------------------------------------------------------------ +/*! +// @file +// @version $Revision: 1127 $ (last changed revision) +// @date $Date: 2015-10-01 13:33:33 +0200 (Do, 01 Okt 2015) $ (last change date) +// @author $Author: hentsch $ (last changed by) +*/ + + +#ifndef __BOOST_MASK_VOXELIZATION_THREAD_H +#define __BOOST_MASK_VOXELIZATION_THREAD_H + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" +#include "rttbMaskVoxel.h" +#include "rttbInvalidParameterException.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rttb +{ + namespace masks + { + namespace boostRedesign + { + /*! @class BoostMaskGenerateMaskVoxelListThread + * + */ + class BoostMaskVoxelizationThread + { + + public: + typedef ::boost::geometry::model::polygon< ::boost::geometry::model::d2::point_xy > + BoostPolygon2D; + typedef std::vector BoostPolygonVector;//polygon with or without holes + typedef std::map + BoostPolygonMap;//map of the z index with the vector of boost 2d polygon + typedef std::vector VoxelIndexVector; + typedef ::boost::multi_array BoostArray2D; + typedef ::boost::shared_ptr<::boost::lockfree::queue> + VoxelizationQueuePointer; + typedef ::boost::shared_ptr<::boost::lockfree::queue> + VoxelizationIndexQueuePointer; + + BoostMaskVoxelizationThread(const BoostPolygonMap& APolygonMap, + const VoxelIndexVector& aGlobalBoundingBox, VoxelizationIndexQueuePointer aResultIndexQueue, + VoxelizationQueuePointer aVoxelizationQueue); + + + void operator()(); + + + private: + + + typedef std::deque BoostPolygonDeque; + typedef ::boost::geometry::model::ring< ::boost::geometry::model::d2::point_xy > + BoostRing2D; + typedef ::boost::geometry::model::d2::point_xy BoostPoint2D; + + + BoostPolygonMap _geometryCoordinateBoostPolygonMap; + VoxelIndexVector _globalBoundingBox; + VoxelizationQueuePointer _resultVoxelizationQueue; + VoxelizationIndexQueuePointer _resultIndexQueue; + + /*! @brief Get intersection polygons of the contour and a voxel polygon + * @param aVoxelIndex3D The 3d grid index of the voxel + * @param intersectionSlicePolygons The polygons of the slice intersecting the voxel + * @return Return all intersetion polygons of the structure and the voxel + */ + static BoostPolygonDeque getIntersections(const rttb::VoxelGridIndex3D& aVoxelIndex3D, + const BoostPolygonVector& intersectionSlicePolygons); + + /*! @brief Get the voxel 2d contour polygon in geometry coordinate*/ + static BoostRing2D get2DContour(const rttb::VoxelGridIndex3D& aVoxelGrid3D); + + /*! @brief Calculate the area of all polygons + * @param aPolygonDeque The deque of polygons + * @return Return the area of all polygons + */ + static double calcArea(const BoostPolygonDeque& aPolygonDeque); + }; + + } + + + } +} + +#endif \ No newline at end of file diff --git a/code/models/rttbBioModelScatterPlots.cpp b/code/models/rttbBioModelScatterPlots.cpp index 0918a14..e3430e2 100644 --- a/code/models/rttbBioModelScatterPlots.cpp +++ b/code/models/rttbBioModelScatterPlots.cpp @@ -1,219 +1,219 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include #include "rttbBioModelScatterPlots.h" #include "rttbInvalidParameterException.h" namespace rttb { namespace models { /* Initiate Random Number generator with current time */ boost::random::mt19937 rng(static_cast(time(0))); /* Generate random number between 0 and 1 */ boost::random::uniform_01 uniDist(rng); ScatterPlotType getScatterPlotVary1Parameter(BioModel& aModel, int aParamId, BioModelParamType aMean, BioModelParamType aVariance, DoseTypeGy aNormalisationDose, int numberOfPoints, DoseTypeGy aMinDose, DoseTypeGy aMaxDose) { ScatterPlotType scatterPlotData; if (aVariance == 0) { //set to small positive value to avoid negative infinity! aVariance = 1e-30; } if (aMaxDose <= aMinDose) { throw core::InvalidParameterException("Parameter invalid: aMaxDose must be > aMinDose!"); } if (aNormalisationDose <= 0) { throw core::InvalidParameterException("Parameter invalid: aNormalisationDose must be > 0!"); } /* Choose Normal Distribution */ boost::random::normal_distribution gaussian_dist(0, aVariance); /* Create a Gaussian Random Number generator * by binding with previously defined * normal distribution object */ boost::random::variate_generator > generator( rng, gaussian_dist); int i = 0; while (i < numberOfPoints) { double paramValue, probability; double randomValue = generator(); paramValue = randomValue + aMean; probability = normal_pdf(randomValue, aVariance); if (probability > 1) { probability = 1; } //randomly select a dose between aMinDose and aMaxDose double dose = uniDist() * (aMaxDose - aMinDose) + aMinDose; if (probability > 0) { try { aModel.setParameterByID(aParamId, paramValue); aModel.init(dose / aNormalisationDose); double value = aModel.getValue(); std::pair modelProbPair = std::make_pair(value, probability); scatterPlotData.insert(std::pair >(dose, modelProbPair)); i++; } catch (core::InvalidParameterException& /*e*/) { //repeat evaluation to guarantee the correct number of scatter values continue; } } } return scatterPlotData; } double normal_pdf(double aValue, double aVariance) { - static const float inv_sqrt_2pi = 0.3989422804014327; + static const double inv_sqrt_2pi = 0.3989422804014327; double a = (aValue) / aVariance; return inv_sqrt_2pi / aVariance * std::exp(-0.5f * a * a); } ScatterPlotType getScatterPlotVaryParameters(BioModel& aModel, std::vector aParamIdVec, BioModel::ParamVectorType aMeanVec, BioModel::ParamVectorType aVarianceVec, DoseTypeGy aNormalisationDose, int numberOfPoints, DoseTypeGy aMinDose, DoseTypeGy aMaxDose) { ScatterPlotType scatterPlotData; if (aMaxDose <= aMinDose) { throw core::InvalidParameterException("Parameter invalid: aMaxDose must be > aMinDose!"); } if (aNormalisationDose <= 0) { throw core::InvalidParameterException("Parameter invalid: aNormalisationDose must be > 0!"); } //all input vectors need to have the same size if (((aVarianceVec.size() != aMeanVec.size()) || (aVarianceVec.size() != aParamIdVec.size()))) { throw core::InvalidParameterException("Parameter vectors have different sizes!"); } for (GridIndexType v = 0; v < aVarianceVec.size(); v++) { //set to small positive value to avoid negative infinity! if (aVarianceVec.at(v) == 0) { aVarianceVec.at(v) = 1e-30; } } double paramValue; // vary all parameters for each scattered point int i = 0; while (i < numberOfPoints) { double probability = 1; for (GridIndexType j = 0; j < aParamIdVec.size(); j++) { /* Choose Normal Distribution */ boost::random::normal_distribution gaussian_dist(0, aVarianceVec.at(j)); /* Create a Gaussian Random Number generator * by binding with previously defined * normal distribution object */ boost::random::variate_generator > generator( rng, gaussian_dist); double randomValue = generator(); paramValue = randomValue + aMeanVec.at(j); if (aVarianceVec.at(j) != 0) { /* calculate combined probability */ probability = probability * normal_pdf(randomValue, aVarianceVec.at(j)); } else { throw core::InvalidParameterException("Parameter invalid: Variance should not be 0!"); } aModel.setParameterByID(aParamIdVec.at(j), paramValue); } //randomly select a dose between aMinDose and aMaxDose double dose = uniDist() * (aMaxDose - aMinDose) + aMinDose; if (probability > 0) { try { aModel.init(dose / aNormalisationDose); double value = aModel.getValue(); std::pair modelProbPair = std::make_pair(value, probability); scatterPlotData.insert(std::pair >(dose, modelProbPair)); i++; } catch (core::InvalidParameterException& /*e*/) { //repeat evaluation to guarantee the correct number of scatter values continue; } } } return scatterPlotData; } }//end namespace models }//end namespace rttb diff --git a/code/models/rttbIntegration.h b/code/models/rttbIntegration.h index d9dc45b..54e2b49 100644 --- a/code/models/rttbIntegration.h +++ b/code/models/rttbIntegration.h @@ -1,117 +1,117 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __INTEGRATION_H #define __INTEGRATION_H #include "rttbBaseTypeModels.h" namespace rttb { namespace models { - typedef float integrationType; + typedef double integrationType; /*! @class LkbModelFunctor @brief A FunctorType: calculate the transformed LKB-Model using the transformation x = b - (1-t)/t. \int_{-\infty}^{b} dx f(x) = \int_0^1 dt f(b - (1-t)/t)/t^2 */ class LkbModelFunctor { public: /*!b: upper bound of the lkb integration*/ double b; /*! calculate the transformed LKB-Model using the transformation x = b - (1-t)/t.. */ double calculate(double x) const; }; /*! @class tcpModelFunctor @brief .A FunctorType: calculate the transformed TCP-LQ-Model using the transformation x = a + (1-t)/t. \int_{a}^{+\infty} dx f(x) =\int_0^1 dt f(a + (1-t)/t)/t^2 */ class tcpModelFunctor { public: //TCP parameters including alpha mean, alpha variance, rho, volume vector and bed vector TcpParams params; /*! a: lower bound of the tcp integration*/ double a; /*! calculate the transformed TCP-LQ-Model using the transformation x = a + (1-t)/t. */ double calculate(double x) const; }; /*! Function to be integrated for TCP LQ model. @param x: variable of the TCP LQ model @param params: TCP parameters including alpha mean, alpha variance, rho, volume vector and bed vector @return Return the function value */ double tcpFunction(double x, const TcpParams& tcp_params); /*! Compute integration step for f(x) = exp(-pow(t,2)/2). @param x: variable of the lkb function @return Return the function value */ double lkbFunction(double x); /*! Calculate LKB Integration over(-infinity,b). The integral is mapped onto the semi-open interval (0,1] using the transformation x = b - (1-t)/t @param b: upper bound of the lkb integration */ double integrateLKB(double b); /*! Calculate TCP integration over (a, infinity). The integral is mapped onto the semi-open interval (0,1] using the transformation x = a + (1-t)/t @param a: lower bound of the tcp integration @param params: TCP parameters including alpha mean, alpha variance, rho, volume vector and bed vector */ double integrateTCP(double a, const TcpParams& params); /*This function returns the nth stage of refinement of the extended trapezoidal rule. @param BMfunction: function to be integrated, for example a LkbModelFunctor or a tcpModelFunctor @param a: lower bound of the integral @param b: upper bound of the integral @param stepNum: the nth stage @param result: the current result */ template integrationType trapzd(const FunctorType& BMfunction, integrationType a, integrationType b, int stepNum); /*! Iterative integration routine @param BMfunction: function to be integrated, for example a LkbModelFunctor or a tcpModelFunctor @param a: lower bound of the integral @param b: upper bound of the integral @exception throw InvalidParameterException if integral calculation failed. */ template integrationType iterativeIntegration(const FunctorType& BMfunction, integrationType a, integrationType b); } } #endif \ No newline at end of file diff --git a/demoapps/BioModelCalc/BioModelCalcApplicationData.cpp b/demoapps/BioModelCalc/BioModelCalcApplicationData.cpp index 545efc8..ea35a91 100644 --- a/demoapps/BioModelCalc/BioModelCalcApplicationData.cpp +++ b/demoapps/BioModelCalc/BioModelCalcApplicationData.cpp @@ -1,56 +1,57 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include "BioModelCalcApplicationData.h" namespace rttb { namespace apps { namespace bioModelCalc { ApplicationData:: ApplicationData() { this->Reset(); } void ApplicationData:: Reset() { _doseFileName = ""; _outputFileName = ""; + _doseScaling = 1.0; } void populateAppData(boost::shared_ptr argParser, ApplicationData& appData) { appData._doseFileName = argParser->get(argParser->OPTION_DOSE_FILE); appData._doseLoadStyle = argParser->get >(argParser->OPTION_LOAD_STYLE); appData._doseScaling = argParser->get(argParser->OPTION_DOSE_SCALING); appData._outputFileName = argParser->get(argParser->OPTION_OUTPUT_FILE); appData._model = argParser->get(argParser->OPTION_MODEL); appData._modelParameters = argParser->get >(argParser->OPTION_MODEL_PARAMETERS); } } } } diff --git a/demoapps/BioModelCalc/CMakeLists.txt b/demoapps/BioModelCalc/CMakeLists.txt index a649b8d..9e24a9c 100644 --- a/demoapps/BioModelCalc/CMakeLists.txt +++ b/demoapps/BioModelCalc/CMakeLists.txt @@ -1,7 +1,8 @@ MESSAGE (STATUS "generating demo app: BioModelCalc - calculating the radiobiological effect based on dose") +SET(RTTB_Boost_ADDITIONAL_COMPONENT program_options) RTTB_CREATE_APPLICATION(BioModelCalc DEPENDS RTTBCore RTTBITKIO RTTBVirtuosIO RTTBDicomIO RTTBHelaxIO RTTBModels PACKAGE_DEPENDS ArgumentParsingLib BoostBinaries) IF (NOT WIN32) #CMake 3.1 provides target_compile_features(RTTB_Interpolation cxx_auto_type cxx_nullptr cxx_override) to automatically add required compiler flags set(CMAKE_CXX_FLAGS "-std=c++11 -fpermissive") ENDIF() diff --git a/demoapps/DoseTool/CMakeLists.txt b/demoapps/DoseTool/CMakeLists.txt index f7da8ce..dc338ef 100644 --- a/demoapps/DoseTool/CMakeLists.txt +++ b/demoapps/DoseTool/CMakeLists.txt @@ -1,7 +1,7 @@ MESSAGE (STATUS "generating demo app: DoseTool - calculating dose statistics of a structure") - +SET(RTTB_Boost_ADDITIONAL_COMPONENT program_options) RTTB_CREATE_APPLICATION(DoseTool DEPENDS RTTBCore RTTBITKIO RTTBVirtuosIO RTTBDicomIO RTTBHelaxIO RTTBMasks RTTBMasks RTTBBoostMask RTTBOtherIO RTTBAlgorithms PACKAGE_DEPENDS ArgumentParsingLib BoostBinaries) IF (NOT WIN32) #CMake 3.1 provides target_compile_features(RTTB_Interpolation cxx_auto_type cxx_nullptr cxx_override) to automatically add required compiler flags set(CMAKE_CXX_FLAGS "-std=c++11 -fpermissive") ENDIF() diff --git a/demoapps/DoseTool/DoseTool.cpp b/demoapps/DoseTool/DoseTool.cpp index fe2131a..6ef4227 100644 --- a/demoapps/DoseTool/DoseTool.cpp +++ b/demoapps/DoseTool/DoseTool.cpp @@ -1,160 +1,160 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include "DoseToolApplicationData.h" #include "DoseToolHelper.h" #include "DoseToolCmdLineParser.h" #include "boost/shared_ptr.hpp" #include "boost/make_shared.hpp" #include "RTToolboxConfigure.h" #include "rttbException.h" rttb::apps::doseTool::ApplicationData appData; int main(int argc, const char** argv) { int result = 0; boost::shared_ptr argParser; try { std::string appName = "DoseTool"; std::string appVersion = RTTB_FULL_VERSION_STRING; argParser = boost::make_shared(argc, argv, appName, appVersion); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; return 5; } // This is vital. The application needs to exit if the "help" or "version" parameter is set // because this means the other parameters won't be parsed. if (argParser->isSet(argParser->OPTION_HELP) || argParser->isSet(argParser->OPTION_VERSION)) { return 0; } rttb::apps::doseTool::populateAppData(argParser, appData); std::cout << std::endl << "*******************************************" << std::endl; std::cout << "Dose file: " << appData._doseFileName << std::endl; std::cout << "Struct file: " << appData._structFileName << std::endl; std::cout << "Struct name: " << appData._structNameRegex << std::endl; if (appData._computeDoseStatistics) { std::cout << "Dose statistic output file: " << appData._doseStatisticOutputFileName << std::endl; std::cout << "Compute complex statistics: " << appData._computeComplexDoseStatistics << std::endl; if (appData._computeComplexDoseStatistics) { std::cout << "Prescribed dose: " << appData._prescribedDose << std::endl; } std::cout << "Allow self intersections: " << appData._allowSelfIntersection << std::endl; } if (appData._computeDVH) { std::cout << "DVH output file: " << appData._dvhOutputFilename << std::endl; } try { appData._dose = rttb::apps::doseTool::loadDose(appData._doseFileName, appData._doseLoadStyle); } catch (rttb::core::Exception& e) { std::cerr << "RTTB Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 1; } catch (const std::exception& e) { std::cerr << "Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 1; } catch (...) { std::cerr << "Error!!! unknown error while reading dose image." << std::endl; return 1; } //loading of structure file not necessary in ITK case as it can be used directly as mask input. if (appData._structLoadStyle.front() != "itk") { try { appData._struct = rttb::apps::doseTool::loadStruct(appData._structFileName, - appData._structLoadStyle); + appData._structLoadStyle, appData._structNameRegex); } catch (rttb::core::Exception& e) { std::cerr << "RTTB Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 2; } catch (const std::exception& e) { std::cerr << "Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 2; } catch (...) { std::cerr << "Error!!! unknown error while reading struct image." << std::endl; return 2; } } try { rttb::apps::doseTool::processData(appData); } catch (rttb::core::Exception& e) { std::cerr << "RTTB Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 3; } catch (std::exception& e) { std::cerr << "Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 3; } catch (...) { std::cerr << "Error!!! unknown error while processing the data or writing the image." << std::endl; return 3; } return result; } diff --git a/demoapps/DoseTool/DoseToolHelper.cpp b/demoapps/DoseTool/DoseToolHelper.cpp index 81710eb..8bda333 100644 --- a/demoapps/DoseTool/DoseToolHelper.cpp +++ b/demoapps/DoseTool/DoseToolHelper.cpp @@ -1,363 +1,369 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include "DoseToolHelper.h" #include "boost/make_shared.hpp" #include "boost/shared_ptr.hpp" #include "boost/property_tree/ptree.hpp" #include "boost/filesystem.hpp" #include "rttbExceptionMacros.h" #include "rttbVirtuosPlanFileDoseAccessorGenerator.h" #include "rttbDicomFileDoseAccessorGenerator.h" #include "rttbDicomHelaxFileDoseAccessorGenerator.h" #include "rttbITKImageFileAccessorGenerator.h" #include "rttbStructureSetGeneratorInterface.h" #include "rttbDicomFileStructureSetGenerator.h" #include "rttbVirtuosFileStructureSetGenerator.h" #include "rttbITKImageFileMaskAccessorGenerator.h" #include "rttbDVHCalculator.h" #include "rttbDVHXMLFileWriter.h" #include "rttbDoseStatisticsCalculator.h" #include "rttbBoostMaskAccessor.h" #include "rttbGenericMaskedDoseIterator.h" #include "rttbDoseStatisticsXMLWriter.h" #include "rttbVOIindexIdentifier.h" rttb::core::DoseAccessorInterface::DoseAccessorPointer rttb::apps::doseTool::loadDose(const std::string& fileName, const rttb::apps::doseTool::ApplicationData::LoadingStyleArgType& args) { rttb::core::DoseAccessorInterface::DoseAccessorPointer result; std::cout << std::endl << "read dose file... "; if (args.empty() || args[0] == "dicom") { std::cout << "use RTTB dicom IO... "; result = loadDicomDose(fileName); } else if (args[0] == "helax") { std::cout << "use RTTB Helax IO... "; result = loadHelaxDose(fileName); } else if (args[0] == "itk") { std::cout << "use RTTB itk IO... "; result = loadITKDose(fileName); } else if (args[0] == "virtuos") { if (args.size() < 2) { rttbDefaultExceptionStaticMacro( << "Cannot load virtuos dose. Plan file is missing. Specify plan file as 2nd io stlye argument."); } std::cout << "use RTTB virtuos IO... " << std::endl; std::cout << " virtuos plan file: " << args[1] << " ... "; result = loadVirtuosDose(fileName, args[1]); } else { rttbDefaultExceptionStaticMacro( << "Unknown io style selected. Cannot load data. Selected style: " << args[0]); } std::cout << "done." << std::endl; return result; }; rttb::core::DoseAccessorInterface::DoseAccessorPointer rttb::apps::doseTool::loadDicomDose(const std::string& fileName) { rttb::io::dicom::DicomFileDoseAccessorGenerator generator(fileName); return generator.generateDoseAccessor(); }; rttb::core::DoseAccessorInterface::DoseAccessorPointer rttb::apps::doseTool::loadHelaxDose(const std::string& path) { rttb::io::helax::DicomHelaxFileDoseAccessorGenerator generator(path); return generator.generateDoseAccessor(); }; rttb::core::DoseAccessorInterface::DoseAccessorPointer rttb::apps::doseTool::loadITKDose(const std::string& fileName) { rttb::io::itk::ITKImageFileAccessorGenerator generator(fileName); return generator.generateDoseAccessor(); }; rttb::core::DoseAccessorInterface::DoseAccessorPointer rttb::apps::doseTool::loadVirtuosDose(const std::string& fileName, const std::string& planFileName) { rttb::io::virtuos::VirtuosPlanFileDoseAccessorGenerator generator(fileName, planFileName); return generator.generateDoseAccessor(); }; rttb::core::StructureSetGeneratorInterface::StructureSetPointer rttb::apps::doseTool::loadStruct( - const std::string& fileName, const ApplicationData::LoadingStyleArgType& args) + const std::string& fileName, const ApplicationData::LoadingStyleArgType& args, const std::string& structNameRegex) { rttb::core::StructureSetGeneratorInterface::StructureSetPointer result; std::cout << std::endl << "read struct file... "; if (args.empty() || args[0] == "dicom") { std::cout << "use RTTB dicom IO... "; - result = loadDicomStruct(fileName); + result = loadDicomStruct(fileName, structNameRegex); } else if (args[0] == "virtuos") { std::cout << "use RTTB virtuos IO... " << std::endl; std::cout << " virtuos CTX file: " << args[1] << " ... "; result = loadVirtuosStruct(fileName, args[1]); } else { rttbDefaultExceptionStaticMacro( << "Unknown io style selected. Cannot load data. Selected style: " << args[0]); } std::cout << "done." << std::endl; return result; } rttb::core::StructureSetGeneratorInterface::StructureSetPointer rttb::apps::doseTool::loadDicomStruct( - const std::string& fileName) +const std::string& fileName, const std::string& structNameRegex) { rttb::io::dicom::DicomFileStructureSetGenerator generator(fileName); + + if (!structNameRegex.empty()) + { + generator.setStructureLableFilterActive(true); + generator.setFilterRegEx(structNameRegex); + } return generator.generateStructureSet(); } rttb::core::StructureSetGeneratorInterface::StructureSetPointer rttb::apps::doseTool::loadVirtuosStruct( const std::string& fileName, const std::string& ctxFileName) { rttb::io::virtuos::VirtuosFileStructureSetGenerator generator(fileName, ctxFileName.c_str()); return generator.generateStructureSet(); } void rttb::apps::doseTool::processData(rttb::apps::doseTool::ApplicationData& appData) { std::cout << std::endl << "generating masks... "; std::vector maskAccessorPtrVector = generateMasks( appData); std::cout << "done." << std::endl; for (size_t i = 0; i < maskAccessorPtrVector.size(); i++) { core::DoseIteratorInterface::DoseIteratorPointer spDoseIterator(generateMaskedDoseIterator( maskAccessorPtrVector.at(i), appData._dose)); if (appData._computeDoseStatistics) { std::cout << std::endl << "computing dose statistics... "; algorithms::DoseStatistics::DoseStatisticsPointer statistics = calculateDoseStatistics( spDoseIterator, appData._computeComplexDoseStatistics, appData._prescribedDose); std::cout << "done." << std::endl; std::cout << std::endl << "writing dose statistics to file... "; std::string outputFilename; if (appData._multipleStructsMode) { outputFilename = assembleFilenameWithStruct(appData._doseStatisticOutputFileName, appData._structNames.at(i)); } else { outputFilename = appData._doseStatisticOutputFileName; } writeDoseStatisticsFile(statistics, outputFilename, appData._structNames.at(i), appData); std::cout << "done." << std::endl; } if (appData._computeDVH) { std::cout << std::endl << "computing DVH... "; core::DVH::DVHPointer dvh = calculateDVH(spDoseIterator, appData._struct->getUID(), appData._dose->getUID()); std::cout << "done." << std::endl; std::cout << std::endl << "writing DVH to file... "; std::string outputFilename; if (appData._multipleStructsMode) { outputFilename = assembleFilenameWithStruct(appData._dvhOutputFilename, appData._structNames.at(i)); } else { outputFilename = appData._dvhOutputFilename; } writeDVHFile(dvh, outputFilename); std::cout << "done." << std::endl; } } } std::vector rttb::apps::doseTool::generateMasks( rttb::apps::doseTool::ApplicationData& appData) { std::vector maskAccessorPtrVector; if (appData._structLoadStyle.front() == "itk") { maskAccessorPtrVector.push_back(rttb::io::itk::ITKImageFileMaskAccessorGenerator( appData._structFileName).generateMaskAccessor()); } else { std::vector foundIndices = rttb::masks::VOIindexIdentifier::getIndicesByVoiRegex( appData._struct, appData._structNameRegex); std::vector relevantIndices; if (appData._multipleStructsMode) { relevantIndices = foundIndices; } else { if (!foundIndices.empty()) { relevantIndices.push_back(foundIndices.front()); } } appData._structIndices = relevantIndices; bool strict = !appData._allowSelfIntersection; for (size_t i = 0; i < appData._structIndices.size(); i++) { maskAccessorPtrVector.push_back( boost::make_shared (appData._struct->getStructure(appData._structIndices.at(i)), appData._dose->getGeometricInfo(), strict)); maskAccessorPtrVector.at(i)->updateMask(); appData._structNames.push_back(appData._struct->getStructure(appData._structIndices.at( i))->getLabel()); } } return maskAccessorPtrVector; } rttb::core::DoseIteratorInterface::DoseIteratorPointer rttb::apps::doseTool::generateMaskedDoseIterator( rttb::core::MaskAccessorInterface::MaskAccessorPointer maskAccessorPtr, rttb::core::DoseAccessorInterface::DoseAccessorPointer doseAccessorPtr) { boost::shared_ptr maskedDoseIterator = boost::make_shared(maskAccessorPtr, doseAccessorPtr); rttb::core::DoseIteratorInterface::DoseIteratorPointer doseIterator(maskedDoseIterator); return doseIterator; } rttb::algorithms::DoseStatistics::DoseStatisticsPointer rttb::apps::doseTool::calculateDoseStatistics( core::DoseIteratorInterface::DoseIteratorPointer doseIterator, bool calculateComplexDoseStatistics, DoseTypeGy prescribedDose) { rttb::algorithms::DoseStatisticsCalculator doseStatsCalculator(doseIterator); if (calculateComplexDoseStatistics) { return doseStatsCalculator.calculateDoseStatistics(prescribedDose); } else { return doseStatsCalculator.calculateDoseStatistics(); } } rttb::core::DVH::DVHPointer rttb::apps::doseTool::calculateDVH( core::DoseIteratorInterface::DoseIteratorPointer doseIterator, IDType structUID, IDType doseUID) { rttb::core::DVHCalculator calc(doseIterator, structUID, doseUID); rttb::core::DVH::DVHPointer dvh = calc.generateDVH(); return dvh; } void rttb::apps::doseTool::writeDoseStatisticsFile( rttb::algorithms::DoseStatistics::DoseStatisticsPointer statistics, const std::string& filename, const std::string& structName, rttb::apps::doseTool::ApplicationData& appData) { boost::property_tree::ptree originalTree = rttb::io::other::writeDoseStatistics(statistics); //add config part to xml originalTree.add("statistics.config.requestedStructRegex", appData._structNameRegex); originalTree.add("statistics.config.structName", structName); originalTree.add("statistics.config.doseUID", appData._dose->getUID()); originalTree.add("statistics.config.doseFile", appData._doseFileName); originalTree.add("statistics.config.structFile", appData._structFileName); boost::property_tree::ptree reorderedTree, configTree, resultsTree; configTree = originalTree.get_child("statistics.config"); resultsTree = originalTree.get_child("statistics.results"); reorderedTree.add_child("statistics.config", configTree); reorderedTree.add_child("statistics.results", resultsTree); boost::property_tree::write_xml(filename, reorderedTree, std::locale(), boost::property_tree::xml_writer_make_settings('\t', 1)); } std::string rttb::apps::doseTool::assembleFilenameWithStruct(const std::string& originalFilename, const std::string& structName) { boost::filesystem::path originalFile(originalFilename); std::string newFilename = originalFile.stem().string() + "_" + structName + originalFile.extension().string(); boost::filesystem::path newFile(originalFile.parent_path() / newFilename); return newFile.string(); } void rttb::apps::doseTool::writeDVHFile(core::DVH::DVHPointer dvh, const std::string& filename) { DVHType typeCum = { DVHType::Cumulative }; rttb::io::other::DVHXMLFileWriter dvhWriter(filename, typeCum); dvhWriter.writeDVH(dvh); } diff --git a/demoapps/DoseTool/DoseToolHelper.h b/demoapps/DoseTool/DoseToolHelper.h index 29d8c23..8d62fac 100644 --- a/demoapps/DoseTool/DoseToolHelper.h +++ b/demoapps/DoseTool/DoseToolHelper.h @@ -1,128 +1,134 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __DOSETOOL_HELPER_H #define __DOSETOOL_HELPER_H #include "DoseToolApplicationData.h" #include "rttbDoseAccessorInterface.h" #include "rttbDoseIteratorInterface.h" #include "rttbMaskAccessorInterface.h" #include "rttbDoseStatistics.h" #include "rttbDVH.h" namespace rttb { namespace apps { namespace doseTool { /*! @brief loads a dose from a file based on the loadingStyle. @exception Throws an rttb::Exception if loading fails */ core::DoseAccessorInterface::DoseAccessorPointer loadDose(const std::string& fileName, const rttb::apps::doseTool::ApplicationData::LoadingStyleArgType& args); /*! @brief loads a dicom dose from a file. @exception Throws an rttb::Exception if loading fails @sa DicomFileDoseAccessorGenerator */ core::DoseAccessorInterface::DoseAccessorPointer loadDicomDose(const std::string& fileName); /*! @brief loads a helax dose from a file. @exception Throws an rttb::Exception if loading fails @sa DicomHelaxFileDoseAccessorGenerator */ core::DoseAccessorInterface::DoseAccessorPointer loadHelaxDose(const std::string& path); /*! @brief loads an itk dose from a file. @exception Throws an rttb::Exception if loading fails. @details Might be of all formats that ITK know (*.mhd, *.nrrd, ...). The absolute image values are taken as dose. @sa ITKImageFileAccessorGenerator */ core::DoseAccessorInterface::DoseAccessorPointer loadITKDose(const std::string& fileName); /*! @brief loads a virtuos dose from a file. @exception Throws an rttb::Exception if loading fails @sa VirtuosPlanFileDoseAccessorGenerator */ core::DoseAccessorInterface::DoseAccessorPointer loadVirtuosDose(const std::string& fileName, const std::string& planFileName); /*! @brief loads a struct from a file based on the loadingStyle. + You may pass a structure name regex. If is not empty, it will be used to filter structure in the + loading process. Only structures with a name matching the reg ex will be loaded. This speeds up the + loading process significantly if you need only one structure out of a structure set. @exception Throws an rttb::Exception if loading fails @details voxelized itk images are read in generateMask() directly */ core::StructureSetGeneratorInterface::StructureSetPointer loadStruct(const std::string& fileName, - const rttb::apps::doseTool::ApplicationData::LoadingStyleArgType& args); + const rttb::apps::doseTool::ApplicationData::LoadingStyleArgType& args, const std::string& structNameRegex = ""); /*! @brief loads a dicom struct from a file. + You may pass a structure name regex. If is not empty, it will be used to filter structure in the + loading process. Only structures with a name matching the reg ex will be loaded. This speeds up the + loading process significantly if you need only one structure out of a structure set. @exception Throws an rttb::Exception if loading fails @sa DicomFileStructureSetGenerator */ core::StructureSetGeneratorInterface::StructureSetPointer loadDicomStruct( - const std::string& fileName); + const std::string& fileName, const std::string& structNameRegex = ""); /*! @brief loads a virtuos struct from a file. @exception Throws an rttb::Exception if loading fails @sa VirtuosPlanFileDoseAccessorGenerator */ core::StructureSetGeneratorInterface::StructureSetPointer loadVirtuosStruct( const std::string& fileName, const std::string& ctxFileName); /*! @brief Contains the business logic of processing all information to calculate the dose statistics and writing them to an xml file. @details Uses appData for the input data and the correct configuration. */ void processData(ApplicationData& appData); /*! @brief Generates a mask from the struct file by using the boostAccessor. In case of itk image, it directly loads the voxelized image. */ std::vector generateMasks( ApplicationData& appData); algorithms::DoseStatistics::DoseStatisticsPointer calculateDoseStatistics( core::DoseIteratorInterface::DoseIteratorPointer doseIterator, bool calculateComplexDoseStatistics, DoseTypeGy prescribedDose); core::DVH::DVHPointer calculateDVH(core::DoseIteratorInterface::DoseIteratorPointer doseIterator, IDType structUID, IDType doseUID); /*! @brief Writes the dose statistics as XML to a file @details adds a .... part to the RTTB generated xml where the used files and struct names are stored. */ void writeDoseStatisticsFile(algorithms::DoseStatistics::DoseStatisticsPointer statistics, const std::string& filename, const std::string& structName, rttb::apps::doseTool::ApplicationData& appData); void writeDVHFile(core::DVH::DVHPointer dvh, const std::string& filename); core::DoseIteratorInterface::DoseIteratorPointer generateMaskedDoseIterator( core::MaskAccessorInterface::MaskAccessorPointer maskAccessorPtr, core::DoseAccessorInterface::DoseAccessorPointer doseAccessorPtr); std::string assembleFilenameWithStruct(const std::string& originalFilename, const std::string& structName); } } } #endif diff --git a/demoapps/VoxelizerTool/CMakeLists.txt b/demoapps/VoxelizerTool/CMakeLists.txt index 9d39a2d..3e36bb5 100644 --- a/demoapps/VoxelizerTool/CMakeLists.txt +++ b/demoapps/VoxelizerTool/CMakeLists.txt @@ -1,7 +1,7 @@ MESSAGE (STATUS "generating demo app: VoxelizerTool - writing files of voxelized structures") - +SET(RTTB_Boost_ADDITIONAL_COMPONENT program_options) RTTB_CREATE_APPLICATION(VoxelizerTool DEPENDS RTTBITKIO RTTBOTBMask RTTBBoostMask RTTBDicomIO RTTBMasks RTTBCore PACKAGE_DEPENDS ITK BoostBinaries) IF (NOT WIN32) #CMake 3.1 provides target_compile_features(RTTB_Interpolation cxx_auto_type cxx_nullptr cxx_override) to automatically add required compiler flags set(CMAKE_CXX_FLAGS "-std=c++11 -fpermissive") ENDIF() diff --git a/testing/algorithms/DoseStatisticsCalculatorTest.cpp b/testing/algorithms/DoseStatisticsCalculatorTest.cpp index 0633fa8..38f082a 100644 --- a/testing/algorithms/DoseStatisticsCalculatorTest.cpp +++ b/testing/algorithms/DoseStatisticsCalculatorTest.cpp @@ -1,324 +1,324 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include "litCheckMacros.h" #include "rttbBaseType.h" #include "rttbGenericDoseIterator.h" #include "rttbDoseIteratorInterface.h" #include "rttbNullPointerException.h" #include "rttbDoseStatisticsCalculator.h" #include "rttbInvalidDoseException.h" #include "rttbInvalidParameterException.h" #include "rttbDataNotAvailableException.h" #include "../core/DummyDoseAccessor.h" namespace rttb { namespace testing { typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; typedef core::DoseIteratorInterface::DoseIteratorPointer DoseIteratorPointer; typedef rttb::algorithms::DoseStatistics::ResultListPointer ResultListPointer; typedef rttb::algorithms::DoseStatistics::DoseStatisticsPointer DoseStatisticsPointer; /*! @brief DoseStatisticsCalculatorTest - test the API of DoseStatisticsCalculator 1) test constructors 2) test setDoseIterator 3) test calculateDoseSatistics 4) get statistical values */ int DoseStatisticsCalculatorTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; boost::shared_ptr spTestDoseAccessor = boost::make_shared(); DoseAccessorPointer spDoseAccessor(spTestDoseAccessor); const std::vector* doseVals = spTestDoseAccessor->getDoseVector(); boost::shared_ptr spTestDoseIterator = boost::make_shared(spDoseAccessor); DoseIteratorPointer spDoseIterator(spTestDoseIterator); DoseIteratorPointer spDoseIteratorNull; //1) test constructors // the values cannot be accessed from outside, therefore correct default values are not tested CHECK_THROW_EXPLICIT(rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator( spDoseIteratorNull), core::NullPointerException); CHECK_NO_THROW(rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator(spDoseIterator)); rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator(spDoseIterator); //2) test setDoseIterator //3) test calculateDoseStatistics DoseStatisticsPointer theStatistics; //simple dose statistics CHECK_NO_THROW(theStatistics = myDoseStatsCalculator.calculateDoseStatistics()); - CHECK_EQUAL(theStatistics->getMinimumPositions()->empty(), false); - CHECK_EQUAL(theStatistics->getMaximumPositions()->empty(), false); + CHECK_EQUAL(theStatistics->getMinimumVoxelPositions()->empty(), false); + CHECK_EQUAL(theStatistics->getMaximumVoxelPositions()->empty(), false); CHECK_EQUAL(theStatistics->getAllVx().empty(), true); CHECK_EQUAL(theStatistics->getAllDx().empty(), true); CHECK_EQUAL(theStatistics->getAllVx().empty(), true); CHECK_EQUAL(theStatistics->getAllMaxOHx().empty(), true); CHECK_EQUAL(theStatistics->getAllMOHx().empty(), true); CHECK_EQUAL(theStatistics->getAllMOCx().empty(), true); CHECK_EQUAL(theStatistics->getAllMinOCx().empty(), true); //check default values for computeComplexMeasures=true DoseStatisticsPointer theStatisticsDefault; CHECK_NO_THROW(theStatisticsDefault = myDoseStatsCalculator.calculateDoseStatistics(true)); CHECK_NO_THROW(theStatisticsDefault->getVx(0.02 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx(0.05 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx(0.1 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx(0.9 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx(0.95 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx(0.98 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getDx(0.02 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx(0.05 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx(0.1 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx(0.9 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx(0.95 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx(0.98 * theStatisticsDefault->getVolume())); //check manually set reference dose and the default x values CHECK_NO_THROW(theStatistics = myDoseStatsCalculator.calculateDoseStatistics(100.0)); CHECK_THROW_EXPLICIT(theStatistics->getVx(0.1 * theStatistics->getMaximum()), core::DataNotAvailableException); CHECK_NO_THROW(theStatistics->getVx(0.1 * 100.0)); CHECK_NO_THROW(theStatistics->getDx(0.1 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getDx(0.9 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getMOHx(0.95 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getMOCx(0.98 * theStatistics->getVolume())); CHECK_EQUAL(theStatistics->getReferenceDose(), 100.0); //check manually set x values std::vector precomputeDoseValues, precomputeVolumeValues; precomputeDoseValues.push_back(0.01); precomputeDoseValues.push_back(0.02); precomputeDoseValues.push_back(0.05); precomputeVolumeValues.push_back(0.9); precomputeVolumeValues.push_back(0.95); precomputeVolumeValues.push_back(0.99); CHECK_NO_THROW(theStatistics = myDoseStatsCalculator.calculateDoseStatistics(precomputeDoseValues, precomputeVolumeValues)); CHECK_NO_THROW(theStatistics->getVx(0.01 * theStatistics->getMaximum())); CHECK_NO_THROW(theStatistics->getVx(0.02 * theStatistics->getMaximum())); CHECK_NO_THROW(theStatistics->getVx(0.05 * theStatistics->getMaximum())); CHECK_THROW_EXPLICIT(theStatistics->getVx(0.03 * theStatistics->getMaximum()), core::DataNotAvailableException); CHECK_NO_THROW(theStatistics->getDx(0.9 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getDx(0.95 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getDx(0.99 * theStatistics->getVolume())); CHECK_THROW_EXPLICIT(theStatistics->getDx(0.03 * theStatistics->getVolume()), core::DataNotAvailableException); CHECK_EQUAL(theStatistics->getVx(0.02 * theStatistics->getMaximum()), theStatisticsDefault->getVx(0.02 * theStatistics->getMaximum())); CHECK_EQUAL(theStatistics->getVx(0.05 * theStatistics->getMaximum()), theStatisticsDefault->getVx(0.05 * theStatistics->getMaximum())); CHECK_EQUAL(theStatistics->getDx(0.9 * theStatistics->getVolume()), theStatisticsDefault->getDx(0.9 * theStatistics->getVolume())); CHECK_EQUAL(theStatistics->getDx(0.95 * theStatistics->getVolume()), theStatisticsDefault->getDx(0.95 * theStatistics->getVolume())); //check manually set reference dose and x values CHECK_NO_THROW(theStatistics = myDoseStatsCalculator.calculateDoseStatistics(precomputeDoseValues, precomputeVolumeValues, 100.0)); CHECK_THROW_EXPLICIT(theStatistics->getVx(0.01 * theStatistics->getMaximum()), core::DataNotAvailableException); CHECK_NO_THROW(theStatistics->getVx(0.01 * 100.0)); CHECK_NO_THROW(theStatistics->getDx(0.9 * theStatistics->getVolume())); CHECK_EQUAL(theStatistics->getReferenceDose(), 100.0); //MOHx, MOCx, MaxOHx and MinOCx are computed analogous to Dx, they will not be checked. //4) get statistical values CHECK_EQUAL(theStatistics->getNumberOfVoxels(), doseVals->size()); //compute simple statistical values (min, mean, max, stddev) for comparison DoseStatisticType maximum = 0; DoseStatisticType minimum = 1000000; DoseStatisticType mean = 0; DoseStatisticType variance = 0; std::vector::const_iterator doseIt = doseVals->begin(); while (doseIt != doseVals->end()) { if (maximum < *doseIt) { maximum = *doseIt; } if (minimum > *doseIt) { minimum = *doseIt; } mean += *doseIt; ++doseIt; } mean /= doseVals->size(); DoseTypeGy compMean = (int(mean * 100)) / 100; doseIt = doseVals->begin(); while (doseIt != doseVals->end()) { variance += pow(*doseIt - mean, 2); ++doseIt; } variance /= doseVals->size(); DoseStatisticType stdDev = pow(variance, 0.5); //we have some precision problems here... double errorConstantLarger = 1e-2; CHECK_EQUAL(theStatistics->getMaximum(), maximum); CHECK_EQUAL(theStatistics->getMinimum(), minimum); CHECK_CLOSE(theStatistics->getMean(), mean, errorConstantLarger); CHECK_CLOSE(theStatistics->getStdDeviation(), stdDev, errorConstantLarger); CHECK_CLOSE(theStatistics->getVariance(), variance, errorConstantLarger); //check for complex doseStatistics (maximumPositions, minimumPositions, Vx, Dx, MOHx, MOCx, MAXOHx, MinOCx) unsigned int nMax = 0, nMin = 0; doseIt = doseVals->begin(); while (doseIt != doseVals->end()) { if (*doseIt == theStatistics->getMaximum()) { nMax++; } if (*doseIt == theStatistics->getMinimum()) { nMin++; } ++doseIt; } //only 100 positions are stored if (nMax > 100) { nMax = 100; } if (nMin > 100) { nMin = 100; } - auto maximaPositions = theStatistics->getMaximumPositions(); - auto minimaPositions = theStatistics->getMinimumPositions(); + auto maximaPositions = theStatistics->getMaximumVoxelPositions(); + auto minimaPositions = theStatistics->getMinimumVoxelPositions(); CHECK_EQUAL(maximaPositions->size(), nMax); CHECK_EQUAL(minimaPositions->size(), nMin); for (auto maximaPositionsIterator = std::begin(*maximaPositions); maximaPositionsIterator != std::end(*maximaPositions); ++maximaPositionsIterator) { CHECK_EQUAL(maximaPositionsIterator->first, theStatistics->getMaximum()); } for (auto minimaPositionsIterator = std::begin(*minimaPositions); minimaPositionsIterator != std::end(*minimaPositions); ++minimaPositionsIterator) { CHECK_EQUAL(minimaPositionsIterator->first, theStatistics->getMinimum()); } //generate specific example dose maximum = 9.5; minimum = 2.5; mean = 6; int sizeTemplate = 500; std::vector aDoseVector; for (int i = 0; i < sizeTemplate; i++) { aDoseVector.push_back(maximum); aDoseVector.push_back(minimum); } core::GeometricInfo geoInfo = spTestDoseAccessor->getGeometricInfo(); geoInfo.setNumRows(20); geoInfo.setNumColumns(10); geoInfo.setNumSlices(5); boost::shared_ptr spTestDoseAccessor2 = boost::make_shared(aDoseVector, geoInfo); DoseAccessorPointer spDoseAccessor2(spTestDoseAccessor2); boost::shared_ptr spTestDoseIterator2 = boost::make_shared(spDoseAccessor2); DoseIteratorPointer spDoseIterator2(spTestDoseIterator2); rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator2(spDoseIterator2); DoseStatisticsPointer theStatistics3 = myDoseStatsCalculator2.calculateDoseStatistics(); CHECK_EQUAL(theStatistics3->getMaximum(), maximum); CHECK_EQUAL(theStatistics3->getMinimum(), minimum); CHECK_EQUAL(theStatistics3->getMean(), mean); - maximaPositions = theStatistics3->getMaximumPositions(); - minimaPositions = theStatistics3->getMinimumPositions(); + maximaPositions = theStatistics3->getMaximumVoxelPositions(); + minimaPositions = theStatistics3->getMinimumVoxelPositions(); CHECK_EQUAL(maximaPositions->empty(), false); CHECK_EQUAL(minimaPositions->empty(), false); for (auto maximaPositionsIterator = std::begin(*maximaPositions); maximaPositionsIterator != std::end(*maximaPositions); ++maximaPositionsIterator) { CHECK_EQUAL(maximaPositionsIterator->first, theStatistics3->getMaximum()); } for (auto minimaPositionsIterator = std::begin(*minimaPositions); minimaPositionsIterator != std::end(*minimaPositions); ++minimaPositionsIterator) { CHECK_EQUAL(minimaPositionsIterator->first, theStatistics3->getMinimum()); } RETURN_AND_REPORT_TEST_SUCCESS; } }//end namespace testing }//end namespace rttb diff --git a/testing/algorithms/DoseStatisticsTest.cpp b/testing/algorithms/DoseStatisticsTest.cpp index 1184244..31b1620 100644 --- a/testing/algorithms/DoseStatisticsTest.cpp +++ b/testing/algorithms/DoseStatisticsTest.cpp @@ -1,227 +1,227 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include "litCheckMacros.h" #include "rttbBaseType.h" #include "rttbDoseStatistics.h" #include "rttbDataNotAvailableException.h" namespace rttb { namespace testing { typedef rttb::algorithms::DoseStatistics::ResultListPointer ResultListPointer; typedef rttb::algorithms::DoseStatistics::DoseToVolumeFunctionType DoseToVolumeFunctionType; typedef rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType VolumeToDoseFunctionType; /*! @brief DoseStatisticsTest - test the API of DoseStatistics 1) test constructors 2) test setters 3) test getters of complex statistics (with stored key and without stored key) */ int DoseStatisticsTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; DoseStatisticType minimum = 1.0; DoseStatisticType mean = 5.5; DoseStatisticType maximum = 108.2; DoseStatisticType stdDeviation = 10.1; unsigned int numVoxels = 100000; VolumeType volume = numVoxels * (0.5 * 0.5 * 0.5); std::vector > minVoxels; std::vector > maxVoxels; minVoxels.push_back(std::make_pair(1.0, 11)); minVoxels.push_back(std::make_pair(1.0, 22)); minVoxels.push_back(std::make_pair(1.0, 33)); minVoxels.push_back(std::make_pair(1.0, 44)); maxVoxels.push_back(std::make_pair(108.2, 5)); maxVoxels.push_back(std::make_pair(108.2, 6)); maxVoxels.push_back(std::make_pair(108.2, 7)); maxVoxels.push_back(std::make_pair(108.2, 8)); ResultListPointer resultsMinVoxels = boost::make_shared > >(minVoxels); ResultListPointer resultsMaxVoxels = boost::make_shared > >(maxVoxels); DoseToVolumeFunctionType Vx; Vx.insert(std::make_pair(1.1, 1000)); Vx.insert(std::make_pair(106.9, 99000)); VolumeToDoseFunctionType Dx; Dx.insert(std::make_pair(1000, 1.1)); Dx.insert(std::make_pair(99000, 106.9)); VolumeToDoseFunctionType MOHx; MOHx.insert(std::make_pair(1000, 5)); MOHx.insert(std::make_pair(99000, 105.5)); VolumeToDoseFunctionType MOCx; MOCx.insert(std::make_pair(1000, 10)); MOCx.insert(std::make_pair(99000, 99)); VolumeToDoseFunctionType MaxOHx; MaxOHx.insert(std::make_pair(1000, 40)); MaxOHx.insert(std::make_pair(99000, 98.3)); VolumeToDoseFunctionType MinOCx; MinOCx.insert(std::make_pair(1000, 25.5)); MinOCx.insert(std::make_pair(99000, 102.7)); //1) test constructors CHECK_NO_THROW(rttb::algorithms::DoseStatistics aDoseStatistic(minimum, maximum, mean, stdDeviation, numVoxels, volume)); rttb::algorithms::DoseStatistics aDoseStatistic(minimum, maximum, mean, stdDeviation, numVoxels, volume); CHECK_EQUAL(aDoseStatistic.getMinimum(), minimum); CHECK_EQUAL(aDoseStatistic.getMaximum(), maximum); CHECK_EQUAL(aDoseStatistic.getMean(), mean); CHECK_EQUAL(aDoseStatistic.getStdDeviation(), stdDeviation); CHECK_EQUAL(aDoseStatistic.getVariance(), stdDeviation * stdDeviation); CHECK_EQUAL(aDoseStatistic.getNumberOfVoxels(), numVoxels); CHECK_EQUAL(aDoseStatistic.getVolume(), volume); //check default values for unset complex values - CHECK_EQUAL(aDoseStatistic.getMaximumPositions()->empty(), true); - CHECK_EQUAL(aDoseStatistic.getMinimumPositions()->empty(), true); + CHECK_EQUAL(aDoseStatistic.getMaximumVoxelPositions()->empty(), true); + CHECK_EQUAL(aDoseStatistic.getMinimumVoxelPositions()->empty(), true); CHECK_EQUAL(aDoseStatistic.getAllDx().empty(), true); CHECK_EQUAL(aDoseStatistic.getAllVx().empty(), true); CHECK_EQUAL(aDoseStatistic.getAllMOHx().empty(), true); CHECK_EQUAL(aDoseStatistic.getAllMOCx().empty(), true); CHECK_EQUAL(aDoseStatistic.getAllMaxOHx().empty(), true); CHECK_EQUAL(aDoseStatistic.getAllMinOCx().empty(), true); CHECK_NO_THROW(rttb::algorithms::DoseStatistics aDoseStatisticComplex(minimum, maximum, mean, stdDeviation, numVoxels, volume, resultsMaxVoxels, resultsMinVoxels, Dx, Vx, MOHx, MOCx, MaxOHx, MinOCx)); rttb::algorithms::DoseStatistics aDoseStatisticComplex(minimum, maximum, mean, stdDeviation, numVoxels, volume, resultsMaxVoxels, resultsMinVoxels, Dx, Vx, MOHx, MOCx, MaxOHx, MinOCx); - CHECK_EQUAL(aDoseStatisticComplex.getMaximumPositions(), resultsMaxVoxels); - CHECK_EQUAL(aDoseStatisticComplex.getMinimumPositions(), resultsMinVoxels); + CHECK_EQUAL(aDoseStatisticComplex.getMaximumVoxelPositions(), resultsMaxVoxels); + CHECK_EQUAL(aDoseStatisticComplex.getMinimumVoxelPositions(), resultsMinVoxels); CHECK_EQUAL(aDoseStatisticComplex.getAllDx() == Dx, true); CHECK_EQUAL(aDoseStatisticComplex.getAllVx() == Vx, true); CHECK_EQUAL(aDoseStatisticComplex.getAllMOHx() == MOHx, true); CHECK_EQUAL(aDoseStatisticComplex.getAllMOCx() == MOCx, true); CHECK_EQUAL(aDoseStatisticComplex.getAllMaxOHx() == MaxOHx, true); CHECK_EQUAL(aDoseStatisticComplex.getAllMinOCx() == MinOCx, true); //2) test setters (only complex statistics have setters) CHECK_NO_THROW(aDoseStatistic.setMaximumVoxelPositions(resultsMaxVoxels)); CHECK_NO_THROW(aDoseStatistic.setMinimumVoxelPositions(resultsMinVoxels)); CHECK_NO_THROW(aDoseStatistic.setDx(Dx)); CHECK_NO_THROW(aDoseStatistic.setVx(Vx)); CHECK_NO_THROW(aDoseStatistic.setMOHx(MOHx)); CHECK_NO_THROW(aDoseStatistic.setMOCx(MOCx)); CHECK_NO_THROW(aDoseStatistic.setMaxOHx(MaxOHx)); CHECK_NO_THROW(aDoseStatistic.setMinOCx(MinOCx)); - CHECK_EQUAL(aDoseStatistic.getMaximumPositions(), resultsMaxVoxels); - CHECK_EQUAL(aDoseStatistic.getMinimumPositions(), resultsMinVoxels); + CHECK_EQUAL(aDoseStatistic.getMaximumVoxelPositions(), resultsMaxVoxels); + CHECK_EQUAL(aDoseStatistic.getMinimumVoxelPositions(), resultsMinVoxels); CHECK_EQUAL(aDoseStatistic.getAllDx() == Dx, true); CHECK_EQUAL(aDoseStatistic.getAllVx() == Vx, true); CHECK_EQUAL(aDoseStatistic.getAllMOHx() == MOHx, true); CHECK_EQUAL(aDoseStatistic.getAllMOCx() == MOCx, true); CHECK_EQUAL(aDoseStatistic.getAllMaxOHx() == MaxOHx, true); CHECK_EQUAL(aDoseStatistic.getAllMinOCx() == MinOCx, true); //3) test getters of complex statistics(with stored key and without stored key) //getAll*() already tested in (2) Vx.clear(); Vx.insert(std::make_pair(1.1, 1000)); Vx.insert(std::make_pair(5.0, 2300)); Vx.insert(std::make_pair(90, 90500)); Vx.insert(std::make_pair(107, 99000)); Dx.clear(); Dx.insert(std::make_pair(1000, 1.1)); Dx.insert(std::make_pair(2000, 2.0)); Dx.insert(std::make_pair(5000, 10.8)); Dx.insert(std::make_pair(90000, 89.5)); Dx.insert(std::make_pair(98000, 104.4)); Dx.insert(std::make_pair(99000, 106.9)); rttb::algorithms::DoseStatistics aDoseStatisticNewValues(minimum, maximum, mean, stdDeviation, numVoxels, volume); aDoseStatisticNewValues.setDx(Dx); aDoseStatisticNewValues.setVx(Vx); CHECK_NO_THROW(aDoseStatisticNewValues.getVx(1.1)); CHECK_NO_THROW(aDoseStatisticNewValues.getVx(90)); CHECK_NO_THROW(aDoseStatisticNewValues.getDx(1000)); CHECK_NO_THROW(aDoseStatisticNewValues.getDx(98000)); CHECK_EQUAL(aDoseStatisticNewValues.getVx(1.1), Vx.find(1.1)->second); CHECK_EQUAL(aDoseStatisticNewValues.getVx(90), Vx.find(90)->second); CHECK_EQUAL(aDoseStatisticNewValues.getDx(1000), Dx.find(1000)->second); CHECK_EQUAL(aDoseStatisticNewValues.getDx(98000), Dx.find(98000)->second); //test if key-value combination NOT in map CHECK_THROW_EXPLICIT(aDoseStatisticNewValues.getDx(1001), core::DataNotAvailableException); CHECK_THROW_EXPLICIT(aDoseStatisticNewValues.getVx(10), core::DataNotAvailableException); double closestDxKey, closestVxKey; CHECK_NO_THROW(aDoseStatisticNewValues.getDx(900, true, closestDxKey)); CHECK_NO_THROW(aDoseStatisticNewValues.getDx(99001, true, closestDxKey)); CHECK_NO_THROW(aDoseStatisticNewValues.getVx(10, true, closestVxKey)); CHECK_EQUAL(aDoseStatisticNewValues.getDx(900, true, closestDxKey), Dx.find(1000)->second); CHECK_EQUAL(aDoseStatisticNewValues.getDx(99001, true, closestDxKey), Dx.find(99000)->second); CHECK_EQUAL(aDoseStatisticNewValues.getVx(10, true, closestVxKey), Vx.find(5.0)->second); CHECK_EQUAL(closestDxKey, 99000); CHECK_EQUAL(closestVxKey, 5); //equal distance to two values. First value is returned. CHECK_NO_THROW(aDoseStatisticNewValues.getDx(1500, true, closestDxKey)); CHECK_NO_THROW(aDoseStatisticNewValues.getVx(98.5, true, closestVxKey)); CHECK_EQUAL(aDoseStatisticNewValues.getDx(1500, true, closestDxKey), Dx.find(1000)->second); CHECK_EQUAL(aDoseStatisticNewValues.getVx(98.5, true, closestVxKey), Vx.find(90.0)->second); CHECK_EQUAL(closestDxKey, 1000); CHECK_EQUAL(closestVxKey, 90.0); double dummy; CHECK_THROW_EXPLICIT(aDoseStatisticNewValues.getMinOCx(25), core::DataNotAvailableException); CHECK_THROW_EXPLICIT(aDoseStatisticNewValues.getMOHx(9999), core::DataNotAvailableException); CHECK_THROW_EXPLICIT(aDoseStatisticNewValues.getMinOCx(25, true, dummy), core::DataNotAvailableException); CHECK_THROW_EXPLICIT(aDoseStatisticNewValues.getMOHx(9999, true, dummy), core::DataNotAvailableException); RETURN_AND_REPORT_TEST_SUCCESS; } }//end namespace testing }//end namespace rttb diff --git a/testing/core/DVHCalculatorTest.cpp b/testing/core/DVHCalculatorTest.cpp index 8e8cc8d..23f51ef 100644 --- a/testing/core/DVHCalculatorTest.cpp +++ b/testing/core/DVHCalculatorTest.cpp @@ -1,128 +1,128 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ // this file defines the rttbCoreTests for the test driver // and all it expects is that you have a function called RegisterTests #include #include #include "litCheckMacros.h" #include "rttbBaseType.h" #include "rttbDVHCalculator.h" #include "rttbGenericMaskedDoseIterator.h" #include "rttbGenericDoseIterator.h" #include "rttbNullPointerException.h" #include "rttbInvalidParameterException.h" #include "DummyDoseAccessor.h" #include "DummyMaskAccessor.h" namespace rttb { namespace testing { typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; typedef core::GenericMaskedDoseIterator::MaskAccessorPointer MaskAccessorPointer; typedef core::DVHCalculator::DoseIteratorPointer DoseIteratorPointer; typedef core::DVHCalculator::MaskedDoseIteratorPointer MaskedDoseIteratorPointer; /*!@brief DVHTest - test the API of DVH 1) test constructors (values as expected?) */ int DVHCalculatorTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; //create null pointer to DoseIterator DoseIteratorPointer spDoseIteratorNull; const IDType structureID = "myStructure"; const IDType doseIDNull = "myDose"; //1) test constructors (values as expected?) CHECK_THROW_EXPLICIT(core::DVHCalculator myDVHCalc(spDoseIteratorNull, structureID, doseIDNull), core::NullPointerException); //create dummy DoseAccessor boost::shared_ptr spTestDoseAccessor = boost::make_shared(); DoseAccessorPointer spDoseAccessor(spTestDoseAccessor); const std::vector* doseVals = spTestDoseAccessor->getDoseVector(); //create corresponding DoseIterator boost::shared_ptr spTestDoseIterator = boost::make_shared(spDoseAccessor); DoseIteratorPointer spDoseIterator(spTestDoseIterator); const IDType doseID = spDoseAccessor->getUID(); CHECK_NO_THROW(core::DVHCalculator myDVHCalc(spDoseIterator, structureID, doseID)); CHECK_THROW_EXPLICIT(core::DVHCalculator myDVHCalc(spDoseIterator, structureID, doseID, -1), core::InvalidParameterException); int numBins = 21; DoseTypeGy binSize = 0; DoseStatisticType maximum = 0; std::vector::const_iterator doseIt = doseVals->begin(); while (doseIt != doseVals->end()) { if (maximum < *doseIt) { maximum = *doseIt; } - doseIt++; + ++doseIt; } binSize = maximum * 1.5 / numBins; CHECK_NO_THROW(core::DVHCalculator myDVHCalc(spDoseIterator, structureID, doseID, binSize, numBins)); CHECK_THROW_EXPLICIT(core::DVHCalculator myDVHCalc(spDoseIterator, structureID, doseID, 0, 0), core::InvalidParameterException);//aNumberOfBins must be >=0 core::DVHCalculator myDVHCalc(spDoseIterator, structureID, doseID, binSize, 1); CHECK_THROW_EXPLICIT(myDVHCalc.generateDVH(), core::InvalidParameterException);//_numberOfBins must be > max(aDoseIterator)/aDeltaD! //create dummy MaskAccessor boost::shared_ptr spTestMaskAccessor = boost::make_shared(spDoseAccessor->getGeometricInfo()); MaskAccessorPointer spMaskAccessor(spTestMaskAccessor); //create corresponding MaskedDoseIterator boost::shared_ptr spTestMaskedDoseIterator = boost::make_shared(spMaskAccessor, spDoseAccessor); DoseIteratorPointer spMaskedDoseIterator(spTestMaskedDoseIterator); CHECK_NO_THROW(core::DVHCalculator myDVHCalc(spMaskedDoseIterator, structureID, doseID)); //actual calculation is still missing RETURN_AND_REPORT_TEST_SUCCESS; } }//end namespace testing }//end namespace rttb diff --git a/testing/core/DVHTest.cpp b/testing/core/DVHTest.cpp index a338d42..1349013 100644 --- a/testing/core/DVHTest.cpp +++ b/testing/core/DVHTest.cpp @@ -1,191 +1,191 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include #include "litCheckMacros.h" #include "rttbBaseType.h" #include "rttbDVH.h" #include "DummyDoseAccessor.h" #include "DummyMaskAccessor.h" namespace rttb { namespace testing { typedef core::DVH::DataDifferentialType DataDifferentialType; /*! @brief DVHTest - test the API of DVH 1) test constructors (values as expected?) 2) test asignement 3) test set/getLabel 4) test set/get 5) test equality */ int DVHTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; //generate artificial DVH and corresponding statistical values DoseTypeGy binSize = DoseTypeGy(0.1); DoseVoxelVolumeType voxelVolume = 8; DataDifferentialType anEmptyDataDifferential; DataDifferentialType aDataDifferential; DataDifferentialType aDataDifferential2; DataDifferentialType aDataDifferentialRelative; DoseStatisticType maximum = 0; DoseStatisticType minimum = 0; double sum = 0; double squareSum = 0; DoseCalcType value = 0; DVHVoxelNumber numberOfVoxels = 0; // creat default values [0,100) for (int i = 0; i < 100; i++) { value = DoseCalcType((double(rand()) / RAND_MAX) * 1000); numberOfVoxels += value; aDataDifferential.push_back(value); aDataDifferential2.push_back(value * 10); if (value > 0) { maximum = (i + 0.5) * binSize; if (minimum == 0) { minimum = (i + 0.5) * binSize; } } sum += value * (i + 0.5) * binSize; squareSum += value * pow((i + 0.5) * binSize, 2); } DoseStatisticType mean = sum / numberOfVoxels; DoseStatisticType variance = (squareSum / numberOfVoxels - mean * mean); DoseStatisticType stdDeviation = pow(variance, 0.5); std::deque::iterator it; - for (it = aDataDifferential.begin(); it != aDataDifferential.end(); it++) + for (it = aDataDifferential.begin(); it != aDataDifferential.end(); ++it) { aDataDifferentialRelative.push_back((*it) / numberOfVoxels); } const IDType structureID = "myStructure"; const IDType doseID = "myDose"; const IDType voxelizationID = "myVoxelization"; //1) test default constructor (values as expected?) CHECK_THROW(core::DVH(anEmptyDataDifferential, binSize, voxelVolume, structureID, doseID, voxelizationID)); CHECK_THROW(core::DVH(anEmptyDataDifferential, binSize, voxelVolume, structureID, doseID)); CHECK_NO_THROW(core::DVH(aDataDifferential, binSize, voxelVolume, structureID, doseID, voxelizationID)); CHECK_NO_THROW(core::DVH(aDataDifferential, binSize, voxelVolume, structureID, doseID)); CHECK_THROW(core::DVH(aDataDifferential, 0, voxelVolume, structureID, doseID, voxelizationID)); CHECK_THROW(core::DVH(aDataDifferential, 0, voxelVolume, structureID, doseID)); CHECK_THROW(core::DVH(aDataDifferential, binSize, 0, structureID, doseID, voxelizationID)); CHECK_THROW(core::DVH(aDataDifferential, binSize, 0, structureID, doseID)); //2) test asignement core::DVH myTestDVH(aDataDifferential, binSize, voxelVolume, structureID, doseID); CHECK_NO_THROW(core::DVH myOtherDVH = myTestDVH); const core::DVH myOtherDVH = myTestDVH; CHECK_NO_THROW(core::DVH aDVH(myOtherDVH)); //3) test set/getLabel core::DVH myDVH(aDataDifferential, binSize, voxelVolume, structureID, doseID, voxelizationID); StructureLabel label = ""; CHECK_EQUAL(myDVH.getLabel(), label); CHECK_EQUAL(myDVH.getLabel(), label); label = "myLabel"; CHECK_NO_THROW(myDVH.setLabel(label)); CHECK_NO_THROW(myDVH.setLabel(label)); CHECK_EQUAL(myDVH.getLabel(), label); CHECK_EQUAL(myDVH.getLabel(), label); label = "myLabel2"; CHECK_NO_THROW(myDVH.setLabel(label)); CHECK_NO_THROW(myDVH.setLabel(label)); CHECK_EQUAL(myDVH.getLabel(), label); CHECK_EQUAL(myDVH.getLabel(), label); //4) test set/get //IDs CHECK_EQUAL(myDVH.getStructureID(), structureID); CHECK_EQUAL(myDVH.getDoseID(), doseID); CHECK_EQUAL(myDVH.getVoxelizationID(), voxelizationID); /*! is related to #2029*/ myDVH.setDoseID("somethingElse"); CHECK_EQUAL(myDVH.getDoseID(), "somethingElse"); CHECK_EQUAL(myDVH.getVoxelizationID(), voxelizationID); CHECK_EQUAL(myDVH.getStructureID(), structureID); /*! is related to #2029*/ myDVH.setStructureID("somethingOther"); CHECK_EQUAL(myDVH.getDoseID(), "somethingElse"); CHECK_EQUAL(myDVH.getVoxelizationID(), voxelizationID); CHECK_EQUAL(myDVH.getStructureID(), "somethingOther"); //dataDifferential CHECK(myDVH.getDataDifferential() == aDataDifferential); CHECK(myDVH.getDataDifferential(false) == aDataDifferential); CHECK(myDVH.getDataDifferential(true) == aDataDifferentialRelative); CHECK_EQUAL(myDVH.getNumberOfVoxels(), numberOfVoxels); CHECK_EQUAL(myDVH.getDeltaV(), voxelVolume); CHECK_EQUAL(myDVH.getDeltaD(), binSize); CHECK_EQUAL(myDVH.getMaximum(), maximum); CHECK_EQUAL(myDVH.getMinimum(), minimum); CHECK_EQUAL(myDVH.getMean(), mean); CHECK_EQUAL(myDVH.getVariance(), variance); CHECK_EQUAL(myDVH.getStdDeviation(), stdDeviation); int percentage = 20; VolumeType absVol = VolumeType(percentage * numberOfVoxels * voxelVolume / 100.0); CHECK_EQUAL(myDVH.getAbsoluteVolume(percentage), absVol); //5) test equality core::DVH myDVH2(aDataDifferential2, binSize, voxelVolume, structureID, doseID); CHECK(!(myDVH == myDVH2)); CHECK_EQUAL(myDVH, myDVH); core::DVH aDVH(myOtherDVH); CHECK_EQUAL(aDVH, myOtherDVH); RETURN_AND_REPORT_TEST_SUCCESS; } }//end namespace testing }//end namespace rttb diff --git a/testing/core/StructureTest.cpp b/testing/core/StructureTest.cpp index 493da11..c2fde4a 100644 --- a/testing/core/StructureTest.cpp +++ b/testing/core/StructureTest.cpp @@ -1,138 +1,138 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include "litCheckMacros.h" #include "rttbBaseType.h" #include "rttbStructure.h" #include "rttbInvalidParameterException.h" #include "DummyStructure.h" #include "DummyDoseAccessor.h" namespace rttb { namespace testing { /*! @brief StructureTest - tests the API for Structure 1) constructors 2) get/setXX */ int StructureTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; boost::shared_ptr spTestDoseAccessor = boost::make_shared(); DummyStructure myStructGenerator(spTestDoseAccessor->getGeometricInfo()); //1) constructors CHECK_NO_THROW(core::Structure()); core::Structure emptyTestStruct; CHECK_EQUAL("None", emptyTestStruct.getLabel()); CHECK_NO_THROW(emptyTestStruct.getUID()); GridIndexType zPlane = 4; core::Structure rect = myStructGenerator.CreateRectangularStructureCentered(zPlane); CHECK_NO_THROW(core::Structure(rect.getStructureVector())); core::Structure rect2 = core::Structure(rect.getStructureVector()); CHECK_EQUAL(rect.getLabel(), rect2.getLabel()); CHECK(rect.getUID() != rect2.getUID()); PolygonSequenceType rectVec = rect.getStructureVector(); PolygonSequenceType rect2Vec = rect2.getStructureVector(); CHECK_EQUAL(rectVec.size(), rect2Vec.size()); PolygonSequenceType::iterator it = rectVec.begin(); PolygonSequenceType::iterator it2 = rect2Vec.begin(); - for (; it != rectVec.end(); it++) + for (; it != rectVec.end(); ++it) { CHECK_EQUAL(it->size(), it2->size()); PolygonType::iterator pit = it->begin(); PolygonType::iterator pit2 = it2->begin(); - for (; pit != it->end(); pit++) + for (; pit != it->end(); ++pit) { CHECK_EQUAL(*(pit), *(pit2)); - pit2++; + ++pit2; } - it2++; + ++it2; } CHECK_NO_THROW(core::Structure rect3 = rect); core::Structure rect3 = rect; CHECK_EQUAL(rect.getLabel(), rect3.getLabel()); CHECK_EQUAL(rect.getUID(), rect3.getUID()); PolygonSequenceType rect3Vec = rect3.getStructureVector(); CHECK_EQUAL(rectVec.size(), rect3Vec.size()); it = rectVec.begin(); PolygonSequenceType::iterator it3 = rect3Vec.begin(); - for (; it != rectVec.end(); it++) + for (; it != rectVec.end(); ++it) { CHECK_EQUAL(it->size(), it3->size()); PolygonType::iterator pit = it->begin(); PolygonType::iterator pit3 = it3->begin(); - for (; pit != it->end(); pit++) + for (; pit != it->end(); ++pit) { CHECK_EQUAL(*(pit), *(pit3)); - pit3++; + ++pit3; } - it3++; + ++it3; } //2) get/setXX CHECK_EQUAL("None", emptyTestStruct.getLabel()); CHECK_NO_THROW(emptyTestStruct.setLabel("NEW Label")); CHECK_EQUAL("NEW Label", emptyTestStruct.getLabel()); CHECK_NO_THROW(emptyTestStruct.getUID()); CHECK_NO_THROW(emptyTestStruct.setUID("1.2.345.67.8.9")); CHECK_EQUAL("1.2.345.67.8.9", emptyTestStruct.getUID()); CHECK((emptyTestStruct.getStructureVector()).empty()); CHECK_EQUAL(0, emptyTestStruct.getNumberOfEndpoints()); CHECK_EQUAL(4, rect.getNumberOfEndpoints()); CHECK_EQUAL(rect.getNumberOfEndpoints(), rect2.getNumberOfEndpoints()); core::Structure circ = myStructGenerator.CreateTestStructureCircle(zPlane); CHECK_EQUAL(4004, circ.getNumberOfEndpoints()); core::Structure multiPoly = myStructGenerator.CreateTestStructureIntersectingTwoPolygonsInDifferentSlices(zPlane, zPlane + 1); CHECK_EQUAL(8, multiPoly.getNumberOfEndpoints()); RETURN_AND_REPORT_TEST_SUCCESS; } }//testing }//rttb diff --git a/testing/demoapps/DoseTool/DoseToolDVHTest.cpp b/testing/demoapps/DoseTool/DoseToolDVHTest.cpp index b5e9425..6d71d25 100644 --- a/testing/demoapps/DoseTool/DoseToolDVHTest.cpp +++ b/testing/demoapps/DoseTool/DoseToolDVHTest.cpp @@ -1,107 +1,106 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include #include "litCheckMacros.h" #include "boost/filesystem.hpp" namespace rttb { namespace testing { //path to the current running directory. DoseTool is in the same directory (Debug/Release) extern const char* _callingAppPath; static std::string readFile(const std::string& filename); int DoseToolDVHTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; std::string doseToolExecutable; std::string doseFilename; - std::string planFilename; std::string structFilename; std::string structName; std::string referenceXMLFilename; boost::filesystem::path callingPath(_callingAppPath); if (argc > 5) { doseToolExecutable = argv[1]; doseFilename = argv[2]; structFilename = argv[3]; structName = argv[4]; referenceXMLFilename = argv[5]; } std::string voxelizerToolExeWithPath = callingPath.parent_path().string() + "/" + doseToolExecutable; std::string defaultOutputFilename = "dicomDVHOutput.xml"; std::string baseCommand = voxelizerToolExeWithPath; baseCommand += " -d " + doseFilename; baseCommand += " -s " + structFilename; if (structName != "") { baseCommand += " -n " + structName; } else { baseCommand += " -u itk "; } std::string defaultDVHCommand = baseCommand + " -z " + defaultOutputFilename; std::cout << "Command line call: " + defaultDVHCommand << std::endl; CHECK_EQUAL(system(defaultDVHCommand.c_str()), 0); //check if file exists CHECK_EQUAL(boost::filesystem::exists(defaultOutputFilename), true); //check if file is the same than reference file std::string defaultAsIs = readFile(defaultOutputFilename); std::string defaultExpected = readFile(referenceXMLFilename); CHECK_EQUAL(defaultAsIs, defaultExpected); //delete file again CHECK_EQUAL(std::remove(defaultOutputFilename.c_str()), 0); RETURN_AND_REPORT_TEST_SUCCESS; } std::string readFile(const std::string& filename) { std::ifstream fileStream(filename.c_str()); std::string content((std::istreambuf_iterator(fileStream)), (std::istreambuf_iterator())); return content; } } //namespace testing } //namespace rttb diff --git a/testing/demoapps/VoxelizerTool/rttbVoxelizerToolVoxelizerAllStructsTest.cpp b/testing/demoapps/VoxelizerTool/rttbVoxelizerToolVoxelizerAllStructsTest.cpp index 96880da..da93109 100644 --- a/testing/demoapps/VoxelizerTool/rttbVoxelizerToolVoxelizerAllStructsTest.cpp +++ b/testing/demoapps/VoxelizerTool/rttbVoxelizerToolVoxelizerAllStructsTest.cpp @@ -1,111 +1,109 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include "litCheckMacros.h" #include #include /*! @brief VoxelizerToolTest. Tests a selection of structs. */ namespace rttb { namespace testing { //path to the current running directory. VoxelizerTool is in the same directory (Debug/Release) extern const char* _callingAppPath; int VoxelizerToolVoxelizerAllStructsTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; std::string voxelizerToolExe; std::string tempDirectory; std::string structFile; std::string referenceFile; if (argc > 4) { voxelizerToolExe = argv[1]; tempDirectory = argv[2]; structFile = argv[3]; referenceFile = argv[4]; } boost::filesystem::path callingPath(_callingAppPath); std::string voxelizerToolExeWithPath = callingPath.parent_path().string() + "/" + voxelizerToolExe; std::vector structNames; structNames.push_back("Niere.*"); structNames.push_back("Magen/DD"); structNames.push_back("PTV"); //structure names will be used for file naming, BUT '.' in the end will be cropped and '/' will be replaced by '_'. Thus, the different filenames. std::vector filenames; filenames.push_back("Niere re"); filenames.push_back("Magen_DD"); filenames.push_back("PTV"); std::string baseCommand = voxelizerToolExeWithPath; baseCommand += " -s " + structFile; baseCommand += " -r " + referenceFile; baseCommand += " -e \""; for (size_t i = 0; i < structNames.size(); i++) { std::string command = baseCommand + structNames.at(i) + "\""; std::cout << "Command line call: " + command << std::endl; int returnValue = system(command.c_str()); CHECK_EQUAL(returnValue, 0); - const std::string HDRfileName = tempDirectory + "/out_" + filenames.at(i) + ".hdr"; boost::filesystem::path HDRFile(tempDirectory); HDRFile /= "out_" + filenames.at(i) + ".hdr"; - const std::string IMGfileName = tempDirectory + "/out_" + filenames.at(i) + ".img"; boost::filesystem::path IMGFile(tempDirectory); IMGFile /= "out_" + filenames.at(i) + ".img"; CHECK_EQUAL( boost::filesystem::exists(HDRFile), true); CHECK_EQUAL( boost::filesystem::exists(IMGFile), true); if (boost::filesystem::exists(IMGFile)) { boost::filesystem::remove(IMGFile); } if (boost::filesystem::exists(HDRFile)) { boost::filesystem::remove(HDRFile); } } RETURN_AND_REPORT_TEST_SUCCESS; } } } diff --git a/testing/examples/CMakeLists.txt b/testing/examples/CMakeLists.txt index 261299f..e443665 100644 --- a/testing/examples/CMakeLists.txt +++ b/testing/examples/CMakeLists.txt @@ -1,41 +1,41 @@ #----------------------------------------------------------------------------- # Setup the system information test. Write out some basic failsafe # information in case the test doesn't run. #----------------------------------------------------------------------------- SET(CORE_TEST_EXAMPLES ${EXECUTABLE_OUTPUT_PATH}/rttbExamplesTests) SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) #----------------------------------------------------------------------------- ADD_TEST(RTBioModelExampleTest ${CORE_TEST_EXAMPLES} RTBioModelExampleTest "${TEST_DATA_ROOT}/TestDVH/dvh_PTV_HIT.txt" "${TEST_DATA_ROOT}/TestDVH/dvh_test_HT1.txt" "${TEST_DATA_ROOT}/TestDVH/dvh_test_HT2.txt" "${TEST_DATA_ROOT}/TestDVH/dvh_test_HT3.txt" "${TEST_DATA_ROOT}/TestDVH/dvh_test_TV.txt" "${TEST_DATA_ROOT}/TestDVH/dvh_virtuos_diff_trunk6.txt" "${TEST_DATA_ROOT}/TestDVH/dvh_virtuos_diff_trunk8.txt") ADD_TEST(DVHCalculatorExampleTest ${CORE_TEST_EXAMPLES} DVHCalculatorExampleTest "${TEST_DATA_ROOT}/DICOM/StructureSet/RS1.3.6.1.4.1.2452.6.841242143.1311652612.1170940299.4217870819.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwoGridScaling.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwoGridScaling05.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantFiftyGridScaling01.dcm") ADD_TEST(RTDoseIndexTest ${CORE_TEST_EXAMPLES} RTDoseIndexTest "${TEST_DATA_ROOT}/TestDVH/dvh_test_TV.txt" "${TEST_DATA_ROOT}/TestDVH/dvh_test_HT1.txt" "${TEST_DATA_ROOT}/TestDVH/dvh_test_HT2.txt" "${TEST_DATA_ROOT}/TestDVH/dvh_test_HT3.txt") ADD_TEST(RTDoseStatisticsTest ${CORE_TEST_EXAMPLES} RTDoseStatisticsTest "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo_withDoseGridScaling.dcm" "${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac101.dos.gz" "${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac000.vdx" "${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac101.pln") ADD_TEST(RTDVHTest ${CORE_TEST_EXAMPLES} RTDVHTest "${TEST_DATA_ROOT}/TestDVH/dvh_test.txt") ADD_TEST(DVHCalculatorExampleTest ${CORE_TEST_EXAMPLES} DVHCalculatorExampleTest "${TEST_DATA_ROOT}/DICOM/StructureSet/RS1.3.6.1.4.1.2452.6.841242143.1311652612.1170940299.4217870819.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo_withDoseGridScaling.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/LinearIncrease3D.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/LinearIncreaseX.dcm") ADD_TEST(RTBioModelScatterPlotExampleTest ${CORE_TEST_EXAMPLES} RTBioModelScatterPlotExampleTest "${TEST_DATA_ROOT}/TestDVH/dvh_PTV_HIT.txt" "${TEST_DATA_ROOT}/TestDVH/dvh_test_HT1.txt" "${TEST_DATA_ROOT}/TestDVH/dvh_test_TV.txt") ADD_TEST(VoxelizationValidationTest ${CORE_TEST_EXAMPLES} VoxelizationValidationTest "${TEST_DATA_ROOT}/DICOM/StructureSet/RS1.3.6.1.4.1.2452.6.841242143.1311652612.1170940299.4217870819.dcm" -"${TEST_DATA_ROOT}/DICOM/TestDose/LinearIncrease3D.dcm" "${TEST_DATA_ROOT}/boostMask/" "${TEST_DATA_ROOT}/OTBMask/") +"${TEST_DATA_ROOT}/DICOM/TestDose/LinearIncrease3D.dcm" "${TEST_DATA_ROOT}/BoostMask/" "${TEST_DATA_ROOT}/OTBMask/" "${TEST_DATA_ROOT}/BoostMaskRedesign/") RTTB_CREATE_TEST_MODULE(rttbExamples DEPENDS RTTBCore RTTBAlgorithms RTTBMasks RTTBOTBMask RTTBBoostMask RTTBIndices RTTBDicomIO RTTBVirtuosIO RTTBITKIO RTTBOtherIO RTTBModels PACKAGE_DEPENDS Litmus) diff --git a/testing/examples/DoseStatisticsIOVirtuosTest.cpp b/testing/examples/DoseStatisticsIOVirtuosTest.cpp deleted file mode 100644 index fe450bf..0000000 --- a/testing/examples/DoseStatisticsIOVirtuosTest.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// ----------------------------------------------------------------------- -// RTToolbox - DKFZ radiotherapy quantitative evaluation library -// -// Copyright (c) German Cancer Research Center (DKFZ), -// Software development for Integrated Diagnostics and Therapy (SIDT). -// ALL RIGHTS RESERVED. -// See rttbCopyright.txt or -// http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] -// -// This software is distributed WITHOUT ANY WARRANTY; without even -// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -// PURPOSE. See the above copyright notices for more information. -// -//------------------------------------------------------------------------ -/*! -// @file -// @version $Revision$ (last changed revision) -// @date $Date$ (last change date) -// @author $Author$ (last changed by) -*/ - -// this file defines the rttbCoreTests for the test driver -// and all it expects is that you have a function called RegisterTests - -#include -#include - -#include "litCheckMacros.h" - -#include "rttbBaseType.h" -#include "rttbDoseStatistics.h" -#include "rttbDoseStatisticsCalculator.h" -#include "rttbDoseStatisticsXMLWriter.h" -#include "rttbGenericMaskedDoseIterator.h" -#include "rttbDoseIteratorInterface.h" -#include "rttbVirtuosPlanFileDoseAccessorGenerator.h" -#include "rttbVirtuosDoseAccessor.h" -#include "rttbVirtuosFileStructureSetGenerator.h" -#include "rttbBoostMaskAccessor.h" - - -namespace rttb -{ - - namespace testing - { - - /*! @brief OtherIOTest - test the IO for dose statistics - 1) test exception - 2) test writing statistics to xml file - */ - - int DoseStatisticsIOVirtuosTest(int argc, char* argv[]) - { - typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; - typedef core::DoseIteratorInterface::DoseIteratorPointer DoseIteratorPointer; - typedef core::StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; - typedef boost::shared_ptr DoseStatisticsPtr; - typedef core::MaskAccessorInterface::MaskAccessorPointer MaskAccessorPointer; - typedef boost::shared_ptr GeometricInfoPointer; - - PREPARE_DEFAULT_TEST_REPORTING; - - std::string Struct_FILENAME; - std::string BPLCT_FILENAME; - std::string Dose_FILENAME; - std::string Plan_FILENAME; - std::string Struct_NAME; - - if (argc > 5) - { - Struct_FILENAME = argv[1]; - BPLCT_FILENAME = argv[2]; - Dose_FILENAME = argv[3]; - Plan_FILENAME = argv[4]; - Struct_NAME = argv[5]; - } - else - { - std::cout << "at least 5 arguments are expected in DoseStatisticsIOVirtuosTest." << std::endl; - } - - /* generate dummy dose */ - io::virtuos::VirtuosPlanFileDoseAccessorGenerator doseAccessorGenerator(Dose_FILENAME.c_str(), - Plan_FILENAME.c_str()); - - DoseAccessorPointer doseAccessor(doseAccessorGenerator.generateDoseAccessor()); - - StructureSetPointer rtStructureSet = io::virtuos::VirtuosFileStructureSetGenerator( - Struct_FILENAME.c_str(), BPLCT_FILENAME.c_str()).generateStructureSet(); - - GeometricInfoPointer geometricInfoPtr = boost::make_shared - (doseAccessor->getGeometricInfo()); - MaskAccessorPointer maskAccessorPtr = boost::make_shared - (rtStructureSet->getStructure(3), doseAccessor->getGeometricInfo()); - maskAccessorPtr->updateMask(); - - boost::shared_ptr spTestDoseIterator = - boost::make_shared(maskAccessorPtr, doseAccessor); - DoseIteratorPointer spDoseIterator(spTestDoseIterator); - - rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator(spDoseIterator); - rttb::algorithms::DoseStatistics::DoseStatisticsPointer doseStatistics = - myDoseStatsCalculator.calculateDoseStatistics(true); - - /* test writing statistcs to xml file */ - FileNameString fN = "doseStatisticsVirtuosComplex.xml"; - CHECK_NO_THROW(io::other::writeDoseStatistics(doseStatistics, fN)); - - RETURN_AND_REPORT_TEST_SUCCESS; - } - - }//testing -}//rttb - diff --git a/testing/examples/RTBioModelExampleTest.cpp b/testing/examples/RTBioModelExampleTest.cpp index f5950b9..0bf268a 100644 --- a/testing/examples/RTBioModelExampleTest.cpp +++ b/testing/examples/RTBioModelExampleTest.cpp @@ -1,415 +1,415 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ // this file defines the rttbCoreTests for the test driver // and all it expects is that you have a function called RegisterTests #include #include "litCheckMacros.h" #include "rttbBioModel.h" #include "rttbDVHTxtFileReader.h" #include "rttbDVH.h" #include "rttbTCPLQModel.h" #include "rttbNTCPLKBModel.h" #include "rttbNTCPRSModel.h" #include "rttbBioModelScatterPlots.h" #include "rttbBioModelCurve.h" #include "rttbDvhBasedModels.h" #include "rttbDoseIteratorInterface.h" namespace rttb { namespace testing { /*! @brief RTBioModelTest. TCP calculated using a DVH PTV and LQ Model. NTCP tested using 3 Normal Tissue DVHs and LKB/RS Model. Test if calculation in new architecture returns similar results to the original implementation. WARNING: The values for comparison need to be adjusted if the input files are changed! */ int RTBioModelExampleTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; typedef rttb::models::CurveDataType CurveDataType; typedef std::multimap > ScatterPlotType; typedef core::DVH::DVHPointer DVHPointer; //increased accuracy requires double values in the calculation (rttbBaseType.h) double toleranceEUD = 1e-5; double tolerance = 1e-7; //ARGUMENTS: 1: ptv dvh file name // 2: normal tissue 1 dvh file name // 3: normal tissue 2 dvh file name // 4: normal tissue 3 dvh file name //...........5: Virtuos MPM_LR_ah dvh lung file name //...........6: Virtuos MPM_LR_ah dvh target file name std::string DVH_FILENAME_PTV; std::string DVH_FILENAME_NT1; std::string DVH_FILENAME_NT2; std::string DVH_FILENAME_NT3; std::string DVH_FILENAME_TV_TEST; std::string DVH_Virtuos_Target; std::string DVH_Virtuos_Lung; if (argc > 1) { DVH_FILENAME_PTV = argv[1]; } if (argc > 2) { DVH_FILENAME_NT1 = argv[2]; } if (argc > 3) { DVH_FILENAME_NT2 = argv[3]; } if (argc > 4) { DVH_FILENAME_NT3 = argv[4]; } if (argc > 5) { DVH_FILENAME_TV_TEST = argv[5]; } if (argc > 6) { DVH_Virtuos_Lung = argv[6]; } if (argc > 7) { DVH_Virtuos_Target = argv[7]; } //DVH PTV rttb::io::other::DVHTxtFileReader dvhReader = rttb::io::other::DVHTxtFileReader(DVH_FILENAME_PTV); DVHPointer dvhPtr = dvhReader.generateDVH(); CHECK_CLOSE(6.04759613161786830000e+001, models::getEUD(dvhPtr, 10), toleranceEUD); rttb::io::other::DVHTxtFileReader dvhReader_test_tv = rttb::io::other::DVHTxtFileReader( DVH_FILENAME_TV_TEST); DVHPointer dvh_test_tv = dvhReader_test_tv.generateDVH(); //test TCP LQ Model models::BioModelParamType alpha = 0.35; models::BioModelParamType beta = 0.023333333333333; models::BioModelParamType roh = 10000000; int numFractions = 2; DoseTypeGy normalizationDose = 68; rttb::models::TCPLQModel tcplq = rttb::models::TCPLQModel(dvhPtr, alpha, beta, roh, numFractions); CHECK_EQUAL(alpha, tcplq.getAlphaMean()); CHECK_EQUAL(alpha / beta, tcplq.getAlphaBeta()); CHECK_EQUAL(roh, tcplq.getRho()); CHECK_NO_THROW(tcplq.init()); if (tcplq.init()) { CHECK_CLOSE(1.00497232941856940000e-127, tcplq.getValue(), tolerance); } CurveDataType curve = models::getCurveDoseVSBioModel(tcplq, normalizationDose); CurveDataType::iterator it; - for (it = curve.begin(); it != curve.end(); it++) + for (it = curve.begin(); it != curve.end(); ++it) { if ((*it).first < 72) { CHECK_EQUAL(0, (*it).second); } else if ((*it).first > 150) { CHECK((*it).second > 0.9); } } models::BioModelParamType alphaBeta = 10; tcplq.setParameters(alpha, alphaBeta, roh, 0.08); CHECK_EQUAL(alpha, tcplq.getAlphaMean()); CHECK_EQUAL(alphaBeta, tcplq.getAlphaBeta()); CHECK_EQUAL(roh, tcplq.getRho()); if (tcplq.init()) { CHECK_CLOSE(1.84e-005, tcplq.getValue(), tolerance); } normalizationDose = 40; curve = models::getCurveDoseVSBioModel(tcplq, normalizationDose); alpha = 1; alphaBeta = 14.5; tcplq.setAlpha(alpha); tcplq.setAlphaBeta(alphaBeta); tcplq.setRho(roh); CHECK_EQUAL(alpha, tcplq.getAlphaMean()); CHECK_EQUAL(alphaBeta, tcplq.getAlphaBeta()); CHECK_EQUAL(roh, tcplq.getRho()); if (tcplq.init()) { CHECK_CLOSE(0.954885, tcplq.getValue(), toleranceEUD); } alpha = 0.9; alphaBeta = 1; tcplq.setAlpha(alpha); tcplq.setAlphaBeta(alphaBeta); tcplq.setRho(roh); CHECK_EQUAL(alpha, tcplq.getAlphaMean()); CHECK_EQUAL(alphaBeta, tcplq.getAlphaBeta()); CHECK_EQUAL(roh, tcplq.getRho()); if (tcplq.init()) { CHECK_EQUAL(1, tcplq.getValue()); } //TCP LQ Test alpha = 0.3; beta = 0.03; roh = 10000000; numFractions = 20; rttb::models::TCPLQModel tcplq_test = rttb::models::TCPLQModel(dvh_test_tv, alpha, beta, roh, numFractions); CHECK_EQUAL(alpha, tcplq_test.getAlphaMean()); CHECK_EQUAL(alpha / beta, tcplq_test.getAlphaBeta()); CHECK_EQUAL(roh, tcplq_test.getRho()); CHECK_NO_THROW(tcplq_test.init()); if (tcplq_test.init()) { CHECK_CLOSE(9.79050278878883180000e-001, tcplq_test.getValue(), tolerance); } normalizationDose = 60; curve = models::getCurveDoseVSBioModel(tcplq_test, normalizationDose); //DVH HT 1 rttb::io::other::DVHTxtFileReader dvhReader2 = rttb::io::other::DVHTxtFileReader(DVH_FILENAME_NT1); DVHPointer dvhPtr2 = dvhReader2.generateDVH(); CHECK_CLOSE(1.07920836034015810000e+001, models::getEUD(dvhPtr2, 10), toleranceEUD); //test RTNTCPLKBModel rttb::models::NTCPLKBModel lkb = rttb::models::NTCPLKBModel(); models::BioModelParamType aVal = 10; models::BioModelParamType mVal = 0.16; models::BioModelParamType d50Val = 55; CHECK_EQUAL(0, lkb.getA()); CHECK_EQUAL(0, lkb.getM()); CHECK_EQUAL(0, lkb.getD50()); lkb.setDVH(dvhPtr2); CHECK_EQUAL(dvhPtr2, lkb.getDVH()); lkb.setA(aVal); CHECK_EQUAL(aVal, lkb.getA()); lkb.setM(mVal); CHECK_EQUAL(mVal, lkb.getM()); lkb.setD50(d50Val); CHECK_EQUAL(d50Val, lkb.getD50()); CHECK_NO_THROW(lkb.init()); if (lkb.init()) { CHECK_CLOSE(2.53523522831366570000e-007, lkb.getValue(), tolerance); } //test RTNTCPRSModel rttb::models::NTCPRSModel rs = rttb::models::NTCPRSModel(); models::BioModelParamType gammaVal = 1.7; models::BioModelParamType sVal = 1; CHECK_EQUAL(0, rs.getGamma()); CHECK_EQUAL(0, rs.getS()); CHECK_EQUAL(0, rs.getD50()); rs.setDVH(dvhPtr2); CHECK_EQUAL(dvhPtr2, rs.getDVH()); rs.setD50(d50Val); CHECK_EQUAL(d50Val, rs.getD50()); rs.setGamma(gammaVal); CHECK_EQUAL(gammaVal, rs.getGamma()); rs.setS(sVal); CHECK_EQUAL(sVal, rs.getS()); CHECK_NO_THROW(rs.init()); if (rs.init()) { CHECK_CLOSE(3.70385888626145740000e-009, rs.getValue(), tolerance); } //DVH HT 2 rttb::io::other::DVHTxtFileReader dvhReader3 = rttb::io::other::DVHTxtFileReader(DVH_FILENAME_NT2); DVHPointer dvhPtr3 = dvhReader3.generateDVH(); CHECK_CLOSE(1.26287047025885110000e+001, models::getEUD(dvhPtr3, 10), toleranceEUD); //test RTNTCPLKBModel aVal = 10; mVal = 0.16; d50Val = 55; lkb.setDVH(dvhPtr3); CHECK_EQUAL(dvhPtr3, lkb.getDVH()); lkb.setA(aVal); CHECK_EQUAL(aVal, lkb.getA()); lkb.setM(mVal); CHECK_EQUAL(mVal, lkb.getM()); lkb.setD50(d50Val); CHECK_EQUAL(d50Val, lkb.getD50()); if (lkb.init()) { CHECK_CLOSE(7.36294657754956700000e-007, lkb.getValue(), tolerance); } //test RTNTCPRSModel rs = rttb::models::NTCPRSModel(); gammaVal = 1.7; sVal = 1; CHECK_EQUAL(0, rs.getGamma()); CHECK_EQUAL(0, rs.getS()); CHECK_EQUAL(0, rs.getD50()); rs.setDVH(dvhPtr3); CHECK_EQUAL(dvhPtr3, rs.getDVH()); rs.setD50(d50Val); CHECK_EQUAL(d50Val, rs.getD50()); rs.setGamma(gammaVal); CHECK_EQUAL(gammaVal, rs.getGamma()); rs.setS(sVal); CHECK_EQUAL(sVal, rs.getS()); if (rs.init()) { CHECK_CLOSE(1.76778795490939440000e-007, rs.getValue(), tolerance); } //DVH HT 3 rttb::io::other::DVHTxtFileReader dvhReader4 = rttb::io::other::DVHTxtFileReader(DVH_FILENAME_NT3); DVHPointer dvhPtr4 = dvhReader4.generateDVH(); CHECK_CLOSE(2.18212982041056310000e+001, models::getEUD(dvhPtr4, 10), toleranceEUD); //test RTNTCPLKBModel aVal = 10; mVal = 0.16; d50Val = 55; lkb.setDVH(dvhPtr4); CHECK_EQUAL(dvhPtr4, lkb.getDVH()); lkb.setA(aVal); CHECK_EQUAL(aVal, lkb.getA()); lkb.setM(mVal); CHECK_EQUAL(mVal, lkb.getM()); lkb.setD50(d50Val); CHECK_EQUAL(d50Val, lkb.getD50()); if (lkb.init()) { CHECK_CLOSE(8.15234192641929420000e-005, lkb.getValue(), tolerance); } //test RTNTCPRSModel rs = rttb::models::NTCPRSModel(); gammaVal = 1.7; sVal = 1; CHECK_EQUAL(0, rs.getGamma()); CHECK_EQUAL(0, rs.getS()); CHECK_EQUAL(0, rs.getD50()); rs.setDVH(dvhPtr4); CHECK_EQUAL(dvhPtr4, rs.getDVH()); rs.setD50(d50Val); CHECK_EQUAL(d50Val, rs.getD50()); rs.setGamma(gammaVal); CHECK_EQUAL(gammaVal, rs.getGamma()); rs.setS(sVal); CHECK_EQUAL(sVal, rs.getS()); if (rs.init()) { CHECK_CLOSE(2.02607985020919480000e-004, rs.getValue(), tolerance); } //test using Virtuos Pleuramesotheliom MPM_LR_ah //DVH PTV rttb::io::other::DVHTxtFileReader dR_Target = rttb::io::other::DVHTxtFileReader(DVH_Virtuos_Target); DVHPointer dvhPtrTarget = dR_Target.generateDVH(); rttb::io::other::DVHTxtFileReader dR_Lung = rttb::io::other::DVHTxtFileReader(DVH_Virtuos_Lung); DVHPointer dvhPtrLung = dR_Lung.generateDVH(); //test TCP LQ Model models::BioModelParamType alphaMean = 0.34; models::BioModelParamType alphaVarianz = 0.02; models::BioModelParamType alpha_beta = 28; models::BioModelParamType rho = 1200; int numFractionsVirtuos = 27; rttb::models::TCPLQModel tcplqVirtuos = rttb::models::TCPLQModel(dvhPtrTarget, rho, numFractionsVirtuos, alpha_beta, alphaMean, alphaVarianz); if (tcplqVirtuos.init()) { CHECK_CLOSE(0.8894, tcplqVirtuos.getValue(), 1e-4); } models::BioModelParamType d50Mean = 20; models::BioModelParamType m = 0.36; models::BioModelParamType a = 1.06; rttb::models::NTCPLKBModel lkbVirtuos = rttb::models::NTCPLKBModel(dvhPtrLung, d50Mean, m, a); if (lkbVirtuos.init()) { CHECK_CLOSE(0.0397, lkbVirtuos.getValue(), 1e-4); } RETURN_AND_REPORT_TEST_SUCCESS; } }//testing }//rttb diff --git a/testing/examples/RTDVHCalculatorTest.cpp b/testing/examples/RTDVHCalculatorTest.cpp deleted file mode 100644 index fe89538..0000000 --- a/testing/examples/RTDVHCalculatorTest.cpp +++ /dev/null @@ -1,280 +0,0 @@ -// ----------------------------------------------------------------------- -// RTToolbox - DKFZ radiotherapy quantitative evaluation library -// -// (c) Copyright 2007, DKFZ, Heidelberg, Germany -// ALL RIGHTS RESERVED -// -// THIS FILE CONTAINS CONFIDENTIAL AND PROPRIETARY INFORMATION OF DKFZ. -// ANY DUPLICATION, MODIFICATION, DISTRIBUTION, OR -// DISCLOSURE IN ANY FORM, IN WHOLE, OR IN PART, IS STRICTLY PROHIBITED -// WITHOUT THE PRIOR EXPRESS WRITTEN PERMISSION OF DKFZ. -// -//------------------------------------------------------------------------ -/*! -// @file -// @version $Revision$ (last changed revision) -// @date $Date$ (last change date) -// @author zhangl (last changed by) -// @author *none* (Reviewer) -// @author zhangl (Programmer) -// -// Subversion HeadURL: $HeadURL: http://sidt-hpc1/dkfz_repository/NotMeVisLab/SIDT/RTToolbox/branch/restructure/testing/core/DVHCalculatorTest.cpp $ -*/ - -// this file defines the rttbCoreTests for the test driver -// and all it expects is that you have a function called RegisterTests - - -#include "litCheckMacros.h" - - -#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ - -#include "dcmtk/dcmrt/drtdose.h" -#include "dcmtk/dcmrt/drtstruct.h" - -#include "../../code/core/rttbMaskedDoseIteratorInterface_NEW.h" -#include "../../code/core/rttbGenericMaskedDoseIterator_NEW.h" -#include "../../code/core/rttbGenericDoseIterator_NEW.h" -#include "../../code/core/rttbDVHCalculator.h" -#include "../../code/io/dicom/rttbDicomStructureSet.h" -#include "../../code/core/rttbStructure.h" -#include "../../code/core/rttbNullPointerException.h" -#include "../../code/core/rttbInvalidParameterException.h" -#include "../../code/core/rttbInvalidDoseException.h" -#include "../../code/core/rttbException.h" -#include "../../code/core/rttbBaseType_NEW.h" - -//#include "CreateTestStructure.h" - currently not used - -// sollen externe Methoden verwendet werden? -#include "../../code/io/dicom/rttbDicomDoseAccessor.h" -#include "../../code/masks/rttbOTBMaskAccessor.h" -#include "../../code/algorithms/rttbDoseStatistics.h" - -#include - - - - - -namespace rttb -{ - - namespace testing - { - - - - /*! @brief DVHCalculatorTest. - 1. DVH calculator test: use MaskedDicomDoseIterator(&rtdose, &rtstr) -> now use rttbGenericMaskedDoseIterator_NEW.h - 2. DVH Calculator test: use cache of MaskedDicomDoseIterator resetDose -> obsolete - 3. DVH Calculator test: use MaskedDicomDoseIteratorRandomSampling ->obsolete use dummy mask? - 4. DVH Calculator test: use DicomDoseIteratorUseMask -> obsolete/ ../io/dicom? - 5. DVH Calculator test: use manually generated structure and Mask(DoseIteratorInterface*, Structure* ) -> check if this should be done here or in ../masks - 6. DVHCalculation Test using constructor Mask( DoseIteratorInterface* aDoseIter , std::vector aDoseVoxelVector ); -> mask generation should be tested in mask tests use default mask here - */ - - - /*static std::string MASK_FILENAME="../../../RTToolbox/testing/data/DICOM/TestDose/TestMask.dcm"; - static std::string HITDOSE_FILENAME="d:/Dotmobi-SVN/rondo/RondoInterface/Resources/TestData/Dicom/HITDemoData/RTDOSE/HITDemoData_20100316T175500_RTDOSE_450.IMA"; - static std::string HITSTRUCT_FILENAME="d:/Dotmobi-SVN/rondo/RondoInterface/Resources/TestData/Dicom/HITDemoData/RTSTRUCT/HITDemoData_20100407T152606_RTSTRUCT_0.IMA"; - static std::string HITBEAMDOSE_FILENAME="d:/Dotmobi-SVN/rondo/RondoInterface/Resources/TestData/Dicom/HITDemoData/RTDOSE/HITDemoData_20100407T152314_RTDOSE_0.IMA"; - - static std::string DVH_FILENAME_WRITE="../../../RTToolbox/testing/data/TestDVH/dvh_write.txt"; - - static std::string DVH_FILENAME_PTV_HIT="../../../RTToolbox/testing/data/TestDVH/dvh_PTV_HIT.txt";*/ - - int RTDVHCalculatorTest(int argc, char* argv[]) - { - typedef core::GenericDoseIterator::DoseAcessorPointer DoseAcessorPointer; - typedef core::DoseIteratorInterface::DoseIteratorPointer DoseIteratorPointer; - typedef core::MaskedDoseIteratorInterface::MaskAcessorPointer MaskAcessorPointer; - typedef masks::OTBMaskAccessor::StructTypePointer StructTypePointer; - typedef algorithms::DoseStatistics::ResultListPointer ResultListPointer; - - - PREPARE_DEFAULT_TEST_REPORTING; - //ARGUMENTS: 1: structure file name - // 2: dose1 file name - // 3: dose2 file name - // 4: dose3 file name - - std::string STRUCT_FILENAME; - std::string DOSE_FILENAME; - std::string DOSE2_FILENAME; - std::string DOSE3_FILENAME; - - /*STRUCT_FILENAME="d:/RTToolboxProj/trunk/testing/data/DICOM/H000090/RTSTRUCT/H000090_20100610T144958_RTSTRUCT_479.IMA"; - DOSE_FILENAME="d:/RTToolboxProj/trunk/testing/data/DICOM/H000090/RTDOSE/H000090_20100610T144958_RTDOSE_396.IMA"; - DOSE2_FILENAME="d:/RTToolboxProj/trunk/testing/data/DICOM/H000090/RTDOSE/H000090_20100610T144958_RTDOSE_397.IMA"; - DOSE3_FILENAME="d:/RTToolboxProj/trunk/testing/data/DICOM/H000090/RTDOSE/H000090_20100610T144958_RTDOSE_398.IMA";*/ - - - if (argc > 1) - { - STRUCT_FILENAME = argv[1]; - } - - if (argc > 2) - { - DOSE_FILENAME = argv[2]; - } - - if (argc > 3) - { - DOSE2_FILENAME = argv[3]; - } - - if (argc > 4) - { - DOSE3_FILENAME = argv[4]; - } - - - // CHECK_THROW_EXPLICIT(core::DVHCalculator(NULL,"",""),core::NullPointerException); -> tested in individual test - - OFCondition status; - DcmFileFormat fileformat; - - /* read dicom-rt dose */ - ::DRTDoseIOD rtdose; - // rttb::core::Structure* rtstr; - double deltaD = 0; - double deltaV = 0.03; - // use new architecture accessor+iterator - // import dicom files - CHECK_NO_THROW(DoseAcessorPointer spDoseAccessor(new core::DicomDoseAccessor( - DOSE_FILENAME.c_str()))); - DoseAcessorPointer spDoseAccessor(new core::DicomDoseAccessor(DOSE_FILENAME.c_str())); - std::cout << "ProcessDose " << DOSE_FILENAME.c_str() << std::endl; - core::GenericDoseIterator ddit(spDoseAccessor); - - /* read dicom-rt structure set*/ - // use toolbox extensions from Andreas?!: DoseToolDCMStructLoader.h - ::DRTStructureSetIOD rtstruct; - status = fileformat.loadFile(STRUCT_FILENAME.c_str()); - CHECK(status.good()); - - if (!status.good()) - { - std::cerr << "Error: load rt structure set loadFile(STRUCT_FILENAME) failed!" << std::endl; - return -1; - } - - status = rtstruct.read(*fileformat.getDataset()); - - if (!status.good()) - { - std::cerr << "Error: read DRTStructureSetIOD failed!" << std::endl; - return -1; - } - - //get maximum of rtdose - - CHECK_NO_THROW(rttb::core::DicomStructureSet rtstrset = rttb::core::DicomStructureSet(&rtstruct)); - rttb::core::DicomStructureSet rtstrset = rttb::core::DicomStructureSet(&rtstruct); - - clock_t start(clock()); - //std::vector mditVector; - ResultListPointer resultListMax; - ResultListPointer resultListMin; - - if (rtstrset.getNumberOfStructures() > 0) - { - for (int j = 0; j < rtstrset.getNumberOfStructures(); j++) - { - clock_t startLoop(clock()); - StructTypePointer spRtstr = boost::make_shared(*(rtstrset.getStructure(j))); - rttb::core::GeometricInfo geoInf = ddit.getGeometricInfo(); - - boost::shared_ptr pOTBMaskAccessor = - boost::make_shared(spRtstr, geoInf, rtdose); - MaskAcessorPointer spMaskAccessor(pOTBMaskAccessor); - boost::shared_ptr spTestMaskedDoseIterator = - boost::make_shared(spMaskAccessor, - spDoseAccessor); //using Mask - DoseIteratorPointer mddit(spTestMaskedDoseIterator); - mddit->reset(); - std::cout << "Initialization time: " << (clock() - startLoop) / 1000 << " s" << std::endl; - - rttb::core::DVHCalculator calc(mddit, spRtstr->getUID(), mddit->getDoseUID()); - rttb::core::DVH dvh = *(calc.getDVH()); - std::cout << "DVH(mask) max: " << dvh.getMaximum() << "in " << spRtstr->getVolume() << " cm^3" << - std::endl; - } - } - - clock_t finish(clock()); - - std::cout << "DVH Calculation time: " << (finish - start) / 60000 << " min" << std::endl; - - DoseAcessorPointer spDoseAccessor2(new core::DicomDoseAccessor(DOSE2_FILENAME.c_str())); - std::cout << "ProcessDose " << DOSE2_FILENAME.c_str() << std::endl; - - start = clock(); - resultListMax.reset(); - resultListMin.reset(); - - if (rtstrset.getNumberOfStructures() > 0) - { - for (int j = 0; j < rtstrset.getNumberOfStructures(); j++) - { - clock_t startLoop(clock()); - StructTypePointer spRtstr = boost::make_shared(*(rtstrset.getStructure(j))); - rttb::core::GeometricInfo geoInf = ddit.getGeometricInfo(); - - boost::shared_ptr pOTBMaskAccessor = - boost::make_shared(spRtstr, geoInf, rtdose); - MaskAcessorPointer spMaskAccessor(pOTBMaskAccessor); - boost::shared_ptr spTestMaskedDoseIterator = - boost::make_shared(spMaskAccessor, - spDoseAccessor2); //using Mask - DoseIteratorPointer mddit(spTestMaskedDoseIterator); - boost::shared_ptr spTestDoseIterator = - boost::make_shared(spDoseAccessor2); - DoseIteratorPointer ddit2(spTestDoseIterator); - mddit->reset(); - std::cout << "Initialization time: " << (clock() - startLoop) / 1000 << " s" << std::endl; - - rttb::core::DVHCalculator calc2(mddit, spRtstr->getUID(), mddit->getDoseUID()); - rttb::core::DVH dvh2 = *(calc2.getDVH()); - std::cout << "DVH(mask) max: " << dvh2.getMaximum() << "in " << spRtstr->getVolume() << " cm^3" << - std::endl; - - rttb::core::DVHCalculator calc(ddit2, spRtstr->getUID(), ddit2->getDoseUID()); - rttb::core::DVH dvh = *(calc.getDVH()); - std::cout << "DVH(dose) max: " << dvh.getMaximum() << std::endl; - - - //test statistics here? -> ../algorithms - clock_t startStats(clock()); - std::cout << "Statistics: " << std::endl; - rttb::algorithms::DoseStatistics stat(mddit); - std::cout << "Statistics time: " << (clock() - startStats) << " s" << std::endl; - std::cout << "max: " << stat.getMaximum(resultListMax) << std::endl; - std::cout << "mean: " << stat.getMean() << std::endl; - std::cout << "min: " << stat.getMinimum(resultListMin) << std::endl; - std::cout << "std: " << stat.getStdDeviation() << std::endl; - std::cout << "var: " << stat.getVariance() << std::endl; - } - } - - finish = clock(); - - std::cout << "Calculation time: " << (finish - start) / 60000 << " min" << std::endl; - - - - - RETURN_AND_REPORT_TEST_SUCCESS; - - - } - - - - - }//testing -}//rttb - diff --git a/testing/examples/RTDVHTest.cpp b/testing/examples/RTDVHTest.cpp index 2d9cbb8..3bd8bf1 100644 --- a/testing/examples/RTDVHTest.cpp +++ b/testing/examples/RTDVHTest.cpp @@ -1,109 +1,110 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ // this file defines the rttbCoreTests for the test driver // and all it expects is that you have a function called RegisterTests #include "litCheckMacros.h" #include "rttbDVH.h" #include "rttbDVHTxtFileReader.h" #include "rttbBaseType.h" #include "rttbDvhBasedModels.h" namespace rttb { namespace testing { /*! @brief RTDVHTest. Max, min, mean, modal, median, Vx, Dx, EUD, BED, LQED2 are tested. Test if calculation in new architecture returns similar results to the original implementation. Comparison of actual DVH values is performed in DVHCalculatorComparisonTest.cpp. WARNING: The values for comparison need to be adjusted if the input files are changed! */ int RTDVHTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; //ARGUMENTS: 1: dvh file name std::string RTDVH_FILENAME_PTV; if (argc > 1) { RTDVH_FILENAME_PTV = argv[1]; } typedef core::DVH::DVHPointer DVHPointer; /*test RT dvh*/ rttb::io::other::DVHTxtFileReader dvhReader = rttb::io::other::DVHTxtFileReader(RTDVH_FILENAME_PTV); const DoseCalcType expectedValue = 0.01305; //dvhReader DVHPointer dvh = dvhReader.generateDVH(); CHECK_CLOSE(expectedValue, models::getEUD(dvh, 10), errorConstant); std::cout << models::getEUD(dvh, 10) << std::endl; - std::map bedMap = models::calcBEDDVH(dvh, 15, 15); - std::map LqedMap = models::calcLQED2DVH(dvh, 15, 10); + CHECK_NO_THROW(models::calcBEDDVH(dvh, 15, 15)); CHECK_NO_THROW(models::calcLQED2DVH(dvh, 15, 10)); CHECK_NO_THROW(dvh->getDataDifferential(true)); CHECK_EQUAL(1, dvh->calcCumulativeDVH(true).at(0)); CHECK_NO_THROW(models::calcBEDDVH(dvh, 15, 15, true)); CHECK_NO_THROW(models::calcLQED2DVH(dvh, 15, 10, true)); //test statistics (relative cumulative data) CHECK_CLOSE(expectedValue, dvh->getMaximum(), errorConstant); CHECK_CLOSE(expectedValue, dvh->getMinimum(), errorConstant); CHECK_CLOSE(expectedValue, dvh->getMean(), errorConstant); CHECK_CLOSE(expectedValue, dvh->getMedian(), errorConstant); CHECK_CLOSE(expectedValue, dvh->getModal(), errorConstant); CHECK_EQUAL(0, dvh->getVx(0.014)); CHECK_EQUAL(0.125, dvh->getVx(0.01)); CHECK_CLOSE(0.0131, dvh->getDx(100), errorConstant + errorConstant * 10); CHECK_CLOSE(0.013, dvh->getDx(249), errorConstant); CHECK_EQUAL(0, dvh->getDx(251)); - //test statistics (absolute comulative data) + + //test statistics (absolute cumulative data) CHECK_EQUAL(2000, dvh->calcCumulativeDVH(false).at(0)); CHECK_EQUAL(0, dvh->getVx(0.014)); CHECK_EQUAL(250, dvh->getVx(0.01)); CHECK_CLOSE(0.0131, dvh->getDx(100), errorConstant + errorConstant * 10); CHECK_CLOSE(0.013, dvh->getDx(249), errorConstant); CHECK_EQUAL(0, dvh->getDx(251)); + RETURN_AND_REPORT_TEST_SUCCESS; } }//testing }//rttb diff --git a/testing/examples/RTDoseStatisticsTest.cpp b/testing/examples/RTDoseStatisticsTest.cpp index ecc3b28..e3d5818 100644 --- a/testing/examples/RTDoseStatisticsTest.cpp +++ b/testing/examples/RTDoseStatisticsTest.cpp @@ -1,206 +1,206 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ // this file defines the rttbCoreTests for the test driver // and all it expects is that you have a function called RegisterTests #include #include #include "litCheckMacros.h" #include "rttbDoseStatistics.h" #include "rttbDoseStatisticsCalculator.h" #include "rttbDicomDoseAccessor.h" #include "rttbDicomFileDoseAccessorGenerator.h" #include "rttbVirtuosPlanFileDoseAccessorGenerator.h" #include "rttbVirtuosFileStructureSetGenerator.h" #include "rttbBoostMaskAccessor.h" #include "rttbGenericMaskedDoseIterator.h" #include "rttbGenericDoseIterator.h" #include "rttbBaseType.h" namespace rttb { namespace testing { PREPARE_DEFAULT_TEST_REPORTING; /*! @brief RTDoseStatisticsTest. Max, min, mean, standard deviation, variance, Vx, Dx, MOHx, MOCx, MaxOHx, MinOCx are tested. Test if calculation in new architecture returns similar results to the original implementation. WARNING: The values for comparison need to be adjusted if the input files are changed!*/ const double reducedErrorConstant = 0.0001; const double expectedVal = 5.64477e-005; const double volume = 24120.; void testWithDummyDoseData(const std::string& doseFilename); void testWithRealVirtuosDoseDataAndStructure(const std::string& doseFilename, const std::string& structFilename, const std::string& planFilename, unsigned int structureNr); int RTDoseStatisticsTest(int argc, char* argv[]) { std::string RTDOSE_FILENAME; std::string RTDOSE2_FILENAME; std::string RTSTRUCT_FILENAME; std::string RTPLAN_FILENAME; if (argc > 4) { RTDOSE_FILENAME = argv[1]; RTDOSE2_FILENAME = argv[2]; RTSTRUCT_FILENAME = argv[3]; RTPLAN_FILENAME = argv[4]; } else { std::cout << "at least four arguments required for RTDoseStatisticsTest" << std::endl; return -1; } testWithDummyDoseData(RTDOSE_FILENAME); //Structure 2 is RUECKENMARK testWithRealVirtuosDoseDataAndStructure(RTDOSE2_FILENAME, RTSTRUCT_FILENAME, RTPLAN_FILENAME, 2); RETURN_AND_REPORT_TEST_SUCCESS; } void testWithDummyDoseData(const std::string& doseFilename) { typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; typedef core::GenericDoseIterator::DoseIteratorPointer DoseIteratorPointer; typedef algorithms::DoseStatisticsCalculator::ResultListPointer ResultListPointer; io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator1(doseFilename.c_str()); DoseAccessorPointer doseAccessor1(doseAccessorGenerator1.generateDoseAccessor()); //create corresponding DoseIterator ::boost::shared_ptr spDoseIteratorTmp = ::boost::make_shared(doseAccessor1); DoseIteratorPointer spDoseIterator(spDoseIteratorTmp); rttb::algorithms::DoseStatisticsCalculator doseStatisticsCalculator(spDoseIterator); std::vector precomputedDoseValues; precomputedDoseValues.push_back(0); precomputedDoseValues.push_back(0.5); precomputedDoseValues.push_back(1); std::vector precomputedVolumeValues; precomputedVolumeValues.push_back(20000 / volume); precomputedVolumeValues.push_back(1); rttb::algorithms::DoseStatistics::DoseStatisticsPointer doseStatistics = doseStatisticsCalculator.calculateDoseStatistics(precomputedDoseValues, precomputedVolumeValues); CHECK_CLOSE(doseStatistics->getMean(), expectedVal, errorConstant); CHECK_CLOSE(doseStatistics->getStdDeviation(), 0, errorConstant); CHECK_CLOSE(doseStatistics->getVariance(), 0, errorConstant); double dummy; DoseTypeGy vx = doseStatistics->getVx(expectedVal, true, dummy); CHECK_EQUAL(vx, doseStatistics->getVx(0)); CHECK_CLOSE(expectedVal, doseStatistics->getDx(vx), reducedErrorConstant); CHECK_CLOSE(doseStatistics->getMaximum(), expectedVal, errorConstant); CHECK_CLOSE(doseStatistics->getMinimum(), expectedVal, errorConstant); - ResultListPointer minListPtr = doseStatistics->getMinimumPositions(); - ResultListPointer maxListPtr = doseStatistics->getMaximumPositions(); + ResultListPointer minListPtr = doseStatistics->getMinimumVoxelPositions(); + ResultListPointer maxListPtr = doseStatistics->getMaximumVoxelPositions(); CHECK_EQUAL(maxListPtr->size(), 10); CHECK_EQUAL(minListPtr->size(), 10); CHECK_CLOSE(doseStatistics->getDx(24120), doseStatistics->getMinimum(), 0.001); CHECK_CLOSE(doseStatistics->getMOHx(24120), doseStatistics->getMean(), reducedErrorConstant); CHECK_CLOSE(doseStatistics->getMOCx(20000), doseStatistics->getMean(), reducedErrorConstant); CHECK_CLOSE(doseStatistics->getMinOCx(20000), doseStatistics->getMean(), reducedErrorConstant); } void testWithRealVirtuosDoseDataAndStructure(const std::string& doseFilename, const std::string& structFilename, const std::string& planFilename, unsigned int structureNr) { typedef core::GenericDoseIterator::DoseIteratorPointer DoseIteratorPointer; typedef core::GenericMaskedDoseIterator::MaskAccessorPointer MaskAccessorPointer; typedef rttb::algorithms::DoseStatistics::DoseStatisticsPointer DoseStatisticsPointer; typedef core::DoseIteratorInterface::DoseAccessorPointer DoseAccessorPointer; typedef core::StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; typedef algorithms::DoseStatisticsCalculator::ResultListPointer ResultListPointer; DoseAccessorPointer virtuosDoseAccessor = io::virtuos::VirtuosPlanFileDoseAccessorGenerator( doseFilename.c_str(), planFilename.c_str()).generateDoseAccessor(); StructureSetPointer virtuosStructureSet = io::virtuos::VirtuosFileStructureSetGenerator( structFilename.c_str(), doseFilename.c_str()).generateStructureSet(); boost::shared_ptr spOTBMaskAccessorVirtuos = boost::make_shared(virtuosStructureSet->getStructure(structureNr), virtuosDoseAccessor->getGeometricInfo()); spOTBMaskAccessorVirtuos->updateMask(); MaskAccessorPointer spMaskAccessor(spOTBMaskAccessorVirtuos); //create corresponding MaskedDoseIterator boost::shared_ptr spMaskedDoseIteratorTmp = boost::make_shared(spMaskAccessor, virtuosDoseAccessor); DoseIteratorPointer spMaskedDoseIterator(spMaskedDoseIteratorTmp); rttb::algorithms::DoseStatisticsCalculator doseStatisticsCalculatorVirtuos(spMaskedDoseIterator); DoseStatisticsPointer doseStatisticsVirtuos = doseStatisticsCalculatorVirtuos.calculateDoseStatistics(true); //comparison values computed with "old" DoseStatistics implementation CHECK_CLOSE(doseStatisticsVirtuos->getMinimum(), 6.4089, reducedErrorConstant); CHECK_CLOSE(doseStatisticsVirtuos->getMaximum(), 39.0734, reducedErrorConstant); CHECK_CLOSE(doseStatisticsVirtuos->getMean(), 22.5779, reducedErrorConstant); CHECK_CLOSE(doseStatisticsVirtuos->getStdDeviation(), 6.28857, reducedErrorConstant); - ResultListPointer maxPositions = doseStatisticsVirtuos->getMaximumPositions(); - ResultListPointer minPositions = doseStatisticsVirtuos->getMinimumPositions(); + ResultListPointer maxPositions = doseStatisticsVirtuos->getMaximumVoxelPositions(); + ResultListPointer minPositions = doseStatisticsVirtuos->getMinimumVoxelPositions(); CHECK_EQUAL(maxPositions->size(), 1); CHECK_EQUAL(minPositions->size(), 1); CHECK_EQUAL(maxPositions->begin()->first, doseStatisticsVirtuos->getMaximum()); CHECK_EQUAL(maxPositions->begin()->second, 3570772); CHECK_EQUAL(minPositions->begin()->first, doseStatisticsVirtuos->getMinimum()); CHECK_EQUAL(minPositions->begin()->second, 3571264); CHECK_CLOSE(doseStatisticsVirtuos->getDx(0.02 * doseStatisticsVirtuos->getVolume()), 31.8358, reducedErrorConstant); CHECK_CLOSE(doseStatisticsVirtuos->getVx(0.9 * doseStatisticsVirtuos->getMaximum()), 0.471747, reducedErrorConstant); CHECK_CLOSE(doseStatisticsVirtuos->getMOHx(0.1 * doseStatisticsVirtuos->getVolume()), 31.3266, reducedErrorConstant); CHECK_CLOSE(doseStatisticsVirtuos->getMOCx(0.05 * doseStatisticsVirtuos->getVolume()), 9.01261, reducedErrorConstant); CHECK_CLOSE(doseStatisticsVirtuos->getMaxOHx(0.95 * doseStatisticsVirtuos->getVolume()), 10.3764, reducedErrorConstant); CHECK_CLOSE(doseStatisticsVirtuos->getMinOCx(0.98 * doseStatisticsVirtuos->getVolume()), 31.8373, reducedErrorConstant); } }//testing }//rttb diff --git a/testing/examples/VoxelizationValidationTest.cpp b/testing/examples/VoxelizationValidationTest.cpp index d26ebc7..4b1347a 100644 --- a/testing/examples/VoxelizationValidationTest.cpp +++ b/testing/examples/VoxelizationValidationTest.cpp @@ -1,201 +1,247 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ // this file defines the rttbCoreTests for the test driver // and all it expects is that you have a function called RegisterTests #include #include #include #include "litCheckMacros.h" #include "rttbBaseType.h" #include "rttbDVHCalculator.h" #include "rttbGenericMaskedDoseIterator.h" #include "rttbGenericDoseIterator.h" #include "rttbNullPointerException.h" -#include "rttbInvalidParameterException.h" #include "rttbDicomDoseAccessor.h" #include "rttbDicomFileDoseAccessorGenerator.h" #include "rttbDicomFileStructureSetGenerator.h" #include "rttbOTBMaskAccessor.h" #include "rttbDVHTxtFileReader.h" #include "rttbBoostMaskAccessor.h" #include "rttbITKImageMaskAccessorConverter.h" #include "rttbImageWriter.h" +#include "rttbBoostMaskRedesign.h" +#include "rttbBoostMaskRedesignAccessor.h" namespace rttb { namespace testing { /*! @brief VoxelizationValidationTest. Compare two differnt voxelizations: OTB and Boost. Check dvh maximum and minimum for each structure. Check write mask to itk file for further validation. */ int VoxelizationValidationTest(int argc, char* argv[]) { typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; typedef core::GenericMaskedDoseIterator::MaskAccessorPointer MaskAccessorPointer; typedef core::DVHCalculator::DoseIteratorPointer DoseIteratorPointer; typedef core::StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; PREPARE_DEFAULT_TEST_REPORTING; //ARGUMENTS: 1: structure file name // 2: dose1 file name // 3: directory name to write boost mask of all structures // 4: directory name to write OTB mask of all structures std::string RTSTRUCT_FILENAME; std::string RTDOSE_FILENAME; std::string BoostMask_DIRNAME; std::string OTBMask_DIRNAME; - + std::string BoostMaskRedesign_DIRNAME; if (argc > 4) { RTSTRUCT_FILENAME = argv[1]; RTDOSE_FILENAME = argv[2]; BoostMask_DIRNAME = argv[3]; OTBMask_DIRNAME = argv[4]; + BoostMaskRedesign_DIRNAME = argv[5]; } OFCondition status; DcmFileFormat fileformat; /* read dicom-rt dose */ io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator1(RTDOSE_FILENAME.c_str()); DoseAccessorPointer doseAccessor1(doseAccessorGenerator1.generateDoseAccessor()); //create a vector of MaskAccessors (one for each structure) StructureSetPointer rtStructureSet = io::dicom::DicomFileStructureSetGenerator( RTSTRUCT_FILENAME.c_str()).generateStructureSet(); - std::vector rtStructSetMaskAccessorVec; - if (rtStructureSet->getNumberOfStructures() > 0) { for (size_t j = 0; j < rtStructureSet->getNumberOfStructures(); j++) { + if (j != 2 && j != 3) + { std::cout << j << ": " << rtStructureSet->getStructure(j)->getLabel() << std::endl; clock_t start(clock()); //create OTB MaskAccessor ::boost::shared_ptr spOTBMaskAccessor = ::boost::make_shared(rtStructureSet->getStructure(j), doseAccessor1->getGeometricInfo()); spOTBMaskAccessor->updateMask(); MaskAccessorPointer spMaskAccessor(spOTBMaskAccessor); ::boost::shared_ptr spMaskedDoseIteratorTmp = ::boost::make_shared(spMaskAccessor, doseAccessor1); DoseIteratorPointer spMaskedDoseIterator(spMaskedDoseIteratorTmp); rttb::core::DVHCalculator calc(spMaskedDoseIterator, (rtStructureSet->getStructure(j))->getUID(), doseAccessor1->getUID()); rttb::core::DVH dvh = *(calc.generateDVH()); clock_t finish(clock()); std::cout << "OTB Mask Calculation time: " << finish - start << " ms" << std::endl; //Write the mask image to a file. /*! It takes a long time to write all mask files so that RUN_TESTS causes a timeout error. To write all mask files, please use the outcommented code and call the .exe directly! */ /*rttb::io::itk::ITKImageMaskAccessorConverter itkConverter(spOTBMaskAccessor); CHECK(itkConverter.process()); std::stringstream fileNameSstr; fileNameSstr << OTBMask_DIRNAME << j << ".mhd"; rttb::io::itk::ImageWriter writer(fileNameSstr.str(), itkConverter.getITKImage()); CHECK(writer.writeFile());*/ clock_t start2(clock()); //create Boost MaskAccessor MaskAccessorPointer boostMaskAccessorPtr = ::boost::make_shared (rtStructureSet->getStructure(j), doseAccessor1->getGeometricInfo()); CHECK_NO_THROW(boostMaskAccessorPtr->updateMask()); ::boost::shared_ptr spMaskedDoseIteratorTmp2 = ::boost::make_shared(boostMaskAccessorPtr, doseAccessor1); DoseIteratorPointer spMaskedDoseIterator2(spMaskedDoseIteratorTmp2); rttb::core::DVHCalculator calc2(spMaskedDoseIterator2, (rtStructureSet->getStructure(j))->getUID(), doseAccessor1->getUID()); rttb::core::DVH dvh2 = *(calc2.generateDVH()); clock_t finish2(clock()); std::cout << "Boost Mask Calculation and write file time: " << finish2 - start2 << " ms" << std::endl; //Write the mask image to a file. /*! It takes a long time to write all mask files so that RUN_TESTS causes a timeout error. To write all mask files, please use the outcommented code and call the .exe directly! */ /*rttb::io::itk::ITKImageMaskAccessorConverter itkConverter2(boostMaskAccessorPtr); CHECK(itkConverter2.process()); std::stringstream fileNameSstr2; fileNameSstr2 << BoostMask_DIRNAME << j << ".mhd"; rttb::io::itk::ImageWriter writer2(fileNameSstr2.str(), itkConverter2.getITKImage()); CHECK(writer2.writeFile());*/ + //create Boost MaskAccessor redesign + clock_t startR(clock()); + + MaskAccessorPointer boostMaskRPtr + = ::boost::make_shared + (rtStructureSet->getStructure(j), doseAccessor1->getGeometricInfo()); + CHECK_NO_THROW(boostMaskRPtr->updateMask()); + ::boost::shared_ptr spMaskedDoseIteratorTmpR = + ::boost::make_shared(boostMaskRPtr, doseAccessor1); + DoseIteratorPointer spMaskedDoseIteratorR(spMaskedDoseIteratorTmpR); + rttb::core::DVHCalculator calcR(spMaskedDoseIteratorR, (rtStructureSet->getStructure(j))->getUID(), + doseAccessor1->getUID()); + rttb::core::DVH dvhR = *(calcR.generateDVH()); + clock_t finishR(clock()); + std::cout << "Boost Mask Redesign Calculation and write file time: " << finishR - startR << " ms" << + std::endl; + + //Write the mask image to a file. + /*! It takes a long time to write all mask files so that RUN_TESTS causes a timeout error. + To write all mask files, please use the outcommented code and call the .exe directly! + */ + /*rttb::io::itk::ITKImageMaskAccessorConverter itkConverterR(boostMaskRPtr); + CHECK(itkConverterR.process()); + + std::stringstream fileNameSstrR; + fileNameSstrR << BoostMaskRedesign_DIRNAME << j << ".mhd"; + rttb::io::itk::ImageWriter writerR(fileNameSstrR.str(), itkConverterR.getITKImage()); + CHECK(writerR.writeFile());*/ //check close of 2 voxelizatin: OTB and Boost CHECK_CLOSE(dvh.getMaximum(), dvh2.getMaximum(), 0.1); CHECK_CLOSE(dvh.getMinimum(), dvh2.getMinimum(), 0.1); - if (j != 7) //7: Ref.Pkt, mean = -1.#IND + if (j != 7) { CHECK_CLOSE(dvh.getMean(), dvh2.getMean(), 0.1); } CHECK_CLOSE(dvh.getMedian(), dvh2.getMedian(), 0.1); CHECK_CLOSE(dvh.getModal(), dvh2.getModal(), 0.1); //0: Aussenkontur and 3: Niere li. failed. if (j != 0 && j != 3) { CHECK_CLOSE(dvh.getVx(0), dvh2.getVx(0), dvh.getVx(0) * 0.05); //check volume difference < 5% } + //check close of 2 voxelization: Boost and BoostRedesign + CHECK_CLOSE(dvhR.getMaximum(), dvh2.getMaximum(), 0.1); + CHECK_CLOSE(dvhR.getMinimum(), dvh2.getMinimum(), 0.1); + + if (j != 7) + { + CHECK_CLOSE(dvhR.getMean(), dvh2.getMean(), 0.1); + } + + CHECK_CLOSE(dvhR.getMedian(), dvh2.getMedian(), 0.1); + CHECK_CLOSE(dvhR.getModal(), dvh2.getModal(), 0.1); + + //0: Aussenkontur and 3: Niere li. failed. + CHECK_CLOSE(dvhR.getVx(0), dvh2.getVx(0), dvhR.getVx(0) * 0.05); //check volume difference < 5% + + } } } RETURN_AND_REPORT_TEST_SUCCESS; } }//testing }//rttb diff --git a/testing/interpolation/InterpolationMatchPointTransformation/registrationHelper.cpp b/testing/interpolation/InterpolationMatchPointTransformation/registrationHelper.cpp index 8a21f65..ba9af1b 100644 --- a/testing/interpolation/InterpolationMatchPointTransformation/registrationHelper.cpp +++ b/testing/interpolation/InterpolationMatchPointTransformation/registrationHelper.cpp @@ -1,88 +1,86 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #endif #include "registrationHelper.h" #include "litTestImageIO.h" -#include "litCheckMacros.h" #include "litImageTester.h" -#include "litPointSetTester.h" #include int setImageFileNames(std::string targetImage, std::string movingImage, bool isDirectory, AppGlobals& globals) { globals.targetImageFileName = targetImage; globals.movingImageFileName = movingImage; globals.isDirectory = isDirectory; return EXIT_SUCCESS; } int loadData(AppGlobals& globals) { if (!globals.isDirectory) { globals.spTargetImage = lit::TestImageIO::InternalImageType>::readImage( globals.targetImageFileName); } else { globals.spTargetImage = map::io::readImage (globals.targetImageFileName, map::io::ImageSeriesReadStyle::Dicom); } if (globals.spTargetImage.IsNull()) { std::cerr << "Error. Cannot load target image: " << globals.targetImageFileName << std::endl; return EXIT_FAILURE; } if (!globals.isDirectory) { globals.spMovingImage = lit::TestImageIO::InternalImageType>::readImage( globals.movingImageFileName); } else { globals.spMovingImage = map::io::readImage (globals.movingImageFileName, map::io::ImageSeriesReadStyle::Dicom); } if (globals.spMovingImage.IsNull()) { std::cerr << "Error. Cannot load moving image: " << globals.movingImageFileName << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } AppGlobals::AppGlobals() { }; \ No newline at end of file diff --git a/testing/interpolation/InterpolationMatchPointTransformation/simpleRegistrationWorkflow.cpp b/testing/interpolation/InterpolationMatchPointTransformation/simpleRegistrationWorkflow.cpp index e2c0502..5a41fbb 100644 --- a/testing/interpolation/InterpolationMatchPointTransformation/simpleRegistrationWorkflow.cpp +++ b/testing/interpolation/InterpolationMatchPointTransformation/simpleRegistrationWorkflow.cpp @@ -1,113 +1,110 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #undef MAP_SEAL_ALGORITHMS #include "simpleRegistrationWorkflow.h" #include "registrationHelper.h" simpleRegistrationWorkflow::simpleRegistrationWorkflow(std::string targetFilename, std::string movingFilename, - bool isDirectory) + bool isDirectory) : _targetFilename(targetFilename), _movingFilename(movingFilename) { - _targetFilename = targetFilename; - _movingFilename = movingFilename; - setImageFileNames(_targetFilename, _movingFilename, isDirectory, globals); loadData(globals); _spAlgorithmEuler = NULL; } vnl_vector simpleRegistrationWorkflow::getRegistrationParameters( Registration3D3DPointer reg) { typedef map::core::ModelBasedRegistrationKernel<3, 3> ModelBasedRegistrationKernel3D3D; const ModelBasedRegistrationKernel3D3D* pModelBasedDirectKernel3D3D = dynamic_cast(&(reg->getDirectMapping())); if (pModelBasedDirectKernel3D3D) { ModelBasedRegistrationKernel3D3D::ParametersType params = pModelBasedDirectKernel3D3D->getTransformModel()->GetParameters(); return params; } else { return vnl_vector(); } } void simpleRegistrationWorkflow::initializeAndPerformRegistration() { _spAlgorithmEuler = AlgorithmTypeEuler::New(); _spAlgorithmEuler->setProperty("PreinitTransform", map::core::MetaProperty::New(true)); _spAlgorithmEuler->setMovingImage(globals.spMovingImage); _spAlgorithmEuler->setTargetImage(globals.spTargetImage); AlgorithmTypeEuler::RegistrationType::Pointer spRegistration; try { spRegistration = _spAlgorithmEuler->getRegistration(); } catch (const map::core::ExceptionObject& e) { std::cerr << "caught an MatchPoint exception:\n"; e.Print(std::cerr); std::cerr << "\n"; } catch (const itk::ExceptionObject& e) { std::cerr << "caught an ITK exception:\n"; std::cerr << e.GetFile() << ":" << e.GetLine() << ":\n" << e.GetDescription() << "\n"; } catch (const std::exception& e) { std::cerr << "caught an exception:\n"; std::cerr << e.what() << "\n"; } catch (...) { std::cerr << "caught an unknown exception!!!\n"; } } map::core::Registration<3, 3>::Pointer simpleRegistrationWorkflow::getRegistration() { if (_spAlgorithmEuler.IsNull()) { initializeAndPerformRegistration(); } return _spAlgorithmEuler->getRegistration(); }; const itk::Image* simpleRegistrationWorkflow::getTargetImage() { if (_spAlgorithmEuler.IsNull()) { initializeAndPerformRegistration(); } return _spAlgorithmEuler->getTargetImage(); }; diff --git a/testing/io/dicom/DicomDoseAccessorConverterTest.cpp b/testing/io/dicom/DicomDoseAccessorConverterTest.cpp index 8907aec..833f982 100644 --- a/testing/io/dicom/DicomDoseAccessorConverterTest.cpp +++ b/testing/io/dicom/DicomDoseAccessorConverterTest.cpp @@ -1,158 +1,158 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ // this file defines the rttbCoreTests for the test driver // and all it expects is that you have a function called RegisterTests #include #include #include #include "litCheckMacros.h" #include "rttbBaseType.h" #include "rttbDicomDoseAccessor.h" #include "rttbDicomFileDoseAccessorGenerator.h" #include "rttbDicomFileDoseAccessorWriter.h" #include "rttbInvalidDoseException.h" #include "rttbDcmrtException.h" #include "rttbInvalidParameterException.h" namespace rttb { namespace testing { /*!@brief DicomDoseAccessorGeneratorTest - test the generators for dicom data 1) test dicom file generator generateDoseAccessor() 2) test dicom IOD generator generateDoseAccessor() */ int DicomDoseAccessorConverterTest(int argc, char* argv[]) { typedef boost::shared_ptr DRTDoseIODPointer; typedef rttb::io::dicom::DicomDoseAccessor::DoseAccessorPointer DoseAccessorPointer; PREPARE_DEFAULT_TEST_REPORTING; //ARGUMENTS: // 1: the file name of the dose to read // 2: the file name of the dose to write std::string RTDOSE_FILENAME_R; std::string RTDOSE_FILENAME_W; if (argc > 2) { RTDOSE_FILENAME_R = argv[1]; RTDOSE_FILENAME_W = argv[2]; } double errorConstant = 1e-3; DoseAccessorPointer doseAccessor_r = io::dicom::DicomFileDoseAccessorGenerator( RTDOSE_FILENAME_R.c_str()).generateDoseAccessor(); CHECK_NO_THROW(io::dicom::DicomFileDoseAccessorWriter()); io::dicom::DicomFileDoseAccessorWriter doseConverter; CHECK_NO_THROW(doseConverter.setDoseAccessor(doseAccessor_r)); CHECK_NO_THROW(doseConverter.setFileName(RTDOSE_FILENAME_W)); CHECK_EQUAL(doseConverter.process(), true); DoseAccessorPointer doseAccessor_w = io::dicom::DicomFileDoseAccessorGenerator( RTDOSE_FILENAME_W).generateDoseAccessor(); //Check geometricinfo CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImagePositionPatient().x(), doseAccessor_w->getGeometricInfo().getImagePositionPatient().x(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImagePositionPatient().y(), doseAccessor_w->getGeometricInfo().getImagePositionPatient().y(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImagePositionPatient().z(), doseAccessor_w->getGeometricInfo().getImagePositionPatient().z(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImageOrientationColumn().x(), doseAccessor_w->getGeometricInfo().getImageOrientationColumn().x(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImageOrientationColumn().y(), doseAccessor_w->getGeometricInfo().getImageOrientationColumn().y(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImageOrientationColumn().z(), doseAccessor_w->getGeometricInfo().getImageOrientationColumn().z(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImageOrientationRow().x(), doseAccessor_w->getGeometricInfo().getImageOrientationRow().x(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImageOrientationRow().y(), doseAccessor_w->getGeometricInfo().getImageOrientationRow().y(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImageOrientationRow().z(), doseAccessor_w->getGeometricInfo().getImageOrientationRow().z(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getSpacing().x(), doseAccessor_w->getGeometricInfo().getSpacing().x(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getSpacing().y(), doseAccessor_w->getGeometricInfo().getSpacing().y(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getSpacing().z(), doseAccessor_w->getGeometricInfo().getSpacing().z(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getNumColumns(), doseAccessor_w->getGeometricInfo().getNumColumns(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getNumRows(), doseAccessor_w->getGeometricInfo().getNumRows(), errorConstant); CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getNumSlices(), doseAccessor_w->getGeometricInfo().getNumSlices(), errorConstant); //Check pixel data - int size = doseAccessor_r->getGeometricInfo().getNumColumns() * - doseAccessor_r->getGeometricInfo().getNumRows() * - doseAccessor_r->getGeometricInfo().getNumSlices() ; + unsigned int size = doseAccessor_r->getGeometricInfo().getNumColumns() * + doseAccessor_r->getGeometricInfo().getNumRows() * + doseAccessor_r->getGeometricInfo().getNumSlices() ; for (unsigned int index = 0; index < 30; index++) { CHECK_CLOSE(doseAccessor_r->getValueAt(index), doseAccessor_w->getValueAt(index), errorConstant); if (size / 2 - index >= 0 && size / 2 - index < size) { CHECK_CLOSE(doseAccessor_r->getValueAt(size / 2 - index), doseAccessor_w->getValueAt(size / 2 - index), errorConstant); } CHECK_CLOSE(doseAccessor_r->getValueAt(size - index - 1), doseAccessor_w->getValueAt(size - index - 1), errorConstant); } RETURN_AND_REPORT_TEST_SUCCESS; } }//testing }//rttb diff --git a/testing/io/dicom/DicomStructureSetGeneratorTest.cpp b/testing/io/dicom/DicomStructureSetGeneratorTest.cpp index 65e4e41..5f43d25 100644 --- a/testing/io/dicom/DicomStructureSetGeneratorTest.cpp +++ b/testing/io/dicom/DicomStructureSetGeneratorTest.cpp @@ -1,106 +1,106 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ // this file defines the rttbCoreTests for the test driver // and all it expects is that you have a function called RegisterTests #include #include #include #include "litCheckMacros.h" #include "rttbBaseType.h" #include "rttbDicomFileStructureSetGenerator.h" #include "rttbDicomIODStructureSetGenerator.h" #include "rttbDcmrtException.h" #include "rttbInvalidParameterException.h" namespace rttb { namespace testing { /*!@brief DicomIOTest - test structure set generator for dicom data 1) test dicom file structure set generator 2) test dicom IOD structure set generator */ int DicomStructureSetGeneratorTest(int argc, char* argv[]) { typedef core::StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; //typedef boost::shared_ptr DRTStrSetIODPtr; typedef io::dicom::DicomIODStructureSetGenerator::DRTStrSetIODPtr DRTStrSetIODPtr; PREPARE_DEFAULT_TEST_REPORTING; //ARGUMENTS: 1: structure file name std::string RTSTRUCT_FILENAME; if (argc > 1) { RTSTRUCT_FILENAME = argv[1]; } /* structure set */ //1) test dicom file structure set generator CHECK_NO_THROW(io::dicom::DicomFileStructureSetGenerator("")); CHECK_NO_THROW(io::dicom::DicomFileStructureSetGenerator("Test.test")); CHECK_THROW_EXPLICIT(io::dicom::DicomFileStructureSetGenerator("Test.test").generateStructureSet(), rttb::core::InvalidParameterException); CHECK_NO_THROW(io::dicom::DicomFileStructureSetGenerator(RTSTRUCT_FILENAME.c_str())); StructureSetPointer rtStructureSet = io::dicom::DicomFileStructureSetGenerator( RTSTRUCT_FILENAME.c_str()).generateStructureSet(); //2) test dicom IOD structure set generator OFCondition status; DcmFileFormat fileformat; boost::shared_ptr drtStrSetIOD = boost::make_shared(); CHECK_NO_THROW(io::dicom::DicomIODStructureSetGenerator generator1(drtStrSetIOD)); CHECK_THROW_EXPLICIT(io::dicom::DicomIODStructureSetGenerator(drtStrSetIOD).generateStructureSet(), core::InvalidParameterException); - status = fileformat.loadFile(RTSTRUCT_FILENAME.c_str()); - status = drtStrSetIOD->read(*fileformat.getDataset()); + fileformat.loadFile(RTSTRUCT_FILENAME.c_str()); + drtStrSetIOD->read(*fileformat.getDataset()); CHECK_NO_THROW(io::dicom::DicomIODStructureSetGenerator generator2(drtStrSetIOD)); CHECK_NO_THROW(io::dicom::DicomIODStructureSetGenerator(drtStrSetIOD).generateStructureSet()); StructureSetPointer rtStructureSet2 = io::dicom::DicomIODStructureSetGenerator( drtStrSetIOD).generateStructureSet(); CHECK_EQUAL(rtStructureSet2->getNumberOfStructures(), rtStructureSet->getNumberOfStructures()); RETURN_AND_REPORT_TEST_SUCCESS; } }//testing }//rttb diff --git a/testing/io/itk/CMakeLists.txt b/testing/io/itk/CMakeLists.txt index b022953..4a31aa4 100644 --- a/testing/io/itk/CMakeLists.txt +++ b/testing/io/itk/CMakeLists.txt @@ -1,35 +1,35 @@ #----------------------------------------------------------------------------- # Setup the system information test. Write out some basic failsafe # information in case the test doesn't run. #----------------------------------------------------------------------------- SET(ITKIO_TEST ${EXECUTABLE_OUTPUT_PATH}/rttbITKIOTests) SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) #----------------------------------------------------------------------------- ADD_TEST(ITKDoseAccessorGeneratorTest ${ITKIO_TEST} ITKDoseAccessorGeneratorTest "${TEST_DATA_ROOT}/ITK/MatchPointLogo.mhd") ADD_TEST(ITKIOTest ${ITKIO_TEST} ITKIOTest "${TEST_DATA_ROOT}/ITK/MatchPointLogo.mhd") ADD_TEST(ITKDoseAccessorConverterTest ${ITKIO_TEST} ITKDoseAccessorConverterTest "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo.dcm" "${TEST_DATA_ROOT}/ITK/MatchPointLogo.mhd" ) ADD_TEST(ITKBioModelAccessorConverterTest ${ITKIO_TEST} ITKBioModelAccessorConverterTest "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo.dcm") ADD_TEST(ITKMaskAccessorGeneratorTest ${ITKIO_TEST} ITKMaskAccessorGeneratorTest "${TEST_DATA_ROOT}/ITK/Nodes.mhd") ADD_TEST(ITKMaskAccessorConverterTest ${ITKIO_TEST} ITKMaskAccessorConverterTest "${TEST_DATA_ROOT}/DICOM/StructureSet/RS1.3.6.1.4.1.2452.6.841242143.1311652612.1170940299.4217870819.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo.dcm" "${TEST_DATA_ROOT}/ITK/Nodes.mhd") -RTTB_CREATE_TEST_MODULE(rttbITKIO DEPENDS RTTBITKIO RTTBDicomIO RTTBMasks RTTBOTBMask RTTBBoostMask RTTBModels PACKAGE_DEPENDS Boost Litmus MatchPoint ITK DCMTK) +RTTB_CREATE_TEST_MODULE(rttbITKIO DEPENDS RTTBITKIO RTTBDicomIO RTTBMasks RTTBOTBMask RTTBBoostMask RTTBModels PACKAGE_DEPENDS Boost Litmus ITK DCMTK) diff --git a/testing/io/itk/ITKBioModelAccessorConverterTest.cpp b/testing/io/itk/ITKBioModelAccessorConverterTest.cpp index 6e58eb9..0d4c0c5 100644 --- a/testing/io/itk/ITKBioModelAccessorConverterTest.cpp +++ b/testing/io/itk/ITKBioModelAccessorConverterTest.cpp @@ -1,114 +1,112 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ // this file defines the rttbCoreTests for the test driver // and all it expects is that you have a function called RegisterTests #include #include #include "litCheckMacros.h" -#include "litImageTester.h" -#include "litTestImageIO.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "rttbBaseType.h" #include "rttbDoseIteratorInterface.h" #include "rttbAccessorInterface.h" #include "rttbDicomFileDoseAccessorGenerator.h" #include "rttbDicomDoseAccessor.h" #include "rttbLQModelAccessor.h" #include "rttbITKImageAccessor.h" #include "rttbITKImageFileAccessorGenerator.h" #include "rttbITKImageAccessorGenerator.h" #include "rttbITKImageAccessorConverter.h" #include "rttbDoseAccessorProcessorBase.h" #include "rttbDoseAccessorConversionSettingInterface.h" #include "rttbInvalidDoseException.h" namespace rttb { namespace testing { /*!@brief ITKBioModelAccessorConverterTest - test the conversion rttb BioModel accessor ->itk 1) test with dicom file (DicomDoseAccessorGenerator) */ int ITKBioModelAccessorConverterTest(int argc, char* argv[]) { typedef core::AccessorInterface::AccessorPointer AccessorPointer; typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; PREPARE_DEFAULT_TEST_REPORTING; std::string RTDOSE_FILENAME; if (argc > 1) { RTDOSE_FILENAME = argv[1]; } //1) Read dicomFile and test process() and getITKImage() io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator(RTDOSE_FILENAME.c_str()); DoseAccessorPointer doseAccessor(doseAccessorGenerator.generateDoseAccessor()); AccessorPointer LQWithConstantDose = boost::make_shared(doseAccessor, 0.2, 0.02); io::itk::ITKImageAccessorConverter itkConverter(LQWithConstantDose); CHECK_NO_THROW(itkConverter.process()); CHECK_NO_THROW(itkConverter.getITKImage()); io::itk::ITKImageAccessor::ITKImageType::IndexType itkIndex; itkIndex[0] = itkIndex[1] = itkIndex[2] = 0; VoxelGridIndex3D rttbIndex(0, 0, 0); CHECK_EQUAL(itkConverter.getITKImage()->GetPixel(itkIndex), LQWithConstantDose->getValueAt(rttbIndex)); itkIndex[0] = rttbIndex[0] = doseAccessor->getGeometricInfo().getNumColumns() / 2; itkIndex[1] = rttbIndex[1] = doseAccessor->getGeometricInfo().getNumRows() / 2; itkIndex[2] = rttbIndex[2] = doseAccessor->getGeometricInfo().getNumSlices() / 2; CHECK_EQUAL(itkConverter.getITKImage()->GetPixel(itkIndex), LQWithConstantDose->getValueAt(rttbIndex)); itkIndex[0] = rttbIndex[0] = doseAccessor->getGeometricInfo().getNumColumns() - 1; itkIndex[1] = rttbIndex[1] = doseAccessor->getGeometricInfo().getNumRows() - 1; itkIndex[2] = rttbIndex[2] = doseAccessor->getGeometricInfo().getNumSlices() - 1; CHECK_EQUAL(itkConverter.getITKImage()->GetPixel(itkIndex), LQWithConstantDose->getValueAt(rttbIndex)); RETURN_AND_REPORT_TEST_SUCCESS; } }//testing }//rttb diff --git a/testing/io/rttbDoseAccessorTester.cpp b/testing/io/rttbDoseAccessorTester.cpp index 5085f7d..3b05604 100644 --- a/testing/io/rttbDoseAccessorTester.cpp +++ b/testing/io/rttbDoseAccessorTester.cpp @@ -1,152 +1,152 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision $ (last changed revision) // @date $Date $ (last change date) // @author $Author $ (last changed by) */ #include #include "rttbGeometricInfo.h" #include "rttbDoseAccessorTester.h" namespace rttb { namespace testing { DoseAccessorTester::DoseAccessorTester(DoseAccessorPointer aReferenceDose, DoseAccessorPointer aCompareDose) : _referenceDose(aReferenceDose), _compareDose(aCompareDose), _geometryIsSimilar(false), _sameGridSize(false), _conversionFailed(false), _numDifference(0) {} void DoseAccessorTester::setReferenceDose(const DoseAccessorPointer aReferenceDose) { _referenceDose = aReferenceDose; } void DoseAccessorTester::setCompareDose(const DoseAccessorPointer aCompareDose) { _compareDose = aCompareDose; } lit::StringType DoseAccessorTester::getTestDescription(void) const { return "Compare two DoseAccessors and determine if the contained doses are equal."; }; bool DoseAccessorTester::doCheck(void) const { _pResults->onTestStart(getCurrentTestLabel()); _geometryIsSimilar = (_referenceDose->getGeometricInfo() == _compareDose->getGeometricInfo()); if (!_geometryIsSimilar) { return false; } _sameGridSize = (_referenceDose->getGridSize() == _compareDose->getGridSize()); if (!_sameGridSize) { return false; } _numDifference = 0; _maxDifference = 0; VoxelGridIndex3D id3D; for (VoxelGridID id = 0; id < _referenceDose->getGridSize(); id++) { _compareDose->getGeometricInfo().convert(id, id3D); if (!_compareDose->getGeometricInfo().validIndex(id3D)) { _conversionFailed = true; _failedID = id; return false; } if ((_referenceDose-> getValueAt(id) != _referenceDose-> getValueAt(id3D)) || (_compareDose->getValueAt(id) != _compareDose-> getValueAt(id3D))) { _conversionFailed = true; _failedID = id; return false; } if ((_referenceDose->getValueAt(id) != _compareDose->getValueAt(id)) || (_referenceDose->getValueAt(id3D) != _compareDose->getValueAt(id3D))) { - float tmpDifference = abs(_referenceDose->getValueAt(id) - _compareDose->getValueAt(id)); + double tmpDifference = abs(_referenceDose->getValueAt(id) - _compareDose->getValueAt(id)); if (tmpDifference > _maxDifference) { _maxDifference = tmpDifference; } _numDifference++; } }//end for(VoxelGridID id = 0; id < _referenceDose->getGridSize(); id++) return (_numDifference == 0); } void DoseAccessorTester::handleSuccess(void) const { std::ostringstream stream; stream << "Both doses are equal" << std::endl; _pResults->onTestSuccess(getCurrentTestLabel(), stream.str()); } void DoseAccessorTester::handleFailure(void) const { std::ostringstream stream; stream << "Doses are different" << std::endl; if (_geometryIsSimilar) { if (_sameGridSize) { stream << std::endl << "Error voxel count: " << _numDifference << std::endl; stream << std::endl << "Maximum difference: " << _maxDifference << std::endl; if (_conversionFailed) { stream << std::endl << "Index conversion failed in: " << _failedID << std::endl; } } else { stream << "Doses have different grid sizes" << std::endl; stream << "Reference dose contains " << _referenceDose->getGridSize() << " voxels and comparison dose " << _compareDose->getGridSize() << std::endl; } } else { stream << "Doses have different geometry" << std::endl; } _pResults->onTestFailure(getCurrentTestLabel(), stream.str()); } } } \ No newline at end of file diff --git a/testing/io/rttbDoseAccessorTester.h b/testing/io/rttbDoseAccessorTester.h index c77fae0..80e8e40 100644 --- a/testing/io/rttbDoseAccessorTester.h +++ b/testing/io/rttbDoseAccessorTester.h @@ -1,97 +1,97 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __DOSE_ACCESSOR_TESTER_H #define __DOSE_ACCESSOR_TESTER_H #include #include "litTesterBase.h" #include "litString.h" #include "rttbBaseType.h" #include "../../code/core/rttbDoseAccessorInterface.h" namespace rttb { namespace testing { /*! class DoseAccessorTester @brief Tester class for io classes. Compares two given DoseAccessors for similarity. These DoseAccessors are not similar if their geometry or grid size are not similar, - if the conversion of a given ID is invalid for one of the acessors, or if the dose + if the conversion of a given ID is invalid for one of the accessors, or if the dose at a given ID is not the same for both accessors. */ class DoseAccessorTester: public lit::TesterBase { public: typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; private: DoseAccessorPointer _referenceDose; DoseAccessorPointer _compareDose; - mutable float _maxDifference; - mutable float _numDifference; + mutable double _maxDifference; + mutable double _numDifference; mutable bool _geometryIsSimilar; mutable bool _sameGridSize; mutable bool _conversionFailed; mutable VoxelGridID _failedID; public: DoseAccessorTester(DoseAccessorPointer aReferenceDose, DoseAccessorPointer aCompareDose); /*! Set the dose accessor pointer for the dose comparison. */ void setReferenceDose(const DoseAccessorPointer aReferenceDose); void setCompareDose(const DoseAccessorPointer aCompareDose); /*! Returns a string that specifies the test the tester currently performs. */ lit::StringType getTestDescription(void) const; lit::StringType getTestName(void) const { return "DoseAccessorTester"; }; protected: /*! performs the test and checks the results. @result Indicates if the test was successful (true) or if it failed (false) */ bool doCheck(void) const; /*! Function will be called by check() if test was succesfull. */ void handleSuccess(void) const; /*! Function will be called by check() if test was a failure. */ void handleFailure(void) const; }; } } #endif \ No newline at end of file diff --git a/testing/io/virtuos/VirtuosDVHCalculatorExampleTest.cpp b/testing/io/virtuos/VirtuosDVHCalculatorExampleTest.cpp index db9751f..7c5406a 100644 --- a/testing/io/virtuos/VirtuosDVHCalculatorExampleTest.cpp +++ b/testing/io/virtuos/VirtuosDVHCalculatorExampleTest.cpp @@ -1,131 +1,132 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ // this file defines the rttbCoreTests for the test driver // and all it expects is that you have a function called RegisterTests #include #include #include #include "litCheckMacros.h" #include "rttbBaseType.h" #include "rttbDVHCalculator.h" #include "rttbGenericMaskedDoseIterator.h" #include "rttbGenericDoseIterator.h" #include "rttbBoostMaskAccessor.h" #include "rttbVirtuosPlanFileDoseAccessorGenerator.h" #include "rttbVirtuosDoseAccessor.h" #include "rttbVirtuosFileStructureSetGenerator.h" namespace rttb { namespace testing { /*! @brief VirtuosDVHCalculatorExampleTest. Test if calculation in new architecture returns similar results to the original implementation. WARNING: The values for comparison need to be adjusted if the input files are changed! */ int VirtuosDVHCalculatorExampleTest(int argc, char* argv[]) { typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; typedef core::GenericMaskedDoseIterator::MaskAccessorPointer MaskAccessorPointer; typedef core::DVHCalculator::DoseIteratorPointer DoseIteratorPointer; typedef masks::boost::BoostMaskAccessor::StructTypePointer StructTypePointer; typedef core::DVH::DVHPointer DVHPointer; typedef core::StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; PREPARE_DEFAULT_TEST_REPORTING; //ARGUMENTS: 1: virtuos dose file name // 2: virtuos structure file name // 3: virtuos plan file name // 4: virtuos CT file name std::string Virtuos_Dose_File; std::string Virtuos_Structure_File; std::string Virtuos_Plan_File; std::string Virtuos_CT_File; if (argc > 4) { Virtuos_Dose_File = argv[1]; Virtuos_Structure_File = argv[2]; Virtuos_Plan_File = argv[3]; Virtuos_CT_File = argv[4]; } //Virtuos DVH Test io::virtuos::VirtuosPlanFileDoseAccessorGenerator doseAccessorGeneratorVirtuos(Virtuos_Dose_File, Virtuos_Plan_File); DoseAccessorPointer doseAccessorVirtuos(doseAccessorGeneratorVirtuos.generateDoseAccessor()); StructureSetPointer rtStructureSetVirtuos = io::virtuos::VirtuosFileStructureSetGenerator( Virtuos_Structure_File, Virtuos_CT_File).generateStructureSet(); //create MaskAccessor for structure DARM boost::shared_ptr spMaskAccessorVirtuos = boost::make_shared(rtStructureSetVirtuos->getStructure(4), doseAccessorVirtuos->getGeometricInfo()); spMaskAccessorVirtuos->updateMask(); MaskAccessorPointer spMaskAccessor(spMaskAccessorVirtuos); //create corresponding MaskedDoseIterator boost::shared_ptr spMaskedDoseIteratorTmp = boost::make_shared(spMaskAccessor, doseAccessorVirtuos); DoseIteratorPointer spMaskedDoseIterator(spMaskedDoseIteratorTmp); rttb::core::DVHCalculator* calc; CHECK_NO_THROW(calc = new rttb::core::DVHCalculator(spMaskedDoseIterator, (rtStructureSetVirtuos->getStructure(4))->getUID(), doseAccessorVirtuos->getUID())); DVHPointer dvhPtr; CHECK_NO_THROW(dvhPtr = calc->generateDVH()); CHECK_CLOSE(4.08178, dvhPtr->getMaximum(), errorConstant); CHECK_CLOSE(0.0151739, dvhPtr->getMinimum(), errorConstant); CHECK_CLOSE(0.755342, dvhPtr->getMean(), errorConstant); CHECK_CLOSE(0.440044, dvhPtr->getMedian(), errorConstant); CHECK_CLOSE(0.0151739, dvhPtr->getModal(), errorConstant); CHECK_CLOSE(0.835792, dvhPtr->getStdDeviation(), errorConstant); CHECK_CLOSE(0.698549, dvhPtr->getVariance(), errorConstant); CHECK_CLOSE(46573.0193175, dvhPtr->getNumberOfVoxels(), errorConstant); + RETURN_AND_REPORT_TEST_SUCCESS; } }//testing }//rttb diff --git a/testing/io/virtuos/VirtuosStructureSetGeneratorTest.cpp b/testing/io/virtuos/VirtuosStructureSetGeneratorTest.cpp index 011476f..b539af4 100644 --- a/testing/io/virtuos/VirtuosStructureSetGeneratorTest.cpp +++ b/testing/io/virtuos/VirtuosStructureSetGeneratorTest.cpp @@ -1,119 +1,119 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ // this file defines the rttbCoreTests for the test driver // and all it expects is that you have a function called RegisterTests #include #include #include "litCheckMacros.h" #include "rttbBaseType.h" #include "rttbGeometricInfo.h" #include "rttbDoseIteratorInterface.h" #include "rttbVirtuosDoseAccessor.h" #include "rttbVirtuosFileStructureSetGenerator.h" #include "rttbNullPointerException.h" #include "rttbInvalidDoseException.h" #include "rttbInvalidParameterException.h" #include "rttbIndexOutOfBoundsException.h" namespace rttb { namespace testing { /*!@brief VirtuosStructureSetGeneratorTest - test structure set generator for virtuos data 1) test constructor 2) test generateStructureSet */ int VirtuosStructureSetGeneratorTest(int argc, char* argv[]) { typedef core::StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; PREPARE_DEFAULT_TEST_REPORTING; //ARGUMENTS: 1: structure file name ".../testing/data/Virtuos/NHH030000.vdx" // 2: ctx file name (virtuos) ".../testing/data/Virtuos/NHH030000.ctx.gz" std::string RTSTRUCT_FILENAME; std::string CT_FILENAME; if (argc > 1) { RTSTRUCT_FILENAME = argv[1]; } if (argc > 2) { CT_FILENAME = argv[2]; } /* structure set */ //1) check constructor CHECK_THROW_EXPLICIT(io::virtuos::VirtuosFileStructureSetGenerator("aStructureFileName.vdx", CT_FILENAME.c_str()), core::InvalidParameterException); CHECK_THROW_EXPLICIT(io::virtuos::VirtuosFileStructureSetGenerator(RTSTRUCT_FILENAME.c_str(), "aCTFileName.ctx"), core::InvalidParameterException); CHECK_NO_THROW(io::virtuos::VirtuosFileStructureSetGenerator(RTSTRUCT_FILENAME.c_str(), CT_FILENAME.c_str())); //2) check generateStructureSet CHECK_THROW_EXPLICIT(io::virtuos::VirtuosFileStructureSetGenerator("aStructureFileName.vdx", CT_FILENAME.c_str()).generateStructureSet(), core::InvalidParameterException); CHECK_THROW_EXPLICIT(io::virtuos::VirtuosFileStructureSetGenerator(RTSTRUCT_FILENAME.c_str(), "aCTFileName.ctx").generateStructureSet(), core::InvalidParameterException); std::string testFileName = RTSTRUCT_FILENAME; - int gzPosition = testFileName.find(".vdx"); + size_t gzPosition = testFileName.find(".vdx"); if (gzPosition != std::string::npos) { testFileName.erase(gzPosition, testFileName.length()); } CHECK_THROW_EXPLICIT(io::virtuos::VirtuosFileStructureSetGenerator(testFileName, CT_FILENAME.c_str()).generateStructureSet(), core::InvalidParameterException); testFileName = CT_FILENAME; gzPosition = testFileName.find(".ctx"); if (gzPosition != std::string::npos) { testFileName.erase(gzPosition, testFileName.length()); } CHECK_THROW_EXPLICIT(io::virtuos::VirtuosFileStructureSetGenerator(RTSTRUCT_FILENAME.c_str(), testFileName).generateStructureSet(), core::InvalidParameterException); CHECK_NO_THROW(io::virtuos::VirtuosFileStructureSetGenerator(RTSTRUCT_FILENAME.c_str(), CT_FILENAME.c_str()).generateStructureSet()); RETURN_AND_REPORT_TEST_SUCCESS; } }//testing }//rttb diff --git a/testing/masks/boost/BoostMaskRedesignTest.cpp b/testing/masks/boost/BoostMaskRedesignTest.cpp new file mode 100644 index 0000000..6e94c0c --- /dev/null +++ b/testing/masks/boost/BoostMaskRedesignTest.cpp @@ -0,0 +1,98 @@ +// ----------------------------------------------------------------------- +// RTToolbox - DKFZ radiotherapy quantitative evaluation library +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See rttbCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/rttb/copyright.html +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notices for more information. +// +//------------------------------------------------------------------------ +/*! +// @file +// @version $Revision: 1127 $ (last changed revision) +// @date $Date: 2015-10-01 13:33:33 +0200 (Do, 01 Okt 2015) $ (last change date) +// @author $Author: hentsch $ (last changed by) +*/ + +#include +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" + +#include "../../core/DummyStructure.h" +#include "../../core/DummyDoseAccessor.h" +#include "rttbDicomDoseAccessor.h" +#include "rttbDicomFileDoseAccessorGenerator.h" +#include "rttbDicomFileStructureSetGenerator.h" +#include "rttbDicomFileStructureSetGenerator.h" +#include "rttbGenericDoseIterator.h" +#include "rttbDVHCalculator.h" +#include "rttbGenericMaskedDoseIterator.h" +#include "rttbBoostMaskRedesign.h" +#include "rttbBoostMask.h" +#include "rttbBoostMaskAccessor.h" +#include "rttbBoostMaskRedesignAccessor.h" + + +namespace rttb +{ + namespace testing + { + + /*! @brief BoostMaskRedesignTest. + 1) test constructors + 2) test getRelevantVoxelVector + 3) test getMaskAt + */ + int BoostMaskRedesignTest(int argc, char* argv[]) + { + typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; + typedef core::DVHCalculator::DoseIteratorPointer DoseIteratorPointer; + typedef core::StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; + typedef core::GenericMaskedDoseIterator::MaskAccessorPointer MaskAccessorPointer; + + PREPARE_DEFAULT_TEST_REPORTING; + + typedef core::Structure::StructTypePointer StructTypePointer; + + // generate test structure set + boost::shared_ptr spTestDoseAccessor = + boost::make_shared(); + + DummyStructure myStructGenerator(spTestDoseAccessor->getGeometricInfo()); + + GridIndexType zPlane = 4; + core::Structure myTestStruct = myStructGenerator.CreateRectangularStructureCentered(zPlane); + StructTypePointer spMyStruct = boost::make_shared(myTestStruct); + boost::shared_ptr geometricPtr = boost::make_shared + (spTestDoseAccessor->getGeometricInfo()); + + //1) test BoostMask constructor + CHECK_NO_THROW(rttb::masks::boostRedesign::BoostMask(geometricPtr, spMyStruct)); + rttb::masks::boostRedesign::BoostMask boostMask = rttb::masks::boostRedesign::BoostMask( + geometricPtr, spMyStruct); + + //2) test getRelevantVoxelVector + CHECK_NO_THROW(boostMask.getRelevantVoxelVector()); + rttb::masks::boostRedesign::BoostMask::MaskVoxelListPointer list = + boostMask.getRelevantVoxelVector(); + + for (int i = 0; i < list->size(); ++i) + { + std::cout << "id: " << list->at(i).getVoxelGridID() << ", " << list->at( + i).getRelevantVolumeFraction() << std::endl; + } + + + RETURN_AND_REPORT_TEST_SUCCESS; + } + }//testing +}//rttb + diff --git a/testing/masks/boost/CMakeLists.txt b/testing/masks/boost/CMakeLists.txt index 8bc11ea..6dd75d4 100644 --- a/testing/masks/boost/CMakeLists.txt +++ b/testing/masks/boost/CMakeLists.txt @@ -1,21 +1,22 @@ #----------------------------------------------------------------------------- # Setup the system information test. Write out some basic failsafe # information in case the test doesn't run. #----------------------------------------------------------------------------- SET(Boost_Mask_TESTS ${EXECUTABLE_OUTPUT_PATH}/rttbBoostMaskTests) SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) #----------------------------------------------------------------------------- ADD_TEST(BoostMaskAccessorTest ${Boost_Mask_TESTS} BoostMaskAccessorTest ) - +ADD_TEST(BoostMaskRedesignTest ${Boost_Mask_TESTS} BoostMaskRedesignTest +) RTTB_CREATE_TEST_MODULE(rttbBoostMask DEPENDS RTTBDicomIO RTTBMasks RTTBBoostMask PACKAGE_DEPENDS Boost BoostBinaries Litmus DCMTK) diff --git a/testing/masks/boost/files.cmake b/testing/masks/boost/files.cmake index c401dd6..ea3c821 100644 --- a/testing/masks/boost/files.cmake +++ b/testing/masks/boost/files.cmake @@ -1,13 +1,14 @@ SET(CPP_FILES ../../core/DummyStructure.cpp ../../core/CreateTestStructure.cpp ../../core/DummyDoseAccessor.cpp - BoostMaskAccessorTest.cpp + BoostMaskAccessorTest.cpp + BoostMaskRedesignTest.cpp rttbBoostMaskTests.cpp ) SET(H_FILES ../../core/DummyStructure.h ../../core/CreateTestStructure.h ../../core/DummyDoseAccessor.h ) diff --git a/testing/masks/boost/rttbBoostMaskTests.cpp b/testing/masks/boost/rttbBoostMaskTests.cpp index dcf6e16..1f552c0 100644 --- a/testing/masks/boost/rttbBoostMaskTests.cpp +++ b/testing/masks/boost/rttbBoostMaskTests.cpp @@ -1,63 +1,64 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ // this file defines the rttbAlgorithmsTests for the test driver // and all it expects is that you have a function called RegisterTests #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #endif #include "litMultiTestsMain.h" namespace rttb { namespace testing { void registerTests() { LIT_REGISTER_TEST(BoostMaskAccessorTest); + LIT_REGISTER_TEST(BoostMaskRedesignTest); } } } int main(int argc, char* argv[]) { int result = 0; rttb::testing::registerTests(); try { result = lit::multiTestsMain(argc, argv); } catch (const std::exception& /*e*/) { result = -1; } catch (...) { result = -1; } return result; } diff --git a/testing/masks/rttbMaskRectStructTester.h b/testing/masks/rttbMaskRectStructTester.h index 60b2ceb..4b11ac3 100644 --- a/testing/masks/rttbMaskRectStructTester.h +++ b/testing/masks/rttbMaskRectStructTester.h @@ -1,102 +1,102 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __MASK_RECT_TESTER_H #define __MASK_RECT_TESTER_H #include #include "litTesterBase.h" #include "litString.h" #include "rttbBaseType.h" #include "../../code/core/rttbMaskAccessorInterface.h" namespace rttb { namespace testing { /*! class MaskRectStructTester @brief Tests if masked voxel are all inside the given boundaries The boundaries are defined in DummyStructure::CreateRectangularStructureCentered @see DummyStructure */ class MaskRectStructTester: public lit::TesterBase { private: typedef core::MaskAccessorInterface::MaskVoxelListPointer MaskVoxelListPointer; typedef core::MaskAccessorInterface::MaskVoxelList MaskVoxelList; typedef core::MaskAccessorInterface::MaskAccessorPointer MaskAccessorPointer; MaskVoxelListPointer _referenceList; MaskAccessorPointer _maskAccessor; GridIndexType _zIndex; mutable bool _conversionFailed; mutable bool _differentSlice; mutable bool _outsideStructure; mutable bool _wrongRVF; mutable VoxelGridID _failedListIndex; - mutable float _wrongVal; + mutable double _wrongVal; public: MaskRectStructTester(MaskAccessorPointer aMaskAccessor, GridIndexType z); /*! Set the mask accessor pointer for the dose comparison. */ void setAccessor(const MaskAccessorPointer aMaskAccessor); /*! Set index of slice containing the dummy structure. */ void setZ(const GridIndexType z); /*! Returns a string that specifies the test the tester currently performs. */ lit::StringType getTestDescription(void) const; lit::StringType getTestName(void) const { return "MaskRectStructTester"; }; protected: /*! performes the test and checks the results. @result Indicates if the test was successfull (true) or if it failed (false) */ bool doCheck(void) const; /*! Function will be called be check() if test was succesfull. */ void handleSuccess(void) const; /*! Function will be called be check() if test was a failure. */ void handleFailure(void) const; }; } } #endif \ No newline at end of file diff --git a/testing/masks/rttbMaskVoxelListTester.cpp b/testing/masks/rttbMaskVoxelListTester.cpp index 6534588..be7533b 100644 --- a/testing/masks/rttbMaskVoxelListTester.cpp +++ b/testing/masks/rttbMaskVoxelListTester.cpp @@ -1,153 +1,150 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision $ (last changed revision) // @date $Date $ (last change date) // @author $Author $ (last changed by) */ #include #include "rttbMaskVoxelListTester.h" namespace rttb { namespace testing { MaskVoxelListTester::MaskVoxelListTester(MaskVoxelListPointer aReferenceList, MaskVoxelListPointer aCompareList) { _referenceList = aReferenceList; _compareList = aCompareList; _sameSize = false; _masVoxelsDiffer = false; _numDifference = 0; } void MaskVoxelListTester::setReferenceList(const MaskVoxelListPointer aReferenceList) { _referenceList = aReferenceList; } void MaskVoxelListTester::setCompareList(const MaskVoxelListPointer aCompareList) { _compareList = aCompareList; } lit::StringType MaskVoxelListTester::getTestDescription(void) const { return "Compare two MaskVoxelLists and determine if the contained content is equal."; }; bool MaskVoxelListTester::doCheck(void) const { _pResults->onTestStart(getCurrentTestLabel()); _masVoxelsDiffer = false; if (_referenceList->size() == _compareList->size()) { _sameSize = true; } else { _sameSize = false; return false; } _numDifference = 0; _maxDifference = 0; MaskVoxelList::iterator iterR, iterC; iterC = _compareList->begin(); VoxelGridID index = 0; for (iterR = _referenceList->begin(); iterR != _referenceList->end(); ++iterR, ++iterC, ++index) { if (iterR->getVoxelGridID() == iterC->getVoxelGridID()) { if (iterR->getRelevantVolumeFraction() == iterC->getRelevantVolumeFraction()) { continue; } else { - float diff = iterR->getRelevantVolumeFraction() - iterC->getRelevantVolumeFraction(); + double diff = iterR->getRelevantVolumeFraction() - iterC->getRelevantVolumeFraction(); if (diff > _maxDifference) { _maxDifference = diff; } - /*if(diff > 0.001){ - std::cout <getVoxelGridID()<< ": ("<< iterR->getRelevantVolumeFraction() << ","<getRelevantVolumeFraction()<<"); "; - }*/ _numDifference++; } } else { _failedListIndex = index; _masVoxelsDiffer = true; return false; } - }//end for(VoxelGridID id = 0; id < _referenceList->getGridSi... + } if (_numDifference > 0) { return false; } return true; } void MaskVoxelListTester::handleSuccess(void) const { std::ostringstream stream; stream << "Both Lists are equal" << std::endl; _pResults->onTestSuccess(getCurrentTestLabel(), stream.str()); } void MaskVoxelListTester::handleFailure(void) const { std::ostringstream stream; stream << "Lists were different" << std::endl; if (_sameSize) { stream << std::endl << "Error of volume fraction voxel count: " << _numDifference << std::endl; stream << std::endl << "Maximum difference in volume fraction: " << _maxDifference << std::endl; if (_masVoxelsDiffer) { stream << std::endl << "Mask points to different grid position in: " << _failedListIndex << std::endl; } } else { stream << "Lists have different size" << std::endl; stream << "Reference List is " << _referenceList->size() << " voxels long and comparison List " << _compareList->size() << std::endl; } _pResults->onTestFailure(getCurrentTestLabel(), stream.str()); } } } \ No newline at end of file diff --git a/testing/masks/rttbMaskVoxelListTester.h b/testing/masks/rttbMaskVoxelListTester.h index efda9e5..b07d312 100644 --- a/testing/masks/rttbMaskVoxelListTester.h +++ b/testing/masks/rttbMaskVoxelListTester.h @@ -1,95 +1,95 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __MASK_VL_TESTER_H #define __MASK_VL_TESTER_H #include #include "litTesterBase.h" #include "litString.h" #include "rttbBaseType.h" #include "../../code/core/rttbMaskAccessorInterface.h" namespace rttb { namespace testing { /*! class MaskVoxelListTester @brief Tester class for lists. Compares two given lists for similarity. Both lists need to have the same length and have equal values in each element to be considered similar. */ class MaskVoxelListTester: public lit::TesterBase { private: typedef core::MaskAccessorInterface::MaskVoxelListPointer MaskVoxelListPointer; typedef core::MaskAccessorInterface::MaskVoxelList MaskVoxelList; MaskVoxelListPointer _referenceList; MaskVoxelListPointer _compareList; - mutable float _maxDifference; - mutable float _numDifference; + mutable double _maxDifference; + mutable double _numDifference; mutable bool _sameSize; mutable bool _masVoxelsDiffer; mutable VoxelGridID _failedListIndex; public: MaskVoxelListTester(MaskVoxelListPointer aReferenceList, MaskVoxelListPointer aCompareList); /*! Set the dose accessor pointer for the dose comparison. */ void setReferenceList(const MaskVoxelListPointer aReferenceList); void setCompareList(const MaskVoxelListPointer aCompareList); /*! Returns a string that specifies the test the tester currently performs. */ lit::StringType getTestDescription(void) const; lit::StringType getTestName(void) const { return "MaskVoxelListTester"; }; protected: /*! performes the test and checks the results. @result Indicates if the test was successfull (true) or if it failed (false) */ bool doCheck(void) const; /*! Function will be called be check() if test was succesfull. */ void handleSuccess(void) const; /*! Function will be called be check() if test was a failure. */ void handleFailure(void) const; }; } } #endif \ No newline at end of file diff --git a/testing/models/rttbScatterTester.h b/testing/models/rttbScatterTester.h index 8dcef69..ba746df 100644 --- a/testing/models/rttbScatterTester.h +++ b/testing/models/rttbScatterTester.h @@ -1,111 +1,111 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __SCATTER_TESTER_H #define __SCATTER_TESTER_H #include "litTesterBase.h" #include "litString.h" #include "rttbBaseType.h" #include "rttbBioModelScatterPlots.h" #include "rttbBioModelCurve.h" namespace rttb { namespace testing { /*! class ScatterTester @brief Tester class for for model scatter plots. Compares a given scatter plot with a given model curve. The values should be similar for similar doses. Variations should only depend on the variance given for the scatter plot calculation. An additional deviation of 1e-4+givenVariance is ignored. */ class ScatterTester: public lit::TesterBase { private: models::CurveDataType _referenceCurve; models::ScatterPlotType _compareScatter; - /*! Additional variance that is allowed in the comparison. This value ususally corresponds to the value used in + /*! Additional variance that is allowed in the comparison. This value usually corresponds to the value used in the generation of the scatter plot. */ models::BioModelParamType _variance; mutable float _maxDifference; mutable int _numDifference; mutable float _meanDifference; - /*! If true allows up tp 5% of the scatter points to deviate without failing. + /*! If true allows up to 5% of the scatter points to deviate without failing. If false, all points have to correspond. */ bool _allowExceptions; public: ScatterTester(models::CurveDataType aReferenceCurve, models::ScatterPlotType aCompareScatter, models::BioModelParamType aVariance = 0); /*! Set the reference curve used in comparison. */ void setReferenceCurve(const models::CurveDataType aReferenceCurve); /*! Set the scatter data used in comparison. */ void setCompareScatter(const models::ScatterPlotType aCompareScatter); /*! Set the variance that is allowed for the scatter plot. Usually this matches the parameter used in the computation of the scattered values. */ void setVariance(const models::BioModelParamType aVariance); /*! If true allows up tp 5% of the scatter points to deviate without failing. If false, all points have to correspond. */ void setAllowExceptions(const bool allow); /*! Returns a string that specifies the test the tester currently performs. */ lit::StringType getTestDescription(void) const; lit::StringType getTestName(void) const { return "ScatterTester"; }; protected: /*! performes the test and checks the results. @result Indicates if the test was successfull (true) or if it failed (false) */ bool doCheck(void) const; /*! Function will be called be check() if test was succesfull. */ void handleSuccess(void) const; /*! Function will be called be check() if test was a failure. */ void handleFailure(void) const; }; } } #endif \ No newline at end of file